INTERMEDIATE PYTHON 3 | Specialized Collections | The Great Robot Race

Hello guys,

I’ve tried to look up this project in the forum and google. But still couldn’t find out why my code turned out to be an infinite loop.

So, this project has three main kinds of code, which are maze_data.CSV, robot_race.py ( the one I’m working on), and lastly robot_race_functions.py. Both CSV file and robot_race_functions.py was provided at first.

Would be really appreciated if anyone could help me out. I’ve been stuck here for 3 days. :smiling_face_with_tear:

  1. maze_data_1.CSV
#,#,#,#,#,#,#,#,#,#
#,_,_,_,#,_,_,_,$,#
#,_,A,_,_,_,#,_,_,#
#,B,_,_,#,_,#,_,_,#
#,_,C,_,#,_,_,_,_,#
#,#,#,#,#,#,#,#,#,#
  1. robot_race.py
import robot_race_functions as rr
from collections import deque, Counter, namedtuple
from time import time, sleep

maze_file_name = 'maze_data_1.csv'
seconds_between_turns = 0.3
max_turns = 35

# Initialize the robot race
maze_data = rr.read_maze(maze_file_name)
rr.print_maze(maze_data)
walls, goal, bots = rr.process_maze_init(maze_data)

# Populate a deque of all robot commands for the provided maze
robot_moves = deque()
num_of_turns = 0
while not rr.is_race_over(bots) and num_of_turns < max_turns:
  # For every bot in the list of bots, if the bot has not reached the end, add a new move to the robot_moves deque
  # Add your code below!
  for bot in bots:
    if bot.has_finished == False:
      robot_moves.append(rr.compute_robot_logic(walls, goal, bot))
  num_of_turns += 1

# Count the number of moves based on the robot names
# Add your code below!
num_moves = Counter(move[0] for move in robot_moves)


# Count the number of collisions by robot name
# Add your code below!
num_collisions = Counter(move[0] for move in robot_moves if move[2] == True)

# Create a namedtuple to keep track of our robots' points
# Add your code below!
BotScoreData = namedtuple('BotScoreData', 'name, num_moves, num_collisions, score')

# Populate a dict to keep track of the robot movements
# Add your code below!
bot_scores = []
for bot in bots:
  score = num_moves[bot] + num_collisions[bot]
  bot_scores.append(BotScoreData(bot.name, num_moves[bot], num_collisions[bot], score))


bot_data = {}
for bot in bots:
  bot_data[bot.name] = bot

# Move the robots and update the map based on the moves deque
while len(robot_moves) > 0:
  # Make sure to pop moves from the front of the deque
  # Add your code below!
  bot_name, move, hit_wall = robot_moves.popleft()
  bot_data[bot_name].process_move(move)

  # Update the maze characters based on the robot positions and print it to the console
  rr.update_maze_characters(maze_data, bots)
  rr.print_maze(maze_data)
  sleep(seconds_between_turns - time() % seconds_between_turns)

# Print out the results!
rr.print_results(bot_scores)
  1. robot_race_functions.py
import csv
import random as rand


def read_maze(name):
    maze_chars = []
    with open(name, 'r') as csvfile:
        r = csv.reader(csvfile)
        for row in r:
            maze_chars.append(row)
    return maze_chars


def print_maze(maze_data):
    for row in maze_data:
        printed_row = ''
        for col in row:
            printed_row += col + '  '
        print(printed_row)
    print('\n')

def is_race_over(bots):
    done = True
    for bot in bots:
        if not bot.has_finished:
            done = False
    return done


def print_results(bot_score_data):
    bot_score_data.sort(key=lambda b: b.score)
    place = 1
    print("----- RESULTS -----")
    for score_data in bot_score_data:
        print(str(place) + '. Robot: ' + str(score_data.name))
        print('  ' +  'Score: ' + str(score_data.score) + ' Moves: ' + str(score_data.num_moves) + ' Collisions: ' + str(score_data.num_collisions))
        place += 1


def process_maze_init(maze_data):
    walls = []
    goal = None
    bots = []
    for r, row in enumerate(maze_data):
        for c, col in enumerate(row):
            if col == '#':
                walls.append(Wall(c,r))
            elif col == '$':
                goal = Goal(c,r)
            elif col.isalpha():
                bots.append(Robot(c,r, col))
    return [walls, goal, bots]


