diff --git a/game.go b/game.go index 67f7cd2..aefc4b4 100644 --- a/game.go +++ b/game.go @@ -195,6 +195,19 @@ func (g *Game) Moves() []*Move { return append([]*Move(nil), g.moves...) } +// Undo reverts the last move and restores the previous game state. +func (g *Game) Undo() (*Move, error) { + if len(g.moves) == 0 { + return nil, errors.New("chess: no moves to undo") + } + lastMove := g.moves[len(g.moves)-1] + g.moves = g.moves[:len(g.moves)-1] + g.positions = g.positions[:len(g.positions)-1] + g.pos = g.positions[len(g.positions)-1] + g.updatePosition() + return lastMove, nil +} + // Comments returns the comments for the game indexed by moves. func (g *Game) Comments() [][]string { return append([][]string(nil), g.comments...) @@ -378,7 +391,11 @@ func (g *Game) updatePosition() { if g.pos.Turn() == White { g.outcome = BlackWon } + } else if method == NoMethod { + g.method = NoMethod + g.outcome = NoOutcome } + if g.outcome != NoOutcome { return } diff --git a/game_test.go b/game_test.go index ec60e3e..51221e2 100644 --- a/game_test.go +++ b/game_test.go @@ -110,6 +110,61 @@ func TestThreeFoldRepetition(t *testing.T) { } } +func TestUndo(t *testing.T) { + g := NewGame() + _, err := g.Undo() + if err == nil { + t.Fatal("should require at least one move before undo") + } + + moves := []string{ + "Nf3", "Nf6", "Ng1", "Ng8", + } + + for _, m := range moves { + if err = g.MoveStr(m); err != nil { + t.Fatal(err) + } + } + + lastMove, err := g.Undo() + if err != nil { + t.Fatal(err) + } + + if lastMove.String() != "f6g8" { + t.Fatal("last move should be f6g8") + } + + if g.moves[len(g.moves)-1].String() != "f3g1" { + t.Fatal("last move should be f3g1") + } + + g = NewGame() + moves = []string{ + "f4", "e6", "g4", "Qh4", + } + + for _, m := range moves { + if err = g.MoveStr(m); err != nil { + t.Fatal(err) + } + } + + lastMove, err = g.Undo() + if err != nil { + t.Fatal(err) + } + + if err = g.MoveStr("h6"); err != nil { + t.Fatal(err) + } + if g.Outcome() != NoOutcome { + t.Fatal("unexpected game outcome") + } + +} + func TestInvalidThreeFoldRepetition(t *testing.T) { g := NewGame() moves := []string{