数独ソルバー作ってみた

数独ソルバーを作成してみました
いくつか機能追加したかったり、終了後に再度起動できるようにしたかったりするんですがいったん完成で。

```
from matplotlib.pyplot import box
import pygame
from pygame.locals import *
import sys
import copy


class Sudoku():
    def __init__(self):
        # pygame初期化
        pygame.init()
        # キャンバス作成
        self.SURFACE = pygame.display.set_mode((450, 550))    
        # タイトル作成
        pygame.display.set_caption("GAMEをつくろう")
        # フォント指定
        self.font = pygame.font.Font(None, 40)
        self.box = [0]*81
        self.buttons = []
        self.wait_input = False
        self.pos = -1
        self.can_input = True
        self.button_text = "START"
        self.end_flag = False
        self.clear_flag = False
        for i in range(9):
            for j in range(9):
                self.buttons.append(pygame.Rect(j*50, i*50, 49, 49))
        self.box_checker = [
            [0, 1, 2, 9, 10, 11, 18, 19, 20],
            [3, 4, 5, 12, 13, 14, 21, 22, 23],
            [6, 7, 8, 15, 16, 17, 24, 25, 26],
            [27, 28, 29, 36, 37, 38, 45, 46, 47],
            [30, 31, 32, 39, 40, 41, 48, 49, 50],
            [33, 34, 35, 42, 43, 44, 51, 52, 53],
            [54, 55, 56, 63, 64, 65, 72, 73, 74],
            [57, 58, 59, 66, 67, 68, 75, 76, 77],
            [60, 61, 62, 69, 70, 71, 78, 79, 80],
        ]

    def __draw(self):
        """キャンバス内の描画を行う
        """
        for i in range(9):
            for j in range(9):
                box_color = (128, 128, 128) if self.box[i+j*9] else (255, 255, 255)
                pygame.draw.rect(self.SURFACE, box_color, self.buttons[i+j*9])
        for i in range(9):
            for j in range(9):
                if self.box[i+j*9]:
                    self.SURFACE.blit(self.font.render(str(self.box[i+j*9]), True, (0, 0, 0)), (15+i*50, 15+j*50))
        pygame.draw.rect(self.SURFACE, (128, 128, 128), pygame.Rect(0, 450, 450, 100))
        self.SURFACE.blit(self.font.render(self.button_text, True, (255, 255, 255)), (180, 500))
        pygame.display.update() 

    def __get_input(self, event):
        """キーボード入力値を取得し、対象座標の値を更新する
        0-9 -> 入力値に更新
        それ以外 -> 0に更新(0になっている座標がソルバーの対象となる)
        """
        if pygame.key.name(event.key).isdigit():
            self.box[self.pos] = int(pygame.key.name(event.key))
        else:
            self.box[self.pos] = 0
    
    def __solver(self):
        """ソルバー本体
        """
        while True:
            self.__get_event()
            # 入力不可なら終了
            if self.can_input:
                return False
            # 終了フラグが立っていたら終了
            elif self.end_flag:
                if self.clear_flag:
                    return True
                else:
                    return False
            else:
                self.tmp_box = []
                for i in self.box:
                    if i:
                        self.tmp_box.append(i)
                    else:
                        self.tmp_box.append(0)
                dfs_box = copy.deepcopy(self.tmp_box)
                self.__dfs(dfs_box, 0)

    def __display(self, box):
        for i in range(9):
            for j in range(9):
                box_color = (128, 128, 128) if self.box[i+j*9] else (255, 255, 255)
                pygame.draw.rect(self.SURFACE, box_color, self.buttons[i+j*9])
        for i in range(9):
            for j in range(9):
                if box[i+j*9]:
                    self.SURFACE.blit(self.font.render(str(box[i+j*9]), True, (0, 0, 0)), (15+i*50, 15+j*50))
        pygame.display.update()

    def __dfs(self, box, pos):
        for i in range(len(self.tmp_box)):
            if i > pos:
                box[i] = self.tmp_box[i]
            
        if box.count(0) == 0:
            self.end_flag = True
            self.box = copy.deepcopy(box)            
            return
        if box[pos] != 0:
            self.__dfs(box, pos+1)
        else:
            checker = list(range(1,10))
            for i in range(9):
                # 縦軸チェック
                if box[pos%9 + i*9] in checker:
                    checker.remove(box[pos%9 + i*9])
                # 横軸チェック
                if box[pos//9 * 9 + i] in checker:
                    checker.remove(box[pos//9 * 9 + i])
            # 箱の中チェック
            for i in self.box_checker:
                if pos in i:
                    for j in i:
                        if box[j] in checker:
                            checker.remove(box[j])
            if len(checker) != 0:
                for i in checker:
                    box[pos] = i
                    print(box)
                    self.__display(box)
                    self.__dfs(box, pos+1)
                    if box.count(0) != 0:
                        box[pos] = 0
                    else:
                        return
            else:
                box[pos] = 0

    def __get_event(self):
        """イベント処理
        """
        for event in pygame.event.get():
            if event.type == QUIT:  # 閉じるボタンが押されたら終了
                # Pygameの終了（画面を閉じる）
                pygame.quit()
                # プログラムの終了
                sys.exit()
            if self.can_input:
                # 入力可かつ枠内がクリックされた場合
                if event.type == pygame.MOUSEBUTTONDOWN:
                    # 数字入力領域がクリックされた場合、入力待ちとなる
                    if event.pos[0] <= 450 and event.pos[1] <= 450:
                        self.pos = event.pos[0]//50 + event.pos[1]//50 * 9
                        self.wait_input = True
                    # スタートボタンがクリックされた
                    else:
                        self.can_input = False
                        self.button_text = "STOP"
                        self.__draw()
                # 入力待ち状態の場合、キーボードからの入力を取得
                if event.type == KEYDOWN and self.wait_input:
                    self.__get_input(event)
                    self.pos = -1
                    self.wait_input = False
            else:
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if event.pos[1] > 450:
                        self.can_input = True
                        self.button_text = "START"
                        self.__draw()

    # メイン関数
    def main(self):
        self.__draw()
        pygame.display.update()            # 画面更新
        # 表示更新ループ
        while True:
            self.__get_event()
            if not self.can_input:
                self.__solver()
            self.__draw()
            pygame.display.update()            # 画面更新
            if self.end_flag:
                continue

if __name__ == '__main__':
    Sudoku().main()

```