probatio ok

main
Harald Holtmann 2025-09-05 23:15:21 +02:00
parent 3439e4d403
commit 4deb6adb54
2 changed files with 141 additions and 39 deletions

@ -1,3 +1,4 @@
import json
import os.path import os.path
from argparse import Namespace from argparse import Namespace
from typing import List from typing import List
@ -17,13 +18,14 @@ class APIError(Exception):
pass pass
def select_problem(problem: str): def select(problem: str):
response = requests.post(config.contest_url + "/select", json={"problemName": problem, "id": config.id}) response = requests.post(
config.contest_url + "/select", json={"problemName": problem, "id": config.id}
)
if not response.ok: if not response.ok:
raise APIError(response.text) raise APIError(f"{response.status_code}: {response.text}")
data = response.json() return response.json()["problemName"]
return data["problemName"]
def explore(plans: List[str]): def explore(plans: List[str]):
@ -32,6 +34,16 @@ def explore(plans: List[str]):
json={"plans": plans, "id": config.id}, json={"plans": plans, "id": config.id},
) )
if not response.ok: if not response.ok:
raise APIError(response.text) raise APIError(f"{response.status_code}: {response.text}")
return response.json() return response.json()
def guess(layout):
data = {"id": config.id, "map": layout} # , "id": config.id}
print(json.dumps(data))
response = requests.post(config.contest_url + "/guess", json=data)
if not response.ok:
raise APIError(f"{response.status_code}: {response.text}")
return response.json()["correct"]

