Skip to content

Commit 3f52c89

Browse files
Port sudoku-solver from java to python
1 parent 85463bc commit 3f52c89

File tree

7 files changed

+244
-0
lines changed

7 files changed

+244
-0
lines changed

sudoku/sample/sudoku1.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0 0 3 6 0 0
2+
0 2 0 0 0 4
3+
5 0 0 0 6 0
4+
0 3 0 0 0 5
5+
3 0 0 0 1 0
6+
0 0 1 4 0 0

sudoku/sample/sudoku2.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0 0 3 0 1 0
2+
5 6 0 3 2 0
3+
0 5 4 2 0 3
4+
2 0 6 4 5 0
5+
0 1 2 0 4 5
6+
0 4 0 1 0 0

sudoku/sample/sudoku3.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0 0 6 0 0 0
2+
0 0 0 1 0 0
3+
0 0 2 0 4 1
4+
0 5 0 0 6 0
5+
6 4 0 0 0 0
6+
0 0 1 0 0 0

sudoku/sample/sudoku4.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
0 0 3 0 1 0
2+
5 6 0 3 2 0
3+
0 5 4 2 0 3
4+
2 0 6 4 5 0
5+
0 1 2 0 4 5
6+
0 4 0 1 0 0

sudoku/solve.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import math
2+
import timeit
3+
from sys import argv
4+
from typing import List
5+
6+
from typedb.client import TypeDB, TypeDBClient, TypeDBOptions, SessionType, TransactionType
7+
8+
9+
HOST = TypeDB.DEFAULT_ADDRESS
10+
DATABASE_NAME = "sudoku6x6"
11+
QUERY_TEMPLATE = """
12+
match
13+
$connector-hack = -1 isa connector-hack;
14+
{0}
15+
(
16+
{1}
17+
) isa solution;
18+
limit 1;
19+
"""
20+
21+
def format_sudoku(sudoku: List[List[int]]):
22+
return "\n".join(" ".join(map(str, row)) for row in sudoku)
23+
24+
def database_exists(client: TypeDBClient, db_name: str):
25+
return client.databases().contains(db_name)
26+
27+
def setup(client: TypeDBClient, db_name: str):
28+
if database_exists(client, db_name):
29+
client.databases().get(db_name).delete()
30+
client.databases().create(db_name)
31+
32+
with open("sudoku6x6_schema.tql") as f:
33+
schema = f.read()
34+
35+
with client.session(db_name, SessionType.SCHEMA) as session:
36+
with session.transaction(TransactionType.WRITE) as tx:
37+
tx.query().define(schema)
38+
tx.commit()
39+
40+
with open("sudoku6x6_data.tql") as f:
41+
data = f.read()
42+
43+
with client.session(db_name, SessionType.DATA) as session:
44+
with session.transaction(TransactionType.WRITE) as tx:
45+
tx.query().insert(data)
46+
tx.commit()
47+
48+
def solve(client: TypeDBClient, db_name: str, sudoku: List[List[int]]):
49+
# create_query
50+
non_zero = [(i,j,v) for i,row in enumerate(sudoku, 1) for j,v in enumerate(row, 1) if v != 0]
51+
value_assignments = ["$v%d%d = %d isa number; $v%d%d != $connector-hack;"%(i,j,v,i,j) for (i,j,v) in non_zero]
52+
role_players = [ ["pos%d%d: $v%d%d"%(i,j,i,j) for j in range(1,7)] for i in range(1,7) ]
53+
54+
query = QUERY_TEMPLATE.format(
55+
"\n ".join(value_assignments),
56+
",\n ".join(", ".join(rp) for rp in role_players)
57+
)
58+
59+
with client.session(db_name, SessionType.DATA) as session:
60+
with session.transaction(TransactionType.READ, TypeDBOptions().set_infer(True)) as tx:
61+
result = list(tx.query().match(query))
62+
63+
if result:
64+
return [ [result[0].get("v%d%d"%(i,j)).get_value() for j in range(1,7)] for i in range(1,7) ]
65+
else:
66+
return None
67+
68+
69+
70+
def main():
71+
if len(argv) != 2:
72+
print("Usage:")
73+
print("python3 %s setup: Loads required schema & data" % argv[0])
74+
print("python3 %s <sudoku_file>: Reads & solves the sudoku in <sudoku_file>" % argv[0])
75+
76+
client = TypeDB.core_client(HOST)
77+
if argv[1] == "setup":
78+
print("Setting up in database: '%s'..." % DATABASE_NAME)
79+
setup(client, DATABASE_NAME)
80+
return
81+
if not database_exists(client, DATABASE_NAME):
82+
print("Database '%s' does not exist. Setting up..." % DATABASE_NAME)
83+
setup(client, DATABASE_NAME)
84+
85+
with open(argv[1]) as sudoku_file:
86+
sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()]
87+
88+
assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku)
89+
90+
print("Solving:")
91+
print(format_sudoku(sudoku), "\n")
92+
93+
time_start = timeit.default_timer()
94+
solution = solve(client, DATABASE_NAME, sudoku)
95+
time_taken_ms = math.ceil((timeit.default_timer() - time_start) * 1000)
96+
if solution:
97+
print("Found solution in " + str(time_taken_ms) + " ms:")
98+
print(format_sudoku(solution))
99+
else:
100+
print("No solution (took " + str(time_taken_ms) + " ms)")
101+
102+
103+
if __name__=="__main__": main()

