Make en passant and castling work.
This commit is contained in:
parent
c8407881bf
commit
9c7bdf357d
@ -18,6 +18,7 @@ const (
|
||||
MoveMessage MessageType = "move"
|
||||
InvalidMoveMessage MessageType = "invalidMove"
|
||||
ColorDetermined MessageType = "colorDetermined"
|
||||
TakenEnPassant MessageType = "takenEnPassant"
|
||||
)
|
||||
|
||||
func (m WebsocketMessage) IsValidMoveMessage() bool {
|
||||
|
@ -19,3 +19,5 @@ func (b Bishop) GetColor() types.ChessColor {
|
||||
func (b Bishop) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return board.GetNonBlockedDiagonals(fromSquare)
|
||||
}
|
||||
|
||||
func (b Bishop) AfterMoveAction(board *Board, fromSquare types.Coordinate) {}
|
||||
|
@ -13,6 +13,7 @@ type Board struct {
|
||||
position Position
|
||||
history []types.Move
|
||||
colorToMove types.ChessColor
|
||||
state types.AdditionalState
|
||||
}
|
||||
|
||||
func newBoard() Board {
|
||||
@ -20,6 +21,7 @@ func newBoard() Board {
|
||||
position: make(Position),
|
||||
history: make([]types.Move, 0),
|
||||
colorToMove: types.White,
|
||||
state: types.AdditionalState{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,6 +124,9 @@ func (b *Board) CheckAndPlay(move types.Move) (bool, string) {
|
||||
b.history = tempBoard.history
|
||||
b.colorToMove = b.colorToMove.Opposite()
|
||||
b.appendMoveToHistory(move)
|
||||
|
||||
pieceAtStartSquare.AfterMoveAction(b, move.StartSquare)
|
||||
|
||||
return true, ""
|
||||
}
|
||||
|
||||
@ -184,6 +189,7 @@ func (b Board) getCopyOfBoard() Board {
|
||||
position: b.position.getCopyOfPosition(),
|
||||
history: b.history,
|
||||
colorToMove: b.colorToMove,
|
||||
state: b.state,
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,6 +212,8 @@ func (b *Board) handleSpecialMove(move types.Move) bool {
|
||||
if !was {
|
||||
was = piece.HandleEnPassant(b, move, b.getLastMove())
|
||||
}
|
||||
case King:
|
||||
was = piece.HandleCastling(b, move)
|
||||
}
|
||||
return was
|
||||
}
|
||||
|
@ -210,3 +210,167 @@ func Test_CheckMove_HistoryWorks(t *testing.T) {
|
||||
}
|
||||
assert.Equal(t, expectedHistory, board.history)
|
||||
}
|
||||
|
||||
func Test_Promotion_BlackKing(t *testing.T) {
|
||||
t.Run("valid promotion to the right", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 7, Row: 8},
|
||||
}
|
||||
good, reason := board.CheckAndPlay(move)
|
||||
assert.True(t, good)
|
||||
assert.Empty(t, reason)
|
||||
|
||||
assert.Equal(t, King{Color: types.Black}, board.position[types.Coordinate{Col: 7, Row: 8}])
|
||||
assert.Equal(t, Rook{Color: types.Black}, board.position[types.Coordinate{Col: 6, Row: 8}])
|
||||
assert.Empty(t, board.position[types.Coordinate{Col: 5, Row: 8}])
|
||||
assert.Empty(t, board.position[types.Coordinate{Col: 8, Row: 8}])
|
||||
})
|
||||
|
||||
t.Run("valid promotion to the left", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 3, Row: 8},
|
||||
}
|
||||
good, reason := board.CheckAndPlay(move)
|
||||
assert.True(t, good)
|
||||
assert.Empty(t, reason)
|
||||
|
||||
assert.Equal(t, King{Color: types.Black}, board.position[types.Coordinate{Col: 3, Row: 8}])
|
||||
assert.Equal(t, Rook{Color: types.Black}, board.position[types.Coordinate{Col: 4, Row: 8}])
|
||||
assert.Empty(t, board.position[types.Coordinate{Col: 1, Row: 8}])
|
||||
assert.Empty(t, board.position[types.Coordinate{Col: 2, Row: 8}])
|
||||
assert.Empty(t, board.position[types.Coordinate{Col: 5, Row: 8}])
|
||||
})
|
||||
|
||||
t.Run("king moved already", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
//Move black
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 8}, EndSquare: types.Coordinate{Col: 4, Row: 8}})
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 2}, EndSquare: types.Coordinate{Col: 5, Row: 1}})
|
||||
|
||||
//Move black back
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 4, Row: 8}, EndSquare: types.Coordinate{Col: 5, Row: 8}})
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 3, Row: 8},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
|
||||
t.Run("promotion right with piece in between", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 7, Row: 8}] = Bishop{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 7, Row: 8},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
|
||||
t.Run("promotion left with piece next to king", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 4, Row: 8}] = Bishop{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 3, Row: 8},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
|
||||
t.Run("promotion left with piece on castling square", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 3, Row: 8}] = Bishop{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 3, Row: 8},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
|
||||
t.Run("promotion left with piece next to rook", func(t *testing.T) {
|
||||
var board = newBoard()
|
||||
board.position[types.Coordinate{Col: 5, Row: 1}] = King{Color: types.White}
|
||||
|
||||
board.position[types.Coordinate{Col: 5, Row: 8}] = King{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 1, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 8, Row: 8}] = Rook{Color: types.Black}
|
||||
board.position[types.Coordinate{Col: 2, Row: 8}] = Bishop{Color: types.Black}
|
||||
|
||||
//Make dummy move for white
|
||||
board.CheckAndPlay(types.Move{StartSquare: types.Coordinate{Col: 5, Row: 1}, EndSquare: types.Coordinate{Col: 5, Row: 2}})
|
||||
|
||||
move := types.Move{
|
||||
StartSquare: types.Coordinate{Col: 5, Row: 8},
|
||||
EndSquare: types.Coordinate{Col: 3, Row: 8},
|
||||
}
|
||||
good, _ := board.CheckAndPlay(move)
|
||||
assert.False(t, good)
|
||||
})
|
||||
}
|
||||
|
109
chess/king.go
109
chess/king.go
@ -19,3 +19,112 @@ func (k King) GetColor() types.ChessColor {
|
||||
func (k King) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return board.GetNonBlockedKingMoves(fromSquare)
|
||||
}
|
||||
|
||||
func (k King) AfterMoveAction(board *Board, fromSquare types.Coordinate) {
|
||||
switch k.Color {
|
||||
case types.Black:
|
||||
board.state.BlackKingMoved = true
|
||||
case types.White:
|
||||
board.state.WhiteKingMoved = true
|
||||
}
|
||||
}
|
||||
|
||||
func (k King) HandleCastling(board *Board, move types.Move) bool {
|
||||
if k.hadMovedBefore(board) {
|
||||
return false
|
||||
}
|
||||
|
||||
valid, dir := k.movedOnValidCastlingSquare(board, move)
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
|
||||
switch k.Color {
|
||||
case types.White:
|
||||
board.state.WhiteKingMoved = true
|
||||
case types.Black:
|
||||
board.state.BlackKingMoved = true
|
||||
}
|
||||
|
||||
k.playCastlingOnBoard(board, move, dir)
|
||||
return true
|
||||
}
|
||||
|
||||
func (k King) hadMovedBefore(b *Board) bool {
|
||||
var hasMoved bool
|
||||
switch k.Color {
|
||||
case types.White:
|
||||
hasMoved = b.state.WhiteKingMoved
|
||||
case types.Black:
|
||||
hasMoved = b.state.BlackKingMoved
|
||||
}
|
||||
return hasMoved
|
||||
}
|
||||
|
||||
type CastlingDirection string
|
||||
|
||||
const (
|
||||
CastlingRight CastlingDirection = "right"
|
||||
CastlingLeft CastlingDirection = "left"
|
||||
)
|
||||
|
||||
func (k King) movedOnValidCastlingSquare(b *Board, move types.Move) (bool, CastlingDirection) {
|
||||
var valid bool = false
|
||||
|
||||
switch k.Color {
|
||||
case types.White:
|
||||
castlingSquareRight := types.Coordinate{Col: 7, Row: 1}
|
||||
castlingSquareLeft := types.Coordinate{Col: 3, Row: 1}
|
||||
|
||||
if move.EndSquare == castlingSquareRight &&
|
||||
b.getPieceAt(castlingSquareRight) == nil &&
|
||||
b.getPieceAt(*castlingSquareRight.Left(1)) == nil &&
|
||||
!b.state.WhiteHRookMoved {
|
||||
return true, CastlingRight
|
||||
}
|
||||
|
||||
if move.EndSquare == castlingSquareLeft &&
|
||||
b.getPieceAt(castlingSquareLeft) == nil &&
|
||||
b.getPieceAt(*castlingSquareLeft.Right(1)) == nil &&
|
||||
b.getPieceAt(*castlingSquareLeft.Left(1)) == nil &&
|
||||
!b.state.WhiteARookMoved {
|
||||
return true, CastlingLeft
|
||||
}
|
||||
case types.Black:
|
||||
castlingSquareRight := types.Coordinate{Col: 7, Row: 8}
|
||||
castlingSquareLeft := types.Coordinate{Col: 3, Row: 8}
|
||||
|
||||
if move.EndSquare == castlingSquareRight &&
|
||||
b.getPieceAt(castlingSquareRight) == nil &&
|
||||
b.getPieceAt(*castlingSquareRight.Left(1)) == nil &&
|
||||
!b.state.BlackHRookMoved {
|
||||
return true, CastlingRight
|
||||
}
|
||||
|
||||
if move.EndSquare == castlingSquareLeft &&
|
||||
b.getPieceAt(castlingSquareLeft) == nil &&
|
||||
b.getPieceAt(*castlingSquareLeft.Right(1)) == nil &&
|
||||
b.getPieceAt(*castlingSquareLeft.Left(1)) == nil &&
|
||||
!b.state.BlackARookMoved {
|
||||
return true, CastlingLeft
|
||||
}
|
||||
}
|
||||
return valid, CastlingRight
|
||||
}
|
||||
|
||||
func (k King) playCastlingOnBoard(board *Board, move types.Move, dir CastlingDirection) {
|
||||
onRow := move.StartSquare.Row
|
||||
|
||||
switch dir {
|
||||
case CastlingRight:
|
||||
delete(board.position, types.Coordinate{Col: 8, Row: onRow})
|
||||
delete(board.position, types.Coordinate{Col: 5, Row: onRow})
|
||||
board.position[types.Coordinate{Col: 7, Row: onRow}] = King{Color: k.Color}
|
||||
board.position[types.Coordinate{Col: 6, Row: onRow}] = Rook{Color: k.Color}
|
||||
case CastlingLeft:
|
||||
delete(board.position, types.Coordinate{Col: 1, Row: onRow})
|
||||
delete(board.position, types.Coordinate{Col: 5, Row: onRow})
|
||||
board.position[types.Coordinate{Col: 3, Row: onRow}] = King{Color: k.Color}
|
||||
board.position[types.Coordinate{Col: 4, Row: onRow}] = Rook{Color: k.Color}
|
||||
}
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ type Knight struct {
|
||||
Color types.ChessColor
|
||||
}
|
||||
|
||||
func (k Knight) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return k.GetAllNonBlockedMoves(board, fromSquare)
|
||||
func (n Knight) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return n.GetAllNonBlockedMoves(board, fromSquare)
|
||||
}
|
||||
|
||||
func (k Knight) GetColor() types.ChessColor {
|
||||
return k.Color
|
||||
func (n Knight) GetColor() types.ChessColor {
|
||||
return n.Color
|
||||
}
|
||||
|
||||
func (k Knight) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
func (n Knight) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return board.GetNonBlockedKnightMoves(fromSquare)
|
||||
}
|
||||
|
||||
func (n Knight) AfterMoveAction(board *Board, fromSquare types.Coordinate) {}
|
||||
|
@ -187,3 +187,5 @@ func (p Pawn) filterBlockedSquares(board Board, fromSquare types.Coordinate, squ
|
||||
}
|
||||
return lo.Intersect(nonBlockedSquares, squaresToBeFiltered)
|
||||
}
|
||||
|
||||
func (p Pawn) AfterMoveAction(board *Board, fromSquare types.Coordinate) {}
|
||||
|
@ -8,6 +8,7 @@ type Piece interface {
|
||||
GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate
|
||||
GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []types.Coordinate
|
||||
GetColor() types.ChessColor
|
||||
AfterMoveAction(board *Board, fromSquare types.Coordinate)
|
||||
}
|
||||
|
||||
func GetPieceForShortName(name types.PieceShortName, color types.ChessColor) Piece {
|
||||
|
@ -21,3 +21,5 @@ func (q Queen) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) [
|
||||
squares = append(squares, board.GetNonBlockedDiagonals(fromSquare)...)
|
||||
return squares
|
||||
}
|
||||
|
||||
func (q Queen) AfterMoveAction(board *Board, fromSquare types.Coordinate) {}
|
||||
|
@ -12,9 +12,6 @@ func (r Rook) GetAllAttackedSquares(board Board, fromSquare types.Coordinate) []
|
||||
return r.GetAllNonBlockedMoves(board, fromSquare)
|
||||
}
|
||||
|
||||
func (r Rook) AfterMoveAction() {
|
||||
}
|
||||
|
||||
func (r Rook) GetColor() types.ChessColor {
|
||||
return r.Color
|
||||
}
|
||||
@ -22,3 +19,22 @@ func (r Rook) GetColor() types.ChessColor {
|
||||
func (r Rook) GetAllNonBlockedMoves(board Board, fromSquare types.Coordinate) []types.Coordinate {
|
||||
return board.GetNonBlockedRowAndColumn(fromSquare)
|
||||
}
|
||||
|
||||
func (r Rook) AfterMoveAction(board *Board, fromSquare types.Coordinate) {
|
||||
switch r.Color {
|
||||
case types.Black:
|
||||
if fromSquare.Col == types.RangeLastValid {
|
||||
board.state.BlackHRookMoved = true
|
||||
}
|
||||
if fromSquare.Col == types.RangeFirstValid {
|
||||
board.state.BlackARookMoved = true
|
||||
}
|
||||
case types.White:
|
||||
if fromSquare.Col == types.RangeLastValid {
|
||||
board.state.WhiteHRookMoved = true
|
||||
}
|
||||
if fromSquare.Col == types.RangeFirstValid {
|
||||
board.state.WhiteARookMoved = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
package types
|
||||
|
||||
|
||||
|
||||
type ChessColor string
|
||||
|
||||
const (
|
||||
@ -16,3 +14,12 @@ func (c ChessColor) Opposite() ChessColor {
|
||||
return White
|
||||
}
|
||||
}
|
||||
|
||||
type AdditionalState struct {
|
||||
BlackKingMoved bool
|
||||
WhiteKingMoved bool
|
||||
BlackHRookMoved bool
|
||||
BlackARookMoved bool
|
||||
WhiteHRookMoved bool
|
||||
WhiteARookMoved bool
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ func NewPassphrase() Passphrase {
|
||||
var retries int
|
||||
var word string
|
||||
var words int = 2
|
||||
//TODO make sure passphrases are unique
|
||||
//TODO make sure passphrases are unique
|
||||
for words > 0 {
|
||||
retries = 20
|
||||
for {
|
||||
@ -54,6 +54,8 @@ func isProfanity(s string) bool {
|
||||
contains := []string{
|
||||
"nigg",
|
||||
"fag",
|
||||
"ass",
|
||||
"bitch",
|
||||
}
|
||||
startsWith := []string{
|
||||
"spic",
|
||||
|
Loading…
Reference in New Issue
Block a user