183 lines
5.2 KiB
Go
183 lines
5.2 KiB
Go
package chess
|
|
|
|
import (
|
|
"mchess_server/types"
|
|
|
|
"github.com/samber/lo"
|
|
)
|
|
|
|
type Board map[types.Coordinate]Piece
|
|
|
|
func (b Board) Init() {
|
|
var coord types.Coordinate
|
|
|
|
for i := 1; i <= 8; i++ {
|
|
coord.Row = 2
|
|
coord.Col = i
|
|
b[coord] = Pawn{Color: types.White}
|
|
|
|
coord.Row = 7
|
|
coord.Col = i
|
|
b[coord] = Pawn{Color: types.Black}
|
|
}
|
|
|
|
b[types.Coordinate{Row: 1, Col: 1}] = Rook{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 2}] = Knight{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 3}] = Bishop{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 4}] = Queen{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 5}] = King{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 6}] = Bishop{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 7}] = Knight{Color: types.White}
|
|
b[types.Coordinate{Row: 1, Col: 8}] = Rook{Color: types.White}
|
|
|
|
b[types.Coordinate{Row: 8, Col: 1}] = Rook{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 2}] = Knight{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 3}] = Bishop{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 4}] = Queen{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 5}] = King{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 6}] = Bishop{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 7}] = Knight{Color: types.Black}
|
|
b[types.Coordinate{Row: 8, Col: 8}] = Rook{Color: types.Black}
|
|
}
|
|
|
|
func (b Board) CheckMove(move types.Move) (bool, string) {
|
|
// 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
|
|
tempBoard := b.getCopyOfBoard()
|
|
|
|
//Check start square of move
|
|
pieceAtStartSquare := b.getPieceAt(move.StartSquare)
|
|
if pieceAtStartSquare == nil {
|
|
return false, "no piece at start square"
|
|
}
|
|
movingColor := pieceAtStartSquare.GetColor()
|
|
|
|
//Check end square of move
|
|
pieceAtEndSquare := b.getPieceAt(move.EndSquare)
|
|
if pieceAtEndSquare != nil {
|
|
if pieceAtEndSquare.GetColor() == pieceAtStartSquare.GetColor() {
|
|
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 {
|
|
// 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.
|
|
allMovesExceptBlocked := pieceAtStartSquare.GetAllMovesButBlocked(tempBoard, move.StartSquare)
|
|
legal := lo.Contains(allMovesExceptBlocked, move.EndSquare)
|
|
if !legal {
|
|
return false, "not a legal square"
|
|
}
|
|
|
|
//We play the move on the temporary board
|
|
delete(tempBoard, move.StartSquare)
|
|
tempBoard[move.EndSquare] = pieceAtStartSquare
|
|
}
|
|
|
|
//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.
|
|
ownKingCoordinate := tempBoard.getSquareOfPiece(King{Color: movingColor})
|
|
if ownKingCoordinate == nil {
|
|
return false, string(movingColor) + " king not found"
|
|
}
|
|
|
|
kingIsAttacked := tempBoard.isSquareAttacked(*ownKingCoordinate, movingColor.Opposite())
|
|
if kingIsAttacked {
|
|
return false, "king is attacked after move"
|
|
}
|
|
|
|
//Check for checkmat&e
|
|
//Is every square that the king can move to attacked? And can no other
|
|
//piece block? -> checkmate
|
|
|
|
//only check if paths of attacking pieces can be blocked
|
|
|
|
//Maybe for checking checkmate, we have to check the 'path' in which the
|
|
//checkmate is given
|
|
|
|
// |K| | | | |Q|
|
|
// in this scenaria the path are all the squares between queen and king.
|
|
// If a piece can be moved into the path, it is no checkmate
|
|
|
|
//We play the move on the real board
|
|
b = tempBoard
|
|
|
|
return true, ""
|
|
}
|
|
|
|
func (b Board) getSquareOfPiece(piece Piece) *types.Coordinate {
|
|
for k, v := range b {
|
|
if v == piece {
|
|
return &k
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (b Board) isSquareAttacked(square types.Coordinate, byColor types.ChessColor) bool {
|
|
var attackedSquares []types.Coordinate
|
|
|
|
for square, piece := range b {
|
|
attackedSquares = append(attackedSquares, piece.GetAllMovesButBlocked(b, square)...)
|
|
}
|
|
|
|
return lo.Contains(attackedSquares, square)
|
|
}
|
|
|
|
func (b Board) getPieceAt(coord types.Coordinate) Piece {
|
|
piece, found := b[coord]
|
|
if !found {
|
|
return nil
|
|
}
|
|
|
|
return piece
|
|
}
|
|
|
|
func (b Board) handlePossiblePromotion(move types.Move, color types.ChessColor) (bool, types.PieceShortName) {
|
|
var isPromotionMove bool
|
|
var promotionToPiece types.PieceShortName
|
|
|
|
messageContainsPromotion := move.IsPromotionMove()
|
|
|
|
if messageContainsPromotion {
|
|
promotionToPiece = *move.PromotionToPiece
|
|
}
|
|
|
|
switch color {
|
|
case types.White:
|
|
if move.StartSquare.Row == types.RangeLastValid-1 &&
|
|
move.EndSquare.Row == types.RangeLastValid {
|
|
isPromotionMove = true
|
|
}
|
|
|
|
case types.Black:
|
|
if move.StartSquare.Row == types.RangeFirstValid+1 &&
|
|
move.EndSquare.Row == types.RangeFirstValid {
|
|
isPromotionMove = true
|
|
}
|
|
}
|
|
|
|
if isPromotionMove {
|
|
delete(b, move.StartSquare)
|
|
b[move.EndSquare] = GetPieceForShortName(promotionToPiece)
|
|
}
|
|
|
|
return isPromotionMove, promotionToPiece
|
|
}
|
|
|
|
func (b Board) getCopyOfBoard() Board {
|
|
board := make(map[types.Coordinate]Piece)
|
|
|
|
for coord, piece := range b {
|
|
board[coord] = piece
|
|
}
|
|
|
|
return board
|
|
}
|