@Override
 public void nextHashInTier() {
   if (dbh.getHash() < dbh.numHashes() - 1) dbh.next();
   else if (numPieces[BLACK] == 0) {
     numPieces[BLACK] = numPieces[WHITE];
     numPieces[WHITE] = 0;
     dbh.setNums(boardSize - getTier(), 0, getTier());
     if (turn != 0) throw new RuntimeException("Tier finished");
     else turn++;
   } else {
     dbh.setNums(boardSize - getTier(), ++numPieces[WHITE], --numPieces[BLACK]);
   }
   isChildrenValid = false;
 }
  @Override
  public void setStartingPosition(int n) {
    numPieces[BLACK] = numPieces[WHITE] = 0;
    dbh.setNums(boardSize, 0, 0);
    board[height / 2 - 1][width / 2 - 1].setPiece('X');
    board[height / 2][width / 2 - 1].setPiece('O');
    board[height / 2 - 1][width / 2].setPiece('O');
    board[height / 2][width / 2].setPiece('X');

    turn = BLACK;
    isChildrenValid = false;
  }
 private void unhash(int tier, long hash) {
   if (hash >= offsetTable[tier][BLACK][0]) {
     turn = BLACK;
   } else {
     turn = WHITE;
   }
   int offset = Arrays.binarySearch(offsetTable[tier][turn], hash);
   if (offset < 0) offset = -offset - 2;
   hash -= offsetTable[tier][turn][offset];
   numPieces[WHITE] = offset;
   numPieces[BLACK] = tier - offset;
   dbh.setNums(boardSize - tier, numPieces[WHITE], numPieces[BLACK]);
   dbh.unhash(hash);
   char[] charBoard = new char[boardSize];
   dbh.getCharArray(charBoard);
   for (int i = 0; i < boardSize; i++) {
     board[i / width][i % width].setPiece(charBoard[i]);
   }
 }
 public Reversi(Configuration conf) {
   super(conf);
   width = conf.getInteger("gamesman.game.width", 8);
   height = conf.getInteger("gamesman.game.height", 8);
   boardSize = width * height;
   board = new Cell[height][width];
   dbh = new DartboardHasher(boardSize, ' ', 'O', 'X');
   offsetTable = new long[boardSize + 1][2][];
   // initialize offset table
   for (int tier = 0; tier <= boardSize; tier++) {
     long total = 0;
     for (int turn = 0; turn < 2; turn++) {
       offsetTable[tier][turn] = new long[tier + 1];
       for (int offset = 0; offset <= tier; offset++) {
         dbh.setNums(boardSize - tier, offset, tier - offset);
         offsetTable[tier][turn][offset] = total;
         total += dbh.numHashes();
       }
     }
   }
   for (int row = 0; row < height; row++) {
     for (int col = 0; col < width; col++) {
       board[row][col] = new Cell(row, col, row * width + col);
     }
   }
   turn = BLACK;
   board[height / 2 - 1][width / 2 - 1].setPiece('X');
   board[height / 2][width / 2 - 1].setPiece('O');
   board[height / 2 - 1][width / 2].setPiece('O');
   board[height / 2][width / 2].setPiece('X');
   isChildrenValid = false;
   children = newStateArray(maxChildren());
   stringMoves = new String[maxChildren()]; // only for testing.
   oldPosition = new char[boardSize];
   tempPosition = new char[boardSize];
 }