sudoku/sudoku6x6_data.tql

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing,
15+
# software distributed under the License is distributed on an
16+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
# KIND, either express or implied. See the License for the
18+
# specific language governing permissions and limitations
19+
# under the License.
20+
#
21+
22+
insert
23+
$connector-hack = -1 isa connector-hack;
24+
$v1 = 1 isa number;
25+
$v2 = 2 isa number;
26+
$v3 = 3 isa number;
27+
$v4 = 4 isa number;
28+
$v5 = 5 isa number;
29+
$v6 = 6 isa number;
30+
(mem: $v1, mem: $v2, mem: $v3, mem: $v4, mem: $v5, mem: $v6) isa permutation;

sudoku/sudoku6x6_schema.tql

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#
2+
# Copyright (C) 2022 Vaticle
3+
#
4+
# Licensed to the Apache Software Foundation (ASF) under one
5+
# or more contributor license agreements. See the NOTICE file
6+
# distributed with this work for additional information
7+
# regarding copyright ownership. The ASF licenses this file
8+
# to you under the Apache License, Version 2.0 (the
9+
# "License"); you may not use this file except in compliance
10+
# with the License. You may obtain a copy of the License at
11+
#
12+
# http://www.apache.org/licenses/LICENSE-2.0
13+
#
14+
# Unless required by applicable law or agreed to in writing,
15+
# software distributed under the License is distributed on an
16+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17+
# KIND, either express or implied. See the License for the
18+
# specific language governing permissions and limitations
19+
# under the License.
20+
#
21+
22+
define
23+
24+
# A temporary hack for the reasoner planner to consider all given values in a single retrievable.
25+
connector-hack sub attribute, value long;
26+
permutation sub relation, relates mem;
27+
permutation sub relation, relates mem;
28+
permutation sub relation, relates mem;
29+
30+
number sub attribute, value long,
31+
plays permutation:mem,
32+
plays solution:pos11, plays solution:pos12, plays solution:pos13, plays solution:pos14, plays solution:pos15, plays solution:pos16,
33+
plays solution:pos21, plays solution:pos22, plays solution:pos23, plays solution:pos24, plays solution:pos25, plays solution:pos26,
34+
plays solution:pos31, plays solution:pos32, plays solution:pos33, plays solution:pos34, plays solution:pos35, plays solution:pos36,
35+
plays solution:pos41, plays solution:pos42, plays solution:pos43, plays solution:pos44, plays solution:pos45, plays solution:pos46,
36+
plays solution:pos51, plays solution:pos52, plays solution:pos53, plays solution:pos54, plays solution:pos55, plays solution:pos56,
37+
plays solution:pos61, plays solution:pos62, plays solution:pos63, plays solution:pos64, plays solution:pos65, plays solution:pos66;
38+
39+
solution sub relation,
40+
relates pos11, relates pos12, relates pos13, relates pos14, relates pos15, relates pos16,
41+
relates pos21, relates pos22, relates pos23, relates pos24, relates pos25, relates pos26,
42+
relates pos31, relates pos32, relates pos33, relates pos34, relates pos35, relates pos36,
43+
relates pos41, relates pos42, relates pos43, relates pos44, relates pos45, relates pos46,
44+
relates pos51, relates pos52, relates pos53, relates pos54, relates pos55, relates pos56,
45+
relates pos61, relates pos62, relates pos63, relates pos64, relates pos65, relates pos66;
46+
47+
48+
# These rules always fail, but makes each of them "concludable" and we hit the the reasoner instead of traversal
49+
rule dummy-triggers-reasoner:
50+
when {
51+
$v = -1 isa number;
52+
} then {
53+
(mem: $v, mem: $v, mem: $v, mem: $v, mem: $v, mem: $v) isa permutation;
54+
};
55+
56+
rule solution-rule:
57+
when {
58+
(mem: $a1, mem: $b1, mem: $c1, mem: $d1, mem: $e1, mem: $f1) isa permutation;
59+
(mem: $a2, mem: $b2, mem: $c2, mem: $d2, mem: $e2, mem: $f2) isa permutation;
60+
(mem: $a3, mem: $b3, mem: $c3, mem: $d3, mem: $e3, mem: $f3) isa permutation;
61+
(mem: $a4, mem: $b4, mem: $c4, mem: $d4, mem: $e4, mem: $f4) isa permutation;
62+
(mem: $a5, mem: $b5, mem: $c5, mem: $d5, mem: $e5, mem: $f5) isa permutation;
63+
(mem: $a6, mem: $b6, mem: $c6, mem: $d6, mem: $e6, mem: $f6) isa permutation;
64+
65+
(mem: $a1, mem: $a2, mem: $a3, mem: $a4, mem: $a5, mem: $a6) isa permutation;
66+
(mem: $b1, mem: $b2, mem: $b3, mem: $b4, mem: $b5, mem: $b6) isa permutation;
67+
(mem: $c1, mem: $c2, mem: $c3, mem: $c4, mem: $c5, mem: $c6) isa permutation;
68+
(mem: $d1, mem: $d2, mem: $d3, mem: $d4, mem: $d5, mem: $d6) isa permutation;
69+
(mem: $e1, mem: $e2, mem: $e3, mem: $e4, mem: $e5, mem: $e6) isa permutation;
70+
(mem: $f1, mem: $f2, mem: $f3, mem: $f4, mem: $f5, mem: $f6) isa permutation;
71+
72+
(mem: $a1, mem: $b1, mem: $c1, mem: $a2, mem: $b2, mem: $c2) isa permutation;
73+
(mem: $a3, mem: $b3, mem: $c3, mem: $a4, mem: $b4, mem: $c4) isa permutation;
74+
(mem: $a5, mem: $b5, mem: $c5, mem: $a6, mem: $b6, mem: $c6) isa permutation;
75+
(mem: $d1, mem: $e1, mem: $f1, mem: $d2, mem: $e2, mem: $f2) isa permutation;
76+
(mem: $d3, mem: $e3, mem: $f3, mem: $d4, mem: $e4, mem: $f4) isa permutation;
77+
(mem: $d5, mem: $e5, mem: $f5, mem: $d6, mem: $e6, mem: $f6) isa permutation;
78+
} then {
79+
(
80+
pos11: $a1, pos12: $b1, pos13: $c1, pos14: $d1, pos15: $e1, pos16: $f1,
81+
pos21: $a2, pos22: $b2, pos23: $c2, pos24: $d2, pos25: $e2, pos26: $f2,
82+
pos31: $a3, pos32: $b3, pos33: $c3, pos34: $d3, pos35: $e3, pos36: $f3,
83+
pos41: $a4, pos42: $b4, pos43: $c4, pos44: $d4, pos45: $e4, pos46: $f4,
84+
pos51: $a5, pos52: $b5, pos53: $c5, pos54: $d5, pos55: $e5, pos56: $f5,
85+
pos61: $a6, pos62: $b6, pos63: $c6, pos64: $d6, pos65: $e6, pos66: $f6
86+
) isa solution;
87+
};

0 commit comments

Comments
 (0)