def h_walldist(state, fline, walls, grid): """ The new version of h_walldist no longer calls edist_grid, but instead takes the grid as a fourth argument. It retrieves the current position's grid value, and adds an estimate of how long it will take to stop. """ ((x,y),(u,v)) = state hval = float(grid[x][y]) # if this state is going to lead us to crashing, then no. ebest = op.opponent1((x,y),(u,v),fline,walls) if rt.crash(((x,y),(x+u+ebest[0],y+v+ebest[1])),walls): hval = infinity # add a small penalty to favor short stopping distances au = abs(u); av = abs(v); sdu = au*(au-1)/2.0 sdv = av*(av-1)/2.0 sd = max(sdu,sdv) penalty = sd/10.0 # compute location after fastest stop, and add a penalty if it goes through a wall if u < 0: sdu = -sdu if v < 0: sdv = -sdv sx = x + sdu sy = y + sdv if rt.crash([(x,y),(sx,sy)],walls): hval = infinity #hval = max(hval+penalty,sd) return hval
def next_states(state, f_line, walls): """Return a list of states we can go to from state""" states = [] (loc, (vx, vy)) = state for dx in [0, -1, 1, -2, 2]: for dy in [0, -1, 1, -2, 2]: (wx, wy) = (vx + dx, vy + dy) newloc = (loc[0] + wx, loc[1] + wy) err = opponent1(loc, (wx, wy), f_line, walls) errloc = (newloc[0] + err[0], newloc[1] + err[1]) if (not rt.crash((loc, errloc), walls) and not rt.crash( (loc, newloc), walls) and not ((wx, wy) != 0 and (errloc == loc))): states.append((errloc, (wx, wy))) return states
def edist_grid2(fline, walls): global grid xmax = max([max(x, x1) for ((x, y), (x1, y1)) in walls]) ymax = max([max(y, y1) for ((x, y), (x1, y1)) in walls]) grid = [[edistw_to_finish((x, y), fline, walls) for y in range(ymax + 1)] for x in range(xmax + 1)] flag = True print('computing edist grid', end=' ') sys.stdout.flush() while flag: print('.', end='') sys.stdout.flush() flag = False for x in range(xmax + 1): for y in range(ymax + 1): for y1 in range(max(0, y - 1), min(ymax + 1, y + 2)): for x1 in range(max(0, x - 1), min(xmax + 1, x + 2)): if grid[x1][y1] != infinity and not rt.crash( ((x, y), (x1, y1)), walls): if x == x1 or y == y1: d = grid[x1][y1] + 1 else: # In principle, it seems like a taxicab metric should be just as # good, but Euclidean seems to work a little better in my tests. d = grid[x1][y1] + 1.4142135623730951 if d < grid[x][y]: grid[x][y] = d flag = True print(' done') return grid
def h_walldist(state, fline, walls, grid): """ The new version of h_walldist no longer calls edist_grid, but instead takes the grid as a fourth argument. It retrieves the current position's grid value, and adds an estimate of how long it will take to stop. """ ((x, y), (u, v)) = state hval = float(grid[x][y]) # add a small penalty to favor short stopping distances au = abs(u) av = abs(v) sdu = au * (au - 1) / 2.0 sdv = av * (av - 1) / 2.0 sd = max(sdu, sdv) penalty = sd / 10.0 # compute location after fastest stop, and add a penalty if it goes through a wall if u < 0: sdu = -sdu if v < 0: sdv = -sdv sx = x + sdu sy = y + sdv if rt.crash([(x, y), (sx, sy)], walls): penalty += math.sqrt(au**2 + av**2) hval = max(hval + penalty, sd) return hval
def my_states(state, walls, fline): """ Returns a set of possible states that we can move to starting at the given state. We trim states that will crash, states that will lead to certain death, and states that have locations we have been to before. """ data2_file = open('data2.txt', 'r') history = json.load(data2_file) data2_file.close() ((x, y), (u, v)) = state states = set() for delta_x in [-2, -1, 0, 1, 2]: for delta_y in [-2, -1, 0, 1, 2]: move = ((x, y), (x + delta_x, y + delta_y)) new_state = ((x + delta_x, y + delta_y), (delta_x, delta_y)) if not rt.crash(move, walls) and str(new_state[0]) not in history: if hit_or_near_goal(state, fline): # If that state is a goal we MUST be able to go to it (if we don't crash trying to do so). states.add(new_state) elif not moving_to_this_state_straight_up_kill_you( new_state, walls): # But, if it is NOT a goal then we can only go to it if we don't crash along the way and if moving to it won't kill us. states.add(new_state) return states
def edistw_to_finish(point, fline, walls): """ straight-line distance from (x,y) to the finish line ((x1,y1),(x2,y2)). Return infinity if there's no way to do it without intersecting a wall """ # if min(x1,x2) <= x <= max(x1,x2) and min(y1,y2) <= y <= max(y1,y2): # return 0 (x, y) = point ((x1, y1), (x2, y2)) = fline # make a list of distances to each reachable point in fline if x1 == x2: # fline is vertical, so iterate over y ds = [math.sqrt((x1-x)**2 + (y3-y)**2) \ for y3 in range(min(y1,y2),max(y1,y2)+1) \ if not rt.crash(((x,y),(x1,y3)), walls)] else: # fline is horizontal, so iterate over x ds = [math.sqrt((x3-x)**2 + (y1-y)**2) \ for x3 in range(min(x1,x2),max(x1,x2)+1) \ if not rt.crash(((x,y),(x3,y1)), walls)] ds.append(infinity) # for the case where ds is empty return min(ds)
def moving_to_this_state_straight_up_kill_you(state, walls): """ Returns true if moving to this state is certain death. False otherwise. """ wall_hit = False for child in control_error_states(state): loc = state[0] new_loc = child[0] if rt.crash((loc, new_loc), walls): wall_hit = True return wall_hit
def edistw_to_finish(point, fline, walls): """ The function from h_walldist: straight-line distance from (x,y) to the finish line ((x1,y1),(x2,y2)). Return infinity if there's no way to do it without intersecting a wall :param a: point, fline, walls :return: straight-line distance from point to finish line (considering walls) """ (x, y) = point ((x1, y1), (x2, y2)) = fline # make a list of distances to each reachable point in fline if x1 == x2: # fline is vertical, so iterate over y ds = [math.sqrt((x1 - x) ** 2 + (y3 - y) ** 2) \ for y3 in range(min(y1, y2), max(y1, y2) + 1) \ if not racetrack.crash(((x, y), (x1, y3)), walls)] else: # fline is horizontal, so iterate over x ds = [math.sqrt((x3 - x) ** 2 + (y1 - y) ** 2) \ for x3 in range(min(x1, x2), max(x1, x2) + 1) \ if not racetrack.crash(((x, y), (x3, y1)), walls)] ds.append(infinity) # for the case where ds is empty return min(ds)
def edist_grid(fline, walls): """ This functions creates a grid to cache values in the graph. Walls and unreachable nodes are stored as infinity. The function uses a BFS to traverse the grid and find distance values to each node from the finish line. """ global grid xmax = max([max(x, x1) for ((x, y), (x1, y1)) in walls]) ymax = max([max(y, y1) for ((x, y), (x1, y1)) in walls]) visited = [] # initialize grid grid = [[infinity for y in range(ymax + 1)] for x in range(xmax + 1)] # get all reachable points from finish and mark as visited for x in range(xmax + 1): for y in range(ymax + 1): grid[x][y] = edistw_to_finish((x, y), fline, walls) if grid[x][y] != infinity: visited.append((x, y)) queue = visited[:] inifinity_states = [] while queue: (x, y) = queue.pop(0) # for each neighbor of the first node in queue infinity_flag = False for y1 in range(max(0, y - 1), min(ymax + 1, y + 2)): for x1 in range(max(0, x - 1), min(xmax + 1, x + 2)): # if a neighbor is not a wall and not visited # add it to queue and mark as visited # then update grid with new value for (x, y) if not rt.crash(((x, y), (x1, y1)), walls): if (x1, y1) not in visited: queue.append((x1, y1)) visited.append((x1, y1)) if x == x1 or y == y1: d = grid[x1][y1] + 1 else: # In principle, it seems like a taxicab metric should be just as # good, but Euclidean seems to work a little better in my tests. d = grid[x1][y1] + 1.4142135623730951 if d < grid[x][y]: grid[x][y] = d else: infinity_flag = True if infinity_flag: inifinity_states.append((x, y)) # set all wall neighbors to infinity for (x, y) in inifinity_states: grid[x][y] = infinity return grid
def oppcrashcheck(prev, state, walls, n2finish): """ oppcrashcheck will take into account of the error and check if with the error the path from previous state to current state crashes. :param: prev, state, walls, n2finish: near fline points :return: if path from prev to current state crashes. """ (x1, y1) = prev (x2, y2) = state # check if it's near fline and its velocity if (0, 0) if state in n2finish and prev == state: return False for i in range(x2 - 1, x2 + 2): for j in range(y2 - 1, y2 + 2): # check if it crashes if racetrack.crash([(x1, y1), (i, j)], walls): return True return False
def h_proj1(state, fline, walls, grid): """ The first time this function is called, for each gridpoint that's not inside a wall it will cache a rough estimate of the length of the shortest path to the finish line. The computation is done by a breadth-first search going backwards from the finish line, one gridpoint at a time. On all subsequent calls, this function will retrieve the cached value and add an estimate of how long it will take to stop. """ ((x, y), (u, v)) = state # if a crash can be caused, return infinity #(dbest, ebest) = opponent1((x,y),(u,v), fline, walls) #if (dbest == 0): # return infinity # if there are no walls between state and finish, use h_esdist if edistw_to_finish( (x, y), fline, walls) != infinity and grid[x][y] != infinity: return h_esdist(state, fline, walls) hval = float(grid[x][y]) # add a small penalty to favor short stopping distances au = abs(u) av = abs(v) sdu = au * (au - 1) / 2.0 sdv = av * (av - 1) / 2.0 sd = max(sdu, sdv) penalty = sd / 10.0 # compute location after fastest stop, and add a penalty if it goes through a wall if u < 0: sdu = -sdu if v < 0: sdv = -sdv sx = x + sdu sy = y + sdv if rt.crash([(x, y), (sx, sy)], walls): penalty += math.sqrt(au**2 + av**2) hval = max(hval + penalty, sd) return hval
def main(state,finish,walls): """ main function is to calculate for the best velocity of this current state. We put our solution in choices.txt. First, I added the current state to visited state list (using data2.txt) Second, I checked for the current state if it's the goal and velocity (u,v) is in range of (0,0) then the best velocity is (0,0) and we win. If not, we're going to look for the best velocity by searching for the best next state and velocity. Third, we might found some velocity that make us progress in a loop, we will avoid that checking if the next state is visited. Lastly, we return our best velocity choice. """ ((x,y), (u,v)) = state # Retrieve the grid data that the "initialize" function stored in data.txt data_file = open('data.txt', 'r') grid = json.load(data_file) data_file.close() choices_file = open('choices.txt', 'w') # Add visited state to the file data2.txt if (u,v) == (0,0) and edist_to_line((x,y),finish) > 1: data_file = open('data2.txt', 'w') visited = [] visited.append((x,y)) json.dump(visited,data_file) data_file.close() else: data_file = open('data2.txt', 'r') visited = json.load(data_file) data_file.close() data_file = open('data2.txt', 'w') visited.append((x,y)) json.dump(visited,data_file) data_file.close() # Take the new version of h_walldist, which needs the grid as a 4th argument, and # translate it into the three-argument function needed by rt.main h = lambda state,fline,walls: h_walldist(state,fline,walls,grid) # We checked if this current state can be the end if edist_to_line((x,y),finish) <= 1 and abs(u) <= 2 and abs(v) <= 2: velocity = (0,0) print(' proj2_example: finishing, new velocity =', velocity) # We search for the best velocity else: path = rt.main(state,finish,walls,'gbf', h, verbose=0, draw=0) #print(' proj2_example: path =', path) for i in range(len(path)): #print(' proj2_example: path[',i,'] =', path[i]) if path[i] == state: print(' proj2_example: found state', state) velocity = path[i+1][1] # the case to handle if the velocity will lead us to crashing ebest = op.opponent1((x,y),(velocity[0],velocity[1]),finish,walls) if rt.crash(((x,y),(x+velocity[0]+ebest[0],y+velocity[1]+ebest[1])),walls): velocity = (velocity[0]-ebest[0],velocity[1]-ebest[1]) data_file = open('data2.txt', 'r') visited = json.load(data_file) data_file.close() # the case to handle if the velocity will lead us to visited states if visited is not None: ebest = op.opponent1((x,y),(velocity[0],velocity[1]),finish,walls) if [x+velocity[0]+ebest[0],y+velocity[1]+ebest[1]] in visited: velocity = (velocity[0]-ebest[0],velocity[1]-ebest[1]) ebest = op.opponent1((x,y),(velocity[0],velocity[1]),finish,walls) print(' proj2_example: new velocity', velocity) break # need to flush because Python uses buffered output print(velocity,file=choices_file,flush=True)
def h_opp(state, fline, walls): """ The first time this function is called, it will use optimized breath first search to find the cost for all the points on the grid. On all subsequent calls, this function will retrieve the cached value and add an estimate of how long it will take to stop. :param a: state, fline, walls :return: estimate cost of the step """ global g_fline, g_walls if fline != g_fline or walls != g_walls or grid == []: bfs(fline, walls) ((x, y), (u, v)) = state if x > xm or x < 0 or y > ym or y < 0: return infinity # add on ((h1, w1), (h2, w2)) = fline li = listfl(fline) hval = float(grid[x][y]) au = abs(u) av = abs(v) sdu = au * (au - 1) / 2.0 sdv = av * (av - 1) / 2.0 sd = max(sdu, sdv) if u < 0: sdu = -sdu if v < 0: sdv = -sdv sx = x + sdu sy = y + sdv # check if it has already found a solution path # if yes, stop exploring other nodes if li.count((x, y)) > 0 and u == 0 and v == 0: return negainfinity # add a small penalty to favor short stopping distances penalty = sd # compute location after stop, and add a penalty if it goes through a wall if racetrack.crash([(x, y), (sx, sy)], walls): penalty += 1.1 * math.sqrt(au ** 2 + av ** 2) else: penalty -= sd / 10.0 # compute the slowest stopping distance if au % 2 == 0: ssu = (au - 2) * au / 4 else: ssu = (au - 1) * au / 4 if av % 2 == 0: ssv = (av - 2) * av / 4 else: ssv = (av - 1) * av / 4 if u < 0: ssu = -ssu if v < 0: ssv = -ssv ssx = x + int(ssu) ssy = y + int(ssv) if not racetrack.crash([(x, y), (ssx, ssy)], walls): # check if the slowest stop point land on the fline # if yes, significantly reduce the return cost if (ssx, ssy) in li: penalty -= sd / 4.0 # check if the slowest stopping x or y point is on the fline and reduce the return cost elif li.count((h1, ssy)) > 0 or li.count((h2, ssy)) > 0 or li.count((ssx, w1)) > 0 or li.count((ssx, w2)) > 0: penalty -= sd / 10.0 hval += penalty return hval
def bfs(fline, walls): """ Use a breath-first-search from the fline to compute costs for points on the grid (combine edistw_to_finish and edist_grid into one time traversal of the nodes; significantly reduce the runtime) :param: fline, walls :return: return the grid """ global grid, g_fline, g_walls, xmax, ymax xmax = max([max(x, x0) for ((x, y), (x0, y0)) in walls]) ymax = max([max(y, y0) for ((x, y), (x0, y0)) in walls]) grid = [[infinity for i in range(ymax + 1)] for j in range(xmax + 1)] ((x1, y1), (x2, y2)) = fline frontier = deque([]) visited = [] # set the points on fline as 0 in the grid # and put all neighbors of the fline in the frontier if x1 == x2: for y3 in range(min(y1, y2), max(y1, y2) + 1): grid[x1][y3] = 0 visited.append((x1, y3)) for y3 in range(min(y1, y2), max(y1, y2) + 1): for n in range(max(0, y3 - 1), min(ymax + 1, y3 + 2)): for m in range(max(0, x1 - 1), min(xmax + 1, x1 + 2)): if frontier.count((m, n)) == 0 and grid[m][n] == infinity: frontier.append((m, n)) else: for x3 in range(min(x1, x2), max(x1, x2) + 1): grid[x3][y1] = 0 visited.append((x3, y1)) for x3 in range(min(x1, x2), max(x1, x2) + 1): for n in range(max(0, y1 - 1), min(ymax + 1, y1 + 2)): for m in range(max(0, x3 - 1), min(xmax + 1, x3 + 2)): if frontier.count((m, n)) == 0 and grid[m][n] == infinity: frontier.append((m, n)) # use breath-first-search to compute the costs in the grid while frontier: (v1, v2) = frontier.popleft() visited.append((v1, v2)) grid[v1][v2] = edistw_to_finish((v1, v2), fline, walls) # check if edistw_to_finish is able to compute the cost # if not, compute the cost using its non-infinity neighbors if grid[v1][v2] == infinity: for g in range(max(0, v1 - 1), min(xmax + 1, v1 + 2)): for h in range(max(0, v2 - 1), min(ymax + 1, v2 + 2)): if grid[g][h] != infinity and not racetrack.crash(((v1, v2), (g, h)), walls): if g == v1 or h == v2: d = grid[g][h] + 1 else: d = grid[g][h] + 1.4142135623730951 if d < grid[v1][v2]: grid[v1][v2] = d if grid[v1][v2] != infinity: for i in range(max(0, v1 - 1), min(xmax + 1, v1 + 2)): for j in range(max(0, v2 - 1), min(ymax + 1, v2 + 2)): if frontier.count((i, j)) == 0 and visited.count((i, j)) == 0: ##and grid[i][j] == infinity: frontier.append((i, j)) g_fline = fline g_walls = walls return grid