背景と要件
このプロジェクトでは、従来の数字を使用する数独の代わりに、動物の画像を用いた視覚的な数独パズルを生成します。具体的には、4つの異なる動物の画像を使って4×4のグリッドを構成し、各動物が各行・各列・各ブロックに一度だけ出現するように配置します。
実装手順
1. グリッド構造の解析
まず、標準的な4×4数独のロジックを理解します。これは以下の条件を満たす必要があります:
- 各行には1〜4の数字(または対応する動物)がそれぞれ一度ずつ現れる
- 各列にも同様の制約が適用される
- 2×2のサブグリッド内でも同じ数字の重複は禁止
2. パズル生成アルゴリズム
import random
import copy
def create_base_grid():
# 4x4の基本グリッドを作成
grid = [[0] * 4 for _ in range(4)]
def fill_grid(row, col):
if row == 4:
return True
next_row = row if col < 3 else row + 1
next_col = (col + 1) % 4
box_row = row // 2
box_col = col // 2
numbers = list(range(1, 5))
random.shuffle(numbers)
for num in numbers:
if (num not in grid[row] and
all(grid[i][col] != num for i in range(4)) and
all(num != grid[i][j] for i in range(box_row*2, box_row*2+2)
for j in range(box_col*2, box_col*2+2))):
grid[row][col] = num
if fill_grid(next_row, next_col):
return True
grid[row][col] = 0
return False
fill_grid(0, 0)
return grid
def generate_puzzle(difficulty_level):
base = create_base_grid()
puzzle = copy.deepcopy(base)
# 空白セルの数を決定(難易度レベルに応じて変化)
blanks_count = int(16 * difficulty_level / 10)
blank_positions = random.sample(range(16), blanks_count)
for pos in blank_positions:
row, col = divmod(pos, 4)
puzzle[row][col] = 0
return puzzle
3. Wordドキュメントへの出力
生成されたパズルをWord形式で出力するために、python-docxライブラリを使用します:
from docx import Document
from docx.shared import Cm
import os
def export_to_word(puzzles, output_path):
doc = Document()
table = doc.add_table(rows=9, cols=9) # 2つのパズルを連結表示
# テーブルの罫線設定
for i in range(9):
for j in range(9):
cell = table.cell(i, j)
cell.width = Cm(1.5)
# パズルデータの挿入
for idx, puzzle in enumerate(puzzles):
start_row = 0 if idx == 0 else 5
start_col = 0 if idx == 0 else 5
for i in range(4):
for j in range(4):
cell_value = str(puzzle[i][j]) if puzzle[i][j] != 0 else ""
table.cell(start_row + i, start_col + j).text = cell_value
doc.save(output_path)
4. 画像置換処理
数値を動物画像に置き換えるために、事前に準備した動物画像ファイルを使用します:
def replace_numbers_with_images(doc_path, image_folder):
doc = Document(doc_path)
animal_images = [os.path.join(image_folder, f) for f in os.listdir(image_folder)]
for table in doc.tables:
for row in table.rows:
for cell in row.cells:
if cell.text in ['1', '2', '3', '4']:
cell.text = ""
paragraph = cell.paragraphs[0]
run = paragraph.add_run()
img_index = int(cell.text) - 1
run.add_picture(animal_images[img_index], width=Cm(1.2))
doc.save(doc_path)
5. PDF変換と結合
最終的に生成されたWordファイルをPDFに変換し、複数のパズルを一つのPDFファイルに結合します:
from docx2pdf import convert
from PyPDF2 import PdfMerger
import glob
def convert_and_merge(word_files, output_pdf):
merger = PdfMerger()
for word_file in word_files:
pdf_file = word_file.replace('.docx', '.pdf')
convert(word_file, pdf_file)
merger.append(pdf_file)
merger.write(output_pdf)
merger.close()
使用例
以下のようにして、指定した難易度レベルのパズルを一括生成できます:
# 難易度レベル5-8のパズルをそれぞれ10セットずつ生成
for level in range(5, 9):
puzzles = [generate_puzzle(level) for _ in range(10)]
export_to_word(puzzles, f"sudoku_level_{level}.docx")