为什么我的 move_car 函数在单元测试期间没有更新汽车坐标,但在主文件中按预期工作?

问题描述 投票:0回答:1

我正在构建一个高峰时间游戏,其中汽车可以在 6x6 网格上移动。每辆车都由其起始坐标、方向(“v”或“h”)和长度表示。通过在游戏状态下更新汽车的 x、y 坐标来移动汽车。

问题是:当我在主文件中正常运行游戏时,汽车按预期移动并且坐标更新正确。但是,当我通过单元测试调用相同的 move_car 函数时,汽车的位置没有按预期改变。

示例: B车:

起始位置:(2, 0) 方向:“v”(垂直) 长度:3

相关测试如下:

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)"
    )

当我运行测试时,它失败并出现以下错误:

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

First differing element 1:
0
1

但是当我使用相同的功能在主文件中手动移动汽车时,坐标会正确更新并且汽车按预期移动。

这是游戏的完整代码:

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[i]
        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)


以及单元测试中使用的值:


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
    )
}


我已经尝试过:

  • 确认汽车属性,其中 B 是垂直的 ('v') 并且具有正确的起始位置 (2, 0)。

  • 添加打印语句以验证调用 move_car 之前和之后 B 车的位置:

    之前:(2, 0) 之后:(2, 0)(测试期间不变)。

  • 在主文件中调用move_car(在测试之外)。坐标按预期更新。

python
1个回答
0
投票

Pythin 的

tuple
是不可变的,你的
TEST_GAME_GAME['cars']
被定义为一个元组。虽然
deepcopy()
复制结构,但元组仍然保持不变,从而防止
move_car()
对其进行修改。因此,更改您的测试以不使用元组。

© www.soinside.com 2019 - 2024. All rights reserved.