/***************************************************************************** * 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; } } }