github.com/frankkopp/FrankyGo@v1.0.3/internal/movegen/movegen.go (about)

     1  //
     2  // FrankyGo - UCI chess engine in GO for learning purposes
     3  //
     4  // MIT License
     5  //
     6  // Copyright (c) 2018-2020 Frank Kopp
     7  //
     8  // Permission is hereby granted, free of charge, to any person obtaining a copy
     9  // of this software and associated documentation files (the "Software"), to deal
    10  // in the Software without restriction, including without limitation the rights
    11  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    12  // copies of the Software, and to permit persons to whom the Software is
    13  // furnished to do so, subject to the following conditions:
    14  //
    15  // The above copyright notice and this permission notice shall be included in all
    16  // copies or substantial portions of the Software.
    17  //
    18  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    19  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    20  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    21  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    22  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    23  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    24  // SOFTWARE.
    25  //
    26  
    27  // Package movegen contains functionality to create moves on a
    28  // chess position. It implements several variants like
    29  // generate pseudo legal moves, legal moves or on demand
    30  // generation of pseudo legal moves.
    31  package movegen
    32  
    33  import (
    34  	"fmt"
    35  	"regexp"
    36  	"strings"
    37  
    38  	"github.com/op/go-logging"
    39  
    40  	"github.com/frankkopp/FrankyGo/internal/attacks"
    41  	"github.com/frankkopp/FrankyGo/internal/config"
    42  	"github.com/frankkopp/FrankyGo/internal/history"
    43  	myLogging "github.com/frankkopp/FrankyGo/internal/logging"
    44  	"github.com/frankkopp/FrankyGo/internal/moveslice"
    45  	"github.com/frankkopp/FrankyGo/internal/position"
    46  	. "github.com/frankkopp/FrankyGo/internal/types"
    47  )
    48  
    49  var log *logging.Logger
    50  
    51  // Movegen data structure. Create new move generator via
    52  //  movegen.NewMoveGen()
    53  // Creating this directly will not work.
    54  type Movegen struct {
    55  	pseudoLegalMoves *moveslice.MoveSlice
    56  	legalMoves       *moveslice.MoveSlice
    57  
    58  	onDemandMoves          *moveslice.MoveSlice
    59  	currentODZobrist       position.Key
    60  	onDemandEvasionTargets Bitboard
    61  	currentODStage         int8
    62  	takeIndex              int
    63  
    64  	killerMoves  [2]Move
    65  	pvMove       Move
    66  	pvMovePushed bool
    67  	historyData  *history.History
    68  }
    69  
    70  // //////////////////////////////////////////////////////
    71  // // Public
    72  // //////////////////////////////////////////////////////
    73  
    74  // GenMode generation modes for on demand move generation.
    75  type GenMode int
    76  
    77  // GenMode generation modes for on demand move generation.
    78  const (
    79  	GenZero     GenMode = 0b00
    80  	GenNonQuiet GenMode = 0b01
    81  	GenQuiet    GenMode = 0b10
    82  	GenAll      GenMode = 0b11
    83  )
    84  
    85  // NewMoveGen creates a new instance of a move generator
    86  // This is the only time when we allocate new memory. The instance
    87  // will not create any move lists during normal move generation
    88  // as it will reuse pre-created internal lists which will
    89  // be returned via pointer to a caller.
    90  // OBS: Be careful when trying to store the list of generated
    91  // moves as the underlying list will be changed when move gen
    92  // is called again. A deep copy is necessary if you need a
    93  // copy of the move list.
    94  // For a deep copy use:
    95  //  moveslice.MoveSlice.Clone()
    96  func NewMoveGen() *Movegen {
    97  	if log == nil {
    98  		log = myLogging.GetLog()
    99  	}
   100  	tmpMg := &Movegen{
   101  		pseudoLegalMoves: moveslice.NewMoveSlice(MaxMoves),
   102  		legalMoves:       moveslice.NewMoveSlice(MaxMoves),
   103  
   104  		onDemandMoves:          moveslice.NewMoveSlice(MaxMoves),
   105  		currentODZobrist:       0,
   106  		onDemandEvasionTargets: BbZero,
   107  		currentODStage:         odNew,
   108  		takeIndex:              0,
   109  
   110  		killerMoves:  [2]Move{MoveNone, MoveNone},
   111  		pvMove:       MoveNone,
   112  		pvMovePushed: false,
   113  		historyData:  nil,
   114  	}
   115  	return tmpMg
   116  }
   117  
   118  // GeneratePseudoLegalMoves generates pseudo moves for the next player. Does not check if
   119  // king is left in check or if it passes an attacked square when castling or has been in check
   120  // before castling.
   121  //
   122  // If a PV move is set with setPV(Move pv) this move will be returned first and will
   123  // not be returned at its normal place.
   124  //
   125  // Killer moves will be played as soon as possible after non quiet moves. As Killer moves
   126  // are stored for the whole ply a Killer move might not be valid for the current position.
   127  // Therefore we need to wait until they are generated. Killer moves will then be pushed
   128  // to the top of the the quiet moves.
   129  //
   130  // Evasion is a parameter given when the position is in check and only evasion moves should
   131  // be generated. For testing purposes this is a parameter but obviously we could determine
   132  // checks very quickly internally in this function.
   133  // The idea of evasion is to avoid generating moves which are obviously not getting the
   134  // king out of check. This may reduce the total number of generated moves but there might
   135  // still be a few non legal moves. This is the case if considering and calculating all
   136  // possible scenarios is more expensive than to just generate the move and dismiss is later.
   137  // Because of beta cuts off we quite often will never have to check the full legality
   138  // of these moves anyway.
   139  func (mg *Movegen) GeneratePseudoLegalMoves(p *position.Position, mode GenMode, evasion bool) *moveslice.MoveSlice {
   140  	// re-use move list
   141  	mg.pseudoLegalMoves.Clear()
   142  
   143  	// when in check only generate moves either blocking or capturing the attacker
   144  	if evasion {
   145  		mg.onDemandEvasionTargets = mg.getEvasionTargets(p)
   146  	}
   147  
   148  	// first generate all non quiet moves
   149  	if mode&GenNonQuiet != 0 {
   150  		mg.generatePawnMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   151  		// castling never captures
   152  		mg.generateKingMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   153  		mg.generateMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   154  	}
   155  	// second generate all other moves
   156  	if mode&GenQuiet != 0 {
   157  		mg.generatePawnMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   158  		if !evasion { // no castling when in check
   159  			mg.generateCastling(p, GenQuiet, mg.pseudoLegalMoves)
   160  		}
   161  		mg.generateKingMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   162  		mg.generateMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.pseudoLegalMoves)
   163  	}
   164  
   165  	// PV, Killer and history handling
   166  	mg.updateSortValues(p, mg.pseudoLegalMoves)
   167  
   168  	// sort moves
   169  	mg.pseudoLegalMoves.Sort()
   170  
   171  	// remove internal sort value
   172  	mg.pseudoLegalMoves.ForEach(func(i int) {
   173  		mg.pseudoLegalMoves.Set(i, mg.pseudoLegalMoves.At(i).MoveOf())
   174  	})
   175  
   176  	return mg.pseudoLegalMoves
   177  }
   178  
   179  // GenerateLegalMoves generates legal moves for the next player.
   180  // Uses GeneratePseudoLegalMoves and filters out illegal moves.
   181  // Usually only used for root moves generation as this is expensive. During
   182  // the AlphaBeta search we will only use pseudo legal move generation.
   183  func (mg *Movegen) GenerateLegalMoves(position *position.Position, mode GenMode) *moveslice.MoveSlice {
   184  	mg.legalMoves.Clear()
   185  	mg.GeneratePseudoLegalMoves(position, mode, false)
   186  	mg.pseudoLegalMoves.FilterCopy(mg.legalMoves, func(i int) bool {
   187  		return position.IsLegalMove(mg.pseudoLegalMoves.At(i))
   188  	})
   189  	return mg.legalMoves
   190  }
   191  
   192  // GetNextMove is the main function for phased generation of pseudo legal moves.
   193  // It returns the next move for the given position and will usually be called in a
   194  // loop during search. As we hope for an early beta cut this will save time as not
   195  // all moves will have been generated.
   196  //
   197  // To reuse this on the same position a call to ResetOnDemand() is necessary. This
   198  // is not necessary when a different position is called as this func will reset it self
   199  // in this case.
   200  //
   201  // If a PV move is set with setPV(Move pv) this will be returned first
   202  // and will not be returned at its normal place.
   203  //
   204  // Killer moves will be played as soon as possible. As Killer moves are stored for
   205  // the whole ply a Killer move might not be valid for the current position. Therefore
   206  // we need to wait until they are generated by the phased move generation. Killers will
   207  // then be pushed to the top of the list of the generation stage.
   208  //
   209  // Evasion is a parameter given when the position is in check and only evasion moves should
   210  // be generated. For testing purposes this is a parameter for now but obviously we could
   211  // determine checks very quickly internally in this function.
   212  // The idea of evasion is to avoid generating moves which are obviously not getting the
   213  // king out of check. This may reduce the total number of generated moves but there might
   214  // still be a few non legal moves. This is the case if considering and calculating all
   215  // possible scenarios is more expensive than to just generate the move and dismiss is later.
   216  // Because of beta cuts off we quite often will never have to check the full legality
   217  // of these moves anyway.
   218  func (mg *Movegen) GetNextMove(p *position.Position, mode GenMode, evasion bool) Move {
   219  
   220  	// if the position changes during iteration the iteration
   221  	// will be reset and generation will be restart with the
   222  	// new position.
   223  	if p.ZobristKey() != mg.currentODZobrist {
   224  		mg.onDemandMoves.Clear()
   225  		mg.onDemandEvasionTargets = BbZero
   226  		mg.currentODStage = odNew
   227  		mg.pvMovePushed = false
   228  		mg.takeIndex = 0
   229  		mg.currentODZobrist = p.ZobristKey()
   230  	}
   231  
   232  	// when in check only generate moves either blocking or capturing the attacker
   233  	if evasion && mg.onDemandEvasionTargets == BbZero {
   234  		mg.onDemandEvasionTargets = mg.getEvasionTargets(p)
   235  	}
   236  
   237  	// ad takeIndex
   238  	// With the takeIndex we can take from the front of the vector
   239  	// without removing the element from the vector which would
   240  	// be expensive as all elements would have to be shifted.
   241  	// (although our Moveslice class can handle this efficiently
   242  	// through a similar mechanism)
   243  
   244  	// If the list is currently empty and we have not generated all moves yet
   245  	// generate the next batch until we have new moves or there are no more
   246  	// moves to generate
   247  	if mg.onDemandMoves.Len() == 0 {
   248  		mg.fillOnDemandMoveList(p, mode, evasion)
   249  	}
   250  
   251  	// If we have generated moves we will return the first move and
   252  	// increase the takeIndex to the next move. If the list is empty
   253  	// even after all stages of generating we have no more moves
   254  	// and return MOVE_NONE
   255  	// If we have pushed a pvMove into the list we will need to
   256  	// skip this pvMove for each subsequent phases.
   257  	if mg.onDemandMoves.Len() != 0 {
   258  
   259  		// Handle PvMove
   260  		// if we pushed a pv move and the list is not empty we
   261  		// check if the pv is the next move in list and skip it.
   262  		if mg.currentODStage != od1 &&
   263  			mg.pvMovePushed &&
   264  			(*mg.onDemandMoves)[mg.takeIndex].MoveOf() == mg.pvMove.MoveOf() {
   265  
   266  			// skip pv move
   267  			mg.takeIndex++
   268  
   269  			// We found the pv move and skipped it.
   270  			// No need to check this for this generation cycle
   271  			mg.pvMovePushed = false
   272  
   273  			// PV move last in move list
   274  			if mg.takeIndex >= mg.onDemandMoves.Len() {
   275  				// The pv move was the last move in this iterations list.
   276  				// We will try to generate more moves. If no more moves
   277  				// can be generated we will return MOVE_NONE.
   278  				// Otherwise we return the move below.
   279  				mg.takeIndex = 0
   280  				mg.onDemandMoves.Clear()
   281  				mg.fillOnDemandMoveList(p, mode, false)
   282  				// no more moves - return MOVE_NONE
   283  				if mg.onDemandMoves.Len() == 0 {
   284  					return MoveNone
   285  				}
   286  			}
   287  		}
   288  
   289  		// we have at least one move in the list and
   290  		// it is not the pvMove. Increase the takeIndex
   291  		// and return the move
   292  		// (remove internal sort value)
   293  		move := (*mg.onDemandMoves)[mg.takeIndex].MoveOf()
   294  		mg.takeIndex++
   295  		if mg.takeIndex >= mg.onDemandMoves.Len() {
   296  			mg.takeIndex = 0
   297  			mg.onDemandMoves.Clear()
   298  		}
   299  		return move
   300  	}
   301  
   302  	// no more moves to be generated
   303  	mg.takeIndex = 0
   304  	mg.pvMovePushed = false
   305  	return MoveNone
   306  }
   307  
   308  // ResetOnDemand resets the move on demand generator to start fresh.
   309  // Also deletes Killer and PV moves.
   310  func (mg *Movegen) ResetOnDemand() {
   311  	mg.onDemandMoves.Clear()
   312  	mg.onDemandEvasionTargets = BbZero
   313  	mg.currentODStage = odNew
   314  	mg.currentODZobrist = 0
   315  	mg.pvMove = MoveNone
   316  	mg.pvMovePushed = false
   317  	mg.takeIndex = 0
   318  }
   319  
   320  // SetPvMove sets a PV move which should be returned first by
   321  // the OnDemand MoveGenerator.
   322  func (mg *Movegen) SetPvMove(move Move) {
   323  	mg.pvMove = move.MoveOf()
   324  }
   325  
   326  // StoreKiller provides the on demand move generator with a new killer move
   327  // which should be returned as soon as possible when generating moves with
   328  // the on demand generator.
   329  func (mg *Movegen) StoreKiller(move Move) {
   330  	// check if already stored in first slot - if so return
   331  	moveOf := move.MoveOf()
   332  	if mg.killerMoves[0] == moveOf {
   333  		return
   334  	} else if mg.killerMoves[1] == moveOf { // if in second slot move it to first
   335  		mg.killerMoves[1] = mg.killerMoves[0]
   336  		mg.killerMoves[0] = moveOf
   337  	} else {
   338  		// add it to first slot und move first to second
   339  		mg.killerMoves[1] = mg.killerMoves[0]
   340  		mg.killerMoves[0] = moveOf
   341  	}
   342  }
   343  
   344  // SetHistoryData provides a pointer to the search's history data
   345  // for the move generator so it can optimize sorting.
   346  func (mg *Movegen) SetHistoryData(historyData *history.History) {
   347  	mg.historyData = historyData
   348  }
   349  
   350  // HasLegalMove determines if we have at least one legal move. We only have to find
   351  // one legal move. We search for any KING, PAWN, KNIGHT, BISHOP, ROOK, QUEEN move
   352  // and return immediately if we found one.
   353  // The order of our search is approx from the most likely to the least likely.
   354  func (mg *Movegen) HasLegalMove(position *position.Position) bool {
   355  
   356  	us := position.NextPlayer()
   357  	usBb := position.OccupiedBb(us)
   358  
   359  	// KING
   360  	// We do not need to check castling as possible castling implies King or Rook moves
   361  	kingSquare := position.KingSquare(us)
   362  	tmpMoves := GetAttacksBb(King, kingSquare, BbZero) &^ usBb
   363  	for tmpMoves != 0 {
   364  		toSquare := tmpMoves.PopLsb()
   365  		if position.IsLegalMove(CreateMove(kingSquare, toSquare, Normal, PtNone)) {
   366  			return true
   367  		}
   368  	}
   369  
   370  	myPawns := position.PiecesBb(us, Pawn)
   371  	occupiedBb := position.OccupiedAll()
   372  	opponentBb := position.OccupiedBb(us.Flip())
   373  
   374  	// PAWN
   375  	// pawns - check step one to unoccupied squares
   376  	tmpMoves = ShiftBitboard(myPawns, us.MoveDirection()) & ^position.OccupiedAll()
   377  	// pawns double - check step two to unoccupied squares
   378  	tmpMovesDouble := ShiftBitboard(tmpMoves&us.PawnDoubleRank(), us.MoveDirection()) & ^position.OccupiedAll()
   379  	// double pawn steps
   380  	for tmpMovesDouble != 0 {
   381  		toSquare := tmpMovesDouble.PopLsb()
   382  		fromSquare := toSquare.To(us.Flip().MoveDirection()).To(us.Flip().MoveDirection())
   383  		if position.IsLegalMove(CreateMove(fromSquare, toSquare, Normal, PtNone)) {
   384  			return true
   385  		}
   386  	}
   387  	// normal single pawn steps
   388  	tmpMoves &= ^us.PromotionRankBb()
   389  	for tmpMoves != 0 {
   390  		toSquare := tmpMoves.PopLsb()
   391  		fromSquare := toSquare.To(us.Flip().MoveDirection())
   392  		if position.IsLegalMove(CreateMove(fromSquare, toSquare, Normal, PtNone)) {
   393  			return true
   394  		}
   395  	}
   396  
   397  	// normal pawn captures to the west (includes promotions)
   398  	tmpMoves = ShiftBitboard(myPawns, us.MoveDirection()+West) & opponentBb
   399  	for tmpMoves != 0 {
   400  		toSquare := tmpMoves.PopLsb()
   401  		fromSquare := toSquare.To(us.Flip().MoveDirection() + East)
   402  		if position.IsLegalMove(CreateMove(fromSquare, toSquare, Normal, PtNone)) {
   403  			return true
   404  		}
   405  	}
   406  
   407  	// normal pawn captures to the east - promotions first
   408  	tmpMoves = ShiftBitboard(myPawns, us.MoveDirection()+East) & opponentBb
   409  	for tmpMoves != 0 {
   410  		toSquare := tmpMoves.PopLsb()
   411  		fromSquare := toSquare.To(us.Flip().MoveDirection() + West)
   412  		if position.IsLegalMove(CreateMove(fromSquare, toSquare, Normal, PtNone)) {
   413  			return true
   414  		}
   415  	}
   416  
   417  	// OFFICERS
   418  	for pt := Knight; pt <= Queen; pt++ {
   419  		pieces := position.PiecesBb(us, pt)
   420  		for pieces != 0 {
   421  			fromSquare := pieces.PopLsb()
   422  			moves := GetAttacksBb(pt, fromSquare, occupiedBb)
   423  			for moves != 0 {
   424  				toSquare := moves.PopLsb()
   425  				if position.IsLegalMove(CreateMove(fromSquare, toSquare, Normal, PtNone)) {
   426  					return true
   427  				}
   428  			}
   429  		}
   430  	}
   431  
   432  	// en passant captures
   433  	enPassantSquare := position.GetEnPassantSquare()
   434  	if enPassantSquare != SqNone {
   435  		// left
   436  		tmpMoves = ShiftBitboard(enPassantSquare.Bb(), us.Flip().MoveDirection()+West) & myPawns
   437  		if tmpMoves != 0 {
   438  			fromSquare := tmpMoves.PopLsb()
   439  			if position.IsLegalMove(CreateMove(fromSquare, fromSquare.To(us.MoveDirection()+East), EnPassant, PtNone)) {
   440  				return true
   441  			}
   442  		}
   443  		// right
   444  		tmpMoves = ShiftBitboard(enPassantSquare.Bb(), us.Flip().MoveDirection()+East) & myPawns
   445  		if tmpMoves != 0 {
   446  			fromSquare := tmpMoves.PopLsb()
   447  			if position.IsLegalMove(CreateMove(fromSquare, fromSquare.To(us.MoveDirection()+West), EnPassant, PtNone)) {
   448  				return true
   449  			}
   450  		}
   451  	}
   452  
   453  	// no move found
   454  	return false
   455  }
   456  
   457  // Regex for UCI notation (UCI).
   458  var regexUciMove = regexp.MustCompile("([a-h][1-8][a-h][1-8])([NBRQnbrq])?")
   459  
   460  // GetMoveFromUci Generates all legal moves and matches the given UCI
   461  // move string against them. If there is a match the actual move is returned.
   462  // Otherwise MoveNone is returned.
   463  //
   464  // As this uses string creation and comparison this is not very efficient.
   465  // Use only when performance is not critical.
   466  func (mg *Movegen) GetMoveFromUci(posPtr *position.Position, uciMove string) Move {
   467  	matches := regexUciMove.FindStringSubmatch(uciMove)
   468  	if matches == nil {
   469  		return MoveNone
   470  	}
   471  
   472  	// get the parts from the pattern match
   473  	movePart := matches[1]
   474  	promotionPart := ""
   475  	if len(matches) == 3 {
   476  		// we allow lower case promotion letters
   477  		// not really UCI but many input files have this wrong
   478  		promotionPart = strings.ToUpper(matches[2])
   479  	}
   480  
   481  	// check against all legal moves on position
   482  	mg.GenerateLegalMoves(posPtr, GenAll)
   483  	for _, m := range *mg.legalMoves {
   484  		if m.StringUci() == movePart+promotionPart {
   485  			// move found
   486  			return m
   487  		}
   488  	}
   489  	// move not found
   490  	return MoveNone
   491  }
   492  
   493  var regexSanMove = regexp.MustCompile("([NBRQK])?([a-h])?([1-8])?x?([a-h][1-8]|O-O-O|O-O)(=?([NBRQ]))?([!?+#]*)?")
   494  
   495  // GetMoveFromSan Generates all legal moves and matches the given SAN
   496  // move string against them. If there is a match the actual move is returned.
   497  // Otherwise MoveNone is returned.
   498  //
   499  // As this uses string creation and comparison this is not very efficient.
   500  // Use only when performance is not critical.
   501  func (mg *Movegen) GetMoveFromSan(posPtr *position.Position, sanMove string) Move {
   502  	matches := regexSanMove.FindStringSubmatch(sanMove)
   503  	if matches == nil {
   504  		return MoveNone
   505  	}
   506  
   507  	// get parts
   508  	pieceType := matches[1]
   509  	disambFile := matches[2]
   510  	disambRank := matches[3]
   511  	toSquare := matches[4]
   512  	promotion := matches[6]
   513  	// checkSign := matches[7] - ignore
   514  
   515  	movesFound := 0
   516  	moveFromSAN := MoveNone
   517  
   518  	// check against all legal moves on position
   519  	mg.GenerateLegalMoves(posPtr, GenAll)
   520  	for _, genMove := range *mg.legalMoves {
   521  
   522  		// castling moves
   523  		if genMove.MoveType() == Castling {
   524  			kingToSquare := genMove.To()
   525  			var castlingString string
   526  			switch kingToSquare {
   527  			case SqG1: // white king side
   528  				fallthrough
   529  			case SqG8: // black king side
   530  				castlingString = "O-O"
   531  			case SqC1: // white queen side
   532  				fallthrough
   533  			case SqC8: // black queen side
   534  				castlingString = "O-O-O"
   535  			default:
   536  				log.Error("Move type CASTLING but wrong to square: %s %s", castlingString, kingToSquare.String())
   537  				continue
   538  			}
   539  			if castlingString == toSquare {
   540  				moveFromSAN = genMove
   541  				movesFound++
   542  				continue
   543  			}
   544  		}
   545  
   546  		// normal moves
   547  		moveTarget := genMove.To().String()
   548  		if moveTarget == toSquare {
   549  
   550  			// determine if piece types match - if not skip
   551  			legalPt := posPtr.GetPiece(genMove.From()).TypeOf()
   552  			legalPtChar := legalPt.Char()
   553  			if (len(pieceType) == 0 || legalPtChar != pieceType) &&
   554  				(len(pieceType) != 0 || legalPt != Pawn) {
   555  				continue
   556  			}
   557  
   558  			// Disambiguation File
   559  			if len(disambFile) != 0 && genMove.From().FileOf().String() != disambFile {
   560  				continue
   561  			}
   562  
   563  			// Disambiguation Rank
   564  			if len(disambRank) != 0 && genMove.From().RankOf().String() != disambRank {
   565  				continue
   566  			}
   567  
   568  			// promotion
   569  			if (len(promotion) != 0 && genMove.PromotionType().Char() != promotion) ||
   570  				(len(promotion) == 0 && genMove.MoveType() == Promotion) {
   571  				continue
   572  			}
   573  
   574  			// we should have our move if we end up here
   575  			moveFromSAN = genMove
   576  			movesFound++
   577  		}
   578  	}
   579  
   580  	// we should only have one move here
   581  	if movesFound > 1 {
   582  		log.Warningf("SAN move %s is ambiguous (%d matches) on %s!", sanMove, movesFound, posPtr.StringFen())
   583  	} else if movesFound == 0 || !moveFromSAN.IsValid() {
   584  		log.Warningf("SAN move not valid! SAN move %s not found on position: %s", sanMove, posPtr.StringFen())
   585  	} else {
   586  		return moveFromSAN
   587  	}
   588  	// no move found
   589  	return MoveNone
   590  }
   591  
   592  // ValidateMove validates if a move is a valid legal move on the given position
   593  func (mg *Movegen) ValidateMove(p *position.Position, move Move) bool {
   594  	if move == MoveNone {
   595  		return false
   596  	}
   597  	ml := mg.GenerateLegalMoves(p, GenAll)
   598  	for _, m := range *ml {
   599  		if move.MoveOf() == m {
   600  			return true
   601  		}
   602  	}
   603  	return false
   604  }
   605  
   606  // PvMove returns the current PV move
   607  func (mg *Movegen) PvMove() Move {
   608  	return mg.pvMove
   609  }
   610  
   611  // KillerMoves returns a pointer to the killer moves array
   612  func (mg *Movegen) KillerMoves() *[2]Move {
   613  	return &mg.killerMoves
   614  }
   615  
   616  // String returns a string representation of a MoveGen instance
   617  func (mg *Movegen) String() string {
   618  	return fmt.Sprintf("MoveGen: { OnDemand Stage: { %d }, PV Move: %s Killer Move 1: %s Killer Move 2: %s }",
   619  		mg.currentODStage, mg.pvMove.String(), mg.killerMoves[0].String(), mg.killerMoves[1].String())
   620  }
   621  
   622  // //////////////////////////////////////////////////////
   623  // // Private
   624  // //////////////////////////////////////////////////////
   625  
   626  // States for the on demand move generator
   627  const (
   628  	odNew = iota
   629  	odPv  = iota
   630  	od1   = iota
   631  	od2   = iota
   632  	od3   = iota
   633  	od4   = iota
   634  	od5   = iota
   635  	od6   = iota
   636  	od7   = iota
   637  	od8   = iota
   638  	odEnd = iota
   639  )
   640  
   641  // This calls the actual generation of moves in phases. The phases match roughly
   642  // the order of most promising moves first.
   643  func (mg *Movegen) fillOnDemandMoveList(p *position.Position, mode GenMode, evasion bool) {
   644  	for mg.onDemandMoves.Len() == 0 && mg.currentODStage < odEnd {
   645  		switch mg.currentODStage {
   646  		case odNew:
   647  			mg.currentODStage = odPv
   648  			fallthrough
   649  		case odPv:
   650  			// If a pvMove is set we return it first and filter it out before
   651  			// returning a move
   652  			if mg.pvMove != MoveNone {
   653  				switch mode {
   654  				case GenAll:
   655  					mg.pvMovePushed = true
   656  					mg.onDemandMoves.PushBack(mg.pvMove)
   657  				case GenNonQuiet:
   658  					if p.IsCapturingMove(mg.pvMove) {
   659  						mg.pvMovePushed = true
   660  						mg.onDemandMoves.PushBack(mg.pvMove)
   661  					}
   662  				case GenQuiet:
   663  					if !p.IsCapturingMove(mg.pvMove) {
   664  						mg.pvMovePushed = true
   665  						mg.onDemandMoves.PushBack(mg.pvMove)
   666  					}
   667  				}
   668  			}
   669  			// decide which state we should continue with
   670  			// captures or non captures or both
   671  			if mode&GenNonQuiet != 0 {
   672  				mg.currentODStage = od1
   673  			} else {
   674  				mg.currentODStage = od4
   675  			}
   676  		case od1: // capture
   677  			mg.generatePawnMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   678  			mg.updateSortValues(p, mg.onDemandMoves)
   679  			mg.currentODStage = od2
   680  		case od2:
   681  			mg.generateMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   682  			mg.updateSortValues(p, mg.onDemandMoves)
   683  			mg.currentODStage = od3
   684  		case od3:
   685  			mg.generateKingMoves(p, GenNonQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   686  			mg.currentODStage = od4
   687  		case od4:
   688  			if mode&GenQuiet != 0 {
   689  				mg.currentODStage = od5
   690  			} else {
   691  				mg.currentODStage = odEnd
   692  			}
   693  		case od5: // non capture
   694  			mg.generatePawnMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   695  			mg.updateSortValues(p, mg.onDemandMoves)
   696  			mg.currentODStage = od6
   697  		case od6:
   698  			if !evasion { // no castlings when in check
   699  				mg.generateCastling(p, GenQuiet, mg.onDemandMoves)
   700  				mg.updateSortValues(p, mg.onDemandMoves)
   701  			}
   702  			mg.currentODStage = od7
   703  		case od7:
   704  			mg.generateMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   705  			mg.updateSortValues(p, mg.onDemandMoves)
   706  			mg.currentODStage = od8
   707  		case od8:
   708  			mg.generateKingMoves(p, GenQuiet, evasion, mg.onDemandEvasionTargets, mg.onDemandMoves)
   709  			mg.updateSortValues(p, mg.onDemandMoves)
   710  			mg.currentODStage = odEnd
   711  		case odEnd:
   712  			break
   713  		}
   714  		// sort the list according to sort values encoded in the move
   715  		if mg.onDemandMoves.Len() > 0 {
   716  			mg.onDemandMoves.Sort()
   717  		}
   718  	} // while onDemandMoves.empty()
   719  }
   720  
   721  // Move order heuristics based on history data.
   722  func (mg *Movegen) updateSortValues(p *position.Position, moveList *moveslice.MoveSlice) {
   723  	us := p.NextPlayer()
   724  	for i := 0; i < len(*moveList); i++ {
   725  		move := &(*moveList)[i]
   726  		switch {
   727  		case move.MoveOf() == mg.pvMove: // PV move
   728  			(*move).SetValue(ValueMax)
   729  		case move.MoveOf() == mg.killerMoves[1]: // Killer 2
   730  			(*move).SetValue(-4001)
   731  		case move.MoveOf() == mg.killerMoves[0]: // Killer 1
   732  			(*move).SetValue(-4000)
   733  		case mg.historyData != nil: // historical search data
   734  
   735  			// History Count
   736  			// Moves that cause a beta cut in the search get an increasing value
   737  			// which favors many repetitions and deep searches.
   738  			// We use the history count to improve the sort value of a move
   739  			// If and how much a sort value has to be improved for a move is
   740  			// difficult to predict - this needs testing and experimentation.
   741  			// The current way is a hard cut for values <1000 and then 1 point
   742  			// per 1000 count points.
   743  			// It is also yet unclear if the history count table should be
   744  			// reused for several consecutive searches or just for one search.
   745  			// TODO: Testing
   746  			count := mg.historyData.HistoryCount[us][move.From()][move.To()]
   747  			value := Value(count / 100)
   748  
   749  			// Counter Move History
   750  			// When we have a counter move which caused a beta cut off before we
   751  			// bump up its sort value
   752  			// TODO: Testing
   753  			if mg.historyData.CounterMoves[p.LastMove().From()][p.LastMove().To()] == move.MoveOf() {
   754  				value += 500
   755  			}
   756  
   757  			// update move sort value
   758  			if value > 0 { // only touch the value if it would be improved
   759  				preValue := move.ValueOf()
   760  				(*move).SetValue(preValue + value)
   761  				// out.Printf("HistoryCount: %s = %d / %d ==> %d \n", move.StringUci(), count, preValue, preValue+value)
   762  			}
   763  		}
   764  	}
   765  }
   766  
   767  // getEvasionTargets returns the number of attackers and a Bitboard with target
   768  // squares for generated moves when the position has check against the next
   769  // player. Most of the moves will not even be generated as they will not
   770  // have these target squares. These target squares cover the attacking
   771  // (checker) piece and any squares in between the attacker and the king
   772  // in case of the attacker being a slider.
   773  // If we have more than one attacker we can skip everything apart from
   774  // king moves.
   775  func (mg *Movegen) getEvasionTargets(p *position.Position) Bitboard {
   776  	us := p.NextPlayer()
   777  	ourKing := p.KingSquare(us)
   778  	// find all target squares which either capture or block the attacker
   779  	evasionTargets := attacks.AttacksTo(p, ourKing, us.Flip())
   780  	// we can only block attacks of sliders of there is not more
   781  	// than one attacker
   782  	if evasionTargets.PopCount() == 1 {
   783  		atck := evasionTargets.Lsb()
   784  		// sliding pieces
   785  		if p.GetPiece(atck).TypeOf() > Knight {
   786  			evasionTargets |= Intermediate(atck, ourKing)
   787  		}
   788  	}
   789  	return evasionTargets
   790  }
   791  
   792  func (mg *Movegen) generatePawnMoves(position *position.Position, mode GenMode, evasion bool, evasionTargets Bitboard, ml *moveslice.MoveSlice) {
   793  
   794  	nextPlayer := position.NextPlayer()
   795  	myPawns := position.PiecesBb(nextPlayer, Pawn)
   796  	oppPieces := position.OccupiedBb(nextPlayer.Flip())
   797  	gamePhase := position.GamePhase()
   798  	piece := MakePiece(nextPlayer, Pawn)
   799  
   800  	// captures
   801  	if mode&GenNonQuiet != 0 {
   802  
   803  		// This algorithm shifts the own pawn bitboard in the direction of pawn captures
   804  		// and ANDs it with the opponents pieces. With this we get all possible captures
   805  		// and can easily create the moves by using a loop over all captures and using
   806  		// the backward shift for the from-Square.
   807  		// All moves get sort values so that sort order should be:
   808  		//   captures: most value victim least value attacker - promotion piece value
   809  		//   non captures: killer (TBD), promotions, castling, normal moves (position value)
   810  		// Values for sorting are descending - the most valuable move has the highest value.
   811  		// Values are not compatible to position evaluation values outside of the move
   812  		// generator.
   813  
   814  		// When we are in check only evasion moves are generated. E.g. all moves need to
   815  		// target these evasion squares. That is either capturing the attacker or blocking
   816  		// a sliding attacker.
   817  
   818  		var tmpCaptures, promCaptures Bitboard
   819  
   820  		for _, dir := range []Direction{West, East} {
   821  			// normal pawn captures - promotions first
   822  			tmpCaptures = ShiftBitboard(myPawns, nextPlayer.MoveDirection()+dir) & oppPieces
   823  			if evasion {
   824  				tmpCaptures &= evasionTargets
   825  			}
   826  
   827  			promCaptures = tmpCaptures & nextPlayer.PromotionRankBb()
   828  			// promotion captures
   829  			for promCaptures != 0 {
   830  				toSquare := promCaptures.PopLsb()
   831  				fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection() - dir)
   832  				// value is the delta of values from the two pieces involved minus the promoted pawn
   833  				value := position.GetPiece(toSquare).ValueOf() - (2 * Pawn.ValueOf())
   834  				// add the possible promotion moves to the move list and also add value of the promoted piece type
   835  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Queen, value+Queen.ValueOf()))
   836  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Knight, value+Knight.ValueOf()))
   837  				// rook and bishops are usually redundant to queen promotion (except in stale mate situations)
   838  				// therefore we give them lower sort order
   839  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Rook, value+Rook.ValueOf()-Value(2000)))
   840  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Bishop, value+Bishop.ValueOf()-Value(2000)))
   841  			}
   842  
   843  			// non promotion pawn captures
   844  			tmpCaptures &= ^nextPlayer.PromotionRankBb()
   845  			for tmpCaptures != 0 {
   846  				toSquare := tmpCaptures.PopLsb()
   847  				fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection() - dir)
   848  				// value is the delta of values from the two pieces involved plus the positional value
   849  				value := position.GetPiece(toSquare).ValueOf() - position.GetPiece(fromSquare).ValueOf() +
   850  					PosValue(piece, toSquare, gamePhase)
   851  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
   852  			}
   853  		}
   854  
   855  		// en passant captures
   856  		enPassantSquare := position.GetEnPassantSquare()
   857  		if enPassantSquare != SqNone {
   858  			for _, dir := range []Direction{West, East} {
   859  				tmpCaptures = ShiftBitboard(enPassantSquare.Bb(), nextPlayer.Flip().MoveDirection()+dir) & myPawns
   860  				if tmpCaptures != 0 {
   861  					fromSquare := tmpCaptures.PopLsb()
   862  					toSquare := fromSquare.To(nextPlayer.MoveDirection() - dir)
   863  					// value is the positional value of the piece at this game phase
   864  					value := PosValue(piece, toSquare, gamePhase)
   865  					ml.PushBack(CreateMoveValue(fromSquare, toSquare, EnPassant, PtNone, value))
   866  				}
   867  			}
   868  		}
   869  
   870  		// if this is enabled we treat Queen and Knight promotions as non quiet moves
   871  		if config.Settings.Search.UsePromNonQuiet {
   872  			promMoves := ShiftBitboard(myPawns, nextPlayer.MoveDirection()) &
   873  				^position.OccupiedAll() &
   874  				nextPlayer.PromotionRankBb()
   875  			if evasion {
   876  				promMoves &= evasionTargets
   877  			}
   878  			for promMoves != 0 {
   879  				toSquare := promMoves.PopLsb()
   880  				fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection())
   881  				// value for non captures is lowered by 10k
   882  				value := -Pawn.ValueOf()
   883  				// add the possible promotion moves to the move list and also add value of the promoted piece type
   884  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Queen, value+Queen.ValueOf()))
   885  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Knight, value+Knight.ValueOf()))
   886  			}
   887  		}
   888  	}
   889  
   890  	// non captures
   891  	if mode&GenQuiet != 0 {
   892  
   893  		//  Move my pawns forward one step and keep all on not occupied squares
   894  		//  Move pawns now on rank 3 (rank 6) another square forward to check for pawn doubles.
   895  		//  Loop over pawns remaining on unoccupied squares and add moves.
   896  
   897  		// When we are in check only evasion moves are generated. E.g. all moves need to
   898  		// target these evasion squares. That is either capturing the attacker or blocking
   899  		// a sliding attacker.
   900  
   901  		// pawns - check step one to unoccupied squares
   902  		tmpMoves := ShiftBitboard(myPawns, nextPlayer.MoveDirection()) & ^position.OccupiedAll()
   903  		// pawns double - check step two to unoccupied squares
   904  		tmpMovesDouble := ShiftBitboard(tmpMoves&nextPlayer.PawnDoubleRank(), nextPlayer.MoveDirection()) & ^position.OccupiedAll()
   905  
   906  		if evasion {
   907  			tmpMoves &= evasionTargets
   908  			tmpMovesDouble &= evasionTargets
   909  		}
   910  
   911  		// single pawn steps - promotions first
   912  		promMoves := tmpMoves & nextPlayer.PromotionRankBb()
   913  		for promMoves != 0 {
   914  			toSquare := promMoves.PopLsb()
   915  			fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection())
   916  			// value for non captures is lowered by 10k
   917  			value := Value(-10_000)
   918  			// if this is enabled we treat Queen and Knight promotions as non quiet moves
   919  			// and they are generated there
   920  			if !config.Settings.Search.UsePromNonQuiet {
   921  				// add the possible promotion moves to the move list and also add value of the promoted piece type
   922  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Queen, value+Queen.ValueOf()))
   923  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Knight, value+Knight.ValueOf()))
   924  			}
   925  			// rook and bishops are usually redundant to queen promotion (except in stale mate situations)
   926  			// therefore we give them lower sort order
   927  			ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Rook, value+Rook.ValueOf()-Value(2000)))
   928  			ml.PushBack(CreateMoveValue(fromSquare, toSquare, Promotion, Bishop, value+Bishop.ValueOf()-Value(2000)))
   929  		}
   930  		// double pawn steps
   931  		for tmpMovesDouble != 0 {
   932  			toSquare := tmpMovesDouble.PopLsb()
   933  			fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection()).
   934  				To(nextPlayer.Flip().MoveDirection())
   935  			value := Value(-10_000) + PosValue(piece, toSquare, gamePhase)
   936  			ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
   937  		}
   938  		// normal single pawn steps
   939  		tmpMoves &= ^nextPlayer.PromotionRankBb()
   940  		for tmpMoves != 0 {
   941  			toSquare := tmpMoves.PopLsb()
   942  			fromSquare := toSquare.To(nextPlayer.Flip().MoveDirection())
   943  			value := Value(-10_000) + PosValue(piece, toSquare, gamePhase)
   944  			ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
   945  		}
   946  	}
   947  }
   948  
   949  func (mg *Movegen) generateCastling(position *position.Position, mode GenMode, ml *moveslice.MoveSlice) {
   950  	nextPlayer := position.NextPlayer()
   951  	occupiedBB := position.OccupiedAll()
   952  
   953  	// castling - pseudo castling - we will not check if we are in check after the move
   954  	// or if we have passed an attacked square with the king or if the king has been in check
   955  
   956  	if mode&GenQuiet != 0 && position.CastlingRights() != CastlingNone {
   957  		cr := position.CastlingRights()
   958  		if nextPlayer == White { // white
   959  			if cr.Has(CastlingWhiteOO) && Intermediate(SqE1, SqH1)&occupiedBB == 0 {
   960  				ml.PushBack(CreateMoveValue(SqE1, SqG1, Castling, PtNone, Value(-5000)))
   961  			}
   962  			if cr.Has(CastlingWhiteOOO) && Intermediate(SqE1, SqA1)&occupiedBB == 0 {
   963  				ml.PushBack(CreateMoveValue(SqE1, SqC1, Castling, PtNone, Value(-5000)))
   964  			}
   965  		} else { // black
   966  			if cr.Has(CastlingBlackOO) && Intermediate(SqE8, SqH8)&occupiedBB == 0 {
   967  				ml.PushBack(CreateMoveValue(SqE8, SqG8, Castling, PtNone, Value(-5000)))
   968  			}
   969  			if cr.Has(CastlingBlackOOO) && Intermediate(SqE8, SqA8)&occupiedBB == 0 {
   970  				ml.PushBack(CreateMoveValue(SqE8, SqC8, Castling, PtNone, Value(-5000)))
   971  			}
   972  		}
   973  	}
   974  }
   975  
   976  func (mg *Movegen) generateKingMoves(p *position.Position, mode GenMode, evasion bool, evasionTargets Bitboard, ml *moveslice.MoveSlice) {
   977  	us := p.NextPlayer()
   978  	them := us.Flip()
   979  	piece := MakePiece(us, King)
   980  	gamePhase := p.GamePhase()
   981  	kingSquareBb := p.PiecesBb(us, King)
   982  	fromSquare := kingSquareBb.PopLsb()
   983  
   984  	// pseudo attacks include all moves no matter if the king would be in check
   985  	pseudoMoves := GetAttacksBb(King, fromSquare, BbZero)
   986  
   987  	// captures
   988  	if mode&GenNonQuiet != 0 {
   989  		captures := pseudoMoves & p.OccupiedBb(them)
   990  		for captures != 0 {
   991  			toSquare := captures.PopLsb()
   992  			// in case we are in check we only generate king moves to target squares which
   993  			// are not attacked by the opponent
   994  			if !evasion || attacks.AttacksTo(p, toSquare, them).PopCount() == 0 {
   995  				value := p.GetPiece(toSquare).ValueOf() - p.GetPiece(fromSquare).ValueOf() + PosValue(piece, toSquare, gamePhase)
   996  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
   997  			}
   998  		}
   999  	}
  1000  
  1001  	// non captures
  1002  	if mode&GenQuiet != 0 {
  1003  		nonCaptures := pseudoMoves &^ p.OccupiedAll()
  1004  		for nonCaptures != 0 {
  1005  			toSquare := nonCaptures.PopLsb()
  1006  			// in case we are in check we only generate king moves to target squares which
  1007  			// are not attacked by the opponent
  1008  			if !evasion || attacks.AttacksTo(p, toSquare, them).PopCount() == 0 {
  1009  				value := Value(-10_000) + PosValue(piece, toSquare, gamePhase)
  1010  				ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
  1011  			}
  1012  		}
  1013  	}
  1014  }
  1015  
  1016  // generates officers moves using the attacks pre-computed with magic bitboards
  1017  // Performance improvement to the previous loop based version:
  1018  // Old version:
  1019  // Test took 2.0049508s for 10.000.000 iterations
  1020  // Test took 200 ns per iteration
  1021  // Iterations per sec 4.987.653
  1022  // This version:
  1023  // Test took 1.516326s for 10.000.000 iterations
  1024  // Test took 151 ns per iteration
  1025  // Iterations per sec 6.594.887
  1026  // Improvement: +32%
  1027  func (mg *Movegen) generateMoves(position *position.Position, mode GenMode, evasion bool, evasionTargets Bitboard, ml *moveslice.MoveSlice) {
  1028  	nextPlayer := position.NextPlayer()
  1029  	gamePhase := position.GamePhase()
  1030  	occupiedBb := position.OccupiedAll()
  1031  
  1032  	// loop through all piece types, get pseudo attacks for the piece and
  1033  	// AND it with the opponents pieces.
  1034  	// For sliding pieces check if there are other pieces in between the
  1035  	// piece and the target square. If free this is a valid move (or
  1036  	// capture)
  1037  	// When we are in check (evasion=true) only evasion moves are generated. E.g. all
  1038  	// moves need to target these evasion squares. That is either capturing the
  1039  	// attacker or blocking a sliding attacker.
  1040  
  1041  	for pt := Knight; pt <= Queen; pt++ {
  1042  		pieces := position.PiecesBb(nextPlayer, pt)
  1043  		piece := MakePiece(nextPlayer, pt)
  1044  
  1045  		for pieces != 0 {
  1046  			fromSquare := pieces.PopLsb()
  1047  
  1048  			moves := GetAttacksBb(pt, fromSquare, occupiedBb)
  1049  
  1050  			// captures
  1051  			if mode&GenNonQuiet != 0 {
  1052  				captures := moves & position.OccupiedBb(nextPlayer.Flip())
  1053  				if evasion {
  1054  					captures &= evasionTargets
  1055  				}
  1056  				for captures != 0 {
  1057  					toSquare := captures.PopLsb()
  1058  					value := position.GetPiece(toSquare).ValueOf() - position.GetPiece(fromSquare).ValueOf() + PosValue(piece, toSquare, gamePhase)
  1059  					ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
  1060  				}
  1061  			}
  1062  
  1063  			// non captures
  1064  			if mode&GenQuiet != 0 {
  1065  				nonCaptures := moves &^ occupiedBb
  1066  				if evasion {
  1067  					nonCaptures &= evasionTargets
  1068  				}
  1069  				for nonCaptures != 0 {
  1070  					toSquare := nonCaptures.PopLsb()
  1071  					value := Value(-10_000) + PosValue(piece, toSquare, gamePhase)
  1072  					ml.PushBack(CreateMoveValue(fromSquare, toSquare, Normal, PtNone, value))
  1073  				}
  1074  			}
  1075  		}
  1076  	}
  1077  }