diff --git a/harald/api.py b/harald/api.py index b879417..4fd7044 100644 --- a/harald/api.py +++ b/harald/api.py @@ -21,9 +21,7 @@ class APIError(Exception): 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: raise APIError(f"{response.status_code}: {response.text}") @@ -77,8 +75,7 @@ def explore(plans: List[str]): def guess(layout): - data = {"id": config.id, "map": layout} # , "id": config.id} - print(json.dumps(data)) + data = {"map": layout, "id": config.id} response = requests.post(config.contest_url + "/guess", json=data) if not response.ok: raise APIError(f"{response.status_code}: {response.text}") diff --git a/harald/explore.py b/harald/explore.py index 29e6ac0..0be60e8 100755 --- a/harald/explore.py +++ b/harald/explore.py @@ -140,9 +140,7 @@ class Explore: # self.dump() p, rid = self.neighbors[pl][d] - assert ( - rid is None or rid == room_id - ), f"penultimate {path} {pl}: {rid} != {room_id}" + assert rid is None or rid == room_id, f"penultimate {path} {pl}: {rid} != {room_id}" self.neighbors[pl][d] = (p, room_id) # self.dump() @@ -185,9 +183,7 @@ class Explore: assert path2 in self.rooms, 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) @@ -196,13 +192,9 @@ class Explore: self.unification_id[pmerge] = path merged_neighbors = [] - for n, ((p, rid), (pm, rmid)) in enumerate( - zip(self.neighbors[path], self.neighbors[pmerge]) - ): + for n, ((p, rid), (pm, rmid)) in enumerate(zip(self.neighbors[path], self.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") if rmid: merged_neighbors.append((self._path(pm), rmid)) else: @@ -233,9 +225,7 @@ class Explore: new.append((np_, rid)) if rid: try: - assert ( - np_ in self.rooms - ), f"unify: path {np} {np_} of {(p, ns)} not in rooms" + assert np_ in self.rooms, f"unify: path {np} {np_} of {(p, ns)} not in rooms" except AssertionError as exc: self.dump() raise exc @@ -261,11 +251,9 @@ class Explore: yield d, path def is_explored(self): - return next(self.unexplored(), None) is None and all( - len(p) == 1 for p in self.room_ids.values() - ) + return next(self.unexplored(), None) is None and all(len(p) == 1 for p in self.room_ids.values()) - def guess(self, orig=None): + def guess(self, orig=None, start=Path()): aedi = graph.Aedificium(self.problem) ids = {} @@ -280,22 +268,15 @@ class Explore: for src_door, (trg_path, _) in enumerate(ns): src = (src_path, src_door) trg_door = next( - ( - j - for j, (p, rid) in enumerate(self.neighbors[trg_path]) - if p == src_path - ), + (j for j, (p, rid) in enumerate(self.neighbors[trg_path]) if p == src_path), None, ) if trg_door is None: - raise ExploreError( - f"backlink not found: {(src, trg_path, self.neighbors[trg_path])}" - ) + raise ExploreError(f"backlink not found: {(src, trg_path, self.neighbors[trg_path])}") trg = (trg_path, trg_door) if (src, trg) in connected or (trg, src) in connected: continue - print(src, trg) connected.add((src, trg)) connections.append( { @@ -314,7 +295,7 @@ class Explore: layout = { "rooms": [int(rooms[p]) for p in ids.keys()], - "startingRoom": ids[Path()], + "startingRoom": ids[start], "connections": connections, } @@ -329,7 +310,7 @@ class Explore: return ret for d, (q, _) in enumerate(self.neighbors[p]): - queue.append((q, Path([d]))) + queue.append((q, ret + Path([d]))) assert False, "return path not found" @@ -367,75 +348,87 @@ def room_solve(problem, nrooms, plen): print("score", ex.score) -def get_mark(rid): - return Path("[" + str(int(rid[0]) ^ 3) + "]") +def get_mark(rid, mask): + return Path("[" + str(int(rid[0]) ^ mask) + "]") -def mark_solve(problem, nrooms, plen): - ex0 = Explore(problem, [d + d + d + d + d + d for d in DOORS[:plen]]) - api.select(ex0.problem) - res = ex0.explore(Path()) +def apply_mask(rid, mask): + return "".join(map(lambda d: str(int(d) & ~mask), rid)) - ex0.update(Path(), res) - ex0.dump() - while True: - door, unexplored = next(ex0.unexplored(), (None, None)) - if unexplored is None: - break +def mark_solve(problem, nrooms): + probes = [d + d + d + d + d + d for d in DOORS[:2]] + api.select(problem) - print("explore", door, unexplored) - path = unexplored + Path([door]) - res = ex0.explore(path) - ex0.update(path, res) - ex0.dump() + masks = [0, 1, 2] + exs = [] + loop = Path() + mark_loop = Path() - ex0.unify_all() - ex0.dump() + for mask in masks: + print("exploration mask", mask) + if mask != 0: + for p, rid in exs[-1].rooms.items(): + q = exs[-1].returnfrom(p) + loop = loop + p + q + mark_loop = mark_loop + p + get_mark(rid, mask) + q - print("found all rooms") - mark = Path() - for p, rid in ex0.rooms.items(): - q = ex0.returnfrom(p) - mark = mark + p + get_mark(rid) + q + print("marked", mark_loop, loop) + assert exs[-1].walk(loop) == Path(), f"loop doesn't close {exs[-1].walk(loop)}" - print("marked", mark) + ex = Explore(problem, probes) + exs.append(ex) - ex1 = Explore(problem, [d + d + d + d + d + d for d in DOORS[:plen]]) - res = ex1.explore(Path(), mark=mark) - ex1.update(Path(), res) - ex1.dump() + res = ex.explore(Path(), mark=mark_loop) + ex.update(Path(), res) + ex.dump() - while True: - door, unexplored = next(ex1.unexplored(), (None, None)) - if unexplored is None: + while True: + door, unexplored = next(ex.unexplored(), (None, None)) + if unexplored is None: + break + + print("explore", door, unexplored) + path = unexplored + Path([door]) + res = ex.explore(path, mark=mark_loop) + ex.update(path, res) + ex.unify_all() + ex.dump() + + assert ex.is_explored(), "not fully explored" + if nrooms % len(ex.rooms) != 0: + raise ExploreError(f"not all rooms could be identifed {len(ex.rooms)}/{nrooms}") + if len(ex.rooms) == nrooms: + print("found all rooms") break - print("explore", door, unexplored) - path = unexplored + Path([door]) - res = ex1.explore(path, mark=mark) - ex1.update(path, res) - ex1.dump() - - ex1.unify_all() - ex1.dump() - - print("explored", ex1.is_explored()) - if ex1.is_explored(): - # get old markings - orig = {} - for path in ex1.rooms.keys(): - p = ex0.walk(path) - rid = ex0.rooms[p] - orig[path] = rid[0] - print(path, p, rid) - - ex0.dump() - ex1.dump() - print(orig) - print("guess", ex1.guess(orig=orig)) - print("score", ex0.score + ex1.score) - assert False + if len(ex.rooms) != nrooms: + raise ExploreError(f"not all rooms could be identifed {len(ex.rooms)}/{nrooms}") + for e in exs: + e.dump() + + # get old markings + orig = {} + for path in ex.rooms.keys(): + p = exs[0].walk(path) + rid = exs[0].rooms[p] + orig[path] = rid[0] + print(path, p, rid) + print(orig) + # try to identify original start + start = apply_mask(exs[0].rooms[Path()], mask) + starts = [] + all_masked = [] + for p, rid in ex.rooms.items(): + mrid = apply_mask(rid, mask) + all_masked.append(mrid) + if mrid == start: + starts.append(p) + print("start", start, starts, all_masked) + assert len(starts) > 0 + + print("guess", ex.guess(orig=orig, start=starts[0])) + print("score", sum(e.score for e in exs)) def path_solve(problem, nrooms, plen): @@ -498,23 +491,17 @@ if __name__ == "__main__": with open(os.path.join("..", "problems.json")) as h: problems = json.loads(h.read()) - problems = { - p["problem"]: {"size": p["size"], "idx": i} for i, p in enumerate(problems) - } + problems = {p["problem"]: {"size": p["size"], "idx": i} for i, p in enumerate(problems)} if problem not in problems: raise ExploreError(f"unknown problem {problem}") try: - if problems[problem]["idx"] < 6: - room_solve(problem, problems[problem]["size"], int(sys.argv[2])) - else: - mark_solve(problem, problems[problem]["size"], int(sys.argv[2])) - + mark_solve(problem, problems[problem]["size"]) api.clean_explore_cache() - # except ExploreError as exc: - # api.clean_explore_cache() - # raise exc + except ExploreError as exc: + api.clean_explore_cache() + raise exc except Exception as exc: api.write_explore_cache() raise exc