Implement en passant and much more!
This commit is contained in:
parent
9793c37582
commit
f79e5be008
197
chess/board.go
197
chess/board.go
@ -6,67 +6,76 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Board map[types.Coordinate]Piece
|
type Position map[types.Coordinate]Piece
|
||||||
|
|
||||||
func (b Board) Init() {
|
type Board struct {
|
||||||
|
position Position
|
||||||
|
history []types.Move
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBoard() Board {
|
||||||
|
return Board{
|
||||||
|
position: make(Position),
|
||||||
|
history: make([]types.Move, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Board) Init() {
|
||||||
var coord types.Coordinate
|
var coord types.Coordinate
|
||||||
|
|
||||||
for i := 1; i <= 8; i++ {
|
for i := 1; i <= 8; i++ {
|
||||||
coord.Row = 2
|
coord.Row = 2
|
||||||
coord.Col = i
|
coord.Col = i
|
||||||
b[coord] = Pawn{Color: types.White}
|
b.position[coord] = Pawn{Color: types.White}
|
||||||
|
|
||||||
coord.Row = 7
|
coord.Row = 7
|
||||||
coord.Col = i
|
coord.Col = i
|
||||||
b[coord] = Pawn{Color: types.Black}
|
b.position[coord] = Pawn{Color: types.Black}
|
||||||
}
|
}
|
||||||
|
|
||||||
b[types.Coordinate{Row: 1, Col: 1}] = Rook{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 1}] = Rook{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 2}] = Knight{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 2}] = Knight{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 3}] = Bishop{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 3}] = Bishop{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 4}] = Queen{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 4}] = Queen{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 5}] = King{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 5}] = King{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 6}] = Bishop{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 6}] = Bishop{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 7}] = Knight{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 7}] = Knight{Color: types.White}
|
||||||
b[types.Coordinate{Row: 1, Col: 8}] = Rook{Color: types.White}
|
b.position[types.Coordinate{Row: 1, Col: 8}] = Rook{Color: types.White}
|
||||||
|
|
||||||
b[types.Coordinate{Row: 8, Col: 1}] = Rook{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 1}] = Rook{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 2}] = Knight{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 2}] = Knight{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 3}] = Bishop{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 3}] = Bishop{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 4}] = Queen{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 4}] = Queen{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 5}] = King{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 5}] = King{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 6}] = Bishop{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 6}] = Bishop{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 7}] = Knight{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 7}] = Knight{Color: types.Black}
|
||||||
b[types.Coordinate{Row: 8, Col: 8}] = Rook{Color: types.Black}
|
b.position[types.Coordinate{Row: 8, Col: 8}] = Rook{Color: types.Black}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Board) CheckMove(move types.Move) (bool, string) {
|
func (b *Board) CheckAndPlay(move types.Move) (bool, string) {
|
||||||
// We make a copy of the original board to play moves on it,
|
// We make a copy of the original board to play moves on it,
|
||||||
// We can play the move on it and then check if it is invalid
|
// We can play the move on it and then check if it is invalid
|
||||||
tempBoard := b.getCopyOfBoard()
|
tempBoard := b.getCopyOfBoard()
|
||||||
|
|
||||||
//Check start square of move
|
//Check start square of move
|
||||||
pieceAtStartSquare := b.getPieceAt(move.StartSquare)
|
pieceAtStartSquare := tempBoard.getPieceAt(move.StartSquare)
|
||||||
if pieceAtStartSquare == nil {
|
if pieceAtStartSquare == nil {
|
||||||
return false, "no piece at start square"
|
return false, "no piece at start square"
|
||||||
}
|
}
|
||||||
movingColor := pieceAtStartSquare.GetColor()
|
move.ColorMoved = pieceAtStartSquare.GetColor()
|
||||||
|
move.PieceMoved = GetShortNameForPiece(pieceAtStartSquare)
|
||||||
|
|
||||||
//Check end square of move
|
//Check end square of move
|
||||||
pieceAtEndSquare := b.getPieceAt(move.EndSquare)
|
pieceAtEndSquare := tempBoard.getPieceAt(move.EndSquare)
|
||||||
if pieceAtEndSquare != nil {
|
if pieceAtEndSquare != nil {
|
||||||
if pieceAtEndSquare.GetColor() == pieceAtStartSquare.GetColor() {
|
if pieceAtEndSquare.GetColor() == pieceAtStartSquare.GetColor() {
|
||||||
return false, "same-coloured piece at end square"
|
return false, "same-coloured piece at end square"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var wasPromotionMove bool
|
|
||||||
// var piece types.PieceShortName
|
|
||||||
switch pieceAtStartSquare.(type) {
|
|
||||||
case Pawn:
|
|
||||||
wasPromotionMove, _ = tempBoard.handlePossiblePromotion(move, movingColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !wasPromotionMove {
|
wasSpecialMove := tempBoard.handleSpecialMove(move)
|
||||||
|
|
||||||
|
if !wasSpecialMove {
|
||||||
// At the moment, we do not need to check if the correct color is moving,
|
// At the moment, we do not need to check if the correct color is moving,
|
||||||
//since we are only reading moves from the player whose turn it is.
|
//since we are only reading moves from the player whose turn it is.
|
||||||
allMovesExceptBlocked := pieceAtStartSquare.GetAllMovesButBlocked(tempBoard, move.StartSquare)
|
allMovesExceptBlocked := pieceAtStartSquare.GetAllMovesButBlocked(tempBoard, move.StartSquare)
|
||||||
@ -76,18 +85,18 @@ func (b Board) CheckMove(move types.Move) (bool, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//We play the move on the temporary board
|
//We play the move on the temporary board
|
||||||
delete(tempBoard, move.StartSquare)
|
delete(tempBoard.position, move.StartSquare)
|
||||||
tempBoard[move.EndSquare] = pieceAtStartSquare
|
tempBoard.position[move.EndSquare] = pieceAtStartSquare
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check if king of moving color is in check -> move not allowed
|
//Check if king of moving color is in check -> move not allowed
|
||||||
//Do that by checking if the king is in a square attacked by the other color.
|
//Do that by checking if the king is in a square attacked by the other color.
|
||||||
ownKingCoordinate := tempBoard.getSquareOfPiece(King{Color: movingColor})
|
ownKingCoordinate := tempBoard.getSquareOfPiece(King{Color: move.ColorMoved})
|
||||||
if ownKingCoordinate == nil {
|
if ownKingCoordinate == nil {
|
||||||
return false, string(movingColor) + " king not found"
|
return false, string(move.ColorMoved) + " king not found"
|
||||||
}
|
}
|
||||||
|
|
||||||
kingIsAttacked := tempBoard.isSquareAttacked(*ownKingCoordinate, movingColor.Opposite())
|
kingIsAttacked := tempBoard.isSquareAttacked(*ownKingCoordinate, move.ColorMoved.Opposite())
|
||||||
if kingIsAttacked {
|
if kingIsAttacked {
|
||||||
return false, "king is attacked after move"
|
return false, "king is attacked after move"
|
||||||
}
|
}
|
||||||
@ -106,13 +115,15 @@ func (b Board) CheckMove(move types.Move) (bool, string) {
|
|||||||
// If a piece can be moved into the path, it is no checkmate
|
// If a piece can be moved into the path, it is no checkmate
|
||||||
|
|
||||||
//We play the move on the real board
|
//We play the move on the real board
|
||||||
b = tempBoard
|
b.position = tempBoard.position
|
||||||
|
b.history = tempBoard.history
|
||||||
|
b.appendMoveToHistory(move)
|
||||||
|
|
||||||
return true, ""
|
return true, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Board) getSquareOfPiece(piece Piece) *types.Coordinate {
|
func (b Board) getSquareOfPiece(piece Piece) *types.Coordinate {
|
||||||
for k, v := range b {
|
for k, v := range b.position {
|
||||||
if v == piece {
|
if v == piece {
|
||||||
return &k
|
return &k
|
||||||
}
|
}
|
||||||
@ -123,7 +134,7 @@ func (b Board) getSquareOfPiece(piece Piece) *types.Coordinate {
|
|||||||
func (b Board) isSquareAttacked(square types.Coordinate, byColor types.ChessColor) bool {
|
func (b Board) isSquareAttacked(square types.Coordinate, byColor types.ChessColor) bool {
|
||||||
var attackedSquares []types.Coordinate
|
var attackedSquares []types.Coordinate
|
||||||
|
|
||||||
for square, piece := range b {
|
for square, piece := range b.position {
|
||||||
attackedSquares = append(attackedSquares, piece.GetAllMovesButBlocked(b, square)...)
|
attackedSquares = append(attackedSquares, piece.GetAllMovesButBlocked(b, square)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +142,7 @@ func (b Board) isSquareAttacked(square types.Coordinate, byColor types.ChessColo
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b Board) getPieceAt(coord types.Coordinate) Piece {
|
func (b Board) getPieceAt(coord types.Coordinate) Piece {
|
||||||
piece, found := b[coord]
|
piece, found := b.position[coord]
|
||||||
if !found {
|
if !found {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -139,17 +150,59 @@ func (b Board) getPieceAt(coord types.Coordinate) Piece {
|
|||||||
return piece
|
return piece
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Board) handlePossiblePromotion(move types.Move, color types.ChessColor) (bool, types.PieceShortName) {
|
func (b *Board) appendMoveToHistory(move types.Move) {
|
||||||
|
b.history = append(b.history, move)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Board) getLastMove() types.Move {
|
||||||
|
if len(b.history) < 1 {
|
||||||
|
return types.Move{}
|
||||||
|
}
|
||||||
|
return b.history[len(b.history)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b Board) getCopyOfBoard() Board {
|
||||||
|
return Board{
|
||||||
|
position: b.position.getCopyOfPosition(),
|
||||||
|
history: b.history,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Position) getCopyOfPosition() Position {
|
||||||
|
position := make(Position)
|
||||||
|
|
||||||
|
for coord, piece := range p {
|
||||||
|
position[coord] = piece
|
||||||
|
}
|
||||||
|
return position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Board) handleSpecialMove(move types.Move) bool {
|
||||||
|
var was bool
|
||||||
|
var pieceAtStartSquare = b.getPieceAt(move.StartSquare)
|
||||||
|
|
||||||
|
switch pieceAtStartSquare.(type) {
|
||||||
|
case Pawn:
|
||||||
|
was = b.handlePossiblePromotion(move)
|
||||||
|
if !was {
|
||||||
|
was = b.handleEnPassant(move, b.getLastMove())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return was
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Board) handlePossiblePromotion(move types.Move) bool {
|
||||||
var isPromotionMove bool
|
var isPromotionMove bool
|
||||||
var promotionToPiece types.PieceShortName
|
var promotionToPiece types.PieceShortName
|
||||||
|
|
||||||
|
//TODO(m): What if message does not contain a promotion, but should be because a pawn is moved to the end square
|
||||||
messageContainsPromotion := move.IsPromotionMove()
|
messageContainsPromotion := move.IsPromotionMove()
|
||||||
|
|
||||||
if messageContainsPromotion {
|
if messageContainsPromotion {
|
||||||
promotionToPiece = *move.PromotionToPiece
|
promotionToPiece = *move.PromotionToPiece
|
||||||
}
|
}
|
||||||
|
|
||||||
switch color {
|
switch move.ColorMoved {
|
||||||
case types.White:
|
case types.White:
|
||||||
if move.StartSquare.Row == types.RangeLastValid-1 &&
|
if move.StartSquare.Row == types.RangeLastValid-1 &&
|
||||||
move.EndSquare.Row == types.RangeLastValid {
|
move.EndSquare.Row == types.RangeLastValid {
|
||||||
@ -164,19 +217,63 @@ func (b Board) handlePossiblePromotion(move types.Move, color types.ChessColor)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if isPromotionMove {
|
if isPromotionMove {
|
||||||
delete(b, move.StartSquare)
|
delete(b.position, move.StartSquare)
|
||||||
b[move.EndSquare] = GetPieceForShortName(promotionToPiece)
|
b.position[move.EndSquare] = GetPieceForShortName(promotionToPiece, move.ColorMoved)
|
||||||
}
|
}
|
||||||
|
|
||||||
return isPromotionMove, promotionToPiece
|
return isPromotionMove
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Board) getCopyOfBoard() Board {
|
func (b *Board) handleEnPassant(move, lastMove types.Move) bool {
|
||||||
board := make(map[types.Coordinate]Piece)
|
var wasEnPassant bool
|
||||||
|
|
||||||
for coord, piece := range b {
|
if lastMove.PieceMoved != types.PawnShortName {
|
||||||
board[coord] = piece
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return board
|
switch move.ColorMoved {
|
||||||
|
case types.White:
|
||||||
|
if lastMove.StartSquare.Row != 7 || lastMove.EndSquare.Row != 5 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.StartSquare.Row != 5 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.EndSquare.Row != 6 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||||
|
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||||
|
wasEnPassant = true
|
||||||
|
}
|
||||||
|
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||||
|
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||||
|
wasEnPassant = true
|
||||||
|
}
|
||||||
|
case types.Black:
|
||||||
|
if lastMove.StartSquare.Row != 2 || lastMove.EndSquare.Row != 4 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.StartSquare.Row != 4 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.EndSquare.Row != 3 {
|
||||||
|
wasEnPassant = false
|
||||||
|
}
|
||||||
|
if move.StartSquare.Col == lastMove.EndSquare.Col+1 &&
|
||||||
|
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||||
|
wasEnPassant = true
|
||||||
|
}
|
||||||
|
if move.StartSquare.Col == lastMove.EndSquare.Col-1 &&
|
||||||
|
move.EndSquare.Col == lastMove.EndSquare.Col {
|
||||||
|
wasEnPassant = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if wasEnPassant { //play the move
|
||||||
|
delete(b.position, lastMove.EndSquare)
|
||||||
|
b.position[move.EndSquare] = GetPieceForShortName(move.PieceMoved, move.ColorMoved)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wasEnPassant
|
||||||
}
|
}
|
||||||
|
@ -8,64 +8,191 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func Test_CheckMove_validPawnMove(t *testing.T) {
|
func Test_CheckMove_validPawnMove(t *testing.T) {
|
||||||
var board = make(Board)
|
var board = newBoard()
|
||||||
|
|
||||||
board[types.Coordinate{Col: 1, Row: 1}] = Pawn{Color: types.White}
|
board.position[types.Coordinate{Col: 1, Row: 2}] = Pawn{Color: types.White}
|
||||||
board[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White}
|
board.position[types.Coordinate{Col: 2, Row: 4}] = Pawn{Color: types.Black}
|
||||||
board[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black}
|
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||||
|
|
||||||
move := types.Move{
|
move := types.Move{
|
||||||
StartSquare: types.Coordinate{Col: 1, Row: 1},
|
StartSquare: types.Coordinate{Col: 1, Row: 2},
|
||||||
EndSquare: types.Coordinate{Col: 1, Row: 2},
|
EndSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
}
|
}
|
||||||
|
|
||||||
good, _ := board.CheckMove(move)
|
good, _ := board.CheckAndPlay(move)
|
||||||
|
assert.True(t, good)
|
||||||
|
|
||||||
|
//we take the pawn
|
||||||
|
secondMove := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 2, Row: 4},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
|
}
|
||||||
|
good, _ = board.CheckAndPlay(secondMove)
|
||||||
assert.True(t, good)
|
assert.True(t, good)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_CheckMove_invalidPawnMoves(t *testing.T) {
|
func Test_CheckMove_enPassant(t *testing.T) {
|
||||||
var board = make(Board)
|
var board = newBoard()
|
||||||
|
|
||||||
board[types.Coordinate{Col: 2, Row: 5}] = Pawn{Color: types.White}
|
board.position[types.Coordinate{Col: 6, Row: 4}] = Pawn{Color: types.Black}
|
||||||
board[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White}
|
board.position[types.Coordinate{Col: 5, Row: 2}] = Pawn{Color: types.White}
|
||||||
board[types.Coordinate{Col: 7, Row: 5}] = Queen{Color: types.Black}
|
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||||
board[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black}
|
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||||
|
|
||||||
|
move := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 5, Row: 2},
|
||||||
|
EndSquare: types.Coordinate{Col: 5, Row: 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
good, reason := board.CheckAndPlay(move)
|
||||||
|
assert.True(t, good)
|
||||||
|
assert.Empty(t, reason)
|
||||||
|
assert.Equal(t, Pawn{Color: types.White}, board.position[types.Coordinate{Col: 5, Row: 4}])
|
||||||
|
|
||||||
|
newMove := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 6, Row: 4},
|
||||||
|
EndSquare: types.Coordinate{Col: 5, Row: 3},
|
||||||
|
}
|
||||||
|
good, reason = board.CheckAndPlay(newMove)
|
||||||
|
assert.True(t, good)
|
||||||
|
assert.Empty(t, reason)
|
||||||
|
// the black pawn is on its correct square
|
||||||
|
assert.Equal(t, Pawn{Color: types.Black}, board.position[types.Coordinate{Col: 5, Row: 3}])
|
||||||
|
//the white pawn is gone
|
||||||
|
assert.Nil(t, board.position[types.Coordinate{Col: 5, Row: 4}])
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CheckMove_invalidPawnMoves(t *testing.T) {
|
||||||
|
t.Run("pawn is blocked", func(t *testing.T) {
|
||||||
|
var board = newBoard()
|
||||||
|
|
||||||
|
board.position[types.Coordinate{Col: 2, Row: 5}] = Pawn{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 7, Row: 5}] = Queen{Color: types.Black}
|
||||||
|
board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black}
|
||||||
|
board.position[types.Coordinate{Col: 2, Row: 6}] = Pawn{Color: types.Black}
|
||||||
|
|
||||||
move := types.Move{
|
move := types.Move{
|
||||||
StartSquare: types.Coordinate{Col: 2, Row: 5},
|
StartSquare: types.Coordinate{Col: 2, Row: 5},
|
||||||
EndSquare: types.Coordinate{Col: 2, Row: 6},
|
EndSquare: types.Coordinate{Col: 2, Row: 6},
|
||||||
}
|
}
|
||||||
|
legalMove, _ := board.CheckAndPlay(move)
|
||||||
t.Run("pawn is blocked", func(t *testing.T) {
|
|
||||||
testBoard := board.getCopyOfBoard()
|
|
||||||
testBoard[types.Coordinate{Col: 2, Row: 6}] = Pawn{Color: types.Black}
|
|
||||||
legalMove, _ := testBoard.CheckMove(move)
|
|
||||||
assert.False(t, legalMove)
|
assert.False(t, legalMove)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("king of moving color is in check after move", func(t *testing.T) {
|
t.Run("pawn moves to the side", func(t *testing.T) {
|
||||||
good, _ := board.CheckMove(move)
|
var board = newBoard()
|
||||||
assert.False(t, good)
|
boardBeforeMove := board
|
||||||
|
|
||||||
|
board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black}
|
||||||
|
board.position[types.Coordinate{Col: 2, Row: 5}] = Pawn{Color: types.White}
|
||||||
|
|
||||||
|
move := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 2, Row: 5},
|
||||||
|
EndSquare: types.Coordinate{Col: 3, Row: 5},
|
||||||
|
}
|
||||||
|
legal, _ := board.CheckAndPlay(move)
|
||||||
|
|
||||||
|
assert.False(t, legal)
|
||||||
|
assert.Equal(t, boardBeforeMove, board)
|
||||||
|
|
||||||
|
move = types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 2, Row: 5},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 5},
|
||||||
|
}
|
||||||
|
legal, _ = board.CheckAndPlay(move)
|
||||||
|
|
||||||
|
assert.False(t, legal)
|
||||||
|
assert.Equal(t, boardBeforeMove, board)
|
||||||
|
|
||||||
|
move = types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 2, Row: 5},
|
||||||
|
EndSquare: types.Coordinate{Col: 6, Row: 5},
|
||||||
|
}
|
||||||
|
legal, _ = board.CheckAndPlay(move)
|
||||||
|
|
||||||
|
assert.False(t, legal)
|
||||||
|
assert.Equal(t, boardBeforeMove, board)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// t.Run("king of moving color is in check after move", func(t *testing.T) {
|
||||||
|
// good, _ := board.CheckMove(move)
|
||||||
|
// assert.False(t, good)
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_CheckMove_validPromotion(t *testing.T) {
|
func Test_CheckMove_validPromotion(t *testing.T) {
|
||||||
var board Board = make(Board)
|
var board Board = newBoard()
|
||||||
|
|
||||||
board[types.Coordinate{Col: 1, Row: 7}] = Pawn{Color: types.White}
|
board.position[types.Coordinate{Col: 1, Row: 7}] = Pawn{Color: types.White}
|
||||||
board[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White}
|
board.position[types.Coordinate{Col: 1, Row: 1}] = King{Color: types.White}
|
||||||
|
|
||||||
board[types.Coordinate{Col: 8, Row: 7}] = King{Color: types.Black}
|
board.position[types.Coordinate{Col: 8, Row: 7}] = King{Color: types.Black}
|
||||||
|
|
||||||
shortName := types.Queen
|
shortName := types.QueenShortName
|
||||||
move := types.Move{
|
move := types.Move{
|
||||||
StartSquare: types.Coordinate{Col: 1, Row: 7},
|
StartSquare: types.Coordinate{Col: 1, Row: 7},
|
||||||
EndSquare: types.Coordinate{Col: 1, Row: 8},
|
EndSquare: types.Coordinate{Col: 1, Row: 8},
|
||||||
PromotionToPiece: &shortName,
|
PromotionToPiece: &shortName,
|
||||||
}
|
}
|
||||||
|
good, reason := board.CheckAndPlay(move)
|
||||||
good, reason := board.CheckMove(move)
|
|
||||||
|
|
||||||
assert.Empty(t, reason)
|
assert.Empty(t, reason)
|
||||||
assert.True(t, good)
|
assert.True(t, good)
|
||||||
|
assert.Equal(t, Queen{Color: types.White}, board.getPieceAt(types.Coordinate{Row: 8, Col: 1}))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CheckMove_HistoryWorks(t *testing.T) {
|
||||||
|
var board = newBoard()
|
||||||
|
|
||||||
|
board.position[types.Coordinate{Col: 3, Row: 7}] = Pawn{Color: types.Black}
|
||||||
|
board.position[types.Coordinate{Col: 1, Row: 2}] = Pawn{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 1, Row: 5}] = King{Color: types.White}
|
||||||
|
board.position[types.Coordinate{Col: 8, Row: 5}] = King{Color: types.Black}
|
||||||
|
|
||||||
|
firstMove := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 1, Row: 2},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
|
}
|
||||||
|
secondMove := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 3, Row: 7},
|
||||||
|
EndSquare: types.Coordinate{Col: 3, Row: 5},
|
||||||
|
}
|
||||||
|
thirdMove := types.Move{
|
||||||
|
StartSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 4},
|
||||||
|
}
|
||||||
|
|
||||||
|
good, _ := board.CheckAndPlay(firstMove)
|
||||||
|
assert.True(t, good)
|
||||||
|
|
||||||
|
good, _ = board.CheckAndPlay(secondMove)
|
||||||
|
assert.True(t, good)
|
||||||
|
|
||||||
|
good, _ = board.CheckAndPlay(thirdMove)
|
||||||
|
assert.True(t, good)
|
||||||
|
|
||||||
|
expectedHistory := []types.Move{
|
||||||
|
{
|
||||||
|
StartSquare: types.Coordinate{Col: 1, Row: 2},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
|
PieceMoved: 'p',
|
||||||
|
ColorMoved: "white",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StartSquare: types.Coordinate{Col: 3, Row: 7},
|
||||||
|
EndSquare: types.Coordinate{Col: 3, Row: 5},
|
||||||
|
PieceMoved: 'p',
|
||||||
|
ColorMoved: "black",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StartSquare: types.Coordinate{Col: 1, Row: 3},
|
||||||
|
EndSquare: types.Coordinate{Col: 1, Row: 4},
|
||||||
|
PieceMoved: 'p',
|
||||||
|
ColorMoved: "white",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Equal(t, expectedHistory, board.history)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
package chess
|
package chess
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"mchess_server/api"
|
"mchess_server/api"
|
||||||
"mchess_server/types"
|
"mchess_server/types"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@ -25,7 +25,7 @@ const (
|
|||||||
func NewGame() *Game {
|
func NewGame() *Game {
|
||||||
var game Game = Game{
|
var game Game = Game{
|
||||||
id: uuid.New(),
|
id: uuid.New(),
|
||||||
board: make(map[types.Coordinate]Piece),
|
board: newBoard(),
|
||||||
}
|
}
|
||||||
game.board.Init()
|
game.board.Init()
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ func (game *Game) Handle() {
|
|||||||
gameState = CheckMove
|
gameState = CheckMove
|
||||||
|
|
||||||
case CheckMove:
|
case CheckMove:
|
||||||
valid, reason := game.board.CheckMove(receivedMove)
|
valid, reason := game.board.CheckAndPlay(receivedMove)
|
||||||
|
|
||||||
if valid {
|
if valid {
|
||||||
gameState = CheckPlayerChange
|
gameState = CheckPlayerChange
|
||||||
|
@ -9,22 +9,41 @@ type Piece interface {
|
|||||||
GetColor() types.ChessColor
|
GetColor() types.ChessColor
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPieceForShortName(name types.PieceShortName) Piece {
|
func GetPieceForShortName(name types.PieceShortName, color types.ChessColor) Piece {
|
||||||
var piece Piece
|
var piece Piece
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case 'p':
|
case 'p':
|
||||||
piece = Pawn{}
|
piece = Pawn{Color: color}
|
||||||
case 'q':
|
case 'q':
|
||||||
piece = Queen{}
|
piece = Queen{Color: color}
|
||||||
case 'k':
|
case 'k':
|
||||||
piece = King{}
|
piece = King{Color: color}
|
||||||
case 'b':
|
case 'b':
|
||||||
piece = Bishop{}
|
piece = Bishop{Color: color}
|
||||||
case 'r':
|
case 'r':
|
||||||
piece = Rook{}
|
piece = Rook{Color: color}
|
||||||
case 'n':
|
case 'n':
|
||||||
piece = Knight{}
|
piece = Knight{Color: color}
|
||||||
}
|
}
|
||||||
return piece
|
return piece
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetShortNameForPiece(piece Piece) types.PieceShortName {
|
||||||
|
var name types.PieceShortName
|
||||||
|
switch piece.(type) {
|
||||||
|
case Pawn:
|
||||||
|
name = 'p'
|
||||||
|
case Queen:
|
||||||
|
name = 'q'
|
||||||
|
case King:
|
||||||
|
name = 'k'
|
||||||
|
case Bishop:
|
||||||
|
name = 'b'
|
||||||
|
case Rook:
|
||||||
|
name = 'r'
|
||||||
|
case Knight:
|
||||||
|
name = 'n'
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
@ -3,12 +3,11 @@ package types
|
|||||||
type Move struct {
|
type Move struct {
|
||||||
StartSquare Coordinate `json:"startSquare"`
|
StartSquare Coordinate `json:"startSquare"`
|
||||||
EndSquare Coordinate `json:"endSquare"`
|
EndSquare Coordinate `json:"endSquare"`
|
||||||
|
PieceMoved PieceShortName
|
||||||
|
ColorMoved ChessColor
|
||||||
PromotionToPiece *PieceShortName `json:"promotionToPiece,omitempty"`
|
PromotionToPiece *PieceShortName `json:"promotionToPiece,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Move) IsPromotionMove() bool {
|
func (m Move) IsPromotionMove() bool {
|
||||||
if m.PromotionToPiece != nil {
|
return m.PromotionToPiece != nil
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,10 @@ package types
|
|||||||
type PieceShortName rune
|
type PieceShortName rune
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Pawn PieceShortName = 'p'
|
PawnShortName PieceShortName = 'p'
|
||||||
Rook PieceShortName = 'r'
|
RookShortName PieceShortName = 'r'
|
||||||
Knight PieceShortName = 'n'
|
KnightShortName PieceShortName = 'n'
|
||||||
Bishop PieceShortName = 'b'
|
BishopShortName PieceShortName = 'b'
|
||||||
Queen PieceShortName = 'q'
|
QueenShortName PieceShortName = 'q'
|
||||||
King PieceShortName = 'k'
|
KingShortName PieceShortName = 'k'
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user