/* sudoku.c - sudoku game
 *
 * Writing a fun Su-Do-Ku game has turned out to be a difficult exercise.
 * The biggest difficulty is keeping the game fun - and this means allowing
 * the user to make mistakes. The game is not much fun if it prevents the
 * user from making moves, or if it informs them of an incorrect move.
 * With movement constraints, the 'game' is little more than an automated
 * solver (and no fun at all).
 *
 * Another challenge is generating good puzzles that are entertaining to
 * solve. It is certainly true that there is an art to creating good
 * Su-Do-Ku puzzles, and that good hand generated puzzles are more
 * entertaining than many computer generated puzzles - I just hope that
 * the algorithm implemented here provides fun puzzles. It is an area
 * that needs work. The puzzle classification is very simple, and could
 * also do with work. Finally, understanding the automatically generated
 * hints is sometimes more work than solving the puzzle - a better, and
 * more human friendly, mechanism is needed.
 *
 * Comments, suggestions, and contributions are always welcome - send email
 * to: mike 'at' laurasia.com.au. Note that this code assumes a single
 * threaded process, makes extensive use of global variables, and has
 * not been written to be reused in other applications. The code makes no
 * use of dynamic memory allocation, and hence, requires no heap. It should
 * also run with minimal stack space.
 *
 * This code and accompanying files have been placed into the public domain
 * by Michael Kennett, July 2005. It is provided without any warranty
 * whatsoever, and in no event shall Michael Kennett be liable for
 * any damages of any kind, however caused, arising from this software.
 */

#include "plugin.h"

#include "sudoku.h"
#include "templates.h"
#include "generator.h"

#define assert(x)

/* Common state encoding in a 32-bit integer:
 *   bits  0-6    index
 *         7-15   state  [bit high signals digits not possible]
 *        16-19   digit
 *           20   fixed  [set if digit initially fixed]
 *           21   choice [set if solver chose this digit]
 *           22   ignore [set if ignored by reapply()]
 *           23   unused
 *        24-26   hint
 *        27-31   unused
 */
#define INDEX_MASK              0x0000007f
#define GET_INDEX(val)          (INDEX_MASK&(val))
#define SET_INDEX(val)          (val)

#define STATE_MASK              0x0000ff80
#define STATE_SHIFT             (7-1)                        /* digits 1..9 */
#define DIGIT_STATE(digit)      BIT_N(STATE_SHIFT+(digit))

#define DIGIT_MASK              0x000f0000
#define DIGIT_SHIFT             16
#define GET_DIGIT(val)          (((val)&DIGIT_MASK)>>(DIGIT_SHIFT))
#define SET_DIGIT(val)          ((val)<<(DIGIT_SHIFT))

#define FIXED                   0x00100000
#define CHOICE                  0x00200000
#define IGNORED                 0x00400000

/* Hint codes (c.f. singles(), pairs(), findmoves()) */
#define HINT_ROW                0x01000000
#define HINT_COLUMN             0x02000000
#define HINT_BLOCK              0x04000000

/* For a general board it may be necessary to do backtracking (i.e. to
 * rewind the board to an earlier state), and make choices during the
 * solution process. This can be implemented naturally using recursion,
 * but it is more efficient to maintain a single board.
 */
static int board[ 81 ];

/* Addressing board elements: linear array 0..80 */
#define ROW(idx)                ((idx)/9)
#define COLUMN(idx)             ((idx)%9)
#define BLOCK(idx)              (3*(ROW(idx)/3)+(COLUMN(idx)/3))
#define INDEX(row,col)          (9*(row)+(col))

/* Blocks indexed 0..9 */
#define IDX_BLOCK(row,col)      (3*((row)/3)+((col)/3))
#define TOP_LEFT(block)         (INDEX(block/3,block%3))

/* Board state */
#define STATE(idx)              ((board[idx])&STATE_MASK)
#define DIGIT(idx)              (GET_DIGIT(board[idx]))
#define HINT(idx)               ((board[idx])&HINT_MASK)
#define IS_EMPTY(idx)           (0 == DIGIT(idx))
#define DISALLOWED(idx,digit)   ((board[idx])&DIGIT_STATE(digit))
#define IS_FIXED(idx)           (board[idx]&FIXED)

