"""A solver for a vial-type game.""" import time import math from fibonacci_heap import FibonacciHeap from vial_game import Game from exceptions import NoSolutions def print_progress(amount_done): extra_symbol = ['▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'][ math.floor(amount_done * 160) % 8 ] progress_bar = "\r|{}{}{}| {}% ".format( '█'*math.floor(amount_done*20), extra_symbol, ' '*(math.ceil((1-amount_done)*20)-1), math.floor(amount_done*100) ) print(progress_bar, end="") def solve_game(starting_state: Game): """Solves a vial game.""" if starting_state.is_solved: return [] attempted_states = {starting_state.vial_string(): []} states = FibonacciHeap((starting_state, []), starting_state.min_moves) amount_done = 0 moves_amount = -1 while len(states) > 0: (current_state, moves), value = states.extract_min() if amount_done + 0.00125 < len(moves)/value: amount_done = (len(moves)/value) print_progress(amount_done) if len(moves) > moves_amount: moves_amount = len(moves) print(f"{len(states) + 1} ({len(attempted_states)}){' '*20}", end="\r") last_to_vial = moves[-1][1] if len(moves) > 1 else None possible_moves = current_state.possible_moves(last_to_vial) for from_vial, to_vial in possible_moves: new_state = current_state.copy() new_state.move(from_vial, to_vial) if new_state.vials[to_vial].is_solved: star = "*" else: star = " " new_state_moves = moves + [(from_vial, to_vial, star)] if new_state.is_solved: return new_state_moves if new_state.vial_string() not in attempted_states: attempted_states[new_state.vial_string()] = new_state_moves new_state_value = new_state.min_moves + len(new_state_moves) states.insert((new_state, new_state_moves), new_state_value) raise NoSolutions() def format_instructions(instructions, start_time): """Format instructions outputted by solve_game().""" time_in_milliseconds = math.floor((time.time()-start_time)*1000) time_text = "" if time_in_milliseconds >= (1000*60): time_in_minutes = str(math.floor(time_in_milliseconds/(1000*60))) time_text += time_in_minutes+"m " if time_in_milliseconds >= 1000: time_in_seconds = str(math.floor(time_in_milliseconds/1000)%60) time_text += time_in_seconds+"s " time_text += str(time_in_milliseconds%1000)+"ms " formatted_instructions = f"Time elapsed: {time_text}\n" formatted_instructions += "Instructions:\n" for i, instruction in enumerate(instructions): if i%7 == 0 and i != 0: formatted_instructions += "\n" if i%21 == 0 and i != 0: formatted_instructions += "\n" if i%63 == 0 and i != 0: formatted_instructions += "\n\n" from_vial = instruction[0] to_vial = instruction[1] star = instruction[2] step = f" {star}{from_vial:2} --> {to_vial:2}{star} \t" formatted_instructions += step return formatted_instructions