[Python] Why does my move_car function not update car coordinates during a unit test, but...

Discussão em 'Python' iniciado por Stack, Dezembro 14, 2024.

  1. Stack

    Stack Membro Participativo

    I'm building a Rush Hour game where cars can be moved on a 6x6 grid. Each car is represented by its starting coordinates, orientation ('v' or 'h'), and length. The cars are moved by updating their x, y coordinates in the game state.

    Here's the issue: when I run the game normally in the main file, cars move as expected and their coordinates update correctly. However, when I call the same move_car function via a unit test, the car's position does not change as expected.

    Example: For car B:

    Starting position: (2, 0) Orientation: 'v' (vertical) Length: 3

    Here’s the relevant test:

    def test_move_car(self):
    game = deepcopy(TEST_GAME_GAME)
    move_car(game, 1, 'DOWN') # Moving car B down
    self.assertTupleEqual(
    game['cars'][1][0],
    (2, 1),
    "The coordinates of car B are incorrect. It should have moved down to (2, 1)"
    )



    When I run the test, it fails with the following error:

    AssertionError: Tuples differ: (2, 0) != (2, 1)

    First differing element 1:
    0
    1



    But when I move the car manually in the main file using the same function, the coordinates update properly and the car moves as expected.

    Here's the full code of the game:

    def parse_game(game_file_path: str) -> dict:

    with open(game_file_path, 'r') as file:
    grid_data = file.read()


    lines = [line.strip() for line in grid_data.splitlines() if line.strip()]
    #print(f"Initial lines (with borders): {lines}")

    max_moves = int(lines[-1])
    lines = lines[:-1]

    grid = [line[1:-1] for line in lines[1:-1]]
    #print(f"Grid without borders: {grid}")

    height = len(grid)
    width = len(grid[0])
    #print(f"Height: {height}, Width: {width}")

    game = {
    'width': width,
    'height': height,
    'max_moves': max_moves,
    'cars': [],
    }

    car_coordinates = {}

    for y in range(height):
    for x in range(width):
    char = grid[y][x]
    if char.isalpha(): # If it's a letter (representing a car)
    if char not in car_coordinates:
    car_coordinates[char] = []
    car_coordinates[char].append((x, y))

    # Debugging: Show the car coordinates dictionary
    print(f"Car coordinates: {car_coordinates}")

    car_index = 0
    for car in sorted(car_coordinates.keys()): # Sort the cars so they are processed in alphabetical order
    coords = car_coordinates[car]
    is_vertical = False
    is_horizontal = False
    length = len(coords)

    if length > 1: #if the car has more than one coordinate, check if it's vertical or horizontal
    if coords[0][0] == coords[1][0]: # Same X means vertical
    is_vertical = True
    else: # Same Y means horizontal
    is_horizontal = True


    print(f"Processing car {car}: coordinates {coords}, is_vertical={is_vertical}, is_horizontal={is_horizontal}, length={length}")

    #adds car data to the game dictionary, including the car index umu
    if is_vertical:
    start_x, start_y = coords[0]
    game['cars'].append([(start_x, start_y), 'v', length]) # Append as vertical car
    elif is_horizontal:
    start_x, start_y = coords[0]
    game['cars'].append([(start_x, start_y), 'h', length]) # Append as horizontal car

    car_index += 1 # Increment car index for the next car in the index

    # Final game state debugging
    print(f"Final game state: {game}")

    return game



    def is_win(game: dict) -> bool:
    car_a = game["cars"][0]
    (x, y), orientation, size = car_a
    width = game["width"]
    return orientation == 'h' and x + size == width


    def move_car(game: dict, car_index: int, direction: str) -> bool:
    cars = game['cars']
    width = game["width"]
    height = game["height"]
    car = cars[car_index]
    (x, y), orientation, size = car


    def get_occupied_coords(game, exclude_index=None):
    occupied = set()
    for i, other_car in enumerate(game["cars"]):
    if i == exclude_index:
    continue
    (ox, oy), o_orientation, o_size = other_car
    if o_orientation == "h":
    occupied.update((ox + j, oy) for j in range(o_size))
    elif o_orientation == "v":
    occupied.update((ox, oy + j) for j in range(o_size))
    return occupied

    occupied_coords = get_occupied_coords(game, car_index)

    if orientation == "h":
    if direction == "left":
    if x - 1 >= 0 and (x - 1, y) not in occupied_coords:
    x -= 1
    else:
    return False
    elif direction == "right":
    if x + size < width and (x + size, y) not in occupied_coords:
    x += 1
    else:
    return False
    else:
    return False

    elif orientation == "v":
    if direction == "up":
    if y - 1 >= 0 and (x, y - 1) not in occupied_coords:
    y -= 1
    cars[car_index] = [(x, y + j) for j in range(size)] #Recalculate positions
    #print(f"After move: {cars[car_index]}") #prints the updated state of the car


    else:
    return False
    elif direction == "down":
    if y + size < height and (x, y + size) not in occupied_coords:
    y += 1
    cars[car_index] = [(x, y + j) for j in range(size)] #Recalculate positions
    #print(f"After move: {cars[car_index]}") #prints the updated state of the car

    else:
    return False
    else:
    return False

    cars[car_index] = ((x, y), orientation, size)
    #print(f"After move: {cars[car_index]}")

    return True


    def get_game_str(game: dict, current_move: int) -> str:
    width = game["width"]
    height = game["height"]
    cars = game["cars"]
    car_letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    colors = [
    "\u001b[47m", "\u001b[41m", "\u001b[42m", "\u001b[43m",
    "\u001b[44m", "\u001b[45m", "\u001b[46m", "\u001b[48;5;1m"
    ]

    grid = [["\u001b[40m" + "." + "\u001b[0m" for _ in range(width)] for _ in range(height)]

    for i, car in enumerate(cars):
    (x, y), orientation, size = car
    if x < 0 or y < 0 or x >= width or y >= height:
    continue
    car_letter = car_letters
    car_color = colors[i % len(colors)]

    if orientation == "h":
    for j in range(size):
    if x + j < width:
    grid[y][x + j] = car_color + car_letter + "\u001b[0m"

    elif orientation == "v":
    for j in range(size):
    if y + j < height:
    grid[y + j][x] = car_color + car_letter + "\u001b[0m"

    grid_str = ''
    for row in grid:
    grid_str += "|" + "".join(row) + "|" + "\n"

    grid_str = "+" + "-" * width + "+" + "\n" + grid_str + "+" + "-" * width + "+"
    max_moves = game.get('max_moves', 0)
    grid_str += f"\nCurrent Move: {current_move}/{max_moves}"
    return grid_str


    def play_game(game: dict) -> int:
    current_move = 0
    max_moves = game['max_moves']

    while current_move < max_moves:
    print(get_game_str(game, current_move))

    if is_win(game):
    print("Car A has exited! You win!")
    return 0

    car_index = int(input("Enter car index to move (0 for A, 1 for B, etc.): "))
    if car_index < 0 or car_index >= len(game["cars"]):
    print("Invalid car index. Try again.")
    continue

    direction = input("Enter direction (left, right, up, down): ").lower()
    if direction not in ['left', 'right', 'up', 'down']:
    print("Invalid direction. Try again.")
    continue

    print("Game state before move: ")
    print(game['cars'])

    if move_car(game, car_index, direction):
    print("Game state after move:")
    print(game['cars'])
    current_move += 1
    else:
    print("Move not possible. Try again.")

    print("Out of moves! Game over.")


    return 1



    if __name__ == '__main__':
    game = parse_game('game_file.txt')
    play_game(game)




    and the values used in the unit test:


    TEST_GAME_STR = """\
    +------+
    |..BCCC|
    |..B...|
    |AAB....
    |D..EEF|
    |D...GF|
    |.HHHGF|
    +------+
    40
    """

    TEST_GAME_GAME = {
    'width': 6,
    'height': 6,
    'max_moves': 40,
    'cars': (
    [(0, 2), 'h', 2], # Car A
    [(2, 0), 'v', 3], # Car B
    [(3, 0), 'h', 3], # Car C
    [(0, 3), 'v', 2], # Car D
    [(3, 3), 'h', 2], # Car E
    [(5, 3), 'v', 3], # Car F
    [(4, 4), 'v', 2], # Car G
    [(1, 5), 'h', 3] # Car H
    )
    }




    I've tried:


    • confirming car properties where B is vertical ('v') and has the correct starting position (2, 0).


    • adding print statements to verify the position of car B before and after calling move_car:

      Before: (2, 0) After: (2, 0) (unchanged during the test).


    • calling move_car in the main file (outside the test). The coordinates update as expected.

    Continue reading...

Compartilhe esta Página