from z3 import *
class Car:
def __init__(self, is_vertical, base_pos, length, start, color):
self.is_vertical = is_vertical
self.base = base_pos
self.length = length
self.start = start
self.color = color
dimension = 6
red_car = Car(False, 2, 2, 3, "red")
cars = [
Car(True, 0, 3, 0, 'yellow'),
Car(False, 3, 3, 0, 'blue'),
red_car,
# ... more cars
]
bv3 = BitVecSort(3)
state = Function('state', [bv3 for _ in cars] + [BoolSort()])
fp = Fixedpoint()
fp.set("fp.engine", "datalog")
fp.register_relation(state)
def num(i):
return BitVecVal(i, bv3)
def bound(i):
return Const(cars[i].color, bv3)
fp.declare_var([bound(i) for i in range(len(cars))])
# Initial state
fp.fact(state([num(cars[i].start) for i in range(len(cars))]))
# Define transitions for each car movement
def mk_transition(row, col, i0, j, car0, car_idx):
body = [state([num(i0) if i == car_idx else bound(i)
for i in range(len(cars))])]
# Check no other car blocks this position
for idx, car in enumerate(cars):
if car != car0:
if car.is_vertical and car.base == col:
for i in range(dimension):
if i <= row < i + car.length:
body.append(bound(idx) != num(i))
# Add transition rule
label = f"{car0.color} {i0}->{j}"
fp.rule(state([num(j) if i == car_idx else bound(i)
for i in range(len(cars))]), body, label)
# Add all possible moves for each car
for idx, car in enumerate(cars):
for i in range(dimension):
if car.is_vertical:
if i + car.length < dimension: # Move down
mk_transition(i + car.length, car.base, i, i+1, car, idx)
if i > 0: # Move up
mk_transition(i - 1, car.base, i, i-1, car, idx)
else:
if i + car.length < dimension: # Move right
mk_transition(car.base, i + car.length, i, i+1, car, idx)
if i > 0: # Move left
mk_transition(car.base, i - 1, i, i-1, car, idx)
# Goal: red car reaches exit position (column 4)
goal = state([num(4) if cars[i] == red_car else bound(i)
for i in range(len(cars))])
if fp.query(goal) == sat:
print("Solution found!")
# Extract solution trace from answer
answer = fp.get_answer()
print(answer)