# Thursday 15th June 2023 (15/6/2023) import time import _thread FILL_CODES = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" NORTH = 32 SOUTH = 128 EAST = 64 WEST = 16 DIRECTIONS = (NORTH, EAST, SOUTH, WEST) VISITED = 8 class Solver: def __init__(self, target_cell, start_pos=240, phase=0, direction=0, conn=False): """maze solver class""" self.phase = phase # phase of solving self.start_pos = start_pos # this is where we started. This value will not be changed self.end_pos = target_cell # this is where we will ultimately finish. This value will not be changed. self.pos = start_pos # where are we in the maze? self.target = target_cell # where are we going? self.direction = direction # the direction of the robot self.solved = False # is self.mazework up to date? self.cells_visited = 0 # the number of cells that have been visited self.phase2_to_3_commands = ("-",) self.current_phase_change_command = 0 self.mazemap = [] # where the walls are self.mazework = [] # how far are the cells from the finish? for i in range(256): # create an empty maze self.mazemap.append(0) self.mazework.append(0) for i in range(len(self.mazemap)): # add walls across the top, bottom, and sides if i < 16: self.mazemap[i] |= NORTH if i > 239: self.mazemap[i] |= SOUTH if i % 16 == 0: self.mazemap[i] |= WEST self.mazemap[i + 15] |= EAST if conn: import socket import network # the network library is needed to connect to Wi-Fi # open the secrets file and get the network SSID and password secrets_file = open("secrets.txt") net_name, pwd = secrets_file.read().split(" --- ") secrets_file.close() print(net_name, pwd) # connect to the network print("Connecting...") self.wlan = network.WLAN(network.AP_IF) self.wlan.config(essid=net_name, password=pwd) self.wlan.active(True) while not self.wlan.active(): pass # wait for a connection to be established # a connection has been established! ip_addr = self.wlan.ifconfig()[0] print("Connected!") print("IP address: " + ip_addr) # create the socket to send the maze data over s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # , socket.BTPROTO_RFCOMM) s.bind((ip_addr, 21300)) s.listen(1) print("Waiting for connection...") self.conn, addr = s.accept() self.send_flag = False _thread.start_new_thread(self.thread_job, ()) else: self.conn = None def thread_job(): while True: if self.send_flag: self.send_maze_data() def setp(self, strg): digit = 0 for i in range(len(strg)): ch = strg[i].upper() # case doesn't matter if ch in "0123456789": # a number sets the robot's position to a certain tile if digit == 0: self.pos = int(ch) else: self.pos = self.pos * 10 + int(ch) digit = 1 else: # otherwise, it's a movement or new wall if digit == 1: pass # print(pos) digit = 0 if ch == "U": # move up self.pos -= 16 elif ch == "D": # move down self.pos += 16 elif ch == "R": # move right self.pos += 1 elif ch == "L": # move left self.pos -= 1 self.dowall(ch) def dowall(self, wall): """inserts a wall into the maze in cell pos wall can be N, W, S, or E""" bit = 0 othpos = 0 othbit = 0 if wall == "W": bit = WEST othpos = self.pos - 1 othbit = EAST elif wall == "E": bit = EAST othpos = self.pos + 1 othbit = WEST elif wall == "N": bit = NORTH othpos = self.pos - 16 othbit = SOUTH elif wall == "S": bit = SOUTH othpos = self.pos + 16 othbit = NORTH self.mazemap[self.pos] |= bit if 0 <= othpos < 256: self.mazemap[othpos] |= othbit # other pos of the wall def printmaze(self): """displays the maze to the shell window""" line1 = "" line2 = "" for i in range(len(self.mazemap)): line1 += "+" if i == self.pos: code = "&" if self.direction == 0: code = "^" elif self.direction == 1: code = ">" elif self.direction == 2: code = "V" elif self.direction == 3: code = "<" else: code = FILL_CODES[self.mazework[i] % 32] if (self.mazemap[i] & NORTH) != 0 or ((self.mazemap[i - 16] & SOUTH) != 0 and i > 15): line1 += "-" else: line1 += " " if (self.mazemap[i] & WEST) != 0 or (self.mazemap[i - 1] & EAST) != 0: line2 += "|" + code else: line2 += " " + code if (i + 1) % 16 == 0 and self.mazemap[i] & EAST != 0: line2 += "|" if (i + 1) % 16 == 0: line1 += "+" print(line1) print(line2) line1, line2 = "", "" print("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+") def solvepart(self): self.solved = True for i in range(len(self.mazemap)): if i == self.target: self.mazework[i] = 0 else: high = 254 if (self.mazemap[i] & NORTH) == 0: if self.mazework[i - 16] < high: high = self.mazework[i - 16] if (self.mazemap[i] & EAST) == 0: if self.mazework[i + 1] < high: high = self.mazework[i + 1] if (self.mazemap[i] & SOUTH) == 0: if self.mazework[i + 16] < high: high = self.mazework[i + 16] if (self.mazemap[i] & WEST) == 0: if self.mazework[i - 1] < high: high = self.mazework[i - 1] high += 1 if self.mazework[i] != high: self.mazework[i] = high self.solved = False def solve(self): self.solved = False count = 0 while not self.solved: self.solvepart() count += 1 def send_maze_data(self): """sends the maze data through self.conn""" if self.conn: self.conn.send(bytes(self.mazemap)) self.conn.send(bytes(self.mazework)) self.conn.send(bytes((self.pos, self.target, self.direction))) def thread_send_maze_data(self): self.send_flag = True def wn(self, l, f, r): """what should the robot do based on the wall values?""" if not self.mazemap[self.pos] & VISITED: # is the cell not logged as visited? self.mazemap[self.pos] |= VISITED # log the cell as visited self.cells_visited += 1 maze_changed = False current_cell = self.mazemap[self.pos] # current cell the robot is in if f: # there's a wall in front if not (current_cell & DIRECTIONS[self.direction % 4]): self.dowall(dir_to_letter(self.direction)) maze_changed = True if l: # there's a wall to the left if not (current_cell & DIRECTIONS[(self.direction + 3) % 4]): self.dowall(dir_to_letter((self.direction + 3) % 4)) maze_changed = True if r: # there's a wall to the right if not (current_cell & DIRECTIONS[(self.direction + 1) % 4]): self.dowall(dir_to_letter((self.direction + 1) % 4)) maze_changed = True if maze_changed: self.solve() # resolve the maze current_value = self.mazework[self.pos] # value of the cell the robot is in current_cell = self.mazemap[self.pos] # current cell the robot is in instruction = "s" if self.pos >= 16 and self.mazework[self.pos - 16] < current_value and (not (current_cell & NORTH)): # go up!!! if self.direction == 0: # already facing up! instruction = "+" # go forward elif self.direction == 1: # pointing east instruction = "<" elif self.direction == 2: # south instruction = "O" # U-turn elif self.direction == 3: # west instruction = ">" elif self.pos % 16 < 15 and self.mazework[self.pos + 1] < current_value and ( not (current_cell & EAST)): # go right!!! if self.direction == 0: # up! instruction = ">" elif self.direction == 1: # pointing east instruction = "+" # go forward elif self.direction == 2: # south instruction = "<" elif self.direction == 3: # west instruction = "O" # U-turn elif self.pos < 240 and self.mazework[self.pos + 16] < current_value and ( not (current_cell & SOUTH)): # go down!!! if self.direction == 0: # up! instruction = "O" # U-turn elif self.direction == 1: # pointing east instruction = ">" elif self.direction == 2: # south instruction = "+" # go forward elif self.direction == 3: # west instruction = "<" elif self.pos % 16 > 0 and self.mazework[self.pos - 1] < current_value and ( not (current_cell & WEST)): # go left!!! if self.direction == 0: # up! instruction = "<" elif self.direction == 1: # pointing east instruction = "O" # U-turn elif self.direction == 2: # south instruction = ">" elif self.direction == 3: # west instruction = "+" # go forward # print(self.cells_visited) if instruction == "+": # forward if self.direction == 0: self.pos -= 16 elif self.direction == 1: self.pos += 1 elif self.direction == 2: self.pos += 16 elif self.direction == 3: self.pos -= 1 elif instruction == ">": # turn right self.direction = (self.direction + 1) % 4 if self.direction == 0: self.pos -= 16 elif self.direction == 1: self.pos += 1 elif self.direction == 2: self.pos += 16 elif self.direction == 3: self.pos -= 1 elif instruction == "<": # turn left self.direction = (self.direction + 3) % 4 if self.direction == 0: self.pos -= 16 elif self.direction == 1: self.pos += 1 elif self.direction == 2: self.pos += 16 elif self.direction == 3: self.pos -= 1 elif instruction == "O": # U-turn self.direction = (self.direction + 2) % 4 if self.direction == 0: self.pos -= 16 elif self.direction == 1: self.pos += 1 elif self.direction == 2: self.pos += 16 elif self.direction == 3: self.pos -= 1 elif instruction == "s": """This phase is done. Move onto the next phase""" if not self.mazemap[self.pos] & VISITED: # is the cell not logged as visited? self.mazemap[self.pos] |= VISITED # log the cell as visited self.cells_visited += 1 print("Done!!!") """ if self.cells_visited < self.cells_total and self.phase == 0: # there are still more cells to visit found_new_target = False cell_num = 0 while not found_new_target: # is the cell not visited and reachable? if (not (self.mazemap[cell_num] & VISITED)) and self.mazework[cell_num] < 255: self.target = cell_num found_new_target = True cell_num += 1 self.solve() instruction = self.wn(l, f, r) """ self.phase += 1 # increase the phase if self.phase == 1: self.target = self.start_pos # go back to the start of the maze elif self.phase == 2: # put together a list of commands if self.current_phase_change_command < len(self.phase2_to_3_commands): self.current_phase_change_command += 1 self.phase -= 1 self.direction = (self.direction + 2) % 4 return self.phase2_to_3_commands[self.current_phase_change_command - 1] else: self.target = self.end_pos self.solve() # resolve the maze with the new target self.phase = 3 inst = "" # instruction commands = [] while inst != "s": inst = self.wn(0, 0, 0) commands.append(inst) # shorten command list new_commands = [] run = 0 for x in commands: if x == "+": run += 1 elif run > 0: new_commands.append("+" + str(run)) new_commands.append(x) run = 0 else: new_commands.append(x) return new_commands else: return "s" # we are completely done! self.solve() instruction = self.wn(l, f, r) if not self.mazemap[self.pos] & VISITED: # is the cell not logged as visited? self.mazemap[self.pos] |= VISITED # log the cell as visited self.cells_visited += 1 self.thread_send_maze_data() return instruction # END OF SOLVER CLASS def dir_to_letter(dir_): return "NESW"[dir_] def human_interface(): global solver instruction = "" while instruction != "s": solver.printmaze() values = input("Enter wall values: ").strip().split(" ") for v in range(len(values) - 1, -1, -1): if values[v] == "": del values[v] else: values[v] = int(values[v]) instruction = solver.wn(*values) print(instruction) if __name__ == "__main__": # initialize the solver solver = Solver(conn=False) solver.setp("240UUUUUNRNRNRNRNRNEDEDEDEDEDE") solver.setp("240") solver.solve() try: human_interface() except KeyboardInterrupt: print("Quitting...") if solver.conn: solver.conn.close()