我正在构建一个高峰时间游戏,其中汽车可以在 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(在测试之外)。坐标按预期更新。
Pythin 的
tuple
是不可变的,你的 TEST_GAME_GAME['cars']
被定义为一个元组。虽然 deepcopy()
复制结构,但元组仍然保持不变,从而防止 move_car()
对其进行修改。因此,更改您的测试以不使用元组。