added problem check

main
Harald Holtmann 2025-09-06 07:57:27 +02:00
parent 6c5536f01c
commit 3f0e979dce
2 changed files with 39 additions and 55 deletions

@ -1,5 +1,10 @@
#!/usr/bin/env python3
import api import api
import functools import functools
import json
import os.path
import sys
from collections import defaultdict from collections import defaultdict
@ -42,6 +47,7 @@ class Path:
else: else:
return self return self
class Explore: class Explore:
def __init__(self, problem): def __init__(self, problem):
self.problem = problem self.problem = problem
@ -131,78 +137,53 @@ 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( raise ExploreError(f"ids of '{path1}'({self.rooms[path1]}) and '{path2}'({self.rooms[path2]}) do not match")
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)
room_id = self.rooms[path] room_id = self.rooms[path]
rooms = self.rooms.copy()
room_ids = self.room_ids.copy()
neighbors = self.neighbors.copy()
unifications = self.unifications.copy()
unification_id = self.unification_id.copy()
merged_neighbors = [] merged_neighbors = []
for n, ((p, rid), (pm, rmid)) in enumerate( for n, ((p, rid), (pm, rmid)) in enumerate(zip(self.neighbors[path], self.neighbors[pmerge])):
zip(neighbors[path], neighbors[pmerge])
):
if rid and rmid and rid != rmid: if rid and rmid and rid != rmid:
raise ExploreError( raise ExploreError(f"neighbor {n} of '{path}'({rid}) and '{pmerge}'({rmid}) do not match")
f"neighbor {n} of '{path}'({rid}) and '{pmerge}'({rmid}) do not match"
)
merged_neighbors.append((p.shorten(path, pmerge), rid or rmid)) merged_neighbors.append((p.shorten(path, pmerge), rid or rmid))
# unify paths # unify paths
unification_id[pmerge] = path self.unification_id[pmerge] = path
for p in unifications[pmerge]: for p in self.unifications[pmerge]:
unification_id[p] = path self.unification_id[p] = path
unifications[path] = unifications[path1] | unifications[path2] | {pmerge} self.unifications[path] = self.unifications[path1] | self.unifications[path2] | {pmerge}
if pmerge in unifications: if pmerge in self.unifications:
del unifications[pmerge] del self.unifications[pmerge]
# fix rooms # fix rooms
del rooms[pmerge] del self.rooms[pmerge]
room_ids[room_id] = {p for p in room_ids[room_id] if p != pmerge} self.room_ids[room_id] = {p for p in self.room_ids[room_id] if p != pmerge}
neighbors[path] = merged_neighbors self.neighbors[path] = merged_neighbors
del neighbors[pmerge] del self.neighbors[pmerge]
for p, ns in neighbors.items(): for p, ns in self.neighbors.items():
new = [] new = []
for np, rid in ns: for np, rid in ns:
np = np.shorten(path, pmerge) np = np.shorten(path, pmerge)
new.append((np, rid)) new.append((np, rid))
if rid: if rid:
assert np in rooms, f"path {np} of {(p,ns)} not in rooms" assert np in self.rooms, f"path {np} of {(p, ns)} not in rooms"
neighbors[p] = new self.neighbors[p] = new
unified = self.__class__(self.problem)
unified.rooms = rooms
unified.room_ids = room_ids
unified.neighbors = neighbors
unified.unifications = unifications
unified.unification_id = unification_id
return unified
def unify_all(self): def unify_all(self):
unified = self
while True: while True:
for rid, paths in unified.room_ids.items(): for rid, paths in self.room_ids.items():
if len(paths) > 1: if len(paths) > 1:
paths = list(paths) paths = list(paths)
print("unify", paths[0], paths[1]) print("unify", paths[0], paths[1])
unified = unified.unify(paths[0], paths[1]) self.unify(paths[0], paths[1])
break break
break break
return unified
def unexplored(self): def unexplored(self):
for path, ns in self.neighbors.items(): for path, ns in self.neighbors.items():
for p, rid in ns: for p, rid in ns:
@ -210,9 +191,7 @@ class Explore:
yield p yield p
def is_explored(self): def is_explored(self):
return next(self.unexplored(), None) is None and all( return next(self.unexplored(), None) is None and all(len(p) == 1 for p in self.room_ids.values())
len(p) == 1 for p in self.room_ids.values()
)
def guess(self): def guess(self):
ids = {} ids = {}
@ -225,11 +204,7 @@ class Explore:
src_id = self.rooms[path] src_id = self.rooms[path]
for src_door, (trg_path, trg_id) in enumerate(ns): for src_door, (trg_path, trg_id) in enumerate(ns):
src = (src_id, src_door) src = (src_id, src_door)
trg_door = next( trg_door = next(j for j, (p, rid) in enumerate(self.neighbors[trg_path]) if rid == src_id)
j
for j, (p, rid) in enumerate(self.neighbors[trg_path])
if rid == src_id
)
trg = (trg_id, trg_door) trg = (trg_id, trg_door)
if (src, trg) in connected or (trg, src) in connected: if (src, trg) in connected or (trg, src) in connected:
@ -268,7 +243,7 @@ def solve(problem):
ex.dump() ex.dump()
print("unify") print("unify")
ex = ex.unify_all() ex.unify_all()
ex.dump() ex.dump()
print("explored", ex.is_explored()) print("explored", ex.is_explored())
@ -279,5 +254,14 @@ def solve(problem):
if __name__ == "__main__": if __name__ == "__main__":
# with open("test.rooms") as h: # with open("test.rooms") as h:
# obj = ast.literal_eval(h.read()) # obj = ast.literal_eval(h.read())
problem = sys.argv[1]
with open(os.path.join("..", "problems.json")) as h:
problems = json.loads(h.read())
problems = {p["problem"]: {"size": p["size"]} for p in problems}
if problem not in problems:
raise ExploreError(f"unknown problem {problem}")
solve("primus") solve(problem)