import sys from itertools import combinations from lib import * TOP_FLOOR = 3 part_1 = False if part_1: # g0, c0, g1, c1, ... elevator state = [0, 0, 1, 2, 1, 2, 1, 2, 1, 2, 0] else: state = [0, 0, 0, 0, 0, 0, 1, 2, 1, 2, 1, 2, 1, 2, 0] def is_state_valid(state): for floor in range(0, TOP_FLOOR + 1): gens_on_floor = [i // 2 for i in range(0, len(state) - 1, 2) if state[i] == floor] chips_on_floor = [i // 2 for i in range(1, len(state) - 1, 2) if state[i] == floor] if len(gens_on_floor) > 0: for c in chips_on_floor: if not c in gens_on_floor: return False return True assert is_state_valid([0, 0, 1, 2, 1, 2, 1, 2, 1, 2, 0]) == True assert is_state_valid([0, 0, 1, 0, 1, 2, 1, 2, 1, 2, 0]) == False def is_end_state(state): return all([o == TOP_FLOOR for o in state]) assert is_end_state([0, 0, 1, 0, 1, 2, 1, 2, 1, 2, 0]) == False assert is_end_state([3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]) == True def next_states(state): floor = state[-1] objects_on_floor = [i for i in range(len(state) - 1) if state[i] == floor] nstates = [] for o in objects_on_floor: if floor > 0: nstate = list(state) nstate[o] -= 1 nstate[-1] -= 1 nstates.append(nstate) if floor < TOP_FLOOR: nstate = list(state) nstate[o] += 1 nstate[-1] += 1 nstates.append(nstate) for os in combinations(objects_on_floor, 2): if floor > 0: nstate = list(state) for o in os: nstate[o] -= 1 nstate[-1] -= 1 nstates.append(nstate) if floor < TOP_FLOOR: nstate = list(state) for o in os: nstate[o] += 1 nstate[-1] += 1 nstates.append(nstate) return nstates states = [state] for step in range(1000): nstates = [] seen = set() for state in states: statet = tuple(state) if statet in seen: continue seen.add(statet) for nstate in next_states(state): if not is_state_valid(nstate): continue if is_end_state(nstate): print(step + 1) sys.exit(0) nstates.append(nstate) # Keep search space manageable with pretty bad pruning. nstates.sort(reverse=True) states = nstates[:20000]