/* Record move history, and maintain a counter for the current
 * move number. Concessions are made for the user interface, and
 * allow digit 0 to indicate clearing a square. The move history
 * is used to support 'undo's for the user interface, and hence
 * is larger than required - there is sufficient space to solve
 * the puzzle, undo every move, and then redo the puzzle - and
 * if the user requires more space, then the full history will be
 * lost.
 */
static int idx_history;
static int history[ 3 * 81 ];

/* Possible moves for a given board (c.f. fillmoves()).
 * Also used by choice() when the deterministic solver has failed,
 * and for calculating user hints. The number of hints is stored
 * in num_hints, or -1 if no hints calculated. The number of hints
 * requested by the user since their last move is stored in req_hints;
 * if the user keeps requesting hints, start giving more information.
 * Finally, record the last hint issued to the user; attempt to give
 * different hints each time.
 */
static int idx_possible;
static int possible[ 81 ];

static int pass;    /* count # passes of deterministic solver */

/* Support for template file */
static int tmplt[ 81 ];             /* Template indices */
static int len_tmplt;               /* Number of template indices */

/* Reset global state */
static
void
reset( void )
{
    rb->memset( board, 0x00, sizeof( board ) );
    rb->memset( history, 0x00, sizeof( history ) );
    idx_history = 0;
    pass = 0;
}

/* Management of the move history - compression */
static
void
compress( int limit )
{
    int i, j;
    for( i = j = 0 ; i < idx_history && j < limit ; ++i )
        if( !( history[ i ] & IGNORED ) )
            history[ j++ ] = history[ i ];
    for( ; i < idx_history ; ++i )
        history[ j++ ] = history[ i ];
    idx_history = j;
}

/* Management of the move history - adding a move */
static
void
add_move( int idx, int digit, int choice )
{
    int i;

    if( sizeof( history ) / sizeof( int ) == idx_history )
        compress( 81 );

    /* Never ignore the last move */
    history[ idx_history++ ] = SET_INDEX( idx ) | SET_DIGIT( digit ) | choice;

    /* Ignore all previous references to idx */
    for( i = idx_history - 2 ; 0 <= i ; --i )
        if( GET_INDEX( history[ i ] ) == idx )
        {
            history[ i ] |= IGNORED;
            break;
        }
}

/* Iteration over rows/columns/blocks handled by specialised code.
 * Each function returns a block index - call must manage element/idx.
 */
static
int
idx_row( int el, int idx )      /* Index within a row */
{
    return INDEX( el, idx );
}

static
int
idx_column( int el, int idx )   /* Index within a column */
{
    return INDEX( idx, el );
}

static
int
idx_block( int el, int idx )    /* Index within a block */
{
    return INDEX( 3 * ( el / 3 ) + idx / 3, 3 * ( el % 3 ) + idx % 3 );
}

/* Update board state after setting a digit (clearing not handled)
 */
static
void
update( int idx )
{
    const int row = ROW( idx );
    const int col = COLUMN( idx );
    const int block = IDX_BLOCK( row, col );
    const int mask = DIGIT_STATE( DIGIT( idx ) );
    int i;

    board[ idx ] |= STATE_MASK;  /* filled - no choice possible */

    /* Digit cannot appear in row, column or block */
    for( i = 0 ; i < 9 ; ++i )
    {
        board[ idx_row( row, i ) ] |= mask;
        board[ idx_column( col, i ) ] |= mask;
        board[ idx_block( block, i ) ] |= mask;
    }
}

/* Refresh board state, given move history. Note that this can yield
 * an incorrect state if the user has made errors - return -1 if an
 * incorrect state is generated; else return 0 for a correct state.
 */
