/*****************************************************************************
* DikuMUD (C) 1990, 1991 by: *
* Sebastian Hammer, Michael Seifert, Hans Henrik Staefeldt, Tom Madsen, *
* and Katja Nyboe. *
*---------------------------------------------------------------------------*
* MERC 2.1 (C) 1992, 1993 by: *
* Michael Chastain, Michael Quan, and Mitchell Tse. *
*---------------------------------------------------------------------------*
* SMAUG 1.4 (C) 1994, 1995, 1996, 1998 by: Derek Snider. *
* Team: Thoric, Altrag, Blodkai, Narn, Haus, Scryn, Rennard, Swordbearer, *
* gorog, Grishnakh, Nivek, Tricops, and Fireblade. *
*---------------------------------------------------------------------------*
* SMAUG 1.7 FUSS by: Samson and others of the SMAUG community. *
* Their contributions are greatly appreciated. *
*---------------------------------------------------------------------------*
* LoP (C) 2006, 2007, 2008 by: the LoP team. *
*****************************************************************************/
#include <stdio.h>
#include <string.h>
#include "h/mud.h"
void show_sudoku( CHAR_DATA *ch, bool possible );
char *stime_display( time_t time )
{
static char *stime;
char buf[MSL];
time_t ttime;
int days = 0, hours = 0, mins = 0, seconds = 0;
ttime = time;
days = ( int )( ttime / 43200 );
ttime -= ( days * 43200 );
hours = ( int )( ttime / 3600 );
ttime -= ( hours * 3600 );
mins = ( int )( ttime / 60 );
ttime -= ( mins * 60 );
seconds = ( int )( ttime );
buf[0] = '\0';
if( days > 0 )
snprintf( buf + strlen( buf ), sizeof( buf ) - strlen( buf ), "%d day%s", days, days > 1 ? "s" : "" );
if( hours > 0 )
snprintf( buf + strlen( buf ), sizeof( buf ) - strlen( buf ), "%s%d hour%s",
buf[0] != '\0' ? " " : "", hours, hours > 1 ? "s" : "" );
if( mins > 0 )
snprintf( buf + strlen( buf ), sizeof( buf ) - strlen( buf ), "%s%d minute%s",
buf[0] != '\0' ? " " : "", mins, mins > 1 ? "s" : "" );
if( ( days <= 0 && hours <= 0 && mins <= 0 ) || seconds > 0 )
snprintf( buf + strlen( buf ), sizeof( buf ) - strlen( buf ), "%s%d second%s",
buf[0] != '\0' ? " " : "", seconds, seconds != 1 ? "s" : "" );
stime = buf;
return stime;
}
short get_region( short row, short col )
{
if( row >= 0 && row <= 2 && col >= 0 && col <= 2 )
return 0;
if( row >= 0 && row <= 2 && col >= 3 && col <= 5 )
return 1;
if( row >= 0 && row <= 2 && col >= 6 && col <= 8 )
return 2;
if( row >= 3 && row <= 5 && col >= 0 && col <= 2 )
return 3;
if( row >= 3 && row <= 5 && col >= 3 && col <= 5 )
return 4;
if( row >= 3 && row <= 5 && col >= 6 && col <= 8 )
return 5;
if( row >= 6 && row <= 8 && col >= 0 && col <= 2 )
return 6;
if( row >= 6 && row <= 8 && col >= 3 && col <= 5 )
return 7;
if( row >= 6 && row <= 8 && col >= 6 && col <= 8 )
return 8;
return -1;
}
bool can_use_sudoku( CHAR_DATA *ch, short value, short onrow, short oncol )
{
short row, col, region, onregion;
if( !ch || !ch->pcdata )
return false;
onregion = get_region( onrow, oncol );
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
/* Only really need to check the same row, col or main block */
if( onrow == row || oncol == col || ( region = get_region( row, col ) ) == onregion )
{
/* If its already used in one of them can't use it now */
if( ch->pcdata->sudoku[row][col] == value )
return false;
}
}
}
return true;
}
bool could_use_sudoku( CHAR_DATA *ch, short value, short onrow, short oncol )
{
short row, col, region, onregion;
if( !ch || !ch->pcdata )
return false;
onregion = get_region( onrow, oncol );
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
region = get_region( row, col );
/* Only really need to check the same row, col or main block */
if( onrow == row || oncol == col || region == onregion )
{
/* They should be allowed to reset it to the same number in that spot if they wish */
if( onrow == row && oncol == col )
continue;
/* If its already used in one of them can't use it now */
if( ch->pcdata->dis_sudoku[row][col] == value )
return false;
}
}
}
return true;
}
void set_sudoku( CHAR_DATA *ch, char *argument )
{
short row = -1, col = -1, value = -1;
if( !ch || !ch->pcdata )
return;
if( argument[0] != '\0' )
{
row = ( short ) ( argument[0] - '0' );
row--; /* Have to subtract a row */
if( argument[1] != '\0' )
{
col = ( short ) ( argument[1] - '0' );
col--; /* Have to subtract a col */
if( argument[2] != '\0' )
value = ( short ) ( argument[2] - '0' );
}
}
if( row < 0 || row > 8 )
{
send_to_char( "Invalid row, please specify a row 1-9.\r\n", ch );
return;
}
if( col < 0 || col > 8 )
{
send_to_char( "Invalid column, please specify a row 1-9.\r\n", ch );
return;
}
if( value < 0 || value > 9 )
{
send_to_char( "Invalid number, please specify a number 0-9.\r\n", ch );
return;
}
if( !could_use_sudoku( ch, value, row, col ) && value != 0 )
{
send_to_char( "That value isn't even a possibility in that row and column.\r\n", ch );
return;
}
if( ch->pcdata->dis_sudoku[row][col] == ch->pcdata->sudoku[row][col] && ch->pcdata->sudoku[row][col] != 0 )
{
send_to_char( "That value is a base value and can't be changed!\r\n", ch );
return;
}
ch->pcdata->dis_sudoku[row][col] = value;
ch_printf( ch, "You have set row %d, column %d to %d.\r\n", ( row + 1 ), ( col + 1 ), value );
show_sudoku( ch, false );
}
bool playing_sudoku( CHAR_DATA *ch )
{
short row, col;
if( !ch || !ch->pcdata )
return false;
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
if( ch->pcdata->sudoku[row][col] != 0 )
return true;
}
}
return false;
}
void quit_sudoku( CHAR_DATA *ch )
{
short row, col;
if( !ch || !ch->pcdata )
return;
/* Set the puzzle to nothing */
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
ch->pcdata->dis_sudoku[row][col] = 0;
ch->pcdata->sudoku[row][col] = 0;
}
}
}
void completed_sudoku( CHAR_DATA *ch )
{
short row, col;
if( !ch || !ch->pcdata )
return;
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
if( ch->pcdata->dis_sudoku[row][col] == 0 )
return;
if( !could_use_sudoku( ch, ch->pcdata->dis_sudoku[row][col], row, col ) )
return;
}
}
send_to_char( "You have completed that sudoku puzzle.\r\n", ch );
quit_sudoku( ch );
ch->pcdata->swins++;
ch->pcdata->slasttime = ( current_time - ch->pcdata->sstarttime );
if( ch->pcdata->slasttime > ch->pcdata->sslowesttime || ch->pcdata->sslowesttime == 0 )
ch->pcdata->sslowesttime = ch->pcdata->slasttime;
if( ch->pcdata->slasttime < ch->pcdata->sfastesttime || ch->pcdata->sfastesttime == 0 )
ch->pcdata->sfastesttime = ch->pcdata->slasttime;
}
void show_possibilities( CHAR_DATA *ch )
{
char buf[MSL];
short value = 0, row = 0, col = 0, ccol = 0;
if( !ch || !ch->pcdata )
return;
/* It takes 3 different checks for this stuff for each thing we go to set */
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
if( ch->pcdata->dis_sudoku[row][col] != 0 )
continue;
ch_printf( ch, " [%d] [%d]", ( row + 1 ), ( col + 1 ) );
buf[0] = '\0';
for( value = 1; value <= 9; value++ )
{
if( could_use_sudoku( ch, value, row, col ) )
snprintf( buf + strlen( buf ), sizeof( buf ) - strlen( buf ), " %d ", value );
}
ch_printf( ch, "%-20s", buf );
if( ++ccol == 3 )
{
send_to_char( "\r\n", ch );
ccol = 0;
}
}
}
if( ccol != 0 )
send_to_char( "\r\n", ch );
}
void show_sudoku( CHAR_DATA *ch, bool possible )
{
short row, col, value, rowdis;
if( !ch || !ch->pcdata )
return;
set_char_color( AT_SUDOKU, ch );
if( possible )
{
send_to_char( " Columns \r\n", ch );
send_to_char( "Rows 1 2 3 4 5 6 7 8 9 \r\n", ch );
}
else
{
send_to_char( " Columns \r\n", ch );
send_to_char( "Rows 1 2 3 4 5 6 7 8 9 \r\n", ch );
}
for( row = 0; row < 9; row++ )
{
if( row == 0 || row == 3 || row == 6 )
{
if( possible )
send_to_char( " -------------------------------------------------------\r\n", ch );
else
send_to_char( " -------------------------------------\r\n", ch );
}
else
{
if( possible )
send_to_char( " |----- ----- -----|----- ----- -----|----- ----- -----|\r\n", ch );
else
send_to_char( " |--- --- ---|--- --- ---|--- --- ---|\r\n", ch );
}
for( rowdis = ( possible ? 0 : 1 ); rowdis < ( possible ? 3 : 2 ); rowdis++ )
{
if( rowdis == 1 )
ch_printf( ch, " %d | ", ( row + 1 ) );
else
send_to_char( " | ", ch );
for( col = 0; col < 9; col++ )
{
if( ch->pcdata->dis_sudoku[row][col] > 0 )
{
if( rowdis == 1 )
{
if( possible )
{
if( ch->pcdata->dis_sudoku[row][col] == ch->pcdata->sudoku[row][col] )
ch_printf( ch, "|&G%d&D|", ch->pcdata->dis_sudoku[row][col] );
else
ch_printf( ch, "*&R%d&D*", ch->pcdata->dis_sudoku[row][col] );
}
else
ch_printf( ch, "&[sudoku2]%d&D", ch->pcdata->dis_sudoku[row][col] );
}
else
{
if( ch->pcdata->dis_sudoku[row][col] == ch->pcdata->sudoku[row][col] )
send_to_char( "---", ch );
else
send_to_char( "***", ch );
}
}
else if( possible )
{
for( value = 1; value <= 9; value++ )
{
if( rowdis == 0 && ( value < 1 || value > 3 ) )
continue;
if( rowdis == 1 && ( value < 4 || value > 6 ) )
continue;
if( rowdis == 2 && ( value < 7 || value > 9 ) )
continue;
if( could_use_sudoku( ch, value, row, col ) )
ch_printf( ch, "&W%d&D", value );
else
send_to_char( " ", ch );
}
}
else
send_to_char( " ", ch );
send_to_char( " | ", ch );
}
send_to_char( "\r\n", ch );
}
}
if( possible )
send_to_char( " -------------------------------------------------------\r\n", ch );
else
send_to_char( " -------------------------------------\r\n", ch );
ch_printf( ch, "So far you have been working on this puzzle for %s.\r\n",
stime_display( current_time - ch->pcdata->sstarttime ) );
}
/* Set it back to default */
void reset_sudoku( CHAR_DATA *ch )
{
short row, col;
if( !ch || !ch->pcdata )
return;
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
ch->pcdata->dis_sudoku[row][col] = ch->pcdata->sudoku[row][col];
}
}
}
void new_sudoku( CHAR_DATA *ch )
{
short tries, value = 0, row = 0, col = 0, tried = 0, original = 0;
bool valid = false, restart = false;
if( !ch || !ch->pcdata )
return;
/* It takes 3 different checks for this stuff for each thing we go to set */
for( tries = 0; tries < 1000; tries++ )
{
quit_sudoku( ch );
restart = false;
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
tried = 0;
/* Have to get a value they can use */
for( original = value = number_range( 1, 9 ); value; value++ )
{
if( can_use_sudoku( ch, value, row, col ) )
break;
if( value >= 9 )
value = 0;
/* If it can't find it by this point might as well say so and return */
if( ++tried == 11 )
{
restart = true;
break;
}
}
if( restart )
break;
ch->pcdata->sudoku[row][col] = value;
/* Later we will replace 0 with a difficulty setting of 0 - 10 */
/* Always at least a 15 chance of showing or not showing */
if( number_range( -15, 25 ) > 10 )
ch->pcdata->dis_sudoku[row][col] = value;
if( row == 8 && col == 8 )
valid = true;
}
if( restart )
break;
}
if( valid )
break;
}
/* Show the data */
if( valid )
{
/* Set the default shown */
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
ch->pcdata->sudoku[row][col] = ch->pcdata->dis_sudoku[row][col];
}
}
ch->pcdata->sstarttime = current_time;
show_sudoku( ch, false );
}
else
{
send_to_char( "Sorry but a sudoku puzzle couldn't be created...try again.\r\n", ch );
quit_sudoku( ch );
}
}
void sudoku_stats( CHAR_DATA *ch, char *argument )
{
CHAR_DATA *victim = NULL;
if( !is_immortal( ch ) || !argument || argument[0] == '\0' )
victim = ch;
else if( !( victim = get_char_world( ch, argument ) ) )
{
send_to_char( "Whose sudoku stats would you like to see?\r\n", ch );
return;
}
if( !victim->pcdata )
{
send_to_char( "They don't have any sudoku data to display.\r\n", ch );
return;
}
if( victim != ch )
ch_printf( ch, "Sudoku stats for %s:\r\n", victim->name );
else
send_to_char( "Your Sudoku stats:\r\n", ch );
if( playing_sudoku( victim ) )
{
send_to_char( "( Currently playing a Sudoku Puzzle )\r\n", ch );
ch_printf( ch, "( Been playing it for %ld seconds )\r\n", ( current_time - victim->pcdata->sstarttime ) );
}
ch_printf( ch, "Won: %u, Lost: %u, Total: %u\r\n",
victim->pcdata->swins, victim->pcdata->squits, ( victim->pcdata->swins + victim->pcdata->squits ) );
ch_printf( ch, "Fastest Time: %s\r\n", stime_display( victim->pcdata->sfastesttime ) );
ch_printf( ch, "Slowest Time: %s\r\n", stime_display( victim->pcdata->sslowesttime ) );
ch_printf( ch, "Last Time: %s\r\n", stime_display( victim->pcdata->slasttime ) );
}
CMDF( do_sudoku )
{
char arg[MIL];
if( !ch || !ch->pcdata )
return;
argument = one_argument( argument, arg );
if( !str_cmp( arg, "stats" ) )
{
sudoku_stats( ch, argument );
return;
}
if( !str_cmp( arg, "new" ) )
{
if( playing_sudoku( ch ) )
{
send_to_char( "You already have a sudoku puzzle going. Type 'sudoku quit' to quit that one.\r\n", ch );
return;
}
new_sudoku( ch );
return;
}
if( !playing_sudoku( ch ) )
{
send_to_char( "You aren't working on a sudoku puzzle. Type 'sudoku new' to start one.\r\n", ch );
return;
}
if( !str_cmp( arg, "quit" ) )
{
send_to_char( "You have quit the sudoku puzzle you were doing. Type 'sudoku new' to start one.\r\n", ch );
quit_sudoku( ch );
ch->pcdata->squits++;
return;
}
if( !str_cmp( arg, "reset" ) )
{
send_to_char( "Your sudoku puzzle has been reset to the default.\r\n", ch );
reset_sudoku( ch );
return;
}
/* Allow them to set it */
if( !str_cmp( arg, "set" ) )
{
set_sudoku( ch, argument );
completed_sudoku( ch );
return;
}
if( is_immortal( ch ) )
{
/* Allow them to see the possibilities for the blank places */
if( !str_cmp( arg, "possibilities" ) )
{
show_possibilities( ch );
return;
}
/* Allow them to see the possibilities in the puzzle */
if( !str_cmp( arg, "puzzpossible" ) )
{
show_sudoku( ch, true );
return;
}
}
show_sudoku( ch, false );
}
void fwrite_sudoku( CHAR_DATA *ch, FILE *fp )
{
int row = 0, col = 0;
if( !ch || !ch->pcdata )
return;
if( playing_sudoku( ch ) )
{
for( row = 0; row < 9; row++ )
for( col = 0; col < 9; col++ )
fprintf( fp, "Sudoku %d~\n", ch->pcdata->sudoku[row][col] );
for( row = 0; row < 9; row++ )
for( col = 0; col < 9; col++ )
fprintf( fp, "DisSudoku %d~\n", ch->pcdata->dis_sudoku[row][col] );
}
fprintf( fp, "SudokuWins %u\n", ch->pcdata->swins );
fprintf( fp, "SudokuQuits %u\n", ch->pcdata->squits );
fprintf( fp, "SudokuStart %ld\n", ch->pcdata->sstarttime );
fprintf( fp, "SudokuFast %ld\n", ch->pcdata->sfastesttime );
fprintf( fp, "SudokuSlow %ld\n", ch->pcdata->sslowesttime );
fprintf( fp, "SudokuLast %ld\n", ch->pcdata->slasttime );
}
/* Have to do this while loading the sudoku up */
void fread_sudoku( CHAR_DATA *ch, bool display, FILE *fp )
{
char c;
int row = 0, col = 0, value;
if( !ch || !ch->pcdata )
{
fread_to_eol( fp );
return;
}
/* Start with it empty */
for( row = 0; row < 9; row++ )
{
for( col = 0; col < 9; col++ )
{
if( display )
ch->pcdata->dis_sudoku[row][col] = 0;
else
ch->pcdata->sudoku[row][col] = 0;
}
}
row = col = 0;
for( ;; )
{
c = getc( fp );
if( c == EOF )
{
bug( "%s: EOF", __FUNCTION__ );
return;
}
if( c == '~' )
{
return;
}
if( c == ' ' )
continue;
if( row >= 9 || col >= 9 )
{
bug( "%s: row[%d], col[%d] is over the limits", __FUNCTION__, row, col );
fread_to_eol( fp );
return;
}
value = ( c - '0' );
if( value < 0 || value > 9 )
bug( "%s: value = %d and should be 0 - 9", __FUNCTION__, value );
if( display )
ch->pcdata->dis_sudoku[row][col] = value;
else
ch->pcdata->sudoku[row][col] = value;
if( ++col >= 9 )
{
row++;
col = 0;
}
}
}