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 functools
import json
import os.path
import sys
from collections import defaultdict
@ -13,7 +18,7 @@ class Path:
self.path = path
def __repr__(self):
return "." +self.path if self.path else "."
return "." + self.path if self.path else "."
def __bool__(self):
return bool(self.path)
@ -38,10 +43,11 @@ class Path:
def shorten(self, path, pmerge):
if self.path.startswith(pmerge.path):
return Path(path.path + self.path[len(pmerge.path):])
return Path(path.path + self.path[len(pmerge.path) :])
else:
return self
class Explore:
def __init__(self, problem):
self.problem = problem
@ -131,78 +137,53 @@ 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)
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 = []
for n, ((p, rid), (pm, rmid)) in enumerate(
zip(neighbors[path], 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")
merged_neighbors.append((p.shorten(path, pmerge), rid or rmid))
# unify paths
unification_id[pmerge] = path
for p in unifications[pmerge]:
unification_id[p] = path
self.unification_id[pmerge] = path
for p in self.unifications[pmerge]:
self.unification_id[p] = path
unifications[path] = unifications[path1] | unifications[path2] | {pmerge}
if pmerge in unifications:
del unifications[pmerge]
self.unifications[path] = self.unifications[path1] | self.unifications[path2] | {pmerge}
if pmerge in self.unifications:
del self.unifications[pmerge]
# 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
del neighbors[pmerge]
for p, ns in neighbors.items():
self.neighbors[path] = merged_neighbors
del self.neighbors[pmerge]
for p, ns in self.neighbors.items():
new = []
for np, rid in ns:
np = np.shorten(path, pmerge)
new.append((np, rid))
if rid:
assert np in rooms, f"path {np} of {(p,ns)} not in rooms"
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
assert np in self.rooms, f"path {np} of {(p, ns)} not in rooms"
self.neighbors[p] = new
def unify_all(self):
unified = self
while True:
for rid, paths in unified.room_ids.items():
for rid, paths in self.room_ids.items():
if len(paths) > 1:
paths = list(paths)
print("unify", paths[0], paths[1])
unified = unified.unify(paths[0], paths[1])
self.unify(paths[0], paths[1])
break
break
return unified
def unexplored(self):
for path, ns in self.neighbors.items():
for p, rid in ns:
@ -210,9 +191,7 @@ class Explore:
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()
)
return next(self.unexplored(), None) is None and all(len(p) == 1 for p in self.room_ids.values())
def guess(self):
ids = {}
@ -225,11 +204,7 @@ class Explore:
src_id = self.rooms[path]
for src_door, (trg_path, trg_id) in enumerate(ns):
src = (src_id, src_door)
trg_door = next(
j
for j, (p, rid) in enumerate(self.neighbors[trg_path])
if rid == src_id
)
trg_door = next(j for j, (p, rid) in enumerate(self.neighbors[trg_path]) if rid == src_id)
trg = (trg_id, trg_door)
if (src, trg) in connected or (trg, src) in connected:
@ -268,7 +243,7 @@ def solve(problem):
ex.dump()
print("unify")
ex = ex.unify_all()
ex.unify_all()
ex.dump()
print("explored", ex.is_explored())
@ -279,5 +254,14 @@ def solve(problem):
if __name__ == "__main__":
# with open("test.rooms") as h:
# 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)