static
int
reapply( void )
{
    int digit, idx, j;
    int allok = 0;
    rb->memset( board, 0x00, sizeof( board ) );
    for( j = 0 ; j < idx_history ; ++j )
        if( !( history[ j ] & IGNORED ) && 0 != GET_DIGIT( history[ j ] ) )
        {
            idx = GET_INDEX( history[ j ] );
            digit = GET_DIGIT( history[ j ] );
            if( !IS_EMPTY( idx ) || DISALLOWED( idx, digit ) )
                allok = -1;
            board[ idx ] = SET_DIGIT( digit );
            if( history[ j ] & FIXED )
                board[ idx ] |= FIXED;
            update( idx );
        }
    return allok;
}

/* Clear moves, leaving fixed squares
 */
static
void
clear_moves( void )
{
    for( idx_history = 0 ; history[ idx_history ] & FIXED ; ++idx_history )
        ;
    reapply( );
}

static int digits[ 9 ];    /* # digits expressed in element square */
static int counts[ 9 ];    /* Count of digits (c.f. count_set_digits()) */

/* Count # set bits (within STATE_MASK) */
static
int
numset( int mask )
{
    int i, n = 0;
    for( i = STATE_SHIFT + 1 ; i <= STATE_SHIFT + 9 ; ++i )
        if( mask & BIT_N(i) )
            ++n;
        else
            ++counts[ i - STATE_SHIFT - 1 ];
    return n;
}

static
void
count_set_digits( int el, int (*idx_fn)( int, int ) )
{
    int i;
    rb->memset( counts, 0x00, sizeof( counts ) );
    for( i = 0 ; i < 9 ; ++i )
        digits[ i ] = numset( board[ (*idx_fn)( el, i ) ] );
}

/* Fill square with given digit, and update state.
 * Returns 0 on success, else -1 on error (i.e. invalid fill)
 */
static
int
fill( int idx, int digit )
{
    assert( 0 != digit );

    if( !IS_EMPTY( idx ) )
        return ( DIGIT( idx ) == digit ) ? 0 : -1;

    if( DISALLOWED( idx, digit ) )
        return -1;

    board[ idx ] = SET_DIGIT( digit );
    update( idx );
    add_move( idx, digit, 0 );

    return 0;
}

/* Find all squares with a single digit allowed -- do not mutate board */
static
void
singles( int el, int (*idx_fn)( int, int ), int hintcode )
{
    int i, j, idx;

    count_set_digits( el, idx_fn );

    for( i = 0 ; i < 9 ; ++i )
    {
        if( 1 == counts[ i ] )
        {
            /* Digit 'i+1' appears just once in the element */
            for( j = 0 ; j < 9 ; ++j )
            {
                idx = (*idx_fn)( el, j );
                if( !DISALLOWED( idx, i + 1 ) && idx_possible < 81 )
                    possible[ idx_possible++ ] = SET_INDEX( idx )
                                               | SET_DIGIT( i + 1 )
                                               | hintcode;
            }
        }
        if( 8 == digits[ i ] )
        {
            /* 8 digits are masked at this position - just one remaining */
            idx = (*idx_fn)( el, i );
            for( j = 1 ; j <= 9 ; ++j )
                if( 0 == ( STATE( idx ) & DIGIT_STATE( j ) ) && idx_possible < 81 )
                    possible[ idx_possible++ ] = SET_INDEX( idx )
                                               | SET_DIGIT( j )
                                               | hintcode;
        }
    }
}

/* Given the board state, find all possible 'moves' (i.e. squares with just
 * a single digit).
 *
 * Returns the number of (deterministic) moves (and fills the moves array),
 * or 0 if no moves are possible. This function does not mutate the board
 * state, and hence, can return the same move multiple times (with
 * different hints).
 */
static
int
findmoves( void )
{
    int i;

    rb->yield();

    idx_possible = 0;
    for( i = 0 ; i < 9 ; ++i )
    {
        singles( i, idx_row, HINT_ROW );
        singles( i, idx_column, HINT_COLUMN );
        singles( i, idx_block, HINT_BLOCK );
    }
    return idx_possible;
}

