動物数独生成ツール:4×4グリッドのパズル問題自動作成

背景と要件

このプロジェクトでは、従来の数字を使用する数独の代わりに、動物の画像を用いた視覚的な数独パズルを生成します。具体的には、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")

タグ: Python sudoku-generator docx pdf-conversion image-processing

6月30日 16:50 投稿