增加L0训练阶段的MCTS部分

This commit is contained in:
hisatri
2025-07-23 07:04:10 +08:00
parent 88bed2a1ef
commit 4410defbe5
23 changed files with 5205 additions and 0 deletions

289
tests/test_game_engine.py Normal file
View File

@@ -0,0 +1,289 @@
"""
2048游戏引擎测试
验证新游戏引擎的功能和正确性
"""
import numpy as np
import pytest
from game import Game2048, GameState
class TestGame2048:
"""2048游戏引擎测试类"""
def setup_method(self):
"""测试前的设置"""
self.game = Game2048(height=4, width=4, seed=42)
def test_initialization(self):
"""测试游戏初始化"""
game = Game2048(height=3, width=4, seed=123)
assert game.height == 3
assert game.width == 4
assert game.score == 0
assert game.moves == 0
assert not game.is_over
# 应该有两个初始数字
non_zero_count = np.count_nonzero(game.board)
assert non_zero_count == 2
# 初始数字应该是1或2对数形式的2或4
non_zero_values = game.board[game.board != 0]
assert all(val in [1, 2] for val in non_zero_values)
def test_move_row_left(self):
"""测试行向左移动逻辑"""
# 测试简单移动
row = np.array([0, 1, 0, 2])
result, score = self.game._move_row_left(row)
expected = np.array([1, 2, 0, 0])
np.testing.assert_array_equal(result, expected)
assert score == 0
# 测试合并
row = np.array([1, 1, 2, 2])
result, score = self.game._move_row_left(row)
expected = np.array([2, 3, 0, 0])
np.testing.assert_array_equal(result, expected)
# 分数应该是 2^2 + 2^3 = 4 + 8 = 12
assert score == 12
# 测试复杂情况
row = np.array([1, 1, 1, 1])
result, score = self.game._move_row_left(row)
expected = np.array([2, 2, 0, 0])
np.testing.assert_array_equal(result, expected)
# 分数应该是 2^2 + 2^2 = 4 + 4 = 8
assert score == 8
def test_move_directions(self):
"""测试四个方向的移动"""
# 创建特定的棋盘状态
game = Game2048(height=3, width=3, seed=42)
game.board = np.array([
[1, 0, 1],
[0, 2, 0],
[1, 0, 1]
])
initial_score = game.score
# 测试向左移动
game_left = game.copy()
success = game_left.move(2) # 左
assert success
# 测试向右移动
game_right = game.copy()
success = game_right.move(3) # 右
assert success
# 测试向上移动
game_up = game.copy()
success = game_up.move(0) # 上
assert success
# 测试向下移动
game_down = game.copy()
success = game_down.move(1) # 下
assert success
# 所有移动都应该改变棋盘状态
assert not np.array_equal(game.board, game_left.board)
assert not np.array_equal(game.board, game_right.board)
assert not np.array_equal(game.board, game_up.board)
assert not np.array_equal(game.board, game_down.board)
def test_score_calculation(self):
"""测试分数计算"""
game = Game2048(height=2, width=2, seed=42)
# 设置特定棋盘状态
game.board = np.array([
[1, 2], # 2, 4
[3, 4] # 8, 16
])
# 计算累积分数
total_score = game.calculate_total_score()
# 根据论文公式V(N) = (log2(N) - 1) * N
# V(2) = 0, V(4) = 4, V(8) = 16, V(16) = 48
expected = 0 + 4 + 16 + 48
assert total_score == expected
def test_game_over_detection(self):
"""测试游戏结束检测"""
game = Game2048(height=2, width=2, seed=42)
# 设置无法移动的棋盘
game.board = np.array([
[1, 2], # 2, 4
[3, 4] # 8, 16
])
game._check_game_over()
assert game.is_over
# 测试可以移动的棋盘
game.board = np.array([
[1, 1], # 2, 2 (可以合并)
[3, 4] # 8, 16
])
game.is_over = False
game._check_game_over()
assert not game.is_over
def test_valid_moves(self):
"""测试有效移动检测"""
game = Game2048(height=2, width=2, seed=42)
# 设置可以向所有方向移动的棋盘
game.board = np.array([
[1, 0],
[0, 1]
])
valid_moves = game.get_valid_moves()
assert len(valid_moves) == 4 # 所有方向都可以移动
# 设置无法移动的棋盘
game.board = np.array([
[1, 2],
[3, 4]
])
valid_moves = game.get_valid_moves()
assert len(valid_moves) == 0 # 无法移动
def test_board_display(self):
"""测试棋盘显示"""
game = Game2048(height=2, width=2, seed=42)
# 设置对数形式的棋盘
game.board = np.array([
[0, 1], # 0, 2
[2, 3] # 4, 8
])
display_board = game.get_board_display()
expected = np.array([
[0, 2],
[4, 8]
])
np.testing.assert_array_equal(display_board, expected)
def test_max_tile(self):
"""测试最大数字获取"""
game = Game2048(height=2, width=2, seed=42)
game.board = np.array([
[1, 2], # 2, 4
[3, 4] # 8, 16
])
max_tile = game.get_max_tile()
assert max_tile == 16
def test_state_management(self):
"""测试游戏状态管理"""
game = Game2048(height=2, width=2, seed=42)
# 获取初始状态
initial_state = game.get_state()
assert isinstance(initial_state, GameState)
assert initial_state.score == game.score
assert initial_state.moves == game.moves
assert np.array_equal(initial_state.board, game.board)
# 执行移动
move_success = game.move(2) # 左移
# 获取新状态
new_state = game.get_state()
# 只有移动成功时才检查移动次数
if move_success:
assert new_state.moves == initial_state.moves + 1
assert not np.array_equal(new_state.board, initial_state.board)
else:
# 如果移动失败,尝试其他方向
for direction in range(4):
if game.move(direction):
new_state = game.get_state()
assert new_state.moves == initial_state.moves + 1
assert not np.array_equal(new_state.board, initial_state.board)
break
# 恢复状态
game.set_state(initial_state)
assert game.score == initial_state.score
assert game.moves == initial_state.moves
np.testing.assert_array_equal(game.board, initial_state.board)
def test_copy_functionality(self):
"""测试游戏复制功能"""
game = Game2048(height=3, width=3, seed=42)
# 执行一些操作
game.move(2)
game.move(0)
# 创建副本
game_copy = game.copy()
# 验证副本
assert game_copy.height == game.height
assert game_copy.width == game.width
assert game_copy.score == game.score
assert game_copy.moves == game.moves
assert game_copy.is_over == game.is_over
np.testing.assert_array_equal(game_copy.board, game.board)
# 修改副本不应影响原游戏
game_copy.move(1)
assert game_copy.moves != game.moves
def test_different_board_sizes(self):
"""测试不同大小的棋盘"""
# 测试3x3棋盘
game_3x3 = Game2048(height=3, width=3, seed=42)
assert game_3x3.board.shape == (3, 3)
# 测试2x4矩形棋盘
game_2x4 = Game2048(height=2, width=4, seed=42)
assert game_2x4.board.shape == (2, 4)
# 测试移动功能
success = game_3x3.move(2)
assert isinstance(success, bool)
success = game_2x4.move(0)
assert isinstance(success, bool)
def test_spawn_probability(self):
"""测试数字生成概率"""
# 测试只生成2的情况
game_only_2 = Game2048(height=4, width=4, spawn_prob_4=0.0, seed=42)
# 重置并检查生成的数字
game_only_2.reset()
non_zero_values = game_only_2.board[game_only_2.board != 0]
assert all(val == 1 for val in non_zero_values) # 只有1对数形式的2
# 测试只生成4的情况
game_only_4 = Game2048(height=4, width=4, spawn_prob_4=1.0, seed=42)
game_only_4.reset()
non_zero_values = game_only_4.board[game_only_4.board != 0]
assert all(val == 2 for val in non_zero_values) # 只有2对数形式的4
if __name__ == "__main__":
# 运行测试
print("运行2048游戏引擎测试...")
pytest.main([__file__, "-v"])