/* Strategies for refining the board state
 *  - 'pairs'     if there are two unfilled squares in a given row/column/
 *                block with the same state, and just two possibilities,
 *                then all other unfilled squares in the row/column/block
 *                CANNOT be either of these digits.
 *  - 'block'     if the unknown squares in a block all appear in the same
 *                row or column, then all unknown squares outside the block
 *                and in the same row/column cannot be any of the unknown
 *                squares in the block.
 *  - 'common'    if all possible locations for a digit in a block appear
 *                in a row or column, then that digit cannot appear outside
 *                the block in the same row or column.
 *  - 'position2' if the positions of 2 unknown digits in a block match
 *                identically in precisely 2 positions, then those 2 positions
 *                can only contain the 2 unknown digits.
 *
 * Recall that each state bit uses a 1 to prevent a digit from
 * filling that square.
 */

static
void
pairs( int el, int (*idx_fn)( int, int ) )
{
    int i, j, k, mask, idx;

    rb->yield();

    for( i = 0 ; i < 8 ; ++i )
        if( 7 == digits[ i ] ) /* 2 digits unknown */
            for( j = i + 1 ; j < 9 ; ++j )
            {
                idx = (*idx_fn)( el, i );
                if( STATE( idx ) == STATE( (*idx_fn)( el, j ) ) )
                {
                    /* Found a row/column pair - mask other entries */
                    mask = STATE_MASK ^ (STATE_MASK & board[ idx ] );
                    for( k = 0 ; k < i ; ++k )
                        board[ (*idx_fn)( el, k ) ] |= mask;
                    for( k = i + 1 ; k < j ; ++k )
                        board[ (*idx_fn)( el, k ) ] |= mask;
                    for( k = j + 1 ; k < 9 ; ++k )
                        board[ (*idx_fn)( el, k ) ] |= mask;
                    digits[ j ] = -1; /* now processed */
                }
            }
}

/* Worker: mask elements outside block */
static
void
exmask( int mask, int block, int el, int (*idx_fn)( int, int ) )
{
    int i, idx;

    rb->yield();

    for( i = 0 ; i < 9 ; ++i )
    {
        idx = (*idx_fn)( el, i );
        if( block != BLOCK( idx ) && IS_EMPTY( idx ) )
            board[ idx ] |= mask;
    }
}

/* Worker for block() */
static
void
exblock( int block, int el, int (*idx_fn)( int, int ) )
{
    int i, idx, mask;

    rb->yield();

    /* By assumption, all unknown squares in the block appear in the
     * same row/column, so to construct a mask for these squares, it
     * is sufficient to invert the mask for the known squares in the
     * block.
     */
    mask = 0;
    for( i = 0 ; i < 9 ; ++i )
    {
        idx = idx_block( block, i );
        if( !IS_EMPTY( idx ) )
            mask |= DIGIT_STATE( DIGIT( idx ) );
    }
    exmask( mask ^ STATE_MASK, block, el, idx_fn );
}

static
void
block( int el )
{
    int i, idx = 0, row, col;

    rb->yield();

    /* Find first unknown square */
    for( i = 0 ; i < 9 && !IS_EMPTY( idx = idx_block( el, i ) ) ; ++i )
        ;
    if( i < 9 )
    {
        assert( IS_EMPTY( idx ) );
        row = ROW( idx );
        col = COLUMN( idx );
        for( ++i ; i < 9 ; ++i )
        {
            idx = idx_block( el, i );
            if( IS_EMPTY( idx ) )
            {
                if( ROW( idx ) != row )
                    row = -1;
                if( COLUMN( idx ) != col )
                    col = -1;
            }
        }
        if( 0 <= row )
            exblock( el, row, idx_row );
        if( 0 <= col )
            exblock( el, col, idx_column );
    }
}

static
void
common( int el )
{
    int i, idx, row, col, digit, mask;

    rb->yield();

    for( digit = 1 ; digit <= 9 ; ++digit )
    {
        mask = DIGIT_STATE( digit );
        row = col = -1;  /* Value '9' indicates invalid */
        for( i = 0 ; i < 9 ; ++i )
        {
            /* Digit possible? */
            idx = idx_block( el, i );
            if( IS_EMPTY( idx ) && 0 == ( board[ idx ] & mask ) )
            {
                if( row < 0 )
                    row = ROW( idx );
                else
                if( row != ROW( idx ) )
                    row = 9; /* Digit appears in multiple rows */
                if( col < 0 )
                    col = COLUMN( idx );
                else
                if( col != COLUMN( idx ) )
                    col = 9; /* Digit appears in multiple columns */
            }
        }
        if( -1 != row && row < 9 )
            exmask( mask, el, row, idx_row );
        if( -1 != col && col < 9 )
            exmask( mask, el, col, idx_column );
    }
}

