diff --git a/cmd/silverfish/main.go b/cmd/silverfish/main.go index c323484..44910ae 100644 --- a/cmd/silverfish/main.go +++ b/cmd/silverfish/main.go @@ -84,13 +84,11 @@ func main() { messageChannel := make(chan engine.UciClientMessage, 5) // Used for reporting if an action is done. actionAlertChannel := make(chan bool) - active := false position := engine.StartingPosition() go HandleMessages(messageChannel) -mainloop: for { message := engine.UciClientMessage{} select { @@ -99,30 +97,21 @@ mainloop: continue } - if active { - select { - case <-actionAlertChannel: - active = false - default: - // Do nothing :ye: - } - } else { - switch message.MessageType { - case engine.UciUciClientMessage: - engine.UciSetEngineName("Silverfish 0.0.0a") - engine.UciSetAuthor("李能和赵梁越") - engine.UciSetProtocol(2) - - engine.UciOk() - case engine.UciIsReadyClientMessage: - engine.UciReadyOk() - case engine.UciPositionClientMessage: - position = *message.Position - case engine.UciQuitClientMessage: - break mainloop - case engine.UciGoClientMessage: - go executeGoCommand(actionAlertChannel, &position, message.GoMessage) - } + switch message.MessageType { + case engine.UciUciClientMessage: + engine.UciSetEngineName("Silverfish 0.0.0a") + engine.UciSetAuthor("李能和赵梁越") + engine.UciSetProtocol(2) + + engine.UciOk() + case engine.UciIsReadyClientMessage: + engine.UciReadyOk() + case engine.UciPositionClientMessage: + position = *message.Position + case engine.UciQuitClientMessage: + return + case engine.UciGoClientMessage: + go executeGoCommand(actionAlertChannel, &position, message.GoMessage) } } } diff --git a/engine/evaluation.go b/engine/evaluation.go index 18d3ace..a26614d 100644 --- a/engine/evaluation.go +++ b/engine/evaluation.go @@ -1,6 +1,9 @@ package engine -import "math/bits" +import ( + "math" + "math/bits" +) const Infinity int32 = 1000000 @@ -152,6 +155,52 @@ func (pos *Position) EndgameMaterial(color uint8) int32 { return ans } +func (pos *Position) KingSafety(color uint8) int32 { + safetyScore := int32(1000) + + // Obviously, if the King is in check, it is not going to be safe. + checkers := pos.Checkers(color) + checkerCount := int32(bits.OnesCount64(uint64(checkers))) + safetyScore -= checkerCount * 100 + + kingSquare := pos.GetKingSquare(color) + kingRank := RankOf(kingSquare) + kingFile := FileOf(kingSquare) + + // Enemy pieces being close to the King might be a bit of an issue. + // TODO: Have a more optimal way of doing this later. Probably by using the bitboards and stuff + for square := range NoSquare { + colorOfPiece, piece := pos.GetSquare(square) + + pieceRank := RankOf(square) + pieceFile := FileOf(square) + + rankDiff := Abs(int(pieceRank - kingRank)) + fileDiff := Abs(int(pieceFile - kingFile)) + + distance := math.Sqrt(float64(rankDiff*rankDiff + fileDiff*fileDiff)) + distanceWeight := 10.0 - distance // the closer it is the higher the weight + + if colorOfPiece != color { + switch piece { + case Queen: + safetyScore -= int32(20 * distanceWeight) + case Rook: + safetyScore -= int32(10 * distanceWeight) + case Bishop: + safetyScore -= int32(5 * distanceWeight) + case Knight: + safetyScore -= int32(5 * distanceWeight) + case Pawn: + // Not as small as a pawn storm could be dangerous, + safetyScore -= int32(3 * distanceWeight) + } + } + } + + return safetyScore +} + func Evaluate(pos *Position) int32 { us := pos.Turn them := pos.Turn ^ 1 @@ -161,9 +210,12 @@ func Evaluate(pos *Position) int32 { ourEGMaterial := pos.EndgameMaterial(us) theirEGMaterial := pos.EndgameMaterial(them) + ourKingSafety := pos.KingSafety(us) + theirKingSafety := pos.KingSafety(them) + isEndgame := (ourEGMaterial + theirEGMaterial) <= 1400 - eval := ourMaterial - theirMaterial + eval := ourMaterial + ourKingSafety - theirMaterial - theirKingSafety for piece := Pawn; piece <= King; piece++ { bb := pos.Pieces[us][piece] diff --git a/engine/position.go b/engine/position.go index bb66a65..49d6c7a 100644 --- a/engine/position.go +++ b/engine/position.go @@ -85,6 +85,10 @@ func (pos *Position) PutPiecesBB(pieces [2][6]Bitboard) { } } +func (pos *Position) GetKingSquare(color uint8) Square { + return Lsb(pos.Pieces[color][King]) +} + func (pos *Position) RemovePiece(sq Square) { piece := pos.Board[sq] color := ColorOf(piece) @@ -109,8 +113,7 @@ func (pos *Position) Equals(otherPos Position) bool { pos.EnPassantSquare == otherPos.EnPassantSquare } -// (color, piece) -func (pos *Position) GetSquare(sq Square) (uint8, uint8) { +func (pos *Position) GetSquare(sq Square) (color uint8, piece uint8) { p := pos.Board[sq] if p == NoPiece { return NoColor, NoPiece diff --git a/engine/search.go b/engine/search.go index 59eb7f1..2e0e6a3 100644 --- a/engine/search.go +++ b/engine/search.go @@ -12,17 +12,19 @@ const MaxQuiescenceDepth = 8 // return number in milliseconds func TimeLimit(pos *Position, command *UciGoMessage) time.Duration { var ourTime, ourInc int32 //, theirTime, theirInc int32 - if pos.Turn == White { + switch pos.Turn { + case White: ourTime = command.WTime ourInc = command.WInc // theirTime = command.BTime // theirInc = command.BInc - } else if pos.Turn == Black { + case Black: ourTime = command.BTime ourInc = command.BInc // theirTime = command.WTime // theirInc = command.WInc } + estimatedMovesLeft := max(10, 100-pos.FullMoves()) // multiplying time.Miillisecond twice? return min(MaxMovetime, time.Duration(ourTime/int32(estimatedMovesLeft)+ourInc/4))