def compute_robot_logic(walls, goal, bot):
    moves = [(-1, 0), (1, 0), (0, -1), (0, 1)]
    move_names = {(-1, 0): 'left', (1, 0): 'right', (0, -1): 'down', (0, 1): 'up', (0, 0): 'nothing'}

    selected_move = moves[rand.randint(0,3)]
    move_dist = []
    for m, move in enumerate(moves):
        dist = calc_manhattan_dist(bot.calc_x + move[0], bot.calc_y + move[1], goal.x, goal.y)
        move_dist.append([m,dist])
    move_dist.sort(key=lambda x: x[1])
    if rand.random() < 0.45:
        selected_move = moves[move_dist[0][0]]

    hit_wall = False
    for wall in walls:
        if bot.calc_x + selected_move[0] == wall.x and bot.calc_y + selected_move[1] == wall.y:
            hit_wall = True

    found_alternate_move = False
    if hit_wall:
        for next_move in move_dist:
            move = moves[next_move[0]]
            hit_wall_move = False
            for wall in walls:
                if bot.calc_x + move[0] == wall.x and bot.calc_y + move[1] == wall.y:
                    hit_wall_move = True

            if not hit_wall_move:
                selected_move = move
                found_alternate_move = True
                break

        if not found_alternate_move:
            selected_move = (0,0)

    if bot.calc_x + selected_move[0] == goal.x and bot.calc_y + selected_move[1] == goal.y:
        bot.has_finished = True
        bot.calc_x += selected_move[0]
        bot.calc_y += selected_move[1]
        return bot.name, 'finished', hit_wall

    bot.calc_x += selected_move[0]
    bot.calc_y += selected_move[1]
    return bot.name, move_names[selected_move], hit_wall


def update_maze_characters(old_maze_chars, bots):
    to_replace = []
    for r, row in enumerate(old_maze_chars):
        for c, col in enumerate(row):
            if col.isalpha() or col == '+':
                to_replace.append((c,r))
    for elem in to_replace:
        old_maze_chars[elem[1]][elem[0]] = '_'
    for bot in bots:
        if not bot.remove:
            if old_maze_chars[bot.y][bot.x].isalpha():
                old_maze_chars[bot.y][bot.x] = '+'
            else:
                old_maze_chars[bot.y][bot.x] = bot.name


def calc_manhattan_dist(x1, y1, x2, y2):
    return abs(x1 - x2) + abs(y1 - y2)


class Robot:
    def __init__(self, x, y, name):
        self.x = x
        self.calc_x = x
        self.y = y
        self.calc_y = y
        self.has_finished = False
        self.remove = False
        self.name = name

    def process_move(self, direction):
        if direction == 'left':
            self.x += -1
        if direction == 'right':
            self.x += 1
        if direction == 'up':
            self.y += 1
        if direction == 'down':
            self.y += -1
        if direction == 'finished':
            self.remove = True


class Wall:
    def __init__(self, x, y):
        self.x = x
        self.y = y


class Goal:
    def __init__(self, x, y):
        self.x = x
        self.y = y```

I had a different thing for the namedTuple:
BotScoreData = namedtuple('BotScoreData', ['name', 'num_moves', 'num_collisions', 'score'])

Would you mind sharing your code with me?

Hello!

I took your code and tried it out on my machine, and there is no infinite loop!
It’s took some time but then it ouput all race to the terminal!
Here a snippet of the output:

# # # # # # # #

_ _ _ # _ C _ $

_ _ _ _ _ # _ _

_ _ _ # _ # _ _

_ _ _ # _ _ _ _

# # # # # # # #

# # # # # # # #

_ _ _ # _ _ C $

_ _ _ _ _ # _ _

_ _ _ # _ # _ _

_ _ _ # _ _ _ _

# # # # # # # #

# # # # # # # #

_ _ _ # _ _ _ $

_ _ _ _ _ # _ _

_ _ _ # _ # _ _

_ _ _ # _ _ _ _

# # # # # # # #

----- RESULTS -----

  1. Robot: A
    Score: 0 Moves: 0 Collisions: 0
  2. Robot: B
    Score: 0 Moves: 0 Collisions: 0
  3. Robot: C
    Score: 0 Moves: 0 Collisions: 0

As you can see, your code doesn’t produce a result for robots. This is because you have to pass bot.name instead just a bot to calculate moves and collisions in your for loop:

for bot in bots:
  score = num_moves[bot.name] + num_collisions[bot.name]
  bot_scores.append(BotScoreData(bot.name, num_moves[bot.name], num_collisions[bot.name], score))
5 Likes

Wow… thanks!! So, the problem here is that I’m not patient enough haha

Btw, the reason to put bot.name there was it because bot is an instance?
And, we use bot.name to get the name of the bot.

Is my logic right?

For shure! You have to pass bot.name to index into numbers desired for calculation.
As for output, i can rememner, when i was doing this project by myself, there was instant output to terminal. When i was making my first reply, i checked your code on win10 machine and it took a while to proccess all the code and then output showed up. After your reply i ran your code on Ubuntu machine and their was instant output for every print_maze function. That to say win10 stdout not doing it well, as for codecademy, as Ubuntu use bash terminal stdout and the result is not making you wait. :grin:

1 Like

I see… Yes, I’m using Win10 :smiling_face_with_tear:
First time hearing Ubuntu, will check it out!

Thanks for helping me getting out of this project tho! :green_heart:

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.