From 3f52c89c73c09583d06ef6ccbe9fde618ad60989 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 15 Feb 2023 17:01:21 +0000 Subject: [PATCH 01/16] Port sudoku-solver from java to python --- sudoku/sample/sudoku1.txt | 6 +++ sudoku/sample/sudoku2.txt | 6 +++ sudoku/sample/sudoku3.txt | 6 +++ sudoku/sample/sudoku4.txt | 6 +++ sudoku/solve.py | 103 ++++++++++++++++++++++++++++++++++++ sudoku/sudoku6x6_data.tql | 30 +++++++++++ sudoku/sudoku6x6_schema.tql | 87 ++++++++++++++++++++++++++++++ 7 files changed, 244 insertions(+) create mode 100644 sudoku/sample/sudoku1.txt create mode 100644 sudoku/sample/sudoku2.txt create mode 100644 sudoku/sample/sudoku3.txt create mode 100644 sudoku/sample/sudoku4.txt create mode 100644 sudoku/solve.py create mode 100644 sudoku/sudoku6x6_data.tql create mode 100644 sudoku/sudoku6x6_schema.tql diff --git a/sudoku/sample/sudoku1.txt b/sudoku/sample/sudoku1.txt new file mode 100644 index 00000000..f724a10e --- /dev/null +++ b/sudoku/sample/sudoku1.txt @@ -0,0 +1,6 @@ +0 0 3 6 0 0 +0 2 0 0 0 4 +5 0 0 0 6 0 +0 3 0 0 0 5 +3 0 0 0 1 0 +0 0 1 4 0 0 \ No newline at end of file diff --git a/sudoku/sample/sudoku2.txt b/sudoku/sample/sudoku2.txt new file mode 100644 index 00000000..2b804e5a --- /dev/null +++ b/sudoku/sample/sudoku2.txt @@ -0,0 +1,6 @@ +0 0 3 0 1 0 +5 6 0 3 2 0 +0 5 4 2 0 3 +2 0 6 4 5 0 +0 1 2 0 4 5 +0 4 0 1 0 0 \ No newline at end of file diff --git a/sudoku/sample/sudoku3.txt b/sudoku/sample/sudoku3.txt new file mode 100644 index 00000000..46b546fe --- /dev/null +++ b/sudoku/sample/sudoku3.txt @@ -0,0 +1,6 @@ +0 0 6 0 0 0 +0 0 0 1 0 0 +0 0 2 0 4 1 +0 5 0 0 6 0 +6 4 0 0 0 0 +0 0 1 0 0 0 \ No newline at end of file diff --git a/sudoku/sample/sudoku4.txt b/sudoku/sample/sudoku4.txt new file mode 100644 index 00000000..2b804e5a --- /dev/null +++ b/sudoku/sample/sudoku4.txt @@ -0,0 +1,6 @@ +0 0 3 0 1 0 +5 6 0 3 2 0 +0 5 4 2 0 3 +2 0 6 4 5 0 +0 1 2 0 4 5 +0 4 0 1 0 0 \ No newline at end of file diff --git a/sudoku/solve.py b/sudoku/solve.py new file mode 100644 index 00000000..d7c2a616 --- /dev/null +++ b/sudoku/solve.py @@ -0,0 +1,103 @@ +import math +import timeit +from sys import argv +from typing import List + +from typedb.client import TypeDB, TypeDBClient, TypeDBOptions, SessionType, TransactionType + + +HOST = TypeDB.DEFAULT_ADDRESS +DATABASE_NAME = "sudoku6x6" +QUERY_TEMPLATE = """ +match + $connector-hack = -1 isa connector-hack; + {0} + ( + {1} + ) isa solution; +limit 1; + """ + +def format_sudoku(sudoku: List[List[int]]): + return "\n".join(" ".join(map(str, row)) for row in sudoku) + +def database_exists(client: TypeDBClient, db_name: str): + return client.databases().contains(db_name) + +def setup(client: TypeDBClient, db_name: str): + if database_exists(client, db_name): + client.databases().get(db_name).delete() + client.databases().create(db_name) + + with open("sudoku6x6_schema.tql") as f: + schema = f.read() + + with client.session(db_name, SessionType.SCHEMA) as session: + with session.transaction(TransactionType.WRITE) as tx: + tx.query().define(schema) + tx.commit() + + with open("sudoku6x6_data.tql") as f: + data = f.read() + + with client.session(db_name, SessionType.DATA) as session: + with session.transaction(TransactionType.WRITE) as tx: + tx.query().insert(data) + tx.commit() + +def solve(client: TypeDBClient, db_name: str, sudoku: List[List[int]]): + # create_query + non_zero = [(i,j,v) for i,row in enumerate(sudoku, 1) for j,v in enumerate(row, 1) if v != 0] + 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] + role_players = [ ["pos%d%d: $v%d%d"%(i,j,i,j) for j in range(1,7)] for i in range(1,7) ] + + query = QUERY_TEMPLATE.format( + "\n ".join(value_assignments), + ",\n ".join(", ".join(rp) for rp in role_players) + ) + + with client.session(db_name, SessionType.DATA) as session: + with session.transaction(TransactionType.READ, TypeDBOptions().set_infer(True)) as tx: + result = list(tx.query().match(query)) + + if result: + return [ [result[0].get("v%d%d"%(i,j)).get_value() for j in range(1,7)] for i in range(1,7) ] + else: + return None + + + +def main(): + if len(argv) != 2: + print("Usage:") + print("python3 %s setup: Loads required schema & data" % argv[0]) + print("python3 %s : Reads & solves the sudoku in " % argv[0]) + + client = TypeDB.core_client(HOST) + if argv[1] == "setup": + print("Setting up in database: '%s'..." % DATABASE_NAME) + setup(client, DATABASE_NAME) + return + if not database_exists(client, DATABASE_NAME): + print("Database '%s' does not exist. Setting up..." % DATABASE_NAME) + setup(client, DATABASE_NAME) + + with open(argv[1]) as sudoku_file: + sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()] + + assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku) + + print("Solving:") + print(format_sudoku(sudoku), "\n") + + time_start = timeit.default_timer() + solution = solve(client, DATABASE_NAME, sudoku) + time_taken_ms = math.ceil((timeit.default_timer() - time_start) * 1000) + if solution: + print("Found solution in " + str(time_taken_ms) + " ms:") + print(format_sudoku(solution)) + else: + print("No solution (took " + str(time_taken_ms) + " ms)") + + +if __name__=="__main__": main() diff --git a/sudoku/sudoku6x6_data.tql b/sudoku/sudoku6x6_data.tql new file mode 100644 index 00000000..24ebbb3c --- /dev/null +++ b/sudoku/sudoku6x6_data.tql @@ -0,0 +1,30 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +insert +$connector-hack = -1 isa connector-hack; +$v1 = 1 isa number; +$v2 = 2 isa number; +$v3 = 3 isa number; +$v4 = 4 isa number; +$v5 = 5 isa number; +$v6 = 6 isa number; +(mem: $v1, mem: $v2, mem: $v3, mem: $v4, mem: $v5, mem: $v6) isa permutation; diff --git a/sudoku/sudoku6x6_schema.tql b/sudoku/sudoku6x6_schema.tql new file mode 100644 index 00000000..02e129c2 --- /dev/null +++ b/sudoku/sudoku6x6_schema.tql @@ -0,0 +1,87 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +define + +# A temporary hack for the reasoner planner to consider all given values in a single retrievable. +connector-hack sub attribute, value long; +permutation sub relation, relates mem; +permutation sub relation, relates mem; +permutation sub relation, relates mem; + +number sub attribute, value long, + plays permutation:mem, + plays solution:pos11, plays solution:pos12, plays solution:pos13, plays solution:pos14, plays solution:pos15, plays solution:pos16, + plays solution:pos21, plays solution:pos22, plays solution:pos23, plays solution:pos24, plays solution:pos25, plays solution:pos26, + plays solution:pos31, plays solution:pos32, plays solution:pos33, plays solution:pos34, plays solution:pos35, plays solution:pos36, + plays solution:pos41, plays solution:pos42, plays solution:pos43, plays solution:pos44, plays solution:pos45, plays solution:pos46, + plays solution:pos51, plays solution:pos52, plays solution:pos53, plays solution:pos54, plays solution:pos55, plays solution:pos56, + plays solution:pos61, plays solution:pos62, plays solution:pos63, plays solution:pos64, plays solution:pos65, plays solution:pos66; + +solution sub relation, + relates pos11, relates pos12, relates pos13, relates pos14, relates pos15, relates pos16, + relates pos21, relates pos22, relates pos23, relates pos24, relates pos25, relates pos26, + relates pos31, relates pos32, relates pos33, relates pos34, relates pos35, relates pos36, + relates pos41, relates pos42, relates pos43, relates pos44, relates pos45, relates pos46, + relates pos51, relates pos52, relates pos53, relates pos54, relates pos55, relates pos56, + relates pos61, relates pos62, relates pos63, relates pos64, relates pos65, relates pos66; + + +# These rules always fail, but makes each of them "concludable" and we hit the the reasoner instead of traversal +rule dummy-triggers-reasoner: +when { + $v = -1 isa number; +} then { + (mem: $v, mem: $v, mem: $v, mem: $v, mem: $v, mem: $v) isa permutation; +}; + +rule solution-rule: +when { + (mem: $a1, mem: $b1, mem: $c1, mem: $d1, mem: $e1, mem: $f1) isa permutation; + (mem: $a2, mem: $b2, mem: $c2, mem: $d2, mem: $e2, mem: $f2) isa permutation; + (mem: $a3, mem: $b3, mem: $c3, mem: $d3, mem: $e3, mem: $f3) isa permutation; + (mem: $a4, mem: $b4, mem: $c4, mem: $d4, mem: $e4, mem: $f4) isa permutation; + (mem: $a5, mem: $b5, mem: $c5, mem: $d5, mem: $e5, mem: $f5) isa permutation; + (mem: $a6, mem: $b6, mem: $c6, mem: $d6, mem: $e6, mem: $f6) isa permutation; + + (mem: $a1, mem: $a2, mem: $a3, mem: $a4, mem: $a5, mem: $a6) isa permutation; + (mem: $b1, mem: $b2, mem: $b3, mem: $b4, mem: $b5, mem: $b6) isa permutation; + (mem: $c1, mem: $c2, mem: $c3, mem: $c4, mem: $c5, mem: $c6) isa permutation; + (mem: $d1, mem: $d2, mem: $d3, mem: $d4, mem: $d5, mem: $d6) isa permutation; + (mem: $e1, mem: $e2, mem: $e3, mem: $e4, mem: $e5, mem: $e6) isa permutation; + (mem: $f1, mem: $f2, mem: $f3, mem: $f4, mem: $f5, mem: $f6) isa permutation; + + (mem: $a1, mem: $b1, mem: $c1, mem: $a2, mem: $b2, mem: $c2) isa permutation; + (mem: $a3, mem: $b3, mem: $c3, mem: $a4, mem: $b4, mem: $c4) isa permutation; + (mem: $a5, mem: $b5, mem: $c5, mem: $a6, mem: $b6, mem: $c6) isa permutation; + (mem: $d1, mem: $e1, mem: $f1, mem: $d2, mem: $e2, mem: $f2) isa permutation; + (mem: $d3, mem: $e3, mem: $f3, mem: $d4, mem: $e4, mem: $f4) isa permutation; + (mem: $d5, mem: $e5, mem: $f5, mem: $d6, mem: $e6, mem: $f6) isa permutation; +} then { + ( + pos11: $a1, pos12: $b1, pos13: $c1, pos14: $d1, pos15: $e1, pos16: $f1, + pos21: $a2, pos22: $b2, pos23: $c2, pos24: $d2, pos25: $e2, pos26: $f2, + pos31: $a3, pos32: $b3, pos33: $c3, pos34: $d3, pos35: $e3, pos36: $f3, + pos41: $a4, pos42: $b4, pos43: $c4, pos44: $d4, pos45: $e4, pos46: $f4, + pos51: $a5, pos52: $b5, pos53: $c5, pos54: $d5, pos55: $e5, pos56: $f5, + pos61: $a6, pos62: $b6, pos63: $c6, pos64: $d6, pos65: $e6, pos66: $f6 + ) isa solution; +}; \ No newline at end of file From b9de4387c783bcc672e8fddca14c7ef9c5560a20 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 15 Feb 2023 18:52:57 +0000 Subject: [PATCH 02/16] Move stuff to bazel. Seems to work --- sudoku/BUILD | 79 +++++++++++++++ sudoku/requirements.txt | 1 + sudoku/sample/solution1.txt | 6 ++ sudoku/sample/solution2.txt | 6 ++ sudoku/sample/solution3.txt | 6 ++ sudoku/sample/solution4.txt | 6 ++ sudoku/solve.py | 186 ++++++++++++++++++++++-------------- sudoku/test.py | 58 +++++++++++ 8 files changed, 274 insertions(+), 74 deletions(-) create mode 100644 sudoku/BUILD create mode 100644 sudoku/requirements.txt create mode 100644 sudoku/sample/solution1.txt create mode 100644 sudoku/sample/solution2.txt create mode 100644 sudoku/sample/solution3.txt create mode 100644 sudoku/sample/solution4.txt create mode 100644 sudoku/test.py diff --git a/sudoku/BUILD b/sudoku/BUILD new file mode 100644 index 00000000..cf8035c2 --- /dev/null +++ b/sudoku/BUILD @@ -0,0 +1,79 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +exports_files(["requirements.txt"]) +load("@rules_python//python:python.bzl", "py_binary", "py_test") + + +py_library( + name = "solve", + srcs = ["solve.py"], + deps = [ + "@vaticle_typedb_client_python//:client_python", + ] +) + +py_binary( + name = "solver", + srcs = ["solve.py"], + deps = [ + "solve", + "@vaticle_typedb_client_python//:client_python" + ], + data = [ + "sudoku6x6_schema.tql", + "sudoku6x6_data.tql", + + "sample/sudoku1.txt", + "sample/sudoku2.txt", + "sample/sudoku3.txt", + "sample/sudoku4.txt", + ], +) + +py_test( + name = "test", + srcs = ["test.py"], + deps = [ + "solve", + "@vaticle_typedb_client_python//:client_python" + ], + data = [ + "sudoku6x6_schema.tql", + "sudoku6x6_data.tql", + + "sample/sudoku1.txt", + "sample/sudoku2.txt", + "sample/sudoku3.txt", + "sample/sudoku4.txt", + "sample/solution1.txt", + "sample/solution2.txt", + "sample/solution3.txt", + "sample/solution4.txt", + ] +) +load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + exclude = glob(["*.txt", "*.md", "sample/*"]), + license_type = "apache-header", + size = "small", +) diff --git a/sudoku/requirements.txt b/sudoku/requirements.txt new file mode 100644 index 00000000..e95244b2 --- /dev/null +++ b/sudoku/requirements.txt @@ -0,0 +1 @@ +typedb-client \ No newline at end of file diff --git a/sudoku/sample/solution1.txt b/sudoku/sample/solution1.txt new file mode 100644 index 00000000..2aad1d06 --- /dev/null +++ b/sudoku/sample/solution1.txt @@ -0,0 +1,6 @@ +4 5 3 6 2 1 +1 2 6 5 3 4 +5 1 4 3 6 2 +6 3 2 1 4 5 +3 4 5 2 1 6 +2 6 1 4 5 3 \ No newline at end of file diff --git a/sudoku/sample/solution2.txt b/sudoku/sample/solution2.txt new file mode 100644 index 00000000..79798a87 --- /dev/null +++ b/sudoku/sample/solution2.txt @@ -0,0 +1,6 @@ +4 2 3 5 1 6 +5 6 1 3 2 4 +1 5 4 2 6 3 +2 3 6 4 5 1 +3 1 2 6 4 5 +6 4 5 1 3 2 diff --git a/sudoku/sample/solution3.txt b/sudoku/sample/solution3.txt new file mode 100644 index 00000000..586353cb --- /dev/null +++ b/sudoku/sample/solution3.txt @@ -0,0 +1,6 @@ +2 1 6 4 5 3 +4 3 5 1 2 6 +3 6 2 5 4 1 +1 5 4 3 6 2 +6 4 3 2 1 5 +5 2 1 6 3 4 \ No newline at end of file diff --git a/sudoku/sample/solution4.txt b/sudoku/sample/solution4.txt new file mode 100644 index 00000000..1926bdd0 --- /dev/null +++ b/sudoku/sample/solution4.txt @@ -0,0 +1,6 @@ +4 2 3 5 1 6 +5 6 1 3 2 4 +1 5 4 2 6 3 +2 3 6 4 5 1 +3 1 2 6 4 5 +6 4 5 1 3 2 \ No newline at end of file diff --git a/sudoku/solve.py b/sudoku/solve.py index d7c2a616..5775c68d 100644 --- a/sudoku/solve.py +++ b/sudoku/solve.py @@ -1,70 +1,112 @@ -import math +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + import timeit from sys import argv from typing import List -from typedb.client import TypeDB, TypeDBClient, TypeDBOptions, SessionType, TransactionType - +from typedb.client import TypeDB, TypeDBOptions, SessionType, TransactionType HOST = TypeDB.DEFAULT_ADDRESS DATABASE_NAME = "sudoku6x6" -QUERY_TEMPLATE = """ -match - $connector-hack = -1 isa connector-hack; - {0} - ( - {1} - ) isa solution; -limit 1; - """ - -def format_sudoku(sudoku: List[List[int]]): - return "\n".join(" ".join(map(str, row)) for row in sudoku) - -def database_exists(client: TypeDBClient, db_name: str): - return client.databases().contains(db_name) - -def setup(client: TypeDBClient, db_name: str): - if database_exists(client, db_name): - client.databases().get(db_name).delete() - client.databases().create(db_name) - - with open("sudoku6x6_schema.tql") as f: - schema = f.read() - - with client.session(db_name, SessionType.SCHEMA) as session: - with session.transaction(TransactionType.WRITE) as tx: - tx.query().define(schema) - tx.commit() - - with open("sudoku6x6_data.tql") as f: - data = f.read() - - with client.session(db_name, SessionType.DATA) as session: - with session.transaction(TransactionType.WRITE) as tx: - tx.query().insert(data) - tx.commit() - -def solve(client: TypeDBClient, db_name: str, sudoku: List[List[int]]): - # create_query - non_zero = [(i,j,v) for i,row in enumerate(sudoku, 1) for j,v in enumerate(row, 1) if v != 0] - 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] - role_players = [ ["pos%d%d: $v%d%d"%(i,j,i,j) for j in range(1,7)] for i in range(1,7) ] - - query = QUERY_TEMPLATE.format( - "\n ".join(value_assignments), - ",\n ".join(", ".join(rp) for rp in role_players) - ) - - with client.session(db_name, SessionType.DATA) as session: - with session.transaction(TransactionType.READ, TypeDBOptions().set_infer(True)) as tx: - result = list(tx.query().match(query)) - - if result: - return [ [result[0].get("v%d%d"%(i,j)).get_value() for j in range(1,7)] for i in range(1,7) ] - else: - return None +class Solver: + SCHEMA_FILE = "sudoku/sudoku6x6_schema.tql" + DATA_FILE = "sudoku/sudoku6x6_data.tql" + QUERY_TEMPLATE = """ + match + $connector-hack = -1 isa connector-hack; + {0} + ( + {1} + ) isa solution; + limit 1; + """ + + def __init__(self, host: str, db_name: str): + self.client = TypeDB.core_client(host) + self.db_name = db_name + + def read_sudoku(self, filename: str): + with open(filename) as sudoku_file: + sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()] + assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku) + return sudoku + + def format_sudoku(self, sudoku: List[List[int]]): + return "\n".join(" ".join(map(str, row)) for row in sudoku) + + def database_exists(self): + return self.client.databases().contains(self.db_name) + + def setup(self, force=False): + if self.client.databases().contains(self.db_name): + if force: + self.client.databases().get(self.db_name).delete() + else: + return + + print("Setting up in database: '%s'..." % self.db_name) + self.client.databases().create(self.db_name) + + with open(Solver.SCHEMA_FILE) as f: + schema = f.read() + + with self.client.session(self.db_name, SessionType.SCHEMA) as session: + with session.transaction(TransactionType.WRITE) as tx: + tx.query().define(schema) + tx.commit() + + with open(Solver.DATA_FILE) as f: + data = f.read() + + with self.client.session(self.db_name, SessionType.DATA) as session: + with session.transaction(TransactionType.WRITE) as tx: + tx.query().insert(data) + tx.commit() + + def cleanup(self, delete_database=False): + if delete_database and self.client.databases().contains(self.db_name): + self.client.databases().get(self.db_name).delete() + self.client.close() + + def solve(self, sudoku: List[List[int]]): + # create_query + non_zero = [(i,j,v) for i,row in enumerate(sudoku, 1) for j,v in enumerate(row, 1) if v != 0] + 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] + role_players = [ ["pos%d%d: $v%d%d"%(i,j,i,j) for j in range(1,7)] for i in range(1,7) ] + + query = Solver.QUERY_TEMPLATE.format( + "\n ".join(value_assignments), + ",\n ".join(", ".join(rp) for rp in role_players) + ) + + with self.client.session(self.db_name, SessionType.DATA) as session: + with session.transaction(TransactionType.READ, TypeDBOptions().set_infer(True)) as tx: + result = list(tx.query().match(query)) + + if result: + return [ [result[0].get("v%d%d"%(i,j)).get_value() for j in range(1,7)] for i in range(1,7) ] + else: + return None def main(): @@ -72,32 +114,28 @@ def main(): print("Usage:") print("python3 %s setup: Loads required schema & data" % argv[0]) print("python3 %s : Reads & solves the sudoku in " % argv[0]) + return - client = TypeDB.core_client(HOST) + solver = Solver(HOST, DATABASE_NAME) if argv[1] == "setup": - print("Setting up in database: '%s'..." % DATABASE_NAME) - setup(client, DATABASE_NAME) + solver.setup(force=True) return - if not database_exists(client, DATABASE_NAME): - print("Database '%s' does not exist. Setting up..." % DATABASE_NAME) - setup(client, DATABASE_NAME) - - with open(argv[1]) as sudoku_file: - sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()] - assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku) + solver.setup() + sudoku = solver.read_sudoku(argv[1]) print("Solving:") - print(format_sudoku(sudoku), "\n") + print(solver.format_sudoku(sudoku), "\n") time_start = timeit.default_timer() - solution = solve(client, DATABASE_NAME, sudoku) - time_taken_ms = math.ceil((timeit.default_timer() - time_start) * 1000) + solution = solver.solve(sudoku) + time_taken_ms = int((timeit.default_timer() - time_start) * 1000 + 1) if solution: print("Found solution in " + str(time_taken_ms) + " ms:") - print(format_sudoku(solution)) + print(solver.format_sudoku(solution)) else: print("No solution (took " + str(time_taken_ms) + " ms)") + solver.cleanup() -if __name__=="__main__": main() +if __name__=="__main__": main() \ No newline at end of file diff --git a/sudoku/test.py b/sudoku/test.py new file mode 100644 index 00000000..5b254beb --- /dev/null +++ b/sudoku/test.py @@ -0,0 +1,58 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +import timeit +from os.path import join as path_join +import unittest + +from typedb.client import TypeDB + +from solve import Solver + + +class Test(unittest.TestCase): + DATABASE_NAME = "test_sudoku6x6" + SAMPLE_PATH = "sudoku/sample" + SAMPLES = [("sudoku1.txt", "solution1.txt"), + ("sudoku2.txt", "solution2.txt"), + ("sudoku3.txt", "solution3.txt"), + ("sudoku4.txt", "solution4.txt") + ] + + def setUp(self): + self.solver = Solver(TypeDB.DEFAULT_ADDRESS, Test.DATABASE_NAME) + self.solver.setup(True) + print("Loaded the " + Test.DATABASE_NAME + " schema") + + def test_samples(self): + for (sample_file, solution_file) in Test.SAMPLES: + sudoku = self.solver.read_sudoku(path_join(Test.SAMPLE_PATH, sample_file)) + time_start = timeit.default_timer() + solver_solution = self.solver.solve(sudoku) + print("Solved %s in %d ms"% (sample_file, int(1 + 1000 * (timeit.default_timer() - time_start)))) + expected_solution = self.solver.read_sudoku(path_join(Test.SAMPLE_PATH, solution_file)) + self.assertEqual(expected_solution, solver_solution) + + def tearDown(self): + self.solver.cleanup(True) + + +if __name__ == '__main__': + unittest.main() From 07c2e52bb18f0d43a1d42bacc77b9fae3af72dc0 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 15 Feb 2023 18:57:41 +0000 Subject: [PATCH 03/16] Split solve.py->solver.py,driver.py to be the lib & main resp. --- sudoku/BUILD | 12 +++++------ sudoku/driver.py | 39 ++++++++++++++++++++++++++++++++++ sudoku/{solve.py => solver.py} | 38 +-------------------------------- sudoku/test.py | 2 +- 4 files changed, 47 insertions(+), 44 deletions(-) create mode 100644 sudoku/driver.py rename sudoku/{solve.py => solver.py} (79%) diff --git a/sudoku/BUILD b/sudoku/BUILD index cf8035c2..daea1dc9 100644 --- a/sudoku/BUILD +++ b/sudoku/BUILD @@ -23,18 +23,18 @@ load("@rules_python//python:python.bzl", "py_binary", "py_test") py_library( - name = "solve", - srcs = ["solve.py"], + name = "solver", + srcs = ["solver.py"], deps = [ "@vaticle_typedb_client_python//:client_python", ] ) py_binary( - name = "solver", - srcs = ["solve.py"], + name = "driver", + srcs = ["driver.py"], deps = [ - "solve", + "solver", "@vaticle_typedb_client_python//:client_python" ], data = [ @@ -52,7 +52,7 @@ py_test( name = "test", srcs = ["test.py"], deps = [ - "solve", + "solver", "@vaticle_typedb_client_python//:client_python" ], data = [ diff --git a/sudoku/driver.py b/sudoku/driver.py new file mode 100644 index 00000000..c259b002 --- /dev/null +++ b/sudoku/driver.py @@ -0,0 +1,39 @@ +import timeit +from sys import argv + +from solver import Solver +from typedb.client import TypeDB + +HOST = TypeDB.DEFAULT_ADDRESS +DATABASE_NAME = "sudoku6x6" + +def main(): + if len(argv) != 2: + print("Usage:") + print("python3 %s setup: Loads required schema & data" % argv[0]) + print("python3 %s : Reads & solves the sudoku in " % argv[0]) + return + + solver = Solver(HOST, DATABASE_NAME) + if argv[1] == "setup": + solver.setup(force=True) + return + + solver.setup() + sudoku = solver.read_sudoku(argv[1]) + + print("Solving:") + print(solver.format_sudoku(sudoku), "\n") + + time_start = timeit.default_timer() + solution = solver.solve(sudoku) + time_taken_ms = int((timeit.default_timer() - time_start) * 1000 + 1) + if solution: + print("Found solution in " + str(time_taken_ms) + " ms:") + print(solver.format_sudoku(solution)) + else: + print("No solution (took " + str(time_taken_ms) + " ms)") + + solver.cleanup() + +if __name__=="__main__": main() diff --git a/sudoku/solve.py b/sudoku/solver.py similarity index 79% rename from sudoku/solve.py rename to sudoku/solver.py index 5775c68d..e6989773 100644 --- a/sudoku/solve.py +++ b/sudoku/solver.py @@ -19,15 +19,11 @@ # under the License. # -import timeit -from sys import argv + from typing import List from typedb.client import TypeDB, TypeDBOptions, SessionType, TransactionType -HOST = TypeDB.DEFAULT_ADDRESS -DATABASE_NAME = "sudoku6x6" - class Solver: SCHEMA_FILE = "sudoku/sudoku6x6_schema.tql" DATA_FILE = "sudoku/sudoku6x6_data.tql" @@ -107,35 +103,3 @@ def solve(self, sudoku: List[List[int]]): return [ [result[0].get("v%d%d"%(i,j)).get_value() for j in range(1,7)] for i in range(1,7) ] else: return None - - -def main(): - if len(argv) != 2: - print("Usage:") - print("python3 %s setup: Loads required schema & data" % argv[0]) - print("python3 %s : Reads & solves the sudoku in " % argv[0]) - return - - solver = Solver(HOST, DATABASE_NAME) - if argv[1] == "setup": - solver.setup(force=True) - return - - solver.setup() - sudoku = solver.read_sudoku(argv[1]) - - print("Solving:") - print(solver.format_sudoku(sudoku), "\n") - - time_start = timeit.default_timer() - solution = solver.solve(sudoku) - time_taken_ms = int((timeit.default_timer() - time_start) * 1000 + 1) - if solution: - print("Found solution in " + str(time_taken_ms) + " ms:") - print(solver.format_sudoku(solution)) - else: - print("No solution (took " + str(time_taken_ms) + " ms)") - - solver.cleanup() - -if __name__=="__main__": main() \ No newline at end of file diff --git a/sudoku/test.py b/sudoku/test.py index 5b254beb..6779e8b5 100644 --- a/sudoku/test.py +++ b/sudoku/test.py @@ -24,7 +24,7 @@ from typedb.client import TypeDB -from solve import Solver +from solver import Solver class Test(unittest.TestCase): From 9b8dfe4147f79d56b68e76e4fb84ac9b9080e1eb Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 16 Feb 2023 10:59:03 +0000 Subject: [PATCH 04/16] Update BUILD to use pip_install defined in WORKSPACE --- WORKSPACE | 5 +++++ sudoku/BUILD | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/WORKSPACE b/WORKSPACE index f36b8e91..adedb5a0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -145,6 +145,11 @@ pip_install( requirements = "//telecom/phone_calls/python:requirements.txt" ) +pip_install( + name = "sudoku_pip", + requirements = "//sudoku:requirements.txt" +) + # Load java example dependencies load("//dependencies/maven:artifacts.bzl", vaticle_typedb_examples_maven_artifacts = "artifacts") diff --git a/sudoku/BUILD b/sudoku/BUILD index daea1dc9..fbd75990 100644 --- a/sudoku/BUILD +++ b/sudoku/BUILD @@ -19,14 +19,16 @@ # under the License. # exports_files(["requirements.txt"]) -load("@rules_python//python:python.bzl", "py_binary", "py_test") +load("@rules_python//python:python.bzl", "py_binary", "py_test") +load("@sudoku_pip//:requirements.bzl", sudoku_requirement = "requirement") +# Define targets py_library( name = "solver", srcs = ["solver.py"], deps = [ - "@vaticle_typedb_client_python//:client_python", + sudoku_requirement("typedb-client") ] ) @@ -35,7 +37,7 @@ py_binary( srcs = ["driver.py"], deps = [ "solver", - "@vaticle_typedb_client_python//:client_python" + sudoku_requirement("typedb-client") ], data = [ "sudoku6x6_schema.tql", @@ -53,7 +55,7 @@ py_test( srcs = ["test.py"], deps = [ "solver", - "@vaticle_typedb_client_python//:client_python" + sudoku_requirement("typedb-client") ], data = [ "sudoku6x6_schema.tql", @@ -69,6 +71,8 @@ py_test( "sample/solution4.txt", ] ) + +# Define checkstyle load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") checkstyle_test( name = "checkstyle", From 9fb8a498cd792400e38f890499b844d4f916a8a8 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 16 Feb 2023 15:16:14 +0000 Subject: [PATCH 05/16] Make driver & tests usable from python & bazel --- sudoku/BUILD | 10 ++++------ sudoku/solver.py | 6 +++--- sudoku/test.py | 8 ++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/sudoku/BUILD b/sudoku/BUILD index fbd75990..e1350b30 100644 --- a/sudoku/BUILD +++ b/sudoku/BUILD @@ -29,6 +29,10 @@ py_library( srcs = ["solver.py"], deps = [ sudoku_requirement("typedb-client") + ], + data = [ + ":sudoku6x6_schema.tql", + ":sudoku6x6_data.tql", ] ) @@ -40,9 +44,6 @@ py_binary( sudoku_requirement("typedb-client") ], data = [ - "sudoku6x6_schema.tql", - "sudoku6x6_data.tql", - "sample/sudoku1.txt", "sample/sudoku2.txt", "sample/sudoku3.txt", @@ -58,9 +59,6 @@ py_test( sudoku_requirement("typedb-client") ], data = [ - "sudoku6x6_schema.tql", - "sudoku6x6_data.tql", - "sample/sudoku1.txt", "sample/sudoku2.txt", "sample/sudoku3.txt", diff --git a/sudoku/solver.py b/sudoku/solver.py index e6989773..16e5e24d 100644 --- a/sudoku/solver.py +++ b/sudoku/solver.py @@ -19,14 +19,14 @@ # under the License. # - +import os.path from typing import List from typedb.client import TypeDB, TypeDBOptions, SessionType, TransactionType class Solver: - SCHEMA_FILE = "sudoku/sudoku6x6_schema.tql" - DATA_FILE = "sudoku/sudoku6x6_data.tql" + SCHEMA_FILE = os.path.join(os.path.dirname(__file__), "sudoku6x6_schema.tql") + DATA_FILE = os.path.join(os.path.dirname(__file__), "sudoku6x6_data.tql") QUERY_TEMPLATE = """ match $connector-hack = -1 isa connector-hack; diff --git a/sudoku/test.py b/sudoku/test.py index 6779e8b5..6b43187d 100644 --- a/sudoku/test.py +++ b/sudoku/test.py @@ -19,7 +19,7 @@ # under the License. # import timeit -from os.path import join as path_join +import os.path import unittest from typedb.client import TypeDB @@ -29,7 +29,7 @@ class Test(unittest.TestCase): DATABASE_NAME = "test_sudoku6x6" - SAMPLE_PATH = "sudoku/sample" + SAMPLE_PATH = os.path.join(os.path.dirname(__file__), "sample") SAMPLES = [("sudoku1.txt", "solution1.txt"), ("sudoku2.txt", "solution2.txt"), ("sudoku3.txt", "solution3.txt"), @@ -43,11 +43,11 @@ def setUp(self): def test_samples(self): for (sample_file, solution_file) in Test.SAMPLES: - sudoku = self.solver.read_sudoku(path_join(Test.SAMPLE_PATH, sample_file)) + sudoku = self.solver.read_sudoku(os.path.join(Test.SAMPLE_PATH, sample_file)) time_start = timeit.default_timer() solver_solution = self.solver.solve(sudoku) print("Solved %s in %d ms"% (sample_file, int(1 + 1000 * (timeit.default_timer() - time_start)))) - expected_solution = self.solver.read_sudoku(path_join(Test.SAMPLE_PATH, solution_file)) + expected_solution = self.solver.read_sudoku(os.path.join(Test.SAMPLE_PATH, solution_file)) self.assertEqual(expected_solution, solver_solution) def tearDown(self): From 544cbfc200c178adb219cf00cc354e69224e7ab3 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 16 Feb 2023 15:16:34 +0000 Subject: [PATCH 06/16] Add reamdme --- sudoku/readme.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 sudoku/readme.md diff --git a/sudoku/readme.md b/sudoku/readme.md new file mode 100644 index 00000000..2fd8118b --- /dev/null +++ b/sudoku/readme.md @@ -0,0 +1,106 @@ +# Sudoku +In this example, we consider a smaller 6x6 version of the sudoku. +The aim is to fill in the grid with numbers from 1 to 6 such that no row, column or box contains the same number twice. +Boxes are 2 rows tall x 3 columns wide. +Sudoku is a popular puzzle involving a 9x9 grid. +Some of the numbers will be filled in as part of the puzzle. + + +## Usage +### Setup +If you're using bazel, it will take care of the dependencies for you. +If not, you'll have to install the dependencies using: + +`python -m pip requirements.txt` + +### Running the solver +Through bazel: + + `bazel run //sudoku:driver -- ()` +Directly: + + `python driver.py (|setup)` + +Running with +* `setup` will (re)create the database and load the required schema & data. +* `` will solve the sudoku in the specified file. It will also run setup if required. + - When running through bazel, use the path to the samples is: `sudoku/sample/sudoku*.txt`. + +### Running tests +`bazel test //sudoku:test` or `python test.py` + +## Defining the sudoku file +The file specified through the `sudoku-path` argument must contain a space +delimited list of cells. Empty cells should be filled with `0`. + +Example, for the sudoku: + +| | | 3 | 6 | | | +|---|---|---|---|---|---| +| | 2 | | | | 4 | +| 5 | | | | 6 | | +| | 3 | | | | 5 | +| 3 | | | | 1 | | +| | | 1 | 4 | | | + +The file would contain: +``` +0 0 3 6 0 0 +0 2 0 0 0 4 +5 0 0 0 6 0 +0 3 0 0 0 5 +3 0 0 0 1 0 +0 0 1 4 0 0 +``` + +## Approach +We declare a variable corresponding to each position in the sudoku + +| - | a | b | c | d | e | f | +|---|---|---|---|---|---|---| +| 1 |$a1|$b1|$c1|$d1|$e1|$f1| +| 2 |$a2|$b2|$c2|$d2|$e2|$f2| +| 3 |$a3|$b3|$c3|$d3|$e3|$f3| +| 4 |$a4|$b4|$c4|$d4|$e4|$f4| +| 5 |$a5|$b5|$c5|$d5|$e5|$f5| +| 6 |$a6|$b6|$c6|$d6|$e6|$f6| + +The work is done in the `solution-rule`. +Each row, column and box is constrained to be a permutation of the numbers 1 to 6. + +```typeql +# Each row must be a valid permutation +(mem: $a1, mem: $b1, mem: $c1, mem: $d1, mem: $e1, mem: $f1) isa permutation; +(mem: $a2, mem: $b2, mem: $c2, mem: $d2, mem: $e2, mem: $f2) isa permutation; +(mem: $a3, mem: $b3, mem: $c3, mem: $d3, mem: $e3, mem: $f3) isa permutation; +(mem: $a4, mem: $b4, mem: $c4, mem: $d4, mem: $e4, mem: $f4) isa permutation; +(mem: $a5, mem: $b5, mem: $c5, mem: $d5, mem: $e5, mem: $f5) isa permutation; +(mem: $a6, mem: $b6, mem: $c6, mem: $d6, mem: $e6, mem: $f6) isa permutation; + +# Each column must be a valid permutation +(mem: $a1, mem: $a2, mem: $a3, mem: $a4, mem: $a5, mem: $a6) isa permutation; +(mem: $b1, mem: $b2, mem: $b3, mem: $b4, mem: $b5, mem: $b6) isa permutation; +(mem: $c1, mem: $c2, mem: $c3, mem: $c4, mem: $c5, mem: $c6) isa permutation; +(mem: $d1, mem: $d2, mem: $d3, mem: $d4, mem: $d5, mem: $d6) isa permutation; +(mem: $e1, mem: $e2, mem: $e3, mem: $e4, mem: $e5, mem: $e6) isa permutation; +(mem: $f1, mem: $f2, mem: $f3, mem: $f4, mem: $f5, mem: $f6) isa permutation; + +# Each box must be a valid permutation +(mem: $a1, mem: $b1, mem: $c1, mem: $a2, mem: $b2, mem: $c2) isa permutation; +(mem: $a3, mem: $b3, mem: $c3, mem: $a4, mem: $b4, mem: $c4) isa permutation; +(mem: $a5, mem: $b5, mem: $c5, mem: $a6, mem: $b6, mem: $c6) isa permutation; +(mem: $d1, mem: $e1, mem: $f1, mem: $d2, mem: $e2, mem: $f2) isa permutation; +(mem: $d3, mem: $e3, mem: $f3, mem: $d4, mem: $e4, mem: $f4) isa permutation; +(mem: $d5, mem: $e5, mem: $f5, mem: $d6, mem: $e6, mem: $f6) isa permutation; +``` + +In our database, we have a single permutation instance inserted as follows: +```typeql +$v1 = 1 isa number; +$v2 = 2 isa number; +$v3 = 3 isa number; +$v4 = 4 isa number; +$v5 = 5 isa number; +$v6 = 6 isa number; +(mem: $v1, mem: $v2, mem: $v3, mem: $v4, mem: $v5, mem: $v6) isa permutation; +``` \ No newline at end of file From 094212d62fb11f0c68f98783c1129fa971426314 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 16 Feb 2023 15:29:33 +0000 Subject: [PATCH 07/16] Add query to readme --- sudoku/readme.md | 88 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 61 insertions(+), 27 deletions(-) diff --git a/sudoku/readme.md b/sudoku/readme.md index 2fd8118b..3da1026b 100644 --- a/sudoku/readme.md +++ b/sudoku/readme.md @@ -54,7 +54,10 @@ The file would contain: ``` ## Approach -We declare a variable corresponding to each position in the sudoku +The work is done in the `solution-rule`. + +Each row, column and box is constrained to be a permutation of the numbers 1 to 6. +We have one variable corresponding to each position in the sudoku. | - | a | b | c | d | e | f | |---|---|---|---|---|---|---| @@ -65,37 +68,48 @@ We declare a variable corresponding to each position in the sudoku | 5 |$a5|$b5|$c5|$d5|$e5|$f5| | 6 |$a6|$b6|$c6|$d6|$e6|$f6| -The work is done in the `solution-rule`. -Each row, column and box is constrained to be a permutation of the numbers 1 to 6. - + ```typeql -# Each row must be a valid permutation -(mem: $a1, mem: $b1, mem: $c1, mem: $d1, mem: $e1, mem: $f1) isa permutation; -(mem: $a2, mem: $b2, mem: $c2, mem: $d2, mem: $e2, mem: $f2) isa permutation; -(mem: $a3, mem: $b3, mem: $c3, mem: $d3, mem: $e3, mem: $f3) isa permutation; -(mem: $a4, mem: $b4, mem: $c4, mem: $d4, mem: $e4, mem: $f4) isa permutation; -(mem: $a5, mem: $b5, mem: $c5, mem: $d5, mem: $e5, mem: $f5) isa permutation; -(mem: $a6, mem: $b6, mem: $c6, mem: $d6, mem: $e6, mem: $f6) isa permutation; - -# Each column must be a valid permutation -(mem: $a1, mem: $a2, mem: $a3, mem: $a4, mem: $a5, mem: $a6) isa permutation; -(mem: $b1, mem: $b2, mem: $b3, mem: $b4, mem: $b5, mem: $b6) isa permutation; -(mem: $c1, mem: $c2, mem: $c3, mem: $c4, mem: $c5, mem: $c6) isa permutation; -(mem: $d1, mem: $d2, mem: $d3, mem: $d4, mem: $d5, mem: $d6) isa permutation; -(mem: $e1, mem: $e2, mem: $e3, mem: $e4, mem: $e5, mem: $e6) isa permutation; -(mem: $f1, mem: $f2, mem: $f3, mem: $f4, mem: $f5, mem: $f6) isa permutation; - -# Each box must be a valid permutation -(mem: $a1, mem: $b1, mem: $c1, mem: $a2, mem: $b2, mem: $c2) isa permutation; -(mem: $a3, mem: $b3, mem: $c3, mem: $a4, mem: $b4, mem: $c4) isa permutation; -(mem: $a5, mem: $b5, mem: $c5, mem: $a6, mem: $b6, mem: $c6) isa permutation; -(mem: $d1, mem: $e1, mem: $f1, mem: $d2, mem: $e2, mem: $f2) isa permutation; -(mem: $d3, mem: $e3, mem: $f3, mem: $d4, mem: $e4, mem: $f4) isa permutation; -(mem: $d5, mem: $e5, mem: $f5, mem: $d6, mem: $e6, mem: $f6) isa permutation; +rule solution-rule: +when { + # Each row must be a valid permutation + (mem: $a1, mem: $b1, mem: $c1, mem: $d1, mem: $e1, mem: $f1) isa permutation; + (mem: $a2, mem: $b2, mem: $c2, mem: $d2, mem: $e2, mem: $f2) isa permutation; + (mem: $a3, mem: $b3, mem: $c3, mem: $d3, mem: $e3, mem: $f3) isa permutation; + (mem: $a4, mem: $b4, mem: $c4, mem: $d4, mem: $e4, mem: $f4) isa permutation; + (mem: $a5, mem: $b5, mem: $c5, mem: $d5, mem: $e5, mem: $f5) isa permutation; + (mem: $a6, mem: $b6, mem: $c6, mem: $d6, mem: $e6, mem: $f6) isa permutation; + + # Each column must be a valid permutation + (mem: $a1, mem: $a2, mem: $a3, mem: $a4, mem: $a5, mem: $a6) isa permutation; + (mem: $b1, mem: $b2, mem: $b3, mem: $b4, mem: $b5, mem: $b6) isa permutation; + (mem: $c1, mem: $c2, mem: $c3, mem: $c4, mem: $c5, mem: $c6) isa permutation; + (mem: $d1, mem: $d2, mem: $d3, mem: $d4, mem: $d5, mem: $d6) isa permutation; + (mem: $e1, mem: $e2, mem: $e3, mem: $e4, mem: $e5, mem: $e6) isa permutation; + (mem: $f1, mem: $f2, mem: $f3, mem: $f4, mem: $f5, mem: $f6) isa permutation; + + # Each box must be a valid permutation + (mem: $a1, mem: $b1, mem: $c1, mem: $a2, mem: $b2, mem: $c2) isa permutation; + (mem: $a3, mem: $b3, mem: $c3, mem: $a4, mem: $b4, mem: $c4) isa permutation; + (mem: $a5, mem: $b5, mem: $c5, mem: $a6, mem: $b6, mem: $c6) isa permutation; + (mem: $d1, mem: $e1, mem: $f1, mem: $d2, mem: $e2, mem: $f2) isa permutation; + (mem: $d3, mem: $e3, mem: $f3, mem: $d4, mem: $e4, mem: $f4) isa permutation; + (mem: $d5, mem: $e5, mem: $f5, mem: $d6, mem: $e6, mem: $f6) isa permutation; +} then { + ( + pos11: $a1, pos12: $b1, pos13: $c1, pos14: $d1, pos15: $e1, pos16: $f1, + pos21: $a2, pos22: $b2, pos23: $c2, pos24: $d2, pos25: $e2, pos26: $f2, + pos31: $a3, pos32: $b3, pos33: $c3, pos34: $d3, pos35: $e3, pos36: $f3, + pos41: $a4, pos42: $b4, pos43: $c4, pos44: $d4, pos45: $e4, pos46: $f4, + pos51: $a5, pos52: $b5, pos53: $c5, pos54: $d5, pos55: $e5, pos56: $f5, + pos61: $a6, pos62: $b6, pos63: $c6, pos64: $d6, pos65: $e6, pos66: $f6 + ) isa solution; +}; ``` In our database, we have a single permutation instance inserted as follows: ```typeql +insert $v1 = 1 isa number; $v2 = 2 isa number; $v3 = 3 isa number; @@ -103,4 +117,24 @@ $v4 = 4 isa number; $v5 = 5 isa number; $v6 = 6 isa number; (mem: $v1, mem: $v2, mem: $v3, mem: $v4, mem: $v5, mem: $v6) isa permutation; +``` + + +To query it, We add a statement for the variables which we know the value of (e.g. `$v13 = 3 isa number;`) and query the rule: +```typeql +match + $v13 = 3 isa number; + $v15 = 1 isa number; + # ... + $v62 = 4 isa number; + $v64 = 1 isa number; + ( + pos11: $v11, pos12: $v12, pos13: $v13, pos14: $v14, pos15: $v15, pos16: $v16, + pos21: $v21, pos22: $v22, pos23: $v23, pos24: $v24, pos25: $v25, pos26: $v26, + pos31: $v31, pos32: $v32, pos33: $v33, pos34: $v34, pos35: $v35, pos36: $v36, + pos41: $v41, pos42: $v42, pos43: $v43, pos44: $v44, pos45: $v45, pos46: $v46, + pos51: $v51, pos52: $v52, pos53: $v53, pos54: $v54, pos55: $v55, pos56: $v56, + pos61: $v61, pos62: $v62, pos63: $v63, pos64: $v64, pos65: $v65, pos66: $v66 + ) isa solution; + limit 1; ``` \ No newline at end of file From 44f75066788a5af22d18f52f051c8e3357773b0e Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 20 Feb 2023 14:22:20 +0000 Subject: [PATCH 08/16] Move /sudoku to /games/sudoku --- WORKSPACE | 2 +- {sudoku => games/sudoku}/BUILD | 0 {sudoku => games/sudoku}/driver.py | 0 {sudoku => games/sudoku}/readme.md | 6 +++--- {sudoku => games/sudoku}/requirements.txt | 0 {sudoku => games/sudoku}/sample/solution1.txt | 0 {sudoku => games/sudoku}/sample/solution2.txt | 0 {sudoku => games/sudoku}/sample/solution3.txt | 0 {sudoku => games/sudoku}/sample/solution4.txt | 0 {sudoku => games/sudoku}/sample/sudoku1.txt | 0 {sudoku => games/sudoku}/sample/sudoku2.txt | 0 {sudoku => games/sudoku}/sample/sudoku3.txt | 0 {sudoku => games/sudoku}/sample/sudoku4.txt | 0 {sudoku => games/sudoku}/solver.py | 0 {sudoku => games/sudoku}/sudoku6x6_data.tql | 0 {sudoku => games/sudoku}/sudoku6x6_schema.tql | 0 {sudoku => games/sudoku}/test.py | 0 17 files changed, 4 insertions(+), 4 deletions(-) rename {sudoku => games/sudoku}/BUILD (100%) rename {sudoku => games/sudoku}/driver.py (100%) rename {sudoku => games/sudoku}/readme.md (97%) rename {sudoku => games/sudoku}/requirements.txt (100%) rename {sudoku => games/sudoku}/sample/solution1.txt (100%) rename {sudoku => games/sudoku}/sample/solution2.txt (100%) rename {sudoku => games/sudoku}/sample/solution3.txt (100%) rename {sudoku => games/sudoku}/sample/solution4.txt (100%) rename {sudoku => games/sudoku}/sample/sudoku1.txt (100%) rename {sudoku => games/sudoku}/sample/sudoku2.txt (100%) rename {sudoku => games/sudoku}/sample/sudoku3.txt (100%) rename {sudoku => games/sudoku}/sample/sudoku4.txt (100%) rename {sudoku => games/sudoku}/solver.py (100%) rename {sudoku => games/sudoku}/sudoku6x6_data.tql (100%) rename {sudoku => games/sudoku}/sudoku6x6_schema.tql (100%) rename {sudoku => games/sudoku}/test.py (100%) diff --git a/WORKSPACE b/WORKSPACE index adedb5a0..1ffc4be0 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -147,7 +147,7 @@ pip_install( pip_install( name = "sudoku_pip", - requirements = "//sudoku:requirements.txt" + requirements = "//games/sudoku:requirements.txt" ) # Load java example dependencies diff --git a/sudoku/BUILD b/games/sudoku/BUILD similarity index 100% rename from sudoku/BUILD rename to games/sudoku/BUILD diff --git a/sudoku/driver.py b/games/sudoku/driver.py similarity index 100% rename from sudoku/driver.py rename to games/sudoku/driver.py diff --git a/sudoku/readme.md b/games/sudoku/readme.md similarity index 97% rename from sudoku/readme.md rename to games/sudoku/readme.md index 3da1026b..9922f206 100644 --- a/sudoku/readme.md +++ b/games/sudoku/readme.md @@ -16,7 +16,7 @@ If not, you'll have to install the dependencies using: ### Running the solver Through bazel: - `bazel run //sudoku:driver -- ()` + `bazel run //games/sudoku:driver -- ()` Directly: `python driver.py (|setup)` @@ -24,10 +24,10 @@ Directly: Running with * `setup` will (re)create the database and load the required schema & data. * `` will solve the sudoku in the specified file. It will also run setup if required. - - When running through bazel, use the path to the samples is: `sudoku/sample/sudoku*.txt`. + - When running through bazel, use the path to the samples is: `games/sudoku/sample/sudoku*.txt`. ### Running tests -`bazel test //sudoku:test` or `python test.py` +`bazel test //games/sudoku:test` or `python test.py` ## Defining the sudoku file The file specified through the `sudoku-path` argument must contain a space diff --git a/sudoku/requirements.txt b/games/sudoku/requirements.txt similarity index 100% rename from sudoku/requirements.txt rename to games/sudoku/requirements.txt diff --git a/sudoku/sample/solution1.txt b/games/sudoku/sample/solution1.txt similarity index 100% rename from sudoku/sample/solution1.txt rename to games/sudoku/sample/solution1.txt diff --git a/sudoku/sample/solution2.txt b/games/sudoku/sample/solution2.txt similarity index 100% rename from sudoku/sample/solution2.txt rename to games/sudoku/sample/solution2.txt diff --git a/sudoku/sample/solution3.txt b/games/sudoku/sample/solution3.txt similarity index 100% rename from sudoku/sample/solution3.txt rename to games/sudoku/sample/solution3.txt diff --git a/sudoku/sample/solution4.txt b/games/sudoku/sample/solution4.txt similarity index 100% rename from sudoku/sample/solution4.txt rename to games/sudoku/sample/solution4.txt diff --git a/sudoku/sample/sudoku1.txt b/games/sudoku/sample/sudoku1.txt similarity index 100% rename from sudoku/sample/sudoku1.txt rename to games/sudoku/sample/sudoku1.txt diff --git a/sudoku/sample/sudoku2.txt b/games/sudoku/sample/sudoku2.txt similarity index 100% rename from sudoku/sample/sudoku2.txt rename to games/sudoku/sample/sudoku2.txt diff --git a/sudoku/sample/sudoku3.txt b/games/sudoku/sample/sudoku3.txt similarity index 100% rename from sudoku/sample/sudoku3.txt rename to games/sudoku/sample/sudoku3.txt diff --git a/sudoku/sample/sudoku4.txt b/games/sudoku/sample/sudoku4.txt similarity index 100% rename from sudoku/sample/sudoku4.txt rename to games/sudoku/sample/sudoku4.txt diff --git a/sudoku/solver.py b/games/sudoku/solver.py similarity index 100% rename from sudoku/solver.py rename to games/sudoku/solver.py diff --git a/sudoku/sudoku6x6_data.tql b/games/sudoku/sudoku6x6_data.tql similarity index 100% rename from sudoku/sudoku6x6_data.tql rename to games/sudoku/sudoku6x6_data.tql diff --git a/sudoku/sudoku6x6_schema.tql b/games/sudoku/sudoku6x6_schema.tql similarity index 100% rename from sudoku/sudoku6x6_schema.tql rename to games/sudoku/sudoku6x6_schema.tql diff --git a/sudoku/test.py b/games/sudoku/test.py similarity index 100% rename from sudoku/test.py rename to games/sudoku/test.py From 807b101968fc840e1a7c042ce1a23887536ddca1 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 20 Feb 2023 14:52:38 +0000 Subject: [PATCH 09/16] Move xcom from /gaming to /games --- .factory/automation.yml | 4 ++-- README.md | 4 ++-- {gaming => games}/xcom/BUILD | 12 ++++++------ {gaming => games}/xcom/InventoryItem.java | 2 +- {gaming => games}/xcom/Migration.java | 12 ++++++------ {gaming => games}/xcom/Queries.java | 2 +- {gaming => games}/xcom/README.md | 6 +++--- {gaming => games}/xcom/ResearchTask.java | 2 +- {gaming => games}/xcom/Result.java | 2 +- {gaming => games}/xcom/TransactionMode.java | 2 +- {gaming => games}/xcom/XCOMTest.java | 2 +- {gaming => games}/xcom/config/BUILD | 2 +- {gaming => games}/xcom/config/logback.xml | 0 {gaming => games}/xcom/data/README.md | 0 {gaming => games}/xcom/data/event.csv | 0 {gaming => games}/xcom/data/resource.csv | 0 {gaming => games}/xcom/data/tech.csv | 0 {gaming => games}/xcom/data/tech_required_event.csv | 0 .../xcom/data/tech_required_resource.csv | 0 {gaming => games}/xcom/data/tech_required_tech.csv | 0 {gaming => games}/xcom/data/xcom-db.csv | 0 {gaming => games}/xcom/data/xcom-db.ods | Bin {gaming => games}/xcom/images/tech-tree-mod.jpg | Bin .../xcom/images/xcom2-research-screen.jpg | Bin {gaming => games}/xcom/schema.tql | 0 25 files changed, 26 insertions(+), 26 deletions(-) rename {gaming => games}/xcom/BUILD (88%) rename {gaming => games}/xcom/InventoryItem.java (95%) rename {gaming => games}/xcom/Migration.java (96%) rename {gaming => games}/xcom/Queries.java (99%) rename {gaming => games}/xcom/README.md (94%) rename {gaming => games}/xcom/ResearchTask.java (95%) rename {gaming => games}/xcom/Result.java (97%) rename {gaming => games}/xcom/TransactionMode.java (94%) rename {gaming => games}/xcom/XCOMTest.java (99%) rename {gaming => games}/xcom/config/BUILD (95%) rename {gaming => games}/xcom/config/logback.xml (100%) rename {gaming => games}/xcom/data/README.md (100%) rename {gaming => games}/xcom/data/event.csv (100%) rename {gaming => games}/xcom/data/resource.csv (100%) rename {gaming => games}/xcom/data/tech.csv (100%) rename {gaming => games}/xcom/data/tech_required_event.csv (100%) rename {gaming => games}/xcom/data/tech_required_resource.csv (100%) rename {gaming => games}/xcom/data/tech_required_tech.csv (100%) rename {gaming => games}/xcom/data/xcom-db.csv (100%) rename {gaming => games}/xcom/data/xcom-db.ods (100%) rename {gaming => games}/xcom/images/tech-tree-mod.jpg (100%) rename {gaming => games}/xcom/images/xcom2-research-screen.jpg (100%) rename {gaming => games}/xcom/schema.tql (100%) diff --git a/.factory/automation.yml b/.factory/automation.yml index 7f880da6..e4366308 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -75,7 +75,7 @@ build: bazel run //:typedb-extractor -- dist/typedb-all-linux sudo systemd-run ./dist/typedb-all-linux/typedb server sleep 60 - bazel test //gaming/xcom:test --test_output=errors + bazel test //games/xcom:test --test_output=errors performance: test-performance-typedb-core-server: machine: 16-core-64-gb @@ -165,4 +165,4 @@ build: bazel run //:typedb-extractor -- dist/typedb-all-linux sudo systemd-run ./dist/typedb-all-linux/typedb server sleep 60 - bazel test //gaming/xcom:test --test_output=errors --runs_per_test=20 --jobs=1 + bazel test //games/xcom:test --test_output=errors --runs_per_test=20 --jobs=1 diff --git a/README.md b/README.md index efdfdd42..8ccf5275 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,10 @@ Read the [README](commerce/bookstore/README.md) file for instructions. Check [th or the initial [dataset](commerce/bookstore/python/data) for additional information. All logic accessible in the script files in the [python](commerce/bookstore/python) directory. -### [Gaming: XCOM Project](gaming/xcom) +### [Games: XCOM Project](games/xcom) The XCOM 2 example contains a database of interdependent research tasks in the game XCOM 2, featuring automatic -inference of available research based on completed tasks and available items. See [the schema](gaming/xcom/schema.tql) +inference of available research based on completed tasks and available items. See [the schema](games/xcom/schema.tql) for the examples of reasoner rules inferring attributes. ### [Software: GitHub](software/github) diff --git a/gaming/xcom/BUILD b/games/xcom/BUILD similarity index 88% rename from gaming/xcom/BUILD rename to games/xcom/BUILD index 4fbe46c8..ddf1e4e9 100644 --- a/gaming/xcom/BUILD +++ b/games/xcom/BUILD @@ -45,8 +45,8 @@ java_library( java_test( name = "test", - classpath_resources = ["//gaming/xcom/config:logback-xml"], - test_class = "com.vaticle.typedb.example.gaming.xcom.XCOMTest", + classpath_resources = ["//games/xcom/config:logback-xml"], + test_class = "com.vaticle.typedb.example.games.xcom.XCOMTest", srcs = [ "XCOMTest.java" ], @@ -68,8 +68,8 @@ java_test( java_binary( name = "migration", - classpath_resources = ["//gaming/xcom/config:logback-xml"], - main_class = "com.vaticle.typedb.example.gaming.xcom.Migration", + classpath_resources = ["//games/xcom/config:logback-xml"], + main_class = "com.vaticle.typedb.example.games.xcom.Migration", srcs = ["Migration.java"], deps = [ "@vaticle_typedb_client_java//:client-java", @@ -84,8 +84,8 @@ java_binary( java_binary( name = "queries", - classpath_resources = ["//gaming/xcom/config:logback-xml"], - main_class = "com.vaticle.typedb.example.gaming.xcom.Queries", + classpath_resources = ["//games/xcom/config:logback-xml"], + main_class = "com.vaticle.typedb.example.games.xcom.Queries", srcs = ["Queries.java", "TransactionMode.java", "ResearchTask.java", "InventoryItem.java", "Result.java"], deps = [ "@vaticle_typedb_client_java//:client-java", diff --git a/gaming/xcom/InventoryItem.java b/games/xcom/InventoryItem.java similarity index 95% rename from gaming/xcom/InventoryItem.java rename to games/xcom/InventoryItem.java index 7aef620f..7c9526c8 100644 --- a/gaming/xcom/InventoryItem.java +++ b/games/xcom/InventoryItem.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; public class InventoryItem { public InventoryItem(String name, long quantity) { diff --git a/gaming/xcom/Migration.java b/games/xcom/Migration.java similarity index 96% rename from gaming/xcom/Migration.java rename to games/xcom/Migration.java index 1cee3968..a3f4b66c 100644 --- a/gaming/xcom/Migration.java +++ b/games/xcom/Migration.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; import com.univocity.parsers.csv.CsvParser; import com.univocity.parsers.csv.CsvParserSettings; @@ -82,7 +82,7 @@ static void connectAndMigrate(Collection inputs, String databaseName) thr TypeDBSession schemaSession = client.session(databaseName, TypeDBSession.Type.SCHEMA); TypeDBTransaction schemaTransaction = schemaSession.transaction(TypeDBTransaction.Type.WRITE); Writer queryBuffer = new StringWriter(); - getReader("gaming/xcom/schema.tql").transferTo(queryBuffer); + getReader("games/xcom/schema.tql").transferTo(queryBuffer); schemaTransaction.query().define(queryBuffer.toString()); schemaTransaction.commit(); System.out.println("\nCreated the database.\n"); @@ -111,7 +111,7 @@ static Collection initialiseInputs() { /** define template for constructing a research project TypeQL insert query */ static Input initialiseTechInput() { - return new Input("gaming/xcom/data/tech") { + return new Input("games/xcom/data/tech") { @Override public String template(Json researchProject) { return "insert $research_project isa research-project, has name " + researchProject.at("name") + ";"; @@ -121,7 +121,7 @@ public String template(Json researchProject) { /** define template for constructing a research project tech requirement TypeQL insert query */ static Input initialiseResearchProjectTechRequirementInput() { - return new Input("gaming/xcom/data/tech_required_tech") { + return new Input("games/xcom/data/tech_required_tech") { @Override public String template(Json techRequirement) { // match tech @@ -137,7 +137,7 @@ public String template(Json techRequirement) { /** define template for constructing an item TypeQL insert query */ static Input initialiseItemInput() { - return new Input("gaming/xcom/data/resource") { + return new Input("games/xcom/data/resource") { @Override public String template(Json item) { return "insert $item isa item, has name " + item.at("name") + ";"; @@ -147,7 +147,7 @@ public String template(Json item) { /** define template for constructing a research project resource cost TypeQL insert query */ static Input initialiseResearchResourceCostInput() { - return new Input("gaming/xcom/data/tech_required_resource") { + return new Input("games/xcom/data/tech_required_resource") { @Override public String template(Json researchCost) { // match tech diff --git a/gaming/xcom/Queries.java b/games/xcom/Queries.java similarity index 99% rename from gaming/xcom/Queries.java rename to games/xcom/Queries.java index 1acbe8fd..16737e05 100644 --- a/gaming/xcom/Queries.java +++ b/games/xcom/Queries.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; import com.vaticle.typedb.client.TypeDB; import com.vaticle.typedb.client.api.TypeDBClient; diff --git a/gaming/xcom/README.md b/games/xcom/README.md similarity index 94% rename from gaming/xcom/README.md rename to games/xcom/README.md index 86bee209..34eb2ad3 100644 --- a/gaming/xcom/README.md +++ b/games/xcom/README.md @@ -55,6 +55,6 @@ is able to fetch the correct answer in one simple query. 1. Checkout this repository: `git clone https://github.com/vaticle/typedb-examples && cd typedb-examples` 2. Start the [TypeDB Server](http://docs.vaticle.com/docs/running-typedb/install-and-run#start-the-typedb-server). -3. Build the example: `bazel build //gaming/xcom/...`. -4. Migrate the dataset into TypeDB: `bazel run //gaming/xcom:migration`. -5. Launch the interactive query runner: `bazel run //gaming/xcom:queries`. +3. Build the example: `bazel build //games/xcom/...`. +4. Migrate the dataset into TypeDB: `bazel run //games/xcom:migration`. +5. Launch the interactive query runner: `bazel run //games/xcom:queries`. diff --git a/gaming/xcom/ResearchTask.java b/games/xcom/ResearchTask.java similarity index 95% rename from gaming/xcom/ResearchTask.java rename to games/xcom/ResearchTask.java index 7e58de26..002c9424 100644 --- a/gaming/xcom/ResearchTask.java +++ b/games/xcom/ResearchTask.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; public class ResearchTask { public ResearchTask(String name, double progressPercent) { diff --git a/gaming/xcom/Result.java b/games/xcom/Result.java similarity index 97% rename from gaming/xcom/Result.java rename to games/xcom/Result.java index 7bc7bdf4..6c00fb6e 100644 --- a/gaming/xcom/Result.java +++ b/games/xcom/Result.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; public class Result { public Result() { diff --git a/gaming/xcom/TransactionMode.java b/games/xcom/TransactionMode.java similarity index 94% rename from gaming/xcom/TransactionMode.java rename to games/xcom/TransactionMode.java index 30072291..af579228 100644 --- a/gaming/xcom/TransactionMode.java +++ b/games/xcom/TransactionMode.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; public enum TransactionMode { READ, diff --git a/gaming/xcom/XCOMTest.java b/games/xcom/XCOMTest.java similarity index 99% rename from gaming/xcom/XCOMTest.java rename to games/xcom/XCOMTest.java index 67de12ef..2d158fef 100644 --- a/gaming/xcom/XCOMTest.java +++ b/games/xcom/XCOMTest.java @@ -19,7 +19,7 @@ * under the License. */ -package com.vaticle.typedb.example.gaming.xcom; +package com.vaticle.typedb.example.games.xcom; import com.vaticle.typedb.client.TypeDB; import com.vaticle.typedb.client.api.TypeDBClient; diff --git a/gaming/xcom/config/BUILD b/games/xcom/config/BUILD similarity index 95% rename from gaming/xcom/config/BUILD rename to games/xcom/config/BUILD index ab5694f2..f3ba4cf8 100644 --- a/gaming/xcom/config/BUILD +++ b/games/xcom/config/BUILD @@ -21,7 +21,7 @@ load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") -package(default_visibility = ["//gaming/xcom:__pkg__"]) +package(default_visibility = ["//games/xcom:__pkg__"]) filegroup( name = "logback-xml", diff --git a/gaming/xcom/config/logback.xml b/games/xcom/config/logback.xml similarity index 100% rename from gaming/xcom/config/logback.xml rename to games/xcom/config/logback.xml diff --git a/gaming/xcom/data/README.md b/games/xcom/data/README.md similarity index 100% rename from gaming/xcom/data/README.md rename to games/xcom/data/README.md diff --git a/gaming/xcom/data/event.csv b/games/xcom/data/event.csv similarity index 100% rename from gaming/xcom/data/event.csv rename to games/xcom/data/event.csv diff --git a/gaming/xcom/data/resource.csv b/games/xcom/data/resource.csv similarity index 100% rename from gaming/xcom/data/resource.csv rename to games/xcom/data/resource.csv diff --git a/gaming/xcom/data/tech.csv b/games/xcom/data/tech.csv similarity index 100% rename from gaming/xcom/data/tech.csv rename to games/xcom/data/tech.csv diff --git a/gaming/xcom/data/tech_required_event.csv b/games/xcom/data/tech_required_event.csv similarity index 100% rename from gaming/xcom/data/tech_required_event.csv rename to games/xcom/data/tech_required_event.csv diff --git a/gaming/xcom/data/tech_required_resource.csv b/games/xcom/data/tech_required_resource.csv similarity index 100% rename from gaming/xcom/data/tech_required_resource.csv rename to games/xcom/data/tech_required_resource.csv diff --git a/gaming/xcom/data/tech_required_tech.csv b/games/xcom/data/tech_required_tech.csv similarity index 100% rename from gaming/xcom/data/tech_required_tech.csv rename to games/xcom/data/tech_required_tech.csv diff --git a/gaming/xcom/data/xcom-db.csv b/games/xcom/data/xcom-db.csv similarity index 100% rename from gaming/xcom/data/xcom-db.csv rename to games/xcom/data/xcom-db.csv diff --git a/gaming/xcom/data/xcom-db.ods b/games/xcom/data/xcom-db.ods similarity index 100% rename from gaming/xcom/data/xcom-db.ods rename to games/xcom/data/xcom-db.ods diff --git a/gaming/xcom/images/tech-tree-mod.jpg b/games/xcom/images/tech-tree-mod.jpg similarity index 100% rename from gaming/xcom/images/tech-tree-mod.jpg rename to games/xcom/images/tech-tree-mod.jpg diff --git a/gaming/xcom/images/xcom2-research-screen.jpg b/games/xcom/images/xcom2-research-screen.jpg similarity index 100% rename from gaming/xcom/images/xcom2-research-screen.jpg rename to games/xcom/images/xcom2-research-screen.jpg diff --git a/gaming/xcom/schema.tql b/games/xcom/schema.tql similarity index 100% rename from gaming/xcom/schema.tql rename to games/xcom/schema.tql From 9acb89ff144eb7d4e02bf3abcf366eee72991695 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 20 Feb 2023 14:57:59 +0000 Subject: [PATCH 10/16] Add sudoku test to automation.yml --- .factory/automation.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/.factory/automation.yml b/.factory/automation.yml index e4366308..2258324c 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -69,6 +69,20 @@ build: sudo systemd-run ./dist/typedb-all-linux/typedb server sleep 60 bazel test //telecom/phone_calls/python:test --test_output=errors + test-sudoku: + image: vaticle-ubuntu-22.04 + command: | + export PYENV_ROOT="/opt/pyenv" + pyenv install 3.7.12 + pyenv global 3.7.12 + sudo unlink /usr/bin/python3 + sudo ln -s $(which python3) /usr/bin/python3 + sudo ln -s /usr/bin/python3 /usr/bin/python + sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.7.12/lib/python3.7/site-packages/lsb_release.py + bazel run //:typedb-extractor -- dist/typedb-all-linux + sudo systemd-run ./dist/typedb-all-linux/typedb server + sleep 60 + bazel test //games/sudoku:test --test_output=errors test-xcom: image: vaticle-ubuntu-22.04 command: | @@ -159,6 +173,20 @@ build: sudo systemd-run ./dist/typedb-all-linux/typedb server sleep 60 bazel test //telecom/phone_calls/python:test --test_output=errors --runs_per_test=20 --jobs=1 + benchmark-sudoku-test: + image: vaticle-ubuntu-22.04 + command: | + export PYENV_ROOT="/opt/pyenv" + pyenv install 3.7.12 + pyenv global 3.7.12 + sudo unlink /usr/bin/python3 + sudo ln -s $(which python3) /usr/bin/python3 + sudo ln -s /usr/bin/python3 /usr/bin/python + sudo ln -s /usr/share/pyshared/lsb_release.py /opt/pyenv/versions/3.7.12/lib/python3.7/site-packages/lsb_release.py + bazel run //:typedb-extractor -- dist/typedb-all-linux + sudo systemd-run ./dist/typedb-all-linux/typedb server + sleep 60 + bazel test //games/sudoku:test --test_output=errors --runs_per_test=20 --jobs=1 benchmark-xcom-test: image: vaticle-ubuntu-22.04 command: | From f696a4d7611ff35f28f51836831b4fd4d6d738f0 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Mon, 20 Feb 2023 16:01:15 +0000 Subject: [PATCH 11/16] Update readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 8ccf5275..5154c6d4 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,11 @@ Read the [README](commerce/bookstore/README.md) file for instructions. Check [th or the initial [dataset](commerce/bookstore/python/data) for additional information. All logic accessible in the script files in the [python](commerce/bookstore/python) directory. +### [Games: Sudoku](games/sudoku) + +The sudoku example demonstrates the ability of TypeDB's reasoner to solve a smaller 6x6 version of the classic puzzle. +The sudoku is passed to the driver program in a text file and solved by querying a simple TypeDB database. + ### [Games: XCOM Project](games/xcom) The XCOM 2 example contains a database of interdependent research tasks in the game XCOM 2, featuring automatic From 2072bd875889885c52248ad51a4bc0a042d00aef Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Tue, 21 Feb 2023 09:32:41 +0000 Subject: [PATCH 12/16] Add missing license header to sudoku/driver.py --- games/sudoku/driver.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/games/sudoku/driver.py b/games/sudoku/driver.py index c259b002..84105213 100644 --- a/games/sudoku/driver.py +++ b/games/sudoku/driver.py @@ -1,3 +1,23 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# import timeit from sys import argv From 6aa8fdb5a21f70c93057a68e144a55e3c7bca9a1 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 22 Feb 2023 12:50:24 +0000 Subject: [PATCH 13/16] Remove bazel binary target; Remove bazel instructions from readme; Add argv[2] as typedb address --- games/sudoku/BUILD | 17 +---------------- games/sudoku/driver.py | 10 +++++----- games/sudoku/readme.md | 25 +++++++++---------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/games/sudoku/BUILD b/games/sudoku/BUILD index e1350b30..c61c1e39 100644 --- a/games/sudoku/BUILD +++ b/games/sudoku/BUILD @@ -36,27 +36,12 @@ py_library( ] ) -py_binary( - name = "driver", - srcs = ["driver.py"], - deps = [ - "solver", - sudoku_requirement("typedb-client") - ], - data = [ - "sample/sudoku1.txt", - "sample/sudoku2.txt", - "sample/sudoku3.txt", - "sample/sudoku4.txt", - ], -) - py_test( name = "test", srcs = ["test.py"], deps = [ "solver", - sudoku_requirement("typedb-client") + sudoku_requirement("typedb-client")q ], data = [ "sample/sudoku1.txt", diff --git a/games/sudoku/driver.py b/games/sudoku/driver.py index 84105213..8c0c0cc9 100644 --- a/games/sudoku/driver.py +++ b/games/sudoku/driver.py @@ -24,17 +24,17 @@ from solver import Solver from typedb.client import TypeDB -HOST = TypeDB.DEFAULT_ADDRESS DATABASE_NAME = "sudoku6x6" def main(): - if len(argv) != 2: + if len(argv) < 2: print("Usage:") - print("python3 %s setup: Loads required schema & data" % argv[0]) - print("python3 %s : Reads & solves the sudoku in " % argv[0]) + print("python3 %s setup [typedb_address] Loads required schema & data" % argv[0]) + print("python3 %s [typedb_address] Reads & solves the sudoku in " % argv[0]) return - solver = Solver(HOST, DATABASE_NAME) + host = argv[2] if len(argv) >= 3 else TypeDB.DEFAULT_ADDRESS + solver = Solver(host, DATABASE_NAME) if argv[1] == "setup": solver.setup(force=True) return diff --git a/games/sudoku/readme.md b/games/sudoku/readme.md index 9922f206..03ecd372 100644 --- a/games/sudoku/readme.md +++ b/games/sudoku/readme.md @@ -7,24 +7,17 @@ Some of the numbers will be filled in as part of the puzzle. ## Usage -### Setup -If you're using bazel, it will take care of the dependencies for you. -If not, you'll have to install the dependencies using: - -`python -m pip requirements.txt` +The driver program requires an instance of typedb server to be running. +If you are not running TypeDB locally on the default port, specify the address as the second commandline parameter. +### Setup +1. Install the dependencies: + * `python -m pip requirements.txt` +2. Create the database with required schema and data: + * `python driver.py setup [typedb-address]` + ### Running the solver -Through bazel: - - `bazel run //games/sudoku:driver -- ()` -Directly: - - `python driver.py (|setup)` - -Running with -* `setup` will (re)create the database and load the required schema & data. -* `` will solve the sudoku in the specified file. It will also run setup if required. - - When running through bazel, use the path to the samples is: `games/sudoku/sample/sudoku*.txt`. +`python driver.py [typedb-address]` ### Running tests `bazel test //games/sudoku:test` or `python test.py` From d73e738435c339d390c458ab940c3d4a1ef2e955 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 22 Feb 2023 13:50:24 +0000 Subject: [PATCH 14/16] Refactor based on PR feedback --- games/sudoku/BUILD | 2 +- games/sudoku/requirements.txt | 2 +- games/sudoku/{driver.py => sudoku_solver.py} | 10 ++++--- games/sudoku/test.py | 8 +++--- games/sudoku/{solver.py => typedb_sudoku.py} | 9 ------ games/sudoku/utils.py | 30 ++++++++++++++++++++ 6 files changed, 42 insertions(+), 19 deletions(-) rename games/sudoku/{driver.py => sudoku_solver.py} (90%) rename games/sudoku/{solver.py => typedb_sudoku.py} (90%) create mode 100644 games/sudoku/utils.py diff --git a/games/sudoku/BUILD b/games/sudoku/BUILD index c61c1e39..c95290a0 100644 --- a/games/sudoku/BUILD +++ b/games/sudoku/BUILD @@ -26,7 +26,7 @@ load("@sudoku_pip//:requirements.bzl", sudoku_requirement = "requirement") # Define targets py_library( name = "solver", - srcs = ["solver.py"], + srcs = ["typedb_sudoku.py"], deps = [ sudoku_requirement("typedb-client") ], diff --git a/games/sudoku/requirements.txt b/games/sudoku/requirements.txt index e95244b2..e338baf9 100644 --- a/games/sudoku/requirements.txt +++ b/games/sudoku/requirements.txt @@ -1 +1 @@ -typedb-client \ No newline at end of file +typedb-client==2.14.0 \ No newline at end of file diff --git a/games/sudoku/driver.py b/games/sudoku/sudoku_solver.py similarity index 90% rename from games/sudoku/driver.py rename to games/sudoku/sudoku_solver.py index 8c0c0cc9..556cc9e1 100644 --- a/games/sudoku/driver.py +++ b/games/sudoku/sudoku_solver.py @@ -21,9 +21,11 @@ import timeit from sys import argv -from solver import Solver +from typedb_sudoku import Solver from typedb.client import TypeDB +from utils import read_sudoku, format_sudoku + DATABASE_NAME = "sudoku6x6" def main(): @@ -40,17 +42,17 @@ def main(): return solver.setup() - sudoku = solver.read_sudoku(argv[1]) + sudoku = read_sudoku(argv[1]) print("Solving:") - print(solver.format_sudoku(sudoku), "\n") + print(format_sudoku(sudoku), "\n") time_start = timeit.default_timer() solution = solver.solve(sudoku) time_taken_ms = int((timeit.default_timer() - time_start) * 1000 + 1) if solution: print("Found solution in " + str(time_taken_ms) + " ms:") - print(solver.format_sudoku(solution)) + print(format_sudoku(solution)) else: print("No solution (took " + str(time_taken_ms) + " ms)") diff --git a/games/sudoku/test.py b/games/sudoku/test.py index 6b43187d..e0951131 100644 --- a/games/sudoku/test.py +++ b/games/sudoku/test.py @@ -24,8 +24,8 @@ from typedb.client import TypeDB -from solver import Solver - +from typedb_sudoku import Solver +from utils import read_sudoku class Test(unittest.TestCase): DATABASE_NAME = "test_sudoku6x6" @@ -43,11 +43,11 @@ def setUp(self): def test_samples(self): for (sample_file, solution_file) in Test.SAMPLES: - sudoku = self.solver.read_sudoku(os.path.join(Test.SAMPLE_PATH, sample_file)) + sudoku = read_sudoku(os.path.join(Test.SAMPLE_PATH, sample_file)) time_start = timeit.default_timer() solver_solution = self.solver.solve(sudoku) print("Solved %s in %d ms"% (sample_file, int(1 + 1000 * (timeit.default_timer() - time_start)))) - expected_solution = self.solver.read_sudoku(os.path.join(Test.SAMPLE_PATH, solution_file)) + expected_solution = read_sudoku(os.path.join(Test.SAMPLE_PATH, solution_file)) self.assertEqual(expected_solution, solver_solution) def tearDown(self): diff --git a/games/sudoku/solver.py b/games/sudoku/typedb_sudoku.py similarity index 90% rename from games/sudoku/solver.py rename to games/sudoku/typedb_sudoku.py index 16e5e24d..cffd7751 100644 --- a/games/sudoku/solver.py +++ b/games/sudoku/typedb_sudoku.py @@ -41,15 +41,6 @@ def __init__(self, host: str, db_name: str): self.client = TypeDB.core_client(host) self.db_name = db_name - def read_sudoku(self, filename: str): - with open(filename) as sudoku_file: - sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()] - assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku) - return sudoku - - def format_sudoku(self, sudoku: List[List[int]]): - return "\n".join(" ".join(map(str, row)) for row in sudoku) - def database_exists(self): return self.client.databases().contains(self.db_name) diff --git a/games/sudoku/utils.py b/games/sudoku/utils.py new file mode 100644 index 00000000..8b9a24b7 --- /dev/null +++ b/games/sudoku/utils.py @@ -0,0 +1,30 @@ +# +# Copyright (C) 2022 Vaticle +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +from typing import List + +def read_sudoku(filename: str): + with open(filename) as sudoku_file: + sudoku = [list(map(int, row.split())) for row in sudoku_file if row.strip()] + assert len(sudoku) == 6 and all(len(row)==6 for row in sudoku) + return sudoku + +def format_sudoku(sudoku: List[List[int]]): + return "\n".join(" ".join(map(str, row)) for row in sudoku) From e4cf1312512c7aee29637187bda7feb10ef5269c Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Thu, 23 Mar 2023 11:29:23 +0000 Subject: [PATCH 15/16] Remove stray 'q'; Update version number on python-client --- games/sudoku/BUILD | 2 +- games/sudoku/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/games/sudoku/BUILD b/games/sudoku/BUILD index c95290a0..4c693f3f 100644 --- a/games/sudoku/BUILD +++ b/games/sudoku/BUILD @@ -41,7 +41,7 @@ py_test( srcs = ["test.py"], deps = [ "solver", - sudoku_requirement("typedb-client")q + sudoku_requirement("typedb-client") ], data = [ "sample/sudoku1.txt", diff --git a/games/sudoku/requirements.txt b/games/sudoku/requirements.txt index e338baf9..e121c688 100644 --- a/games/sudoku/requirements.txt +++ b/games/sudoku/requirements.txt @@ -1 +1 @@ -typedb-client==2.14.0 \ No newline at end of file +typedb-client==2.16.1 From 499d6126798b747bbd2107da8f596beab3ff25f5 Mon Sep 17 00:00:00 2001 From: Krishnan Govindraj Date: Wed, 2 Aug 2023 17:44:51 +0100 Subject: [PATCH 16/16] Bump requirements.txt to 2.17.0 --- games/sudoku/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/games/sudoku/requirements.txt b/games/sudoku/requirements.txt index e121c688..45b43c04 100644 --- a/games/sudoku/requirements.txt +++ b/games/sudoku/requirements.txt @@ -1 +1 @@ -typedb-client==2.16.1 +typedb-client==2.17.0