数独作ってみた　#100日チャレンジに感化されて

#### 数独作ってみた
GW中に判定と難易度設定も追加したい！

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/668168/a0dcda6b-c189-4867-8d2a-167574f40c12.gif)

```python
import pygame
import random
import sys
```

```python
# --- 盤面生成用関数（追加）---
def is_valid(board, num, pos):
    row, col = pos
    if num in board[row]:
        return False
    if num in [board[i][col] for i in range(9)]:
        return False
    start_row, start_col = 3 * (row // 3), 3 * (col // 3)
    for i in range(3):
        for j in range(3):
            if board[start_row + i][start_col + j] == num:
                return False
    return True

def find_empty(board):
    for row in range(9):
        for col in range(9):
            if board[row][col] == 0:
                return (row, col)
    return None

def solve(board):
    empty = find_empty(board)
    if not empty:
        return True
    row, col = empty
    numbers = list(range(1, 10))
    random.shuffle(numbers)
    for num in numbers:
        if is_valid(board, num, (row, col)):
            board[row][col] = num
            if solve(board):
                return True
            board[row][col] = 0
    return False

def generate_full_board():
    board = [[0 for _ in range(9)] for _ in range(9)]
    solve(board)
    return board

def make_puzzle(board, holes=40):
    puzzle = [row[:] for row in board]
    count = 0
    while count < holes:
        row = random.randint(0, 8)
        col = random.randint(0, 8)
        if puzzle[row][col] != 0:
            puzzle[row][col] = 0
            count += 1
    return puzzle

# --- Pygame設定 ---
pygame.init()
WIDTH, HEIGHT = 540, 600
ROWS, COLS = 9, 9
CELL_SIZE = WIDTH // COLS

SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("数独 - Sudoku")

FONT = pygame.font.SysFont("arial", 40)
SMALL_FONT = pygame.font.SysFont("arial", 20)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)
BLUE = (50, 50, 255)

# --- ここでランダムな盤面を作る！ ---
full_board = generate_full_board()
board = make_puzzle(full_board, holes=40)  # 空白40個の盤面
selected = None
user_inputs = [[False for _ in range(COLS)] for _ in range(ROWS)]

def draw_grid():
    for i in range(ROWS + 1):
        line_width = 3 if i % 3 == 0 else 1
        pygame.draw.line(SCREEN, BLACK, (0, i * CELL_SIZE), (WIDTH, i * CELL_SIZE), line_width)
        pygame.draw.line(SCREEN, BLACK, (i * CELL_SIZE, 0), (i * CELL_SIZE, WIDTH), line_width)

def draw_reset_button():
    reset_rect = pygame.Rect(WIDTH // 2 - 50, WIDTH + 20, 100, 40)  # ボタンの四角
    pygame.draw.rect(SCREEN, GRAY, reset_rect)
    text = SMALL_FONT.render("Reset", True, BLACK)
    text_rect = text.get_rect(center=reset_rect.center)
    SCREEN.blit(text, text_rect)
    return reset_rect

def draw_numbers():
    for row in range(ROWS):
        for col in range(COLS):
            num = board[row][col]
            if num != 0:
                color = BLUE if user_inputs[row][col] else BLACK
                text = FONT.render(str(num), True, color)
                SCREEN.blit(text, (col * CELL_SIZE + 20, row * CELL_SIZE + 10))

def draw_selected():
    if selected:
        row, col = selected
        pygame.draw.rect(SCREEN, BLUE, (col * CELL_SIZE, row * CELL_SIZE, CELL_SIZE, CELL_SIZE), 3)

def place_number(key):
    global user_inputs
    if selected:
        row, col = selected
        if board[row][col] == 0:
            board[row][col] = key
            user_inputs[row][col] = True

def get_cell_pos(pos):
    x, y = pos
    if x < WIDTH and y < WIDTH:
        return y // CELL_SIZE, x // CELL_SIZE
    return None

def reset_game():
    global full_board, board, user_inputs
    full_board = generate_full_board()
    board = make_puzzle(full_board, holes=40)
    user_inputs = [[False for _ in range(COLS)] for _ in range(ROWS)]

def main():
    global selected, user_inputs
    clock = pygame.time.Clock()
    running = True
    while running:
        SCREEN.fill(WHITE)
        draw_grid()
        draw_numbers()
        draw_selected()
        reset_button = draw_reset_button()  # ボタンも描く！
        pygame.display.flip()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            if event.type == pygame.MOUSEBUTTONDOWN:
                pos = pygame.mouse.get_pos()
                if reset_button.collidepoint(pos):
                    reset_game()  # リセットボタン押されたらリセット！
                    selected = None  # 選択解除も忘れずに
                else:
                    selected = get_cell_pos(pos)

            if event.type == pygame.KEYDOWN:
                if pygame.K_1 <= event.key <= pygame.K_9:
                    place_number(event.key - pygame.K_0)
                elif event.key == pygame.K_DELETE or event.key == pygame.K_BACKSPACE:
                    if selected:
                        row, col = selected
                        if board[row][col] != 0:
                            board[row][col] = 0
                            user_inputs[row][col] = False

        clock.tick(30)

    pygame.quit()
    sys.exit()
    
if __name__ == "__main__":
    main()

```