// Roughly orders moves in order of most likely to be good to least. // Examines all checks first, followed by captures, followed by good moves. // "Good moves" are sorted by their board evaluation after they are played. // If quiescence is set to true, then only checks and captures are returned. func orderedMoves(b *engine.Board, quiescence bool) []*engine.Move { checks := make([]*engine.Move, 0) captures := make([]*engine.Move, 0) rest := make([]*engine.Move, 0) // parentscore := EvalBoard(b) for _, move := range b.AllLegalMoves() { b.ForceMove(move) if b.IsCheck(b.Turn) { checks = append(checks, move) } else if move.Capture != 0 { captures = append(captures, move) } else if !quiescence { childscore := EvalBoard(b) * float64(b.Turn*-1) // if (b.Turn == -1 && childscore > parentscore) || (b.Turn == 1 && childscore < parentscore) { move.Score = childscore rest = append(rest, move) // } } b.UndoMove(move) } if !quiescence { sort.Sort(sort.Reverse(ByScore(rest))) } orderedmoves := make([]*engine.Move, len(checks)+len(captures)+len(rest)) index := 0 for _, l := range [][]*engine.Move{checks, captures, rest} { for _, m := range l { m.Score = 0 orderedmoves[index] = m index++ } } return orderedmoves }
// Child level returns an evaluation func AlphaBetaChild(b *engine.Board, depth int, alpha, beta float64, volatile bool) float64 { var movelist []*engine.Move if b.IsOver() != 0 { return EvalBoard(b) } else if depth == 0 { if !volatile { return EvalBoard(b) } depth += 1 movelist = orderedMoves(b, true) } else { movelist = orderedMoves(b, false) } var score float64 if b.Turn == 1 { for _, move := range movelist { b.ForceMove(move) if !volatile && (move.Capture != 0 || b.IsCheck(b.Turn)) { score = AlphaBetaChild(b, depth-1, alpha, beta, true) } else { score = AlphaBetaChild(b, depth-1, alpha, beta, false) } b.UndoMove(move) if score > alpha { alpha = score } if alpha >= beta { return alpha } } return alpha } else { for _, move := range movelist { b.ForceMove(move) if !volatile && (move.Capture != 0 || b.IsCheck(b.Turn)) { score = AlphaBetaChild(b, depth-1, alpha, beta, true) } else { score = AlphaBetaChild(b, depth-1, alpha, beta, false) } b.UndoMove(move) if score < beta { beta = score } if beta <= alpha { return beta } } return beta } return 0 }
// Standard minmax search with alpha beta pruning. // Initial call: alpha set to lowest value, beta set to highest. // Top level returns a move. func AlphaBeta(b *engine.Board, depth int, alpha, beta float64) *engine.Move { if b.IsOver() != 0 || depth == 0 { return nil } var bestmove *engine.Move = nil var result float64 movelist := orderedMoves(b, false) if b.Turn == 1 { for _, move := range movelist { b.ForceMove(move) if move.Capture != 0 || b.IsCheck(b.Turn) { result = AlphaBetaChild(b, depth-1, alpha, beta, true) } else { result = AlphaBetaChild(b, depth-1, alpha, beta, false) } b.UndoMove(move) if result > alpha { alpha = result bestmove = move bestmove.Score = alpha } if alpha >= beta { bestmove = move bestmove.Score = alpha return bestmove } } if bestmove == nil { return b.AllLegalMoves()[0] } return bestmove } else { for _, move := range movelist { b.ForceMove(move) if move.Capture != 0 || b.IsCheck(b.Turn) { result = AlphaBetaChild(b, depth-1, alpha, beta, true) } else { result = AlphaBetaChild(b, depth-1, alpha, beta, false) } if LOG { fmt.Println(move.ToString(), result) } b.UndoMove(move) if result < beta { beta = result bestmove = move bestmove.Score = beta } if beta <= alpha { bestmove = move bestmove.Score = beta return bestmove } } if bestmove == nil { return b.AllLegalMoves()[0] } return bestmove } if bestmove == nil { return b.AllLegalMoves()[0] } return bestmove }