diff --git a/cmd/rosaline/interfaces/cli.go b/cmd/rosaline/interfaces/cli.go index 698462a..633c332 100644 --- a/cmd/rosaline/interfaces/cli.go +++ b/cmd/rosaline/interfaces/cli.go @@ -82,16 +82,16 @@ func (i cliInterface) Loop() { } } - move := i.searcher.Search(position, depth) - fmt.Println("best move:", move) - fmt.Println("score:", move.Score) + results := i.searcher.Search(position, depth) + fmt.Println("best move:", results.BestMove) + fmt.Println("score:", results.Score) } else if cmd == "evaluate" { score := i.evaluator.Evaluate(position) fmt.Println("score:", score) } else if cmd == "play" { - move := i.searcher.Search(position, DefaultDepth) - position.MakeMove(move.Move) - fmt.Println("played:", move.Move) + results := i.searcher.Search(position, DefaultDepth) + position.MakeMove(results.BestMove) + fmt.Println("played:", results.BestMove) } else if cmd == "fen" { fmt.Println(position.Fen()) } else if cmd == "setfen" { diff --git a/cmd/rosaline/interfaces/uci.go b/cmd/rosaline/interfaces/uci.go index 2d0438a..d931606 100644 --- a/cmd/rosaline/interfaces/uci.go +++ b/cmd/rosaline/interfaces/uci.go @@ -51,9 +51,9 @@ loop: position.MakeUciMove(lastMove) break case "go": - move := i.searcher.Search(position, DefaultDepth) - position.MakeMove(move.Move) - fmt.Println("bestmove", move) + results := i.searcher.Search(position, DefaultDepth) + position.MakeMove(results.BestMove) + fmt.Println("bestmove", results.BestMove) break case "quit": break loop diff --git a/internal/search/negamax.go b/internal/search/negamax.go index 966ae86..249df1d 100644 --- a/internal/search/negamax.go +++ b/internal/search/negamax.go @@ -15,12 +15,21 @@ const ( maxNumberKillerMoves = 128 ) +type SearchResults struct { + BestMove chess.Move + Score int + Depth int + Nodes int +} + type NegamaxSearcher struct { evaluator evaluation.Evaluator drawTable drawTable killerMoves map[chess.Color][]chess.Move killerMoveIndex int + + nodes int } func NewNegamaxSearcher(evaluator evaluation.Evaluator) NegamaxSearcher { @@ -29,29 +38,34 @@ func NewNegamaxSearcher(evaluator evaluation.Evaluator) NegamaxSearcher { drawTable: newDrawTable(), killerMoves: make(map[chess.Color][]chess.Move), killerMoveIndex: 0, + nodes: 0, } } -func (s NegamaxSearcher) Search(position chess.Position, depth int) ScoredMove { +func (s NegamaxSearcher) Search(position chess.Position, depth int) SearchResults { + s.nodes = 0 + moves := position.GenerateMoves(chess.LegalMoveGeneration) - bestMove := ScoredMove{} + bestMove := chess.Move{} bestScore := math.MinInt + 1 for _, move := range moves { position.MakeMove(move) - score := -s.doSearch(position, initialAlpha, initialBeta, depth-1, 0, 0) - scoredMove := NewScoredMove(move, score) + position.Undo() if score > bestScore { bestScore = score - bestMove = scoredMove + bestMove = move } - - position.Undo() } - return bestMove + return SearchResults{ + BestMove: bestMove, + Score: bestScore, + Depth: depth, + Nodes: s.nodes, + } } func (s NegamaxSearcher) scoreMove(position chess.Position, move chess.Move) int { @@ -91,6 +105,8 @@ func (s *NegamaxSearcher) doSearch(position chess.Position, alpha int, beta int, return s.evaluator.AbsoluteEvaluation(position) } + s.nodes++ + slices.SortFunc(moves, func(m1, m2 chess.Move) int { return cmp.Compare(s.scoreMove(position, m1), s.scoreMove(position, m2)) }) diff --git a/internal/search/scoredmove.go b/internal/search/scoredmove.go deleted file mode 100644 index 9513915..0000000 --- a/internal/search/scoredmove.go +++ /dev/null @@ -1,20 +0,0 @@ -package search - -import "rosaline/internal/chess" - -type ScoredMove struct { - Move chess.Move // The move that needs to be made to get the attached score. - Score int // The score of the position after the move has been made. -} - -// NewScoredMove creates a new ScoredMove. -func NewScoredMove(move chess.Move, score int) ScoredMove { - return ScoredMove{ - Move: move, - Score: score, - } -} - -func (m ScoredMove) String() string { - return m.Move.String() -}