@ -1,4 +1,5 @@
import api import api
import ast
from collections import defaultdict from collections import defaultdict
@ -19,7 +20,28 @@ class Explore:
def _path(self, path): def _path(self, path):
return self.unification_id.get(path, path) return self.unification_id.get(path, path)
def id_room(self, path): def save(self):
return {
"problem": self.problem,
"room": dict(self.rooms),
"room_ids": dict(self.room_ids),
"neighbours": dict(self.neighbors),
"unification_id": dict(self.unification_id),
"unifications": dict(self.unifications),
}
@classmethod
def load(cls, obj):
new = cls(obj["problem"])
new.problem = obj["problem"]
new.rooms.update(obj["room"])
new.room_ids.update(obj["room_ids"])
new.neighbors.update(obj["neighbours"])
new.unification_id.update(obj["unification_id"])
new.unifications.update(obj["unifications"])
return new
def explore(self, path):
path = self._path(path) path = self._path(path)
if path in self.room_ids: if path in self.room_ids:
return self.r return self.r
@ -29,7 +51,7 @@ class Explore:
results = api.explore(paths)["results"] results = api.explore(paths)["results"]
# print("id", path, results) print("id", path, results)
label = results[0][-2] label = results[0][-2]
neighbors = [r[-1] for r in results] neighbors = [r[-1] for r in results]
@ -39,7 +61,7 @@ class Explore:
# update pen-ultimate room # update pen-ultimate room
if path: if path:
p, rid = self.neighbors[path[:-1]][int(path[-1])] p, _ = self.neighbors[path[:-1]][int(path[-1])]
self.neighbors[path[:-1]][int(path[-1])] = (p, room_id) self.neighbors[path[:-1]][int(path[-1])] = (p, room_id)
return room_id return room_id
@ -72,7 +94,9 @@ class Explore:
raise ExploreError(f"room '{path2}' not explored") raise ExploreError(f"room '{path2}' not explored")
if self.rooms[path1] != self.rooms[path2]: if self.rooms[path1] != self.rooms[path2]:
raise ExploreError(f"ids of '{path1}'({self.rooms[path1]}) and '{path2}'({self.rooms[path2]}) do not match") raise ExploreError(
f"ids of '{path1}'({self.rooms[path1]}) and '{path2}'({self.rooms[path2]}) do not match"
)
path = min(path1, path2) path = min(path1, path2)
pmerge = max(path1, path2) pmerge = max(path1, path2)
@ -85,9 +109,13 @@ class Explore:
unification_id = self.unification_id.copy() unification_id = self.unification_id.copy()
merged_neighbors = [] merged_neighbors = []
for n, ((p, rid), (pm, rmid)) in enumerate(zip(neighbors[path], neighbors[pmerge])): for n, ((p, rid), (pm, rmid)) in enumerate(
zip(neighbors[path], neighbors[pmerge])
):
if rid and rmid and rid != rmid: if rid and rmid and rid != rmid:
raise ExploreError(f"neighbor {n} of '{path}'({rid}) and '{pmerge}'({rmid}) do not match") raise ExploreError(
f"neighbor {n} of '{path}'({rid}) and '{pmerge}'({rmid}) do not match"
)
merged_neighbors.append((p, rid or rmid)) merged_neighbors.append((p, rid or rmid))
# unify paths # unify paths
@ -125,33 +153,95 @@ class Explore:
return unified return unified
def unify_all(self):
unified = self
def test(): while True:
ex = Explore("probatio") for rid, paths in unified.room_ids.items():
ex.rooms = { if len(paths) > 1:
"": "0101112", paths = list(paths)
"0": "1002001", unified = unified.unify(paths[0], paths[1])
"1": "0101112", break
"2": "1002001", break
"3": "1002001",
"4": "1002001", return unified
"5": "2212022",
def unexplored(self):
for path, ns in self.neighbors.items():
for p, rid in ns:
if not rid:
yield p
def is_explored(self):
return next(self.unexplored(), None) is None and all(
len(p) == 1 for p in self.room_ids.values()
)
def guess(self):
ids = {}
for i, rid in enumerate(self.room_ids.keys()):
ids[rid] = i
connected = set()
connections = []
for path, ns in self.neighbors.items():
src_id = self.rooms[path]
for src_door, (trg_path, trg_id) in enumerate(ns):
src = (ids[src_id], src_door)
trg_door = next(
j
for j, (p, rid) in enumerate(self.neighbors[trg_path])
if rid == src_id
)
trg = (ids[trg_id], trg_door)
if (src, trg) in connected or (trg, src) in connected:
continue
connected.add((src, trg))
connections.append(
{
"from": {"room": src[0], "door": src[1]},
"to": {"room": trg[0], "door": trg[1]},
} }
ex.room_ids = {"0101112": {"", "1"}, "1002001": {"0", "2", "3", "4"}, "2212022": {"5"}} )
ex.neighbors = {
"": [ layout = {
("0", "1002001"), "rooms": list(ids.values()),
("1", "0101112"), "startingRoom": ids[self.rooms[""]],
("2", "1002001"), "connections": connections,
("3", "1002001"),
("4", "1002001"),
("5", "2212022"),
],
"0": [("00", None), ("01", None), ("02", None), ("03", None), ("04", None), ("05", None)],
"1": [("10", None), ("11", None), ("12", None), ("13", None), ("14", None), ("15", None)],
"2": [("20", None), ("21", None), ("22", None), ("23", None), ("24", None), ("25", None)],
"3": [("30", None), ("31", None), ("32", None), ("33", None), ("34", None), ("35", None)],
"4": [("40", None), ("41", None), ("42", None), ("43", None), ("44", None), ("45", None)],
"5": [("50", None), ("51", None), ("52", None), ("53", None), ("54", None), ("55", None)],
} }
return ex # print(layout)
return api.guess(layout)
def test():
ex = Explore("probatio")
api.select(ex.problem)
ex.explore("")
ex.dump()
while True:
unexplored = next(ex.unexplored(), None)
if not unexplored:
break
print("explore", unexplored)
ex.explore(unexplored)
ex.dump()
print("unify")
ex = ex.unify_all()
ex.dump()
print("explored", ex.is_explored())
# with open("test.rooms","w") as h:
# h.write(str(ex.save()))
if ex.is_explored():
print("guess", ex.guess())
if __name__ == "__main__":
# with open("test.rooms") as h:
# obj = ast.literal_eval(h.read())
test()