/* Encoding of positions of a digit (c.f. position2()) - abuse DIGIT_STATE */
static int posn_digit[ 10 ];

static
void
position2( int el )
{
    int digit, digit2, i, mask, mask2, posn, count, idx;

    rb->yield();

    /* Calculate positions of each digit within block */
    for( digit = 1 ; digit <= 9 ; ++digit )
    {
        mask = DIGIT_STATE( digit );
        posn_digit[ digit ] = count = posn = 0;
        for( i = 0 ; i < 9 ; ++i )
            if( 0 == ( mask & board[ idx_block( el, i ) ] ) )
            {
                ++count;
                posn |= DIGIT_STATE( i );
            }
        if( 2 == count )
            posn_digit[ digit ] = posn;
    }
    /* Find pairs of matching positions, and mask */
    for( digit = 1 ; digit < 9 ; ++digit )
        if( 0 != posn_digit[ digit ] )
            for( digit2 = digit + 1 ; digit2 <= 9 ; ++digit2 )
                if( posn_digit[ digit ] == posn_digit[ digit2 ] )
                {
                    mask = STATE_MASK
                           ^ ( DIGIT_STATE( digit ) | DIGIT_STATE( digit2 ) );
                    mask2 = DIGIT_STATE( digit );
                    for( i = 0 ; i < 9 ; ++i )
                    {
                        idx = idx_block( el, i );
                        if( 0 == ( mask2 & board[ idx ] ) )
                        {
                            assert( 0 == (DIGIT_STATE(digit2) & board[idx]) );
                            board[ idx ] |= mask;
                        }
                    }
                    posn_digit[ digit ] = posn_digit[ digit2 ] = 0;
                    break;
                }
}

/* Find some moves for the board; starts with a simple approach (finding
 * singles), and if no moves found, starts using more involved strategies
 * until a move is found. The more advanced strategies can mask states
 * in the board, making this an efficient mechanism, but difficult for
 * a human to understand.
 */
static
int
allmoves( void )
{
    int i, n;

    rb->yield();

    n = findmoves( );
    if( 0 < n )
        return n;

    for( i = 0 ; i < 9 ; ++i )
    {
        count_set_digits( i, idx_row );
        pairs( i, idx_row );

        count_set_digits( i, idx_column );
        pairs( i, idx_column );

        count_set_digits( i, idx_block );
        pairs( i, idx_block );
    }
    n = findmoves( );
    if( 0 < n )
        return n;

    for( i = 0 ; i < 9 ; ++i )
    {
        block( i );
        common( i );
        position2( i );
    }
    return findmoves( );
}

/* Helper: sort based on index */
#if 0 /* unused function */
static
int
cmpindex( const void * a, const void * b )
{
    return GET_INDEX( *((const int *)b) ) - GET_INDEX( *((const int *)a) );
}

/* Return number of hints. The hints mechanism should attempt to find
 * 'easy' moves first, and if none are possible, then try for more
 * cryptic moves.
 */
