Added transposition table (this was tested alone and worked as expected), three fold repetition and null move pruning in my engine and wins more games but some of them end with three fold repetition even in win positions, played against the previous version

I keep a table history to save the current state of the board before making the move, to save the current zobrist key, move, en passant and castling

In make null function I just hash the en passant if exists, switch side, hash it and increment the fiftymove counter

To check for repetitions I do this:

int i;
for (i = 4; i <= board->fiifty_move; i += 2) {
    if (board->zkey == board->history[board->ply - i].zkey) {
        return true;

The question is when checking for repetition it will check also for null move positions, is that correct? If a null move is repeated can we take that as a move even that null move it just switching sides?

I've seen engines that has a array like: hist_rep[index++].zkey = curr_zkey; that is only to check for repetitions and the array is not filled in the make null move function

Others engines has a counter inside the repetition function to check if the position has been repeated before more than one time

for (...) {
    if (zkey[i] == curr_zkey) ++rep;
    if (rep == 2) return true;

What is the correct way?

  • This question is about programming not chess. Try stackoverflow. – Brian Towers Apr 27 '17 at 22:15
  • 4
    @BrianTowers chess engine programming questions are on-topic here. – Herb Apr 27 '17 at 22:41
  • 6
    I'd argue against closing the question. This is on-topic and it talks about chess programming. The question is not about how to write a loop in C++. Discussing about chess here is reasonable. Try not to make our site worse, we don't have enough questions (chess.meta.stackexchange.com/questions/476/…) – SmallChess Apr 28 '17 at 1:37

No. You shouldn't count null-move in repetitions because:

  • Null-move is fake
  • Null-move artificially creates a repetition when there is none

Your implementation is correct, but you might want to exclude null-move. Let's take a look at Stockfish:


bool Position::is_draw(int ply) const {

  if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
      return true;

  int end = std::min(st->rule50, st->pliesFromNull);

  if (end < 4)
    return false;

  StateInfo* stp = st->previous->previous;
  int cnt = 0;

  for (int i = 4; i <= end; i += 2)
      stp = stp->previous->previous;

      // At root position ply is 1, so return a draw score if a position
      // repeats once earlier but after or at the root, or repeats twice
      // strictly before the root.
      if (   stp->key == st->key
          && ++cnt + (ply - i > 0) == 2)
          return true;

  return false;

You already have st->rule50 in your code (board->fifty-move) but you don't have st->pliesFromNull.

Let's check what Stockfish author said about pilesFromNull.


By Joona Kiiski (2010):

They are already excluded, note the use of st->pliesFromNull: 

Null moves can artificially create a repetition 
draw where instead there is no one. 

So use a second counter to reset history after a null move. 

You see, if the strongest engine in the world does it you have no reason for not doing it.

If you check the code carefully, the first time when you do a null-move pliesFromNull is 0.

void Position::do_null_move(StateInfo& newSt) {
    st->pliesFromNull = 0;

later the engine increments the counter in do_move:


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.