Skip to content

Commit

Permalink
feat: added stalemate detection (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
sgatu authored May 12, 2024
1 parent bdfca36 commit cd72c7c
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 23 deletions.
61 changes: 40 additions & 21 deletions game/gamestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type DirectionVector struct {
type MoveResult struct {
Move string
CheckedPlayer PLAYER
CheckMate bool
MateStatus GameStateStatus
}

func newPiece(_type PIECE_TYPE, player PLAYER, hasBeenMoved bool) *Piece {
Expand Down Expand Up @@ -102,13 +102,21 @@ func castleRightsFromByte(b byte) CastleRights {
return cr
}

type GameStateStatus int

const (
STATUS_PLAYING GameStateStatus = iota
STATUS_CHECKMATE
STATUS_STALEMATE
)

type GameState struct {
table [64]*Piece
moves []string
outTable []Piece
playerTurn PLAYER
checkedPlayer PLAYER
checkMate bool
gameStatus GameStateStatus
castleRights CastleRights
}

Expand Down Expand Up @@ -186,9 +194,13 @@ func getPieceFromQualifier(r byte) PIECE_TYPE {
return UNKNOWN_PIECE
}

func (gs *GameState) checkIfCheckMate() bool {
func (gs *GameState) checkIfMate() GameStateStatus {
newStatus := gs.gameStatus
beforeState := gs.table
for i := range gs.table {
if newStatus != STATUS_PLAYING {
break
}
if gs.table[i] != nil && gs.table[i].Player == gs.playerTurn {
moves, _ := gs.getAllAllowedMovements(i, gs.playerTurn)
for _, mv := range moves {
Expand All @@ -197,16 +209,19 @@ func (gs *GameState) checkIfCheckMate() bool {
whiteCheck, blackCheck := gs.checkIfCheck()
gs.table = beforeState
if gs.playerTurn == WHITE_PLAYER && !whiteCheck {
return false
return STATUS_PLAYING
}
if gs.playerTurn == BLACK_PLAYER && !blackCheck {
return false
return STATUS_PLAYING
}

}
}
}
return true
if gs.checkedPlayer != UNKNOWN_PLAYER {
return STATUS_CHECKMATE
}
return STATUS_STALEMATE
}

func posInRange(pos int) bool {
Expand Down Expand Up @@ -597,7 +612,7 @@ func NewGameState() *GameState {
playerTurn: WHITE_PLAYER,
table: table,
outTable: []Piece{},
checkMate: false,
gameStatus: STATUS_PLAYING,
checkedPlayer: UNKNOWN_PLAYER,
moves: []string{},
castleRights: CastleRights{
Expand All @@ -612,7 +627,7 @@ func NewGameState() *GameState {
func FromSerialized(serializedData []byte) (*GameState, error) {
playerTurn := WHITE_PLAYER
checkedPlayer := UNKNOWN_PLAYER
isCheckMate := false
gameStatus := STATUS_PLAYING
table := [64]*Piece{}
outPieces := []Piece{}
moves := []string{}
Expand Down Expand Up @@ -677,7 +692,7 @@ func FromSerialized(serializedData []byte) (*GameState, error) {
continue
}
if i == 2 {
isCheckMate = b == 1
gameStatus = GameStateStatus(b)
continue
}
if i == 3 {
Expand Down Expand Up @@ -720,7 +735,7 @@ func FromSerialized(serializedData []byte) (*GameState, error) {
outTable: outPieces,
moves: moves,
checkedPlayer: checkedPlayer,
checkMate: isCheckMate,
gameStatus: gameStatus,
castleRights: castleRights,
}, nil
}
Expand Down Expand Up @@ -761,11 +776,7 @@ func (gs *GameState) Serialize() ([]byte, error) {

returnBytes = append(returnBytes, byte(gs.playerTurn))
returnBytes = append(returnBytes, byte(gs.checkedPlayer))
if gs.checkMate {
returnBytes = append(returnBytes, byte(1))
} else {
returnBytes = append(returnBytes, byte(0))
}
returnBytes = append(returnBytes, byte(gs.gameStatus))
returnBytes = append(returnBytes, gs.castleRights.Serialize())
returnBytes = append(returnBytes, pieceBytes...)
for _, outPiece := range gs.outTable {
Expand Down Expand Up @@ -823,7 +834,11 @@ func (gs *GameState) GetBoardState() [64]*Piece {
}

func (gs *GameState) InCheckMate() bool {
return gs.checkMate
return gs.gameStatus == STATUS_CHECKMATE
}

func (gs *GameState) InStalemate() bool {
return gs.gameStatus == STATUS_STALEMATE
}

func (gs *GameState) GetCheckedPlayer() PLAYER {
Expand All @@ -835,12 +850,18 @@ func (gs *GameState) UpdateGameState(uciAction string) (*MoveResult, error) {
if err != nil {
return nil, err
}
if gs.checkMate {
if gs.gameStatus == STATUS_CHECKMATE {
return nil, &errors.InvalidMoveError{
Message: "Game in checkmate",
ErrCode: "CHECKMATE",
}
}
if gs.gameStatus == STATUS_STALEMATE {
return nil, &errors.InvalidMoveError{
Message: "Game in stalemate",
ErrCode: "STALEMATE",
}
}

if gs.table[action.posStart] == nil || gs.table[action.posStart].Player != action.who {
return nil, &errors.InvalidMoveError{
Expand Down Expand Up @@ -898,12 +919,10 @@ func (gs *GameState) UpdateGameState(uciAction string) (*MoveResult, error) {
gs.checkedPlayer = BLACK_PLAYER
}
gs.playerTurn = gs.getOppositePlayer(gs.playerTurn)
if (gs.checkedPlayer != UNKNOWN_PLAYER) && gs.checkIfCheckMate() {
gs.checkMate = true
}
gs.gameStatus = gs.checkIfMate()
return &MoveResult{
Move: uciAction,
CheckedPlayer: gs.checkedPlayer,
CheckMate: gs.checkMate,
MateStatus: gs.gameStatus,
}, nil
}
11 changes: 9 additions & 2 deletions handlers/playHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,19 @@ func (ph *PlayHandler) Play(c *gin.Context) {
return
}
case move := <-observeChan:
mateStatusStr := ""
if move.MateStatus == game.STATUS_CHECKMATE {
mateStatusStr = "#"
}
if move.MateStatus == game.STATUS_STALEMATE {
mateStatusStr = "-"
}
outputMessage, err := json.Marshal(struct {
Type string `json:"type"`
Move string `json:"uci"`
MateStatus string `json:"mateStatus"`
CheckedPlayer int `json:"checkedPlayer"`
CheckMate bool `json:"isMate"`
}{Type: "move", Move: move.Move, CheckedPlayer: int(move.CheckedPlayer), CheckMate: move.CheckMate})
}{Type: "move", Move: move.Move, CheckedPlayer: int(move.CheckedPlayer), MateStatus: mateStatusStr})
if err != nil {
fmt.Println("Could not serialize movement")
return
Expand Down

0 comments on commit cd72c7c

Please sign in to comment.