static int
findhints( void )
{
    int i, n, mutated = 0;

    rb->yield();

    n = findmoves( );
    if( n < 2 )
    {
        /* Each call to pairs() can mutate the board state, making the
         * hints very, very cryptic... so later undo the mutations.
         */
        for( i = 0 ; i < 9 ; ++i )
        {
            count_set_digits( i, idx_row );
            pairs( i, idx_row );

            count_set_digits( i, idx_column );
            pairs( i, idx_column );

            count_set_digits( i, idx_block );
            pairs( i, idx_block );
        }
        mutated = 1;
        n = findmoves( );
    }
    if( n < 2 )
    {
        for( i = 0 ; i < 9 ; ++i )
        {
            block( i );
            common( i );
        }
        mutated = 1;
        n = findmoves( );
    }

    /* Sort the possible moves, and allow just one hint per square */
    if( 0 < n )
    {
        int i, j;

        rb->qsort( possible, n, sizeof( int ), cmpindex );
        for( i = 0, j = 1 ; j < n ; ++j )
        {
            if( GET_INDEX( possible[ i ] ) == GET_INDEX( possible[ j ] ) )
            {
                /* Let the user make mistakes - do not assume the
                 * board is in a consistent state.
                 */
                if( GET_DIGIT( possible[i] ) == GET_DIGIT( possible[j] ) )
                    possible[ i ] |= possible[ j ];
            }
            else
                i = j;
        }
        n = i + 1;
    }

    /* Undo any mutations of the board state */
    if( mutated )
        reapply( );

    return n;
}
#endif /* unused function */

/* Deterministic solver; return 0 on success, else -1 on error.
 */
static
int
deterministic( void )
{
    int i, n;

    rb->yield();

    n = allmoves( );
    while( 0 < n )
    {
        ++pass;
        for( i = 0 ; i < n ; ++i )
            if( -1 == fill( GET_INDEX( possible[ i ] ),
                            GET_DIGIT( possible[ i ] ) ) )
                return -1;
        n = allmoves( );
    }
    return 0;
}

/* Return index of square for choice.
 *
 * If no choice is possible (i.e. board solved or inconsistent),
 * return -1.
 *
 * The current implementation finds a square with the minimum
 * number of unknown digits (i.e. maximum # masked digits).
 */
static
int
cmp( const void * e1, const void * e2 )
{
    return GET_DIGIT( *(const int *)e2 ) - GET_DIGIT( *(const int *)e1 );
}

static
int
choice( void )
{
    int i, n;

    rb->yield();

    for( n = i = 0 ; i < 81 ; ++i )
        if( IS_EMPTY( i ) )
        {
            possible[ n ] = SET_INDEX( i ) | SET_DIGIT( numset( board[ i ] ) );

            /* Inconsistency if square unknown, but nothing possible */
            if( 9 == GET_DIGIT( possible[ n ] ) )
                return -2;
            ++n;
        }

    if( 0 == n )
        return -1;      /* All squares known */

    rb->qsort( possible, n, sizeof( possible[ 0 ] ), cmp );
    return GET_INDEX( possible[ 0 ] );
}

/* Choose a digit for the given square.
 * The starting digit is passed as a parameter.
 * Returns -1 if no choice possible.
 */
static
int
choose( int idx, int digit )
{
    rb->yield();

    for( ; digit <= 9 ; ++digit )
        if( !DISALLOWED( idx, digit ) )
        {
            board[ idx ] = SET_DIGIT( digit );
            update( idx );
            add_move( idx, digit, CHOICE );
            return digit;
        }

    return -1;
}

/* Backtrack to a previous choice point, and attempt to reseed
 * the search. Return -1 if no further choice possible, or
 * the index of the changed square.
 *
 * Assumes that the move history and board are valid.
 */
static
int
backtrack( void )
{
    int digit, idx;

    rb->yield();

    for( ; 0 <= --idx_history ; )
        if( history[ idx_history ] & CHOICE )
        {
            /* Remember the last choice, and advance */
            idx = GET_INDEX( history[ idx_history ] );
            digit = GET_DIGIT( history[ idx_history ] ) + 1;
            reapply( );
            if( -1 != choose( idx, digit ) )
                return idx;
        }

    return -1;
}

/* Attempt to solve 'board'; return 0 on success else -1 on error.
 *
 * The solution process attempts to fill-in deterministically as
 * much of the board as possible. Once that is no longer possible,
 * need to choose a square to fill in.
 */
static
int
solve( void )
{
    int idx;

    rb->yield();

    while( 1 )
    {
        if( 0 == deterministic( ) )
        {
            /* Solved, make a new choice, or rewind a previous choice */
            idx = choice( );
            if( -1 == idx )
                return 0;
            else
            if( ( idx < 0 || -1 == choose( idx, 1 ) ) && -1 == backtrack( ) )
                return -1;
        }
        else /* rewind to a previous choice */
        if( -1 == backtrack( ) )
            return -1;
    }
    return -1;
}

