diff --git a/harald/api.py b/harald/api.py index 0ba9bd7..48230d6 100644 --- a/harald/api.py +++ b/harald/api.py @@ -1,3 +1,4 @@ +import json import os.path from argparse import Namespace from typing import List @@ -17,13 +18,14 @@ class APIError(Exception): pass -def select_problem(problem: str): - response = requests.post(config.contest_url + "/select", json={"problemName": problem, "id": config.id}) +def select(problem: str): + response = requests.post( + config.contest_url + "/select", json={"problemName": problem, "id": config.id} + ) if not response.ok: - raise APIError(response.text) + raise APIError(f"{response.status_code}: {response.text}") - data = response.json() - return data["problemName"] + return response.json()["problemName"] def explore(plans: List[str]): @@ -32,6 +34,16 @@ def explore(plans: List[str]): json={"plans": plans, "id": config.id}, ) if not response.ok: - raise APIError(response.text) + raise APIError(f"{response.status_code}: {response.text}") 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"] diff --git a/harald/explore.py b/harald/explore.py index ea6525f..8ea667f 100644 --- a/harald/explore.py +++ b/harald/explore.py @@ -1,4 +1,5 @@ import api +import ast from collections import defaultdict @@ -19,7 +20,28 @@ class Explore: def _path(self, 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) if path in self.room_ids: return self.r @@ -29,7 +51,7 @@ class Explore: results = api.explore(paths)["results"] - # print("id", path, results) + print("id", path, results) label = results[0][-2] neighbors = [r[-1] for r in results] @@ -39,7 +61,7 @@ class Explore: # update pen-ultimate room 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) return room_id @@ -72,7 +94,9 @@ class Explore: raise ExploreError(f"room '{path2}' not explored") 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) pmerge = max(path1, path2) @@ -85,9 +109,13 @@ class Explore: unification_id = self.unification_id.copy() 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: - 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)) # unify paths @@ -125,33 +153,95 @@ class Explore: return unified + def unify_all(self): + unified = self + + while True: + for rid, paths in unified.room_ids.items(): + if len(paths) > 1: + paths = list(paths) + unified = unified.unify(paths[0], paths[1]) + break + break + + return unified + + 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]}, + } + ) + + layout = { + "rooms": list(ids.values()), + "startingRoom": ids[self.rooms[""]], + "connections": connections, + } + # print(layout) + return api.guess(layout) + def test(): ex = Explore("probatio") - ex.rooms = { - "": "0101112", - "0": "1002001", - "1": "0101112", - "2": "1002001", - "3": "1002001", - "4": "1002001", - "5": "2212022", - } - ex.room_ids = {"0101112": {"", "1"}, "1002001": {"0", "2", "3", "4"}, "2212022": {"5"}} - ex.neighbors = { - "": [ - ("0", "1002001"), - ("1", "0101112"), - ("2", "1002001"), - ("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 + 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()