fix: 修复悔棋奇数步崩溃及空历史错误语义 (NoHistory)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 13:38:04 +08:00
parent 8c3289e631
commit cd9c3a79ab
3 changed files with 14 additions and 4 deletions
+8 -3
View File
@@ -99,7 +99,7 @@ impl Board {
/// 悔棋 — 撤销最近一步 /// 悔棋 — 撤销最近一步
pub fn undo(&self) -> Result<Board, MoveError> { pub fn undo(&self) -> Result<Board, MoveError> {
if self.history.is_empty() { if self.history.is_empty() {
return Err(MoveError::GameOver); return Err(MoveError::NoHistory);
} }
let mut new_board = self.clone(); let mut new_board = self.clone();
let last_move = new_board.history.pop().unwrap(); let last_move = new_board.history.pop().unwrap();
@@ -253,9 +253,14 @@ mod tests {
} }
#[test] #[test]
fn test_undo_empty_history() { fn test_undo_empty_history_returns_no_history_error() {
let board = Board::new(15); let board = Board::new(15);
assert_eq!(board.undo(), Err(MoveError::GameOver)); let result = board.undo();
assert!(result.is_err());
match result {
Err(MoveError::NoHistory) => {},
other => panic!("expected NoHistory, got {:?}", other),
}
} }
#[test] #[test]
+2
View File
@@ -68,6 +68,7 @@ pub enum MoveError {
Occupied, Occupied,
ForbiddenMove, ForbiddenMove,
GameOver, GameOver,
NoHistory,
} }
impl std::fmt::Display for MoveError { impl std::fmt::Display for MoveError {
@@ -77,6 +78,7 @@ impl std::fmt::Display for MoveError {
MoveError::Occupied => "该位置已有棋子", MoveError::Occupied => "该位置已有棋子",
MoveError::ForbiddenMove => "禁手位置,不能落子", MoveError::ForbiddenMove => "禁手位置,不能落子",
MoveError::GameOver => "游戏已结束", MoveError::GameOver => "游戏已结束",
MoveError::NoHistory => "没有可撤销的棋子",
}; };
write!(f, "{}", msg) write!(f, "{}", msg)
} }
+4 -1
View File
@@ -94,7 +94,10 @@ pub fn undo(steps: u32, state: State<AppState>) -> Result<(), String> {
let mut board_opt = state.board.lock().map_err(|e| e.to_string())?; let mut board_opt = state.board.lock().map_err(|e| e.to_string())?;
let mut board = board_opt.clone().ok_or("游戏未开始")?; let mut board = board_opt.clone().ok_or("游戏未开始")?;
for _ in 0..steps * 2 { let max_undo = board.history().len() as u32;
let actual_steps = (steps * 2).min(max_undo);
for _ in 0..actual_steps {
board = board.undo().map_err(|e| e.to_string())?; board = board.undo().map_err(|e| e.to_string())?;
} }