static
int
init_template( int template )
{
    int i, row, col;
    int mask;

    reset( );
    len_tmplt = 0;

    /* Consume grid - allow leading spaces and comments at end */
    for( row = 0 ; row < 9 ; ++row )
    {
        mask=0x100;
        for( col = 0 ; col < 9 ; ++col )
        {
            if (templates[template][row] & mask)
                    tmplt[ len_tmplt++ ] = INDEX( row, col );
            mask /= 2;
        }
    }

    /* Construct move history for a template */
    idx_history = 0;
    for( i = 0 ; i < 81 ; ++i )
        if( 0 != DIGIT( i ) )
            history[ idx_history++ ] = i | (DIGIT( i )<<8);

    /* Finally, markup all of these moves as 'fixed' */
    for( i = 0 ; i < idx_history ; ++i )
        history[ i ] |= FIXED;

    return 0;
}

/* Classify a SuDoKu, given its solution.
 *
 * The classification is based on the average number of possible moves
 * for each pass of the deterministic solver - it is a rather simplistic
 * measure, but gives reasonable results. Note also that the classification
 * is based on the first solution found (but does handle the pathological
 * case of multiple solutions). Note that the average moves per pass
 * depends just on the number of squares initially set... this simplifies
 * the statistics collection immensely, requiring just the number of passes
 * to be counted.
 *
 * Return 0 on error, else a string classification.
 */

static
char *
classify( void )
{
    int i, score;

    rb->yield();

    pass = 0;
    clear_moves( );
    if( -1 == solve( ) )
        return 0;

    score = 81;
    for( i = 0 ; i < 81 ; ++i )
        if( IS_FIXED( i ) )
            --score;

    assert( 81 == idx_history );

    for( i = 0 ; i < 81 ; ++i )
        if( history[ i ] & CHOICE )
            score -= 5;

    if( 15 * pass < score )
        return "very easy";
    else
    if( 11 * pass < score )
        return "easy";
    else
    if( 7 * pass < score )
        return "medium";
    else
    if( 4 * pass < score )
        return "hard";
    else
        return "fiendish";
}

/* exchange disjoint, identical length blocks of data */
static
void
exchange( int * a, int * b, int len )
{
    int i, tmp;
    for( i = 0 ; i < len ; ++i )
    {
        tmp = a[ i ];
        a[ i ] = b[ i ];
        b[ i ] = tmp;
    }
}

/* rotate left */
static
void
rotate1_left( int * a, int len )
{
    int i, tmp;
    tmp = a[ 0 ];
    for( i = 1 ; i < len ; ++i )
        a[ i - 1 ] = a[ i ];
    a[ len - 1 ] = tmp;
}

/* rotate right */
static
void
rotate1_right( int * a, int len )
{
    int i, tmp;
    tmp = a[ len - 1 ];
    for( i = len - 1 ; 0 < i ; --i )
        a[ i ] = a[ i - 1 ];
    a[ 0 ] = tmp;
}

/* Generalised left rotation - there is a naturally recursive
 * solution that is best implementation using iteration.
 * Note that it is not necessary to do repeated unit rotations.
 *
 * This function is analogous to 'cutting' a 'pack of cards'.
 *
 * On entry: 0 < idx < len
 */
static
void
rotate( int * a, int len, int idx )
{
    int xdi = len - idx;
    int delta = idx - xdi;

    while( 0 != delta && 0 != idx )
    {
        if( delta < 0 )
        {
            if( 1 == idx )
            {
                rotate1_left( a, len );
                idx = 0;
            }
            else
            {
                exchange( a, a + xdi, idx );
                len = xdi;
            }
        }
        else /* 0 < delta */
        {
            if( 1 == xdi )
            {
                rotate1_right( a, len );
                idx = 0;
            }
            else
            {
                exchange( a, a + idx, xdi );
                a += xdi;
                len = idx;
                idx -= xdi;
            }
        }
        xdi = len - idx;
        delta = idx - xdi;
    }
    if( 0 < idx )
        exchange( a, a + idx, idx );
}

