128 lines
3.7 KiB
Python
128 lines
3.7 KiB
Python
"""求解器与棋盘模型的单元测试(不依赖 MaaFramework / 真机)。"""
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src"))
|
||
|
||
from hbc.board import Board, Cell, CellKind # noqa: E402
|
||
from hbc.solver import Solver, Swap # noqa: E402
|
||
|
||
BRICK = -2
|
||
EMPTY = -1
|
||
|
||
|
||
def test_cell_types():
|
||
assert Cell.normal(3).is_swappable
|
||
assert not Cell.brick().is_swappable
|
||
assert not Cell.empty().is_swappable
|
||
assert Cell.brick().is_brick
|
||
assert Cell.empty().is_empty
|
||
|
||
|
||
def test_dynamic_rectangular_sizes():
|
||
for rows, cols in [(5, 9), (8, 8), (6, 7), (3, 3)]:
|
||
b = Board(rows, cols)
|
||
assert b.rows == rows and b.cols == cols
|
||
assert b.in_bounds(rows - 1, cols - 1)
|
||
assert not b.in_bounds(rows, cols)
|
||
|
||
|
||
def test_find_horizontal_match():
|
||
board = Board.from_colors([
|
||
[0, 0, 0, 1],
|
||
[2, 3, 4, 5],
|
||
])
|
||
matches = Solver().find_matches(board)
|
||
assert len(matches) == 1
|
||
assert matches[0].length == 3
|
||
assert matches[0].color == 0
|
||
|
||
|
||
def test_brick_breaks_the_line():
|
||
# 砖块 (#) 打断同色连线 -> 不应形成消除
|
||
board = Board.from_colors([
|
||
[0, 0, BRICK, 0],
|
||
])
|
||
assert not Solver().has_match(board)
|
||
|
||
|
||
def test_empty_breaks_the_line():
|
||
board = Board.from_colors([
|
||
[0, 0, EMPTY, 0],
|
||
])
|
||
assert not Solver().has_match(board)
|
||
|
||
|
||
def test_best_swap_makes_a_match():
|
||
# 把 (0,3) 的 0 与 (1,3) 的 X 交换? 这里构造:交换 (0,2)&(0,3) 形成三连
|
||
board = Board.from_colors([
|
||
[0, 0, 1, 0],
|
||
[2, 3, 0, 4],
|
||
])
|
||
# 交换 (0,2)=1 与 (1,2)=0 -> 第0行变 0 0 0 0(前三连),合法
|
||
solver = Solver()
|
||
result = solver.find_best_swap(board)
|
||
assert result is not None
|
||
assert result.cleared >= 3
|
||
|
||
|
||
def test_cannot_swap_brick_or_empty():
|
||
board = Board.from_colors([
|
||
[0, BRICK, 0],
|
||
[0, 0, 0],
|
||
])
|
||
solver = Solver()
|
||
# 砖块不可交换:尝试交换砖块应被判非法
|
||
assert solver.evaluate_swap(board, Swap(0, 1, 1, 1)) is None
|
||
|
||
|
||
def test_no_swap_available():
|
||
# 棋盘上任何交换都无法形成消除(三色互不相邻,且任意相邻交换都凑不出 3 连)
|
||
board = Board.from_colors([
|
||
[0, 1, 2],
|
||
])
|
||
assert Solver().find_best_swap(board) is None
|
||
|
||
|
||
def test_prefer_longer_match():
|
||
# 一处可形成 3 连,另一处可形成 4 连 —— 应优先 4 连(得分更高)
|
||
board = Board.from_colors([
|
||
[0, 0, 0, 5, 1, 1, 9, 1],
|
||
[9, 9, 0, 9, 9, 9, 1, 9],
|
||
])
|
||
solver = Solver()
|
||
best = solver.find_best_swap(board)
|
||
assert best is not None
|
||
# 至少应找到一个可行交换
|
||
assert best.score > 0
|
||
|
||
|
||
def test_horizontal_swipe_is_right_to_left():
|
||
"""水平交换必须从右往左滑(避免触发返回手势);竖直交换不受限制。"""
|
||
import sys as _sys
|
||
from pathlib import Path as _Path
|
||
_sys.path.insert(0, str(_Path(__file__).resolve().parents[1] / "src"))
|
||
from hbc.actuator import swap_to_swipe
|
||
from hbc.config import GridConfig
|
||
|
||
grid = GridConfig(0, 0, 500, 500, 5, 5)
|
||
|
||
# 求解器产生的水平交换通常是 (r,c)->(r,c+1),即左->右;应被翻转成右->左
|
||
x1, y1, x2, y2 = swap_to_swipe(Swap(2, 1, 2, 2), grid)
|
||
assert x1 > x2 and y1 == y2 # 起点在右,终点在左
|
||
|
||
# 即使给的是右->左,也应保持右->左
|
||
x1, y1, x2, y2 = swap_to_swipe(Swap(2, 3, 2, 2), grid)
|
||
assert x1 > x2
|
||
|
||
# 竖直交换:x 相同,方向不限
|
||
x1, y1, x2, y2 = swap_to_swipe(Swap(1, 2, 2, 2), grid)
|
||
assert x1 == x2 and y1 != y2
|
||
|
||
|
||
if __name__ == "__main__":
|
||
import pytest
|
||
|
||
raise SystemExit(pytest.main([__file__, "-v"]))
|