From 4ae8a49d8e4c688460c8df2bf7253a7553d80318 Mon Sep 17 00:00:00 2001 From: Dietrich Kappe Date: Wed, 20 Jun 2018 23:40:37 -0500 Subject: [PATCH] Added undo move method and optimized board string methods. Gone from 2 knps to 66 knps. Should replace the string approach to position repitition with zobrist hashing. --- board.go | 15 +++++++++------ game.go | 21 +++++++++++++++++++++ piece.go | 30 ++++++++++++++++++++++++++++++ position.go | 22 ++++++++++++++++++++-- 4 files changed, 80 insertions(+), 8 deletions(-) diff --git a/board.go b/board.go index 628fbb4..d0ca13f 100644 --- a/board.go +++ b/board.go @@ -3,6 +3,7 @@ package chess import ( "strconv" "strings" + "bytes" ) // A Board represents a chess board and its relationship between squares and pieces. @@ -59,28 +60,30 @@ func (b *Board) Draw() string { // String implements the fmt.Stringer interface and returns // a string in the FEN board format: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR +// TODO use zobrist instead func (b *Board) String() string { - fen := "" + var fen bytes.Buffer + var retval string for r := 7; r >= 0; r-- { for f := 0; f < numOfSquaresInRow; f++ { sq := getSquare(File(f), Rank(r)) p := b.Piece(sq) if p != NoPiece { - fen += p.getFENChar() + fen.WriteString(p.getFENChar()) } else { - fen += "1" + fen.WriteString("1") } } if r != 0 { - fen += "/" + fen.WriteString("/") } } for i := 8; i > 1; i-- { repeatStr := strings.Repeat("1", i) countStr := strconv.Itoa(i) - fen = strings.Replace(fen, repeatStr, countStr, -1) + retval = strings.Replace(fen.String(), repeatStr, countStr, -1) } - return fen + return retval } // Piece returns the piece for the given square. diff --git a/game.go b/game.go index 2928914..1e4d516 100644 --- a/game.go +++ b/game.go @@ -170,6 +170,26 @@ func (g *Game) Move(m *Move) error { return nil } +// Unmove unmakes the last move +// and sets the game state to the +// one of the previous move +func (g *Game) Unmove() error { + if (len(g.moves) < 1) { + return fmt.Errorf("chess: no previous moves to unmake") + } + // delete the last move + g.moves = g.moves[:len(g.moves)-1] + // delete the last position + g.positions = g.positions[:len(g.positions)-1] + // update the current position to the last one + g.pos = g.positions[len(g.positions)-1] + // reset outcome, etc. + g.method = NoMethod + g.outcome = NoOutcome + //g.updatePosition() + return nil +} + // MoveStr decodes the given string in game's notation // and calls the Move function. An error is returned if // the move can't be decoded or the move is invalid. @@ -335,6 +355,7 @@ func (g *Game) RemoveTagPair(k string) bool { func (g *Game) updatePosition() { method := g.pos.Status() + if method == Stalemate { g.method = Stalemate g.outcome = Draw diff --git a/piece.go b/piece.go index 50875c5..97e0a1f 100644 --- a/piece.go +++ b/piece.go @@ -183,10 +183,40 @@ var ( ) func (p Piece) getFENChar() string { + switch p { + case WhiteKing: + return "K" + case WhiteQueen: + return "Q" + case WhiteRook: + return "R" + case WhiteBishop: + return "B" + case WhiteKnight: + return "N" + case WhitePawn: + return "P" + case BlackKing: + return "k" + case BlackQueen: + return "q" + case BlackRook: + return "r" + case BlackBishop: + return "b" + case BlackKnight: + return "n" + case BlackPawn: + return "p" + default: + return "" + } + /* for key, piece := range fenPieceMap { if piece == p { return key } } return "" + */ } diff --git a/position.go b/position.go index 59a1cc5..c92ea0d 100644 --- a/position.go +++ b/position.go @@ -43,6 +43,8 @@ func (cr CastleRights) String() string { // to its outcome. Position is translatable to FEN notation. type Position struct { board *Board + cached_board_string string + cached_castle_string string turn Color castleRights CastleRights enPassantSquare Square @@ -74,6 +76,8 @@ func (pos *Position) Update(m *Move) *Position { b.update(m) return &Position{ board: b, + cached_board_string: "", + cached_castle_string: "", turn: pos.turn.Other(), castleRights: ncr, enPassantSquare: pos.updateEnPassantSquare(m), @@ -213,9 +217,23 @@ func (pos *Position) updateEnPassantSquare(m *Move) Square { return NoSquare } +func (pos *Position)boardString() string { + if (pos.cached_board_string == "") { + pos.cached_board_string = pos.board.String() + } + return pos.cached_board_string +} + +func (pos *Position)castleString() string { + if (pos.cached_castle_string == "") { + pos.cached_castle_string = pos.castleRights.String() + } + return pos.cached_castle_string +} + func (pos *Position) samePosition(pos2 *Position) bool { - return pos.board.String() == pos2.board.String() && + return pos.boardString() == pos2.boardString() && pos.turn == pos2.turn && - pos.castleRights.String() == pos2.castleRights.String() && + pos.castleString() == pos2.castleString() && pos.enPassantSquare == pos2.enPassantSquare }