/* Shuffle an array of integers */
static
void
shuffle( int * a, int len )
{
    int i, j, tmp;

    i = len;
    while( 1 <= i )
    {
        j = rb->rand( ) % i;
        tmp = a[ --i ];
        a[ i ] = a[ j ];
        a[ j ] = tmp;
    }
}

/* Generate a SuDoKu puzzle
 *
 * The generation process selects a random template, and then attempts
 * to fill in the exposed squares to generate a board. The order of the
 * digits and of filling in the exposed squares are random.
 */

/* Select random template; sets tmplt, len_tmplt */
static
void
select_template( void )
{
    int i = rb->rand( ) % NUM_TEMPLATES;
    init_template( i );
}

static bool check_buttons(void)
{
    int button = rb->button_get(false);
    return (button && (!(button & BUTTON_REL)) && (!(button & BUTTON_REPEAT)));
}

static
bool
generate( void )
{
    static int digits[ 9 ];

    int i;

start:
    /* Allow the user to abort generation by pressing any button */
    if (check_buttons())
        return false;

    for( i = 0 ; i < 9 ; ++i )
        digits[ i ] = i + 1;

    rotate( digits, 9, 1 + rb->rand( ) % 8 );
    shuffle( digits, 9 );
    select_template( );

    rotate( tmplt, len_tmplt, 1 + rb->rand( ) % ( len_tmplt - 1 ) );
    shuffle( tmplt, len_tmplt );

    reset( );  /* construct a new board */

    for( i = 0 ; i < len_tmplt ; ++i )
        fill( tmplt[ i ], digits[ i % 9 ] );

    /* Allow the user to abort generation by pressing any button */
    if (check_buttons())
        return false;

    rb->yield();

    if( 0 != solve( ) || idx_history < 81 )
        goto start;

    /* Allow the user to abort generation by pressing any button */
    if (check_buttons())
        return false;

    rb->yield();

    for( i = 0 ; i < len_tmplt ; ++i )
        board[ tmplt[ i ] ] |= FIXED;

    /* Construct fixed squares */
    for( idx_history = i = 0 ; i < 81 ; ++i )
        if( IS_FIXED( i ) )
            history[ idx_history++ ] = SET_INDEX( i )
                                     | SET_DIGIT( DIGIT( i ) )
                                     | FIXED;
    clear_moves( );

    if( 0 != solve( ) || idx_history < 81 )
        goto start;
    if( -1 != backtrack( ) && 0 == solve( ) )
        goto start;

    clear_moves( );

    return true;
}

bool sudoku_generate_board(struct sudoku_state_t* state, char** difficulty)
{
    int r,c,i;

    rb->srand(*rb->current_tick);

    rb->button_clear_queue();

    if (!generate()) {
        /* User has aborted with a button press */
        return false;
    }

    i=0;
    for (r=0;r<9;r++) {
        for (c=0;c<9;c++) {
            if( IS_EMPTY( i ) )
                state->startboard[r][c]='0';
            else
                state->startboard[r][c]='0'+GET_DIGIT( board[ i ] );

            state->currentboard[r][c]=state->startboard[r][c];
            i++;
        }
    }

    *difficulty = classify( );
    return true;
}

bool sudoku_solve_board(struct sudoku_state_t* state)
{
    bool ret;
    int r,c,i;

    reset( );
    i=0;
    for (r=0;r<9;r++) {
        for (c=0;c<9;c++) {
            if( state->startboard[r][c]!='0' )
            {
                fill( i, state->startboard[r][c] - '0' );
            }
            i++;
        }
    }

    ret = ( 0 == solve( ) && 81 == idx_history );

    if (ret) {
        i=0;
        for (r=0;r<9;r++) {
            for (c=0;c<9;c++) {
                state->currentboard[r][c]='0'+GET_DIGIT( board[ i ] );
                i++;
            }
        }
    }
    return ret;
}
