claude codeに数独の問題作るコード考えさせてみた
今回はclaude codeに数独の問題を作るコードを作成させてみました。単純に自分が数独が好きで、なんとなく問題作らせてみたいなと思ってやってみました。
数独とは?
数独とは、一般的には9x9の盤面に数字が配置されており、以下のルールに基づいて数字が配置されています。
- 縦横どちらでみても同じ数字は1度しか出てこない
- 盤面を縦横それぞれ均等に3分割したそれぞれの枠内(3x3)で数字の重複はない
そのような盤面から数字を隠した時に、隠された場所に元々あった数字を当てるというゲームです。みなさん一度は目にしたことがあると思いますが、今回はそれの問題と回答を作らせてみます。
早速やってみる!
instruction.mdの実装
今回は以下のinstruction.mdに指示を書きました(毎度のことながら適当な指示になっています)。
## インストラクション
- 数独の問題を自動生成できること
- 問題はproblem_X.txt形式で、Xの部分には通し番号
- answer_X.txtで回答も合わせて作って
- 難易度をlow,middle,high,extremeの4種類指定できるようにして
- 実装はPythonで
問題はproblem_X.txt
という形で何問も作れるようにしており、answer_X.txt
というファイル名で対応する問題の解答を生成させています。また、low
からextreme
の4段階で問題の難しさを変えれるようにしてみました。
実行結果
それではclaude codeが実際に作った結果をみてみましょう。まずはソースコードです。
ソースコード
#!/usr/bin/env python3
"""
Sudoku puzzle generator with multiple difficulty levels
"""
import random
import copy
import os
from datetime import datetime
from typing import List, Tuple, Optional
class SudokuGenerator:
def __init__(self):
self.size = 9
self.box_size = 3
self.difficulty_settings = {
'low': {'min_clues': 45, 'max_clues': 50},
'middle': {'min_clues': 35, 'max_clues': 44},
'high': {'min_clues': 28, 'max_clues': 34},
'extreme': {'min_clues': 22, 'max_clues': 27}
}
def is_valid(self, board: List[List[int]], row: int, col: int, num: int) -> bool:
"""Check if placing num at board[row][col] is valid"""
# Check row
for x in range(9):
if board[row][x] == num:
return False
# Check column
for x in range(9):
if board[x][col] == num:
return False
# Check 3x3 box
start_row = row - row % 3
start_col = col - col % 3
for i in range(3):
for j in range(3):
if board[i + start_row][j + start_col] == num:
return False
return True
def solve_sudoku(self, board: List[List[int]]) -> bool:
"""Solve sudoku using backtracking"""
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if self.is_valid(board, row, col, num):
board[row][col] = num
if self.solve_sudoku(board):
return True
board[row][col] = 0
return False
return True
def generate_complete_board(self) -> List[List[int]]:
"""Generate a complete valid Sudoku board"""
board = [[0 for _ in range(9)] for _ in range(9)]
# Fill diagonal boxes first (they don't affect each other)
for box in range(0, 9, 3):
nums = list(range(1, 10))
random.shuffle(nums)
idx = 0
for i in range(box, box + 3):
for j in range(box, box + 3):
board[i][j] = nums[idx]
idx += 1
# Solve the rest
self.solve_sudoku(board)
return board
def count_solutions(self, board: List[List[int]], limit: int = 2) -> int:
"""Count the number of solutions (up to limit)"""
count = [0]
def solve_count(board):
if count[0] >= limit:
return
for row in range(9):
for col in range(9):
if board[row][col] == 0:
for num in range(1, 10):
if self.is_valid(board, row, col, num):
board[row][col] = num
solve_count(board)
board[row][col] = 0
return
count[0] += 1
solve_count(copy.deepcopy(board))
return count[0]
def create_puzzle(self, difficulty: str) -> Tuple[List[List[int]], List[List[int]]]:
"""Create a puzzle with given difficulty"""
if difficulty not in self.difficulty_settings:
raise ValueError(f"Invalid difficulty. Choose from: {list(self.difficulty_settings.keys())}")
# Generate complete board
solution = self.generate_complete_board()
puzzle = copy.deepcopy(solution)
# Determine number of clues based on difficulty
settings = self.difficulty_settings[difficulty]
num_clues = random.randint(settings['min_clues'], settings['max_clues'])
cells_to_remove = 81 - num_clues
# Remove cells randomly while ensuring unique solution
cells = [(r, c) for r in range(9) for c in range(9)]
random.shuffle(cells)
removed = 0
attempts = 0
max_attempts = cells_to_remove * 3
for row, col in cells:
if removed >= cells_to_remove or attempts >= max_attempts:
break
backup = puzzle[row][col]
puzzle[row][col] = 0
# For extreme difficulty, we allow some ambiguity
if difficulty == 'extreme' or self.count_solutions(puzzle, 2) == 1:
removed += 1
else:
puzzle[row][col] = backup
attempts += 1
return puzzle, solution
def board_to_string(self, board: List[List[int]]) -> str:
"""Convert board to string representation"""
result = []
for i, row in enumerate(board):
if i % 3 == 0 and i != 0:
result.append("-" * 21)
row_str = ""
for j, num in enumerate(row):
if j % 3 == 0 and j != 0:
row_str += "| "
row_str += str(num) if num != 0 else "."
row_str += " "
result.append(row_str)
return "\n".join(result)
def save_puzzle(self, puzzle: List[List[int]], solution: List[List[int]],
problem_number: int, difficulty: str) -> Tuple[str, str]:
"""Save puzzle and solution to files"""
problem_filename = f"problem_{problem_number}.txt"
answer_filename = f"answer_{problem_number}.txt"
# Save problem
with open(problem_filename, 'w') as f:
f.write(f"Sudoku Puzzle #{problem_number}\n")
f.write(f"Difficulty: {difficulty}\n")
f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 30 + "\n\n")
f.write(self.board_to_string(puzzle))
# Save answer
with open(answer_filename, 'w') as f:
f.write(f"Sudoku Solution #{problem_number}\n")
f.write(f"Difficulty: {difficulty}\n")
f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("=" * 30 + "\n\n")
f.write(self.board_to_string(solution))
return problem_filename, answer_filename
def main():
"""Main function to generate sudoku puzzles"""
import argparse
import json
parser = argparse.ArgumentParser(description='Generate Sudoku puzzles')
parser.add_argument('--difficulty', type=str, choices=['low', 'middle', 'high', 'extreme'],
default='middle', help='Difficulty level')
parser.add_argument('--count', type=int, default=1, help='Number of puzzles to generate')
parser.add_argument('--start-number', type=int, default=1, help='Starting number for puzzle files')
args = parser.parse_args()
# Create log directory
log_dir = './claude/log'
os.makedirs(log_dir, exist_ok=True)
# Setup logging
log_file = os.path.join(log_dir, f"sudoku_generation_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
generator = SudokuGenerator()
log_entries = []
print(f"Generating {args.count} puzzle(s) with difficulty: {args.difficulty}")
for i in range(args.count):
problem_number = args.start_number + i
print(f"Generating puzzle #{problem_number}...", end=" ")
try:
puzzle, solution = generator.create_puzzle(args.difficulty)
problem_file, answer_file = generator.save_puzzle(
puzzle, solution, problem_number, args.difficulty
)
log_entry = {
'timestamp': datetime.now().isoformat(),
'problem_number': problem_number,
'difficulty': args.difficulty,
'problem_file': problem_file,
'answer_file': answer_file,
'status': 'success'
}
log_entries.append(log_entry)
print(f"Done! Saved as {problem_file} and {answer_file}")
except Exception as e:
log_entry = {
'timestamp': datetime.now().isoformat(),
'problem_number': problem_number,
'difficulty': args.difficulty,
'status': 'failed',
'error': str(e)
}
log_entries.append(log_entry)
print(f"Failed! Error: {e}")
# Save log
with open(log_file, 'w') as f:
json.dump({
'generation_session': {
'start_time': log_entries[0]['timestamp'] if log_entries else None,
'end_time': datetime.now().isoformat(),
'total_puzzles': args.count,
'difficulty': args.difficulty,
'entries': log_entries
}
}, f, indent=2)
print(f"\nGeneration log saved to: {log_file}")
if __name__ == "__main__":
main()
SudokuGenerator
クラスに問題を生成するコードが書かれています。まずは完全な正解の盤面を作った後に、空白を作ることで作成しているようですね。なお、コードをみるとfor文をネストしていて処理効率が悪そうなところがあるのでitertools.product
とかを使ってきれいにしたいところですが、今回は生成されたものをそのまま使ってみます。
それでは試しにコードを実行してみましょう。各レベルごとに生成した結果を貼り付けますが、実行する際は以下のように実行します。
python sudoku_generator.py --difficulty <low/middle/high/extreme> --count 生成するパズルの個数 --start-number <採番>
生成された結果は以下のようになりました
- 難易度:low
# 問題
Sudoku Puzzle #1
Difficulty: low
Generated: 2025-09-30 20:19:48
==============================
. 8 9 | 3 . 2 | 5 . .
6 5 . | . . . | 1 2 .
. . 1 | 6 8 5 | . 3 .
---------------------
5 . 7 | 1 3 8 | . . 2
3 6 . | 5 9 . | 8 4 1
9 . 8 | . . 4 | . 5 3
---------------------
. . . | 8 5 3 | 6 1 .
. 7 5 | 9 2 . | . 8 .
. 3 . | 7 4 1 | 2 9 .
# 正解
Sudoku Solution #1
Difficulty: low
Generated: 2025-09-30 20:19:48
==============================
4 8 9 | 3 1 2 | 5 7 6
6 5 3 | 4 7 9 | 1 2 8
7 2 1 | 6 8 5 | 4 3 9
---------------------
5 4 7 | 1 3 8 | 9 6 2
3 6 2 | 5 9 7 | 8 4 1
9 1 8 | 2 6 4 | 7 5 3
---------------------
2 9 4 | 8 5 3 | 6 1 7
1 7 5 | 9 2 6 | 3 8 4
8 3 6 | 7 4 1 | 2 9 5
- 難易度:middle
# 問題
Sudoku Puzzle #2
Difficulty: middle
Generated: 2025-09-30 20:20:06
==============================
. 8 . | . 1 6 | 5 7 4
6 . . | 4 7 . | . . 9
4 . . | . . 9 | 2 . 6
---------------------
2 . . | . 6 . | 9 . .
5 . . | 8 . . | 7 . .
. 6 7 | . . . | 8 . .
---------------------
. . . | 6 . . | 1 3 .
. . 1 | . . . | 6 9 .
. 9 6 | . 5 1 | 4 . 8
# 正解
Sudoku Solution #2
Difficulty: middle
Generated: 2025-09-30 20:20:06
==============================
9 8 2 | 3 1 6 | 5 7 4
6 1 5 | 4 7 2 | 3 8 9
4 7 3 | 5 8 9 | 2 1 6
---------------------
2 4 8 | 1 6 7 | 9 5 3
5 3 9 | 8 2 4 | 7 6 1
1 6 7 | 9 3 5 | 8 4 2
---------------------
7 2 4 | 6 9 8 | 1 3 5
8 5 1 | 2 4 3 | 6 9 7
3 9 6 | 7 5 1 | 4 2 8
- 難易度:high
# 問題
Sudoku Puzzle #3
Difficulty: high
Generated: 2025-09-30 20:21:08
==============================
8 7 1 | 4 2 . | . . .
. . 4 | 1 . . | 2 . .
. . 2 | . . 9 | . . .
---------------------
. . 6 | . 9 . | 8 . .
. . . | . . . | . 9 6
7 . 9 | 2 . 1 | . . .
---------------------
. . . | . . . | . . 5
2 1 3 | 5 4 6 | . . .
. . . | 9 . . | 4 . 3
# 正解
Sudoku Solution #3
Difficulty: high
Generated: 2025-09-30 20:21:08
==============================
8 7 1 | 4 2 3 | 5 6 9
9 6 4 | 1 5 7 | 2 3 8
5 3 2 | 6 8 9 | 7 4 1
---------------------
1 4 6 | 3 9 5 | 8 7 2
3 2 5 | 8 7 4 | 1 9 6
7 8 9 | 2 6 1 | 3 5 4
---------------------
4 9 8 | 7 3 2 | 6 1 5
2 1 3 | 5 4 6 | 9 8 7
6 5 7 | 9 1 8 | 4 2 3
- 難易度:extreme
# 問題
Sudoku Puzzle #4
Difficulty: extreme
Generated: 2025-09-30 20:20:59
==============================
. . . | . . . | . . .
. . . | 7 . . | . 3 .
. . . | 6 9 4 | . . .
---------------------
. . 7 | 5 6 . | . . .
. . 9 | . . 8 | . 2 6
. . . | . . 7 | 5 . 1
---------------------
. 7 1 | 8 5 . | . 4 .
. . 8 | . 7 . | . . .
. 9 . | . . . | 8 . .
# 正解
Sudoku Solution #4
Difficulty: extreme
Generated: 2025-09-30 20:20:59
==============================
7 1 4 | 3 2 5 | 9 6 8
9 6 5 | 7 8 1 | 2 3 4
8 3 2 | 6 9 4 | 1 5 7
---------------------
1 2 7 | 5 6 9 | 4 8 3
3 5 9 | 4 1 8 | 7 2 6
4 8 6 | 2 3 7 | 5 9 1
---------------------
2 7 1 | 8 5 3 | 6 4 9
5 4 8 | 9 7 6 | 3 1 2
6 9 3 | 1 4 2 | 8 7 5
正直実際に解いたわけではないのでhighとextremeが本当に難易度的に違うかとかは多hしかめられていないですが、少なくともlowとextremeはあからさまに難易度が違うことがわかります。middleくらいからあまりさはみる限りは感じないですが、もしかするとやってみると難しいのかもしれません。
まとめ
今回はclaude codeに数独を生成させてみました。今回は問題を作らせただけであり実際に一番の醍醐味である問題を解くところができていないので、次回は問題を回答するプログラムを組んでみようと思います。
Discussion