田中太郎
Pythonを使って、ナンプレ(数独)を解くプログラムを作るよ!
Pythonを使ってナンプレを解く
入力
エクセルファイルに数字を入力する
出力
ナンプレの回答が標準出力で出ます。
コード
"""Sudoku."""
import copy
import pandas as pd
def initialize(data):
"""Initialize."""
print("Set initial value")
sel_values = []
for line in range(1,10):
for column in range(1,10):
sel_value = {
"line": line,
"column": column,
"group": 0,
"num_en": {
i: "en" for i in range(1, 10)
},
"num": data[line-1][column-1],
}
# If num exists in the sel, num_en becomes "dis"
if sel_value["num"] != 0:
for i in range(1, 10):
sel_value["num_en"][i] = "dis"
# Set group
# The definition of goup number is the followings
# 1 2 3
# 4 5 6
# 7 8 9
for i in range(0,3):
if (1 <= line <= 3 and 1 + i*3 <= column <= 3 + i*3):
sel_value["group"] = 1 + i
if (4 <= line <= 6 and 1 + i*3 <= column <= 3 + i*3):
sel_value["group"] = 4 + i
if (7 <= line <= 9 and 1 + i*3 <= column <= 3 + i*3):
sel_value["group"] = 7 + i
sel_values.append(copy.deepcopy(sel_value))
return sel_values
def update_num_en(sel_values):
"""Search and update num_en.
Input: sel_values
Output: sel_values"""
# Search the position of updating num_en
disables = []
for sel_value in sel_values:
if (sel_value["num"] != 0):
disable = {
"num": sel_value["num"],
"line": sel_value["line"],
"column": sel_value["column"],
"group": sel_value["group"],
}
disables.append(copy.copy(disable))
# Update num_en using above disables
for sel_value in sel_values:
if sel_value["num"] != 0:
continue
for disable in disables:
if (disable["line"] == sel_value["line"]
or disable["column"] == sel_value["column"]
or disable["group"] == sel_value["group"]):
sel_value["num_en"][disable["num"]] = "dis"
return sel_values
def update_num(sel_values):
"""Update num using num_en in sel_values
Input: sel_values
Output: sel_values
"""
for sel_value in sel_values:
# Initialize
check_flag = 0
put_val = 0
# Search the value of putting enable
for num, status in sel_value["num_en"].items():
if status == "en":
check_flag += 1
put_val = num
if check_flag != 1:
continue
sel_value["num"] = put_val
for sel_value in sel_values:
if sel_value["num"] == 0:
continue
for i in range(1, 10):
sel_value["num_en"][i] = "dis"
return sel_values
def fill_by_category_info(sel_values, category):
"""."""
# Fill num to the field
# Count num_en in same category by num_counter.
# If num_counter is 1, this positino will be filled.
for category_num in range(1,10):
num_counter = {k: 0 for k in range(1,10)}
for sel_value in sel_values:
if sel_value[category] != category_num:
continue
for k, v in sel_value["num_en"].items():
if v == "en":
num_counter[k] += 1
for put_num in range(1, 10):
if num_counter[put_num] != 1:
continue
for sel_value in sel_values:
# same category
if sel_value[category] != category_num:
continue
# The category_num of the category has "en"
if sel_value["num_en"][put_num] != "en":
continue
sel_value["num"] = put_num
return sel_values
def output_sel_value(sel_values):
"""Output_sel_value."""
for i in range(0,81):
print(int(sel_values[i]["num"]), end="")
if i%9 == 8:
print("\n")
def calculate_sudoku(sel_values, iteration_num):
"""Calculate sudoku.
Input: sel_values, iteration
sel_values(list of dict):T.B.D.
iteration(int): iteration number
Output: sel_values
sel_values(list of dict): """
for iteration in range(0, iteration_num):
# Update num_en
sel_values = update_num_en(sel_values)
# Updage num in sel_values
sel_values = update_num(sel_values)
# Update num_en
sel_values = update_num_en(sel_values)
# Fill lines
sel_values = fill_by_category_info(sel_values, "line")
# Fill_columns
sel_values = fill_by_category_info(sel_values, "column")
# Fill boxes
sel_values = fill_by_category_info(sel_values, "group")
return sel_values
def main():
"""Main."""
# Read excel file
print("Read excel file")
df = pd.read_excel("input.xlsx",header=None)
df = df.fillna(0).T
data = list(df.to_dict(orient="list").values())
# Set initial value
# sel_values is list of dict
# dict = {line, column, group, num_en, num}
sel_values = initialize(data)
# Print field num
print("input data")
output_sel_value(sel_values)
# Calculate sudoku
sel_values = calculate_sudoku(sel_values, 100)
# Print field num
print("ouput data")
output_sel_value(sel_values)
if "__main__" == __name__:
main()
print("Success!")
解説
mainの中身だけ説明します。残りは暇なときに追加、修正します。
main
def main():
"""Main."""
# Read excel file
print("Read excel file")
df = pd.read_excel("input.xlsx",header=None)
df = df.fillna(0).T
data = list(df.to_dict(orient="list").values())
pandasのread_excelでエクセルファイルを読み込んで、Pythonのリスト型に変換します。色々ごにょごにょしていますが、最終的にdataにリストのリスト型でデータが入っています。dataの中身は以下になります。
data = [
[1行1列の数字,1行2列の数字… ]
[2行1列の数字,2行2列の数字…]
…
[9行1列の数字,9行2列の数字…]
]
# Set initial value
# sel_values is list of dict
# dict = {line, column, group, num_en, num}
sel_values = initialize(data)
エクセルファイルから読み込んで作ったdataから、ナンプレを解くための型、sel_valuesを作成します。sel_valuesは辞書のリストの形になっています。辞書の中身は
line: 行番号
column: 列番号
group: グループ番号
num_en: 何の数字が入る可能性があるか
num: 何の数字が入っているか
となっています。
# Print field num
print("input data")
output_sel_value(sel_values)
上記は、sel_valuesに入っている値を出力しているだけです
# Calculate sudoku
sel_values = calculate_sudoku(sel_values, 100)
上記は、実際にナンプレの空白部分を埋めていきます。
# Print field num
print("ouput data")
output_sel_value(sel_values)
計算結果を出力します。
コメント