/**
 * Find possible actions down on a column
 *
 * @param state a game state
 * @param col the column to check
 * @return a list of actions
 */
static struct List * actions_down( const struct State * state, int col )
{
    int idx = 0;
    struct List * actions = new_list();

    for(idx = 0; (idx < SIZE) && ((idx + 2) < SIZE); idx++){
      if(  (state->board[ idx ][col] == state->player )
	    && (state->board[ idx + 1 ][ col ] == opposite_player( state->player ))
	    && (state->board[ idx + 2 ][ col ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_row = idx + 2;
	   /* add move to action list for single jump  */
	   add_front( &actions, new_move( idx, col, end_row, col) );
	   while( ((end_row + 2) < SIZE )
	        && (state->board[ end_row ][ col ] == 'O' )
	        && (state->board[ end_row + 1 ][ col ] == opposite_player( state->player ))
	        && (state->board[ end_row + 2 ][ col ] == 'O') ){
	     end_row += 2;
	     /* add optimized moves to action list */
	     add_front( &actions, new_move( idx, col, end_row, col) );
	   }
      }
    }
    
    return actions;
}
/**
 * Find possible actions left on a row
 *
 * @param state a game state
 * @param row the row to check
 * @return a list of actions
 */
static struct List * actions_left( const struct State * state, int row )
{
    int idx = SIZE-1;
    struct List * actions = new_list();

     for(idx = SIZE - 1; (idx > 0) && ((idx - 2) >= 0); idx--){
       if( (state->board[ row ][idx] == state->player )
	    && (state->board[ row ][ idx - 1 ] == opposite_player( state->player ))
	    && (state->board[ row ][ idx - 2 ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_col = idx - 2;
	   /* add move to action list for single jump  */
	   add_front( &actions, new_move( row, idx, row, end_col) );
	   while( ((end_col - 2) > 0 )
	        && (state->board[ row ][ end_col ] == 'O' )
	        && (state->board[ row ][ end_col - 1 ] == opposite_player( state->player ))
	        && (state->board[ row ][ end_col - 2 ] == 'O') ){
	     end_col -= 2;
	     /* add optimized moves to action list */ 
	     add_front( &actions, new_move( row, idx, row, end_col) );
	   }
       }
     }

    return actions;
}
/**
 * Translate an input move
 *
 * @param move a move input by the user
 * @return a move
 */
struct Move * translate_in_move( const char * move )
{
    int length = strnlen( move, 20 );
    int i;
    int valid = 0;
    char start_row[2];
    char start_col[2];
    char end_row[2];
    char end_col[2];

    /* find first letter */
    for( i = 0; i < length; i++ )
        if( isalpha( move[ i ] ) )
        {
            start_row[0] = move[ i ];
            start_row[1] = '\0';
            valid++;
            break;
        }

    /* find next number */
    for( ; i< length; i++ )
        if( isdigit( move[i] ) )
        {
            start_col[0] = move[ i ];
            start_col[1] = '\0';
            valid++;
            break;
        }

    /* find next letter */
    for( ; i < length; i++ )
        if( isalpha( move[ i ] ) )
        {
            end_row[0] = move[ i ];
            end_row[1] = '\0';
            valid++;
            break;
        }

    /* find next number */
    for( ; i < length; i++ )
        if( isdigit( move[ i ] ) )
        {
            end_col[0] = move[ i ];
            end_col[1] = '\0';
            valid++;
            break;
        }

    if( valid != 4 )
        return NULL;

    struct Move * translated_move = new_move( letter2row( start_row ), 
            atoi( start_col ) - 1,
            letter2row( end_row ),
            atoi( end_col ) - 1 );

    return translated_move;
}
Exemplo n.º 4
0
	event_node_ptr fake_event_source::move_mouse(const size_t time, const int x, const int y)
	{
		SDL_Event event;
		event.type = SDL_MOUSEMOTION;
		event.motion.x = static_cast<Uint16>(x);
		event.motion.y = static_cast<Uint16>(y);
		event_node_ptr new_move(new event_node_mouse_motion(time, event));
		add_event(new_move);
		return new_move;
	}
/**
 * Find possible actions left on a row
 *
 * @param state a game state
 * @param row the row to check
 * @return a list of actions
 */
static struct List * actions_left( const struct State * state, int row )
{
    int idx = SIZE-1;
    struct List * actions = new_list();

    /*for( idx = SIZE - 1; idx >= 0; idx-- )
    {
      // find next piece on board
        for( ; state->board[ row ][ idx ] != state->player && idx > 0; idx-- );

        if( idx < 0 )
            continue;
        
        // check for moves
        for( int i = idx - 1; i >= 0; i-- )
        {
            if( state->board[ row ][ i ] == opposite_player( state->player ) )
            {
                if( state->board[ row ][ i - 1 ] == 'O' ||
                    state->board[ row ][ i - 1 ] == '0' )
                {
                   
                    //printf( "(%d,%d), (%d, %d)\n", row, idx, row, i - 1 );
                    add_front( &actions, new_move( row, idx, row, i - 1 ) );

                    // check for more moves
                    continue;
                }
                break;
            }
        }
	}*/

     for(idx = SIZE - 1; (idx > 0) && ((idx - 2) >= 0); idx--){
       if( (state->board[ row ][idx] == state->player )
	    && (state->board[ row ][ idx - 1 ] == opposite_player( state->player ))
	    && (state->board[ row ][ idx - 2 ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_col = idx - 2;
	   while( ((end_col - 2) > 0 )
	        && (state->board[ row ][ end_col ] == 'O' )
	        && (state->board[ row ][ end_col - 1 ] == opposite_player( state->player ))
	        && (state->board[ row ][ end_col - 2 ] == 'O') ){
	     end_col -= 2;
	   }
	   /* add move to action list */
	   add_front( &actions, new_move( row, idx, row, end_col) );
       }
     }

    return actions;
}
/**
 * Find possible actions down on a column
 *
 * @param state a game state
 * @param col the column to check
 * @return a list of actions
 */
static struct List * actions_down( const struct State * state, int col )
{
    int idx = 0;
    struct List * actions = new_list();

    /*for( idx = 0; idx < SIZE; idx++ )
    {
      // find next piece on board
        for( ; state->board[ idx ][ col ] != state->player && 
             idx < SIZE - 1; ++idx );

        if( idx > SIZE - 1 )
            continue;

        // check for moves
        for( int i = 1; i + idx < SIZE; i++ )
        {
            if( state->board[ idx + i ][ col ] == opposite_player( state->player ) )
            {
                if( state->board[ idx + i + 1 ][ col ] == 'O' ||
                    state->board[ idx + i + 1 ][ col ] == '0' )
                {
		  // has move
                    //printf( "(%d,%d), (%d, %d)\n", row, idx, row, idx + i + 1 );
                    add_front( &actions, new_move( idx, col, idx + i + 1, col  ));

                    // check for more moves
                    continue;
                }
                break;
            }
        }
	}*/
    for(idx = 0; (idx < SIZE) && ((idx + 2) < SIZE); idx++){
      if(  (state->board[ idx ][col] == state->player )
	    && (state->board[ idx + 1 ][ col ] == opposite_player( state->player ))
	    && (state->board[ idx + 2 ][ col ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_row = idx + 2;
	   while( ((end_row + 2) < SIZE )
	        && (state->board[ end_row ][ col ] == 'O' )
	        && (state->board[ end_row + 1 ][ col ] == opposite_player( state->player ))
	        && (state->board[ end_row + 2 ][ col ] == 'O') ){
	     end_row += 2;
	   }
	   /* add move to action list */
	   add_front( &actions, new_move( idx, col, end_row, col) );
      }
    }
    
    return actions;
}
/**
 * Find possible actions up on a column
 *
 * @param state a game state
 * @param col the column to check
 * @return a list of actions
 */
static struct List * actions_up( const struct State * state, int col )
{
    int idx = SIZE - 1;
    struct List * actions = new_list();

    /*for( idx = SIZE; idx >= 0; idx-- )
    {
      // find next piece on board
        for( ; state->board[ idx ][ col ] != state->player && 
             idx > 0; --idx );

        if( idx < 0 )
            continue;

        // check for moves
        for( int i = 1; idx - i >= 0; i++ )
        {
            if( state->board[ idx - i ][ col ] == opposite_player( state->player ) )
            {
                if( state->board[ idx - (i+1) ][ col ] == 'O' ||
                    state->board[ idx - (i+1) ][ col ] == '0' )
                {
		  // has move
                    add_front( &actions, new_move( idx, col, idx - (i+1), col ) );

                    // check for more moves 
                    continue;
                }
                break;
            }
        }
	}*/

    for(idx = SIZE - 1; (idx > 0) && ((idx - 2) >= 0); idx--){
      if(  (state->board[ idx ][col] == state->player )
	    && (state->board[ idx - 1 ][ col ] == opposite_player( state->player ))
	    && (state->board[ idx - 2 ][ col ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_row = idx - 2;
	   while( ((end_row - 2) > 0 )
	        && (state->board[ end_row ][ col ] == 'O' )
	        && (state->board[ end_row - 1 ][ col ] == opposite_player( state->player ))
	        && (state->board[ end_row - 2 ][ col ] == 'O') ){
	     end_row -= 2;
	   }
	   /* add move to action list */
	   add_front( &actions, new_move( idx, col, end_row, col) );
      }
    }

    return actions;
}
/**
 * Find possible actions right on a row
 *
 * @param state a game state
 * @param row the row to check
 * @return a list of actions
 */
static struct List * actions_right( const struct State * state, int row )
{
    int idx = 0;
    struct List * actions = new_list();
    
    /*while( idx < SIZE - 1 )
    {
      // find next piece
        for( ; ( state->board[ row ][ idx ] != state->player ) &&
               ( idx < SIZE - 1 ); idx++ );

        //check if overbounds
        if( idx < SIZE - 1 )
        {
	  //find all moves
            for( int i = idx; i + 2 < SIZE; i++ )
                if( state->board[ row ][ i + 1 ] == opposite_player( state->player ) )
                    if( state->board[ row ][ i + 2 ] == 'O' )
                    {
                        add_front( &actions, new_move( row, idx, row, i + 2 ) );
                    }
                    else
                    {
                        break;
                    }
        }
        idx++;
	}*/
    for(idx = 0; (idx < SIZE) && ((idx + 2) < SIZE); idx++){
      if( (state->board[ row ][idx] == state->player )
	    && (state->board[ row ][ idx + 1 ] == opposite_player( state->player ))
	    && (state->board[ row ][ idx + 2 ] == 'O')){
	   /* check for moves  - increment to optimize move*/
	   int end_col = idx + 2;
	   while( ((end_col + 2) < SIZE )
	        && (state->board[ row ][ end_col ] == 'O' )
	        && (state->board[ row ][ end_col + 1 ] == opposite_player( state->player ))
	        && (state->board[ row ][ end_col + 2 ] == 'O') ){
	     end_col += 2;
	   }
	   /* add move to action list */
	   add_front( &actions, new_move( row, idx, row, end_col) );
      }
    }
    
    return actions;
}
Exemplo n.º 9
0
void		add_mob(int npc, int x, int y, enum e_dir direc)
{
	static GLfloat	dir[3] = {0.0, 0.0, 0.0};
	static GLfloat	vec[3] = {0.0, 0.0, 1.0};
	GLfloat			pos[3];
	t_move			*move;
	t_rot			*rot;
	t_anim			*anim;
	t_square		*sq;
	t_mob			*mob;

	set_vecf(pos, (float)x * 2, 0.0, (float)y * 2);
	anim = new_anim(rand() % 360, 360, anim_mob);
	move = new_move(0, 0, pos, dir);
	rot = new_rot(0, vec, 90 * direc, 0);
	mob = new_mob(anim, move, rot, npc);
	sq = g_env->sq + x + y * g_env->mapw;
	g_env->npc[npc].sq = sq;
	sq->mobs = new_link(sq->mobs, mob);
}
/**
 * Translate a beginning move to coordinates
 *
 * @param move a string consisting of the human input (will only read one piece)
 * @return a move consisting of one piece
 */
struct Move * translate_first_in_move( const char * move )
{
    int length = strnlen( move, 20 );
    int i;
    int valid = 0;
    char start_row[2];
    char start_col[2];

    /* find first letter */
    for( i = 0; i < length; i++ )
        if( isalpha( move[ i ] ) )
        {
            start_col[0] = move[ i ];
            start_col[1] = '\0';
            valid++;
            break;
        }

    /* find next number */
    for( ; i< length; i++ )
        if( isdigit( move[i] ) )
        {
            start_row[0] = move[ i ];
            start_row[1] = '\0';
            valid++;
            break;
        }

    /* check input parameters */
    if( valid != 2 )
        return NULL;

    struct Move * translated_move = new_move(
                                        atoi( start_row ) - 1,
                                        letter2row( start_col ),
                                        0,
                                        0 );

    return translated_move;
}
Exemplo n.º 11
0
Move Move::add(const Move & m) const
{
	Move new_move(m.x, m.y);
	return new_move;
}
/**
 * Clone a move
 *
 * @param move a move to clone
 * @return a duplicate move
 */
struct Move * clone_move( const struct Move * move )
{
    return new_move( move->start_row, move->start_col, move->end_row, move->end_col );
}
Exemplo n.º 13
0
// Non-capturing checks.
//------------------------------------------------------------------------------
MoveGen *generate_checks(MoveGen *self) {
    Position *p = self->p;
    int color = p->color, enemy = p->color ^ 1;
    int square = (int)p->king[enemy];

    // Non-capturing Knight checks.
    Bitmask prohibit = maskNone;
    Bitmask checks = knightMoves[square];
    Bitmask outposts = p->outposts[knight(color)];
    while (any(outposts)) {
        int from = pop(&outposts);
        add_piece_move(self, from, knightMoves[from] & checks & ~p->board);
    }

    // Non-capturing Bishop or Queen checks.
    checks = targets_for(p, square, bishop(enemy));
    outposts = p->outposts[bishop(color)] | p->outposts[queen(color)];
    while (any(outposts)) {
        int from = pop(&outposts);
        Bitmask squares = targets_for(p, from, bishop(enemy)) & checks & ~p->outposts[enemy];
        while (any(squares)) {
            int to = pop(&squares);
            Piece piece = p->pieces[to];
            if (!piece) {
                // Empty square: simply move a bishop to check.
                append(self, new_move(p, from, to));
            } else if (color(piece) == color && any(maskDiagonal[from][square])) {
                // Non-empty square occupied by friendly piece on the same
                // diagonal: moving the piece away causes discovered check.
                switch (kind(piece)) {
                case Pawn:
                    // Block pawn promotions (since they are treated as
                    // captures) and en-passant captures.
                    prohibit = maskRank[0] | maskRank[7];
                    if (p->enpassant) {
                        prohibit |= bit[p->enpassant];
                    }
                    add_pawn_move(self, to, targets(p, to) & ~p->board & ~prohibit);
                    break;
                case King:
                    // Make sure the king steps out of attack diaginal.
                    add_king_move(self, to, targets(p, to) & ~p->board & ~maskBlock[from][square]);
                    break;
                default:
                    add_piece_move(self, to, targets(p, to) & ~p->board);
                }
            }
        }
        if (is_queen(p->pieces[from])) {
            // Queen could move straight as a rook and check diagonally as a bishop
            // or move diagonally as a bishop and check straight as a rook.
            Bitmask squares = (targets_for(p, from, rook(color)) & checks) |
                  (targets_for(p, from, bishop(color)) & targets_for(p, square, rook(color)));
            add_piece_move(self, from, squares & ~p->board);
        }
    }

    // Non-capturing Rook or Queen checks.
    checks = targets_for(p, square, rook(enemy));
    outposts = p->outposts[rook(color)] | p->outposts[queen(color)];
    while (any(outposts)) {
        int from = pop(&outposts);
        Bitmask squares = targets_for(p, from, rook(enemy)) & checks & ~p->outposts[enemy];
        while (any(squares)) {
            int to = pop(&squares);
            Piece piece = p->pieces[to];
            if (!piece) {
                // Empty square: simply move a rook to check.
                append(self, new_move(p, from, to));
            } else if (color(piece) == color) {
                if (any(maskStraight[from][square])) {
                    // Non-empty square occupied by friendly piece on the same
                    // file or rank: moving the piece away causes discovered check.
                    switch (kind(piece)) {
                    case Pawn:
                        // If pawn and rook share the same file then non-capturing
                        // discovered check is not possible since the pawn is going
                        // to stay on the same file no matter what.
                        if (col(from) == col(to)) {
                            continue;
                        }
                        // Block pawn promotions (since they are treated as captures)
                        // and en-passant captures.
                        prohibit = maskRank[0] | maskRank[7];
                        if (p->enpassant) {
                            prohibit |= bit[p->enpassant];
                        }
                        add_pawn_move(self, to, targets(p, to) & ~p->board & ~prohibit);
                        break;
                    case King:
                        // Make sure the king steps out of attack file or rank.
                        prohibit = maskNone;
                        if (row(from) == row(square)) {
                            prohibit = maskRank[row(from)];
                        } else {
                            prohibit = maskFile[col(square)];
                        }
                        add_king_move(self, to, targets(p, to) & ~p->board & ~prohibit);
                        break;
                    default:
                        add_piece_move(self, to, targets(p, to) & ~p->board);
                    }
                }
            }
        }
    }

    // Non-capturing Pawn checks.
    outposts = p->outposts[pawn(color)] & maskIsolated[col(square)];
    while (any(outposts)) {
        int from = pop(&outposts);
        Bitmask target = maskPawn[color][square] & targets(p, from);
        if (any(target)) {
            append(self, new_pawn_move(p, from, pop(&target)));
        }
    }

    return self;
}
Exemplo n.º 14
0
int swmain(int argc, char *argv[])
{
	int nexttic;

	nobjects = (OBJECTS *) malloc(100 * sizeof(OBJECTS));

	swinit(argc, argv);
	setjmp(envrestart);

	// sdh 28/10/2001: playmode is called from here now
	// makes for a more coherent progression through the setup process

	if (!playmode)
		getgamemode();
	swinitlevel();

	nexttic = Timer_GetMS();
        skip_time = 0;

	for (;;) {
                int nowtime;

		/* generate a new move command periodically
		 * and send to other players if neccessary */

                nowtime = Timer_GetMS();

		if (nowtime > nexttic
		 && latest_player_time[player] - countmove < MAX_NET_LAG) {

			new_move();

                        /* Be accurate (exact amount between tics);
                         * However, if a large spike occurs between tics,
                         * catch up immediately.
                         */

                        if (nowtime - nexttic > 1000)
                                nexttic = nowtime + (1000/FPS);
                        else
			        nexttic += (1000 / FPS);

                        // wait a bit longer to compensate for lag

                        nexttic += skip_time;
                        skip_time = 0;
		}

		asynupdate();
		swsndupdate();

		/* if we have all the tic commands we need, we can move */

		if (can_move()) {
                        calculate_lag();
			//dump_cmds();
			swmove();
			swdisp();
			swcollsn();
			swsound();
		}

		// sdh 15/11/2001: dont thrash the 
		// processor while waiting
		Timer_Sleep(10);
	}

	return 0;
}
/**
 * Computer player goes second
 *
 * @param game_state the current game state
 * @return a new state
 */
struct State * computer_player_second( struct State * game_state )
{
    struct State * state;
    struct Move * move;
    int random_move = 0;
    int row, col, i, j;

    /* find blank spot */
    for( row = 0; row < SIZE; row++ )
        for( col = 0; col < SIZE; col++ )
            if( game_state->board[row][col] == 'O' )
            {
                i = row;
                j = col;
                break;
            }

    srand( time( NULL ) );

    /* select piece */
    /* valid moves are on either side of empty space */
    if( i == 0 && j == 0 )
    {
        if( ( game_state->board[ i ][ j + 1 ] == 'W' ) ||
            ( game_state->board[ i + 1][ j ] == 'W' ) )
        {
            random_move = rand() % 2;

            switch( random_move )
            {
            case 0:
                move = new_move( i, j+1, 0, 0 );
                break;
            case 1:
                move = new_move( i+1, j, 0, 0 );
                break;
            }
        }

    }
    else if( i == 0 && j == 7 )
    {
        if( (game_state->board[ i ][ j - 1 ] == 'W' ) ||
            (game_state->board[ i + 1 ][ j ] == 'W' ) )
        {
            random_move = rand() % 2;

            switch( random_move )
            {
            case 0:
                move = new_move( i, j-1, 0, 0 );
                break;
            case 1:
                move = new_move( i+1, j, 0, 0 );
                break;
            }
        }
    }
    else if( i == 7 && j == 0 )
    {
        if( (game_state->board[ i ][ j + 1 ] == 'W' ) ||
            (game_state->board[ i - 1 ][ j ] == 'W' ) )
        {
            random_move = rand() % 2;

            switch( random_move )
            {
            case 0:
                move = new_move( i, j+1, 0, 0 );
                break;
            case 1:
                move = new_move( i-1, j, 0, 0 );
                break;
            }
        }
    }
    else if( i == 7 && j == 7 )
    {
        if( (game_state->board[ i ][ j - 1 ] == 'W' ) ||
            (game_state->board[ i - 1 ][ j ] == 'W' ) )
        {
            random_move = rand() % 2;

            switch( random_move )
            {
            case 0:
                move = new_move( i, j-1, 0, 0 );
                break;
            case 1:
                move = new_move( i-1, j, 0, 0 );
                break;
            }
        }
    }
    else
    {
        if( (game_state->board[ i + 1 ][ j ] == 'W' ) ||
            (game_state->board[ i - 1 ][ j ] == 'W' ) ||
            (game_state->board[ i ][ j + 1 ] == 'W' ) ||
            (game_state->board[ i ][ j - 1 ] == 'W' ) )
        {
            random_move = rand() % 4;

            switch( random_move )
            {
            case 0:
                move = new_move( i+1, j, 0, 0 );
                break;
            case 1:
                move = new_move( i-1, j, 0, 0 );
                break;
            case 2:
                move = new_move( i, j+1, 0, 0 );
                break;
            case 3:
                move = new_move( i, j-1, 0, 0 );
                break;
            }
        }
    }

    /* print move */
    printf( "Move chosen: " );
    print_single_move( move );

    /* create new state */
    state = new_state( game_state->board, opposite_player( game_state->player ) );

    /* apply move */
    state->board[ move->start_row ][ move->start_col ] = 'O';
    Free( move, sizeof( struct Move ) );

    return state;
}
/**
 * Computer player goes first
 *
 * @param game_state the current state of the game
 * @return a new game state
 */
struct State * computer_player_first( struct State * game_state )
{
    struct State * state;
    struct Move * move;

    srand( time( NULL ) );

    do
    {
        /* determine a move randomly */
        int random_move = rand() % 8;

        switch( random_move )
        {
        case 0:
            /* center top left */
            move = new_move( 3, 3, 0, 0 );
            break;
        case 1:
            /* center top right */
            move = new_move( 3, 4, 0, 0 );
            break;
        case 2:
            /* center botom left */
            move = new_move( 4, 3, 0, 0 );
            break;
        case 3:
            /* center bottom right */
            move = new_move( 4, 4, 0, 0 );
            break;
#if 1
        case 4:
            /* corner top left */
            move = new_move( 0, 0, 0, 0 );
            break;
        case 5:
            /* corner top right */
            move = new_move( 0, 7, 0, 0 );
            break;
        case 6:
            /* corner bottom left */
            move = new_move( 7, 0, 0, 0 );
            break;
        case 7:
            /* corner bottom right */
            move = new_move( 7, 7, 0, 0 );
            break;
#endif
        }

        /* check if move is valid */
        if( game_state->board[ move->start_row ][ move->start_col ] == 'B' )
        {
            break;
        }
        else
        {
            Free( move, sizeof( struct Move ) );
        }
    }
    while( 1 );

    /* print move */
    printf( "Move chosen: " );
    print_single_move( move );

    /* create new state */
    state = new_state( game_state->board, opposite_player( game_state->player ) );

    /* apply move */
    state->board[ move->start_row ][ move->start_col ] = 'O';

    Free( move, sizeof( struct Move ) );

    return state;

}
Exemplo n.º 17
0
void test_game_suite__000(void) {
    new_game();
    Position *p = start();

    Move m0 = new_move(p, E2, E4);
    Move m1 = new_move(p, E7, E5);
    Move m2 = new_move(p, G1, F3);
    Move m3 = new_move(p, B8, C6);
    Move m4 = new_move(p, F1, B5);
    save_best(0, m0);
    save_best(1, m1);
    save_best(2, m2);
    save_best(3, m3);
    save_best(4, m4);

    cl_check(game.pv[0].size == 1);
    cl_check(game.pv[0].moves[0] == m0);
    cl_check(game.pv[1].size == 2);
    cl_check(game.pv[1].moves[0] == (Move)0);
    cl_check(game.pv[1].moves[1] == m1);
    cl_check(game.pv[2].size == 3);
    cl_check(game.pv[2].moves[0] == (Move)0);
    cl_check(game.pv[2].moves[1] == (Move)0);
    cl_check(game.pv[2].moves[2] == m2);
    cl_check(game.pv[3].size == 4);
    cl_check(game.pv[3].moves[0] == (Move)0);
    cl_check(game.pv[3].moves[1] == (Move)0);
    cl_check(game.pv[3].moves[2] == (Move)0);
    cl_check(game.pv[3].moves[3] == m3);
    cl_check(game.pv[4].size == 5);
    cl_check(game.pv[4].moves[0] == (Move)0);
    cl_check(game.pv[4].moves[1] == (Move)0);
    cl_check(game.pv[4].moves[2] == (Move)0);
    cl_check(game.pv[4].moves[3] == (Move)0);
    cl_check(game.pv[4].moves[4] == m4);

    Move m5 = new_move(p, A7, A6);
    save_best(1, m5);

    cl_check(game.pv[0].size == 1);
    cl_check(game.pv[0].moves[0] == m0);
    cl_check(game.pv[1].size == 3);                       // <--
    cl_check(game.pv[1].moves[0] == (Move)0);
    cl_check(game.pv[1].moves[1] == m5);                  // <--
    cl_check(game.pv[1].moves[2] == game.pv[2].moves[2]); // <--
    cl_check(game.pv[2].size == 3);
    cl_check(game.pv[2].moves[0] == (Move)0);
    cl_check(game.pv[2].moves[1] == (Move)0);
    cl_check(game.pv[2].moves[2] == m2);
    cl_check(game.pv[3].size == 4);
    cl_check(game.pv[3].moves[0] == (Move)0);
    cl_check(game.pv[3].moves[1] == (Move)0);
    cl_check(game.pv[3].moves[2] == (Move)0);
    cl_check(game.pv[3].moves[3] == m3);
    cl_check(game.pv[4].size == 5);
    cl_check(game.pv[4].moves[0] == (Move)0);
    cl_check(game.pv[4].moves[1] == (Move)0);
    cl_check(game.pv[4].moves[2] == (Move)0);
    cl_check(game.pv[4].moves[3] == (Move)0);
    cl_check(game.pv[4].moves[4] == m4);
}