Tuesday, October 5, 2010

Chess techniques

A chess program consist on three main parts:

  • A move generator, from a given position generate all possible moves and/or captures/promotions
    • It has to be FAST, very fast, this function is called millions times, so the faster the better
    • I've done it in three different approaches
      • Manually generate moves for the different pieces types, easy but slow
      • Generate the movements with a bunch of precalculated arrays with steps for different piece types, acceptable speed.
      • Bitboards, a technique that involves storing 0 or 1 in a int64 variable (uh, 64 bits = 64 squares) and start doing boolean operations (and, or, not and xor) depending on the goal, for instance:
        • Convert your board side into a bitboard
        • Convert my board side into a bitboard
        • In my board for every piece do My_BB or Your_BB, scan the resultant bitboard, 1 means you cannot move to that square, 0 means the sqare is empty (which of course does not means that you are able to move there, that depends on the piece rules)
        • Currently I've a mix of arrays and bitboards, but I'm already playing with full bitboards
  • A search function, from a given position try to search as deep as possible with the time left, sounds easy but is quite tricky, for instance, from the initial position at depth 10 yields an amazing amounts of movements
    • 1  20
    • 2  400
    • 3  8.902
    • 4  197.281
    • 5  4.865.609
    • 6  119.060.324
    • 7  3.195.901.860
    • 8  84.998.978.956
    • 9  2.439.530.234.167
    • 10 69.352.859.712.417      (WTF!!!!!)
    • I'm using an AlphaBeta tree reduction which works very good along with many optimizations
      • Killermoves (keep the very good movements and try to use them)
      • Historic moves (increase an arrays of moves historic[0..63][0..63])
      • Transposition tables (aka cache)
      • Aspiration window (rather than using a fix window, move it with the current scoring, very tricky)
      • Iterative deepining (don't search at maximum depth, but use 1, then 2, 3 until time is over, this is sometimes difficult to assimilate the reasons behind)
      • Futility prunning (try not to play positions where your oponent cannot improve your movement)
      • Move ordering (a MUST, the quicker you play the best movements the better)
      • Null move (skip your movements and after your oponents play, see how good or bad is the position)
      • See (static exchange evaluator, only used in capturing, gives scores to captures)
      • Quiescent (plays until there is no capture left in both sides)
      • Principal variant search (if the first move is the best, try to play the rest of the moves a bit of weak)
  • An evaluation function, for a given position evaluate how good is the position for the current side, the more accurate the better, I use the following formula
    • MyScore = 
    • MyPieceValues - YourPieceValues +
    • MyTotalMovements - YourTotalMovements +
    • MyPiecePosition - YourPieceScoring +
    • MyKingDefense - YourKingDefense +
    • MyCastling - YourCastling +
    • A lot of tweeks 
    • The bigger the score, the better the move
I will create a specific post for every single point.

No comments:

Post a Comment