fork download
  1. /* -------------------------------------------------------------------------
  2.  * Title: Target Points v1.6
  3.  * From: migf1
  4.  * Description:
  5.  * a little gambling, board game in text mode, written in ANSI C (C99)
  6.  * see function: do_help() for details
  7.  * Change log:
  8.  * v1.6: - added "reset game" functionality
  9.  * - better separation between user interface and lower level functions
  10.  * v1.5: - added "save game" and "load game" functionality
  11.  * - more compact code + better function names
  12.  * v1.0: original version
  13.  * -------------------------------------------------------------------------
  14.  */
  15.  
  16. #include <stdio.h>
  17. #include <string.h> // for strlen()
  18. #include <stdlib.h> // for srand(), exit()
  19. #include <ctype.h> // for isdigit(), tolower()
  20. #include <time.h> // for time() (used in srand())
  21.  
  22. #define MAXINBUF 255+1 // max chars we read in the input buffer
  23. #define MAXFNAME 255+1 // max length for the game filename
  24. #define MAXPNAME 5+1 // max length for the player's name
  25.  
  26. #define MAXROWS 7 // board's max height
  27. #define MAXCOLS 7 // board's max width
  28.  
  29. #define CREDITS() printf("%76s\n", "\"Target Points\" by migf1");
  30.  
  31. #define PROMPT(plr, mov, maxmovs, pts, maxpts) \
  32. printf("[%s] M:%d/%d, P:%d/%d> ", (plr), (mov+1), (maxmovs), (pts), (maxpts) )
  33.  
  34. #define PAUSE() \
  35. do{ \
  36. char myinbufr[255]=""; \
  37. printf("\npress ENTER..."); \
  38. fgets(myinbufr, 255, stdin); \
  39. }while(0)
  40.  
  41. typedef enum { FALSE=0, TRUE } Bool; // our custom boolean data type
  42.  
  43. // our own data type for errors
  44. typedef enum { ERR_NOERROR=0, ERR_FNAME, ERR_FFILE, ERR_FREAD, ERR_FWRITE } ErrCode;
  45.  
  46. typedef struct cell { // structure of any individual cell
  47. Bool avail; // is it available for selection?
  48. int pos; // cell's position in the board
  49. int val; // cell's value
  50. }Cell;
  51.  
  52. typedef struct gamestatus { // structure of the game itself
  53. char fname[ MAXFNAME ]; // filename for saved game
  54. char player[ MAXPNAME ]; // player name
  55. Cell board[ MAXROWS ][ MAXCOLS ]; // the board
  56. int move, maxmoves; // current & max number of moves
  57. int points, maxpoints; // current & max number of points
  58. int score; // final score
  59. }GameStatus;
  60.  
  61. // ---------------------------------------------------------------------------------
  62. // # Helper Function #
  63. // Read s from standard input
  64. //
  65. char *s_get(char *s, size_t len)
  66. {
  67. char *cp;
  68.  
  69. for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
  70. ; // for-loop with empty body
  71. *cp = '\0'; // null-terminate last character
  72.  
  73. return s;
  74. }
  75.  
  76. // ---------------------------------------------------------------------------------
  77. // # Helper Function #
  78. // Trim leading & trailing spaces from s
  79. //
  80. char *s_trim( char *s )
  81. {
  82. char *cp1; // for parsing the whole s
  83. char *cp2; // for shifting & padding
  84.  
  85. // trim leading & shift left remaining
  86. for (cp1=s; isspace(*cp1); cp1++ ) // skip leading spaces, via cp1
  87. ;
  88. for (cp2=s; *cp1; cp1++, cp2++) // shift left remaining chars, via cp2
  89. *cp2 = *cp1;
  90. *cp2-- = '\0'; // mark end of left trimmed s
  91.  
  92. // replace trailing spaces with '\0's
  93. while ( cp2 > s && isspace(*cp2) )
  94. *cp2-- = '\0'; // pad with '\0'
  95.  
  96. return s;
  97. }
  98.  
  99. // ------------------------------------------------------------------------------------
  100. // # Helper Function #
  101. // Copy null-terminated string src to string dst (up to n-1 chars + '\0')
  102. //
  103. char *s_ncopy( char *dst, const char *src, int n )
  104. {
  105. char *ret = dst;
  106.  
  107. while ( (dst-ret) < n-1 && (*dst=*src) != '\0' )
  108. dst++, src++;
  109.  
  110. if ( *dst )
  111. *dst = 0;
  112.  
  113. return ret;
  114. }
  115.  
  116. // ---------------------------------------------------------------------------------
  117. // Read the player name from the standard input
  118. // IMPORTANT:
  119. // this function is used INSIDE the function: init_game()
  120. //
  121. char *get_playername( char *player, int maxpname )
  122. {
  123. if ( !player )
  124. return NULL;
  125.  
  126. int temp;
  127. char dummy[MAXINBUF] = "";
  128.  
  129. do {
  130. printf("\nPlayer name (%d chars, extras will be ignored)? ", maxpname-1);
  131. s_get(dummy, MAXINBUF);
  132. s_trim(dummy);
  133. temp = strlen(dummy);
  134. if ( temp+1 > maxpname)
  135. dummy[maxpname-1] = '\0';
  136. strcpy(player, dummy);
  137. } while ( !*player );
  138.  
  139. return player;
  140. }
  141.  
  142. // ---------------------------------------------------------------------------------
  143. // # Helper Function #
  144. // Read inbuf form the standard input, trim leading & trailing spaces and convert
  145. // its 1st character to lowercase
  146. //
  147. char *get_command( char *inbuf, int maxinbuf )
  148. {
  149. if ( !inbuf )
  150. return NULL;
  151.  
  152. s_get(inbuf, maxinbuf); // read inbuf
  153. s_trim( inbuf ); // trim & trailing spaces
  154. *inbuf = tolower( *inbuf ); // convert 1st char to lowercase
  155.  
  156. return inbuf; // return inbuf
  157.  
  158. }
  159. // ---------------------------------------------------------------------------------
  160. // Initialize the board (it also populates it with random cell-values)
  161. //
  162. int init_board( int nrows, int ncols, Cell board[nrows][ncols] )
  163. {
  164. register int i,j;
  165. int valsum = 0;
  166.  
  167. for (i=0; i<nrows; i++)
  168. {
  169. for (j=0; j < ncols; j++) {
  170. board[i][j].avail = TRUE;
  171. board[i][j].pos = i * ncols + j;
  172. board[i][j].val = rand() % (nrows*ncols);
  173. valsum += board[i][j].val;
  174. }
  175. }
  176.  
  177. return valsum;
  178. }
  179.  
  180. // ---------------------------------------------------------------------------------
  181. // Initialize the game
  182. //
  183. void init_game( int nrows, int ncols, int maxfname, int maxpname, GameStatus *game )
  184. {
  185. s_ncopy( game->fname, "targetpoints.dat", maxfname); // init filename
  186. get_playername( game->player, maxpname ); // read player name
  187. game->move = 0; // init current move
  188. game->maxmoves = (nrows * ncols) / 2; // init maxmoves
  189. game->points = 0; // init current points
  190. // init both maxpoints and board cells
  191. game->maxpoints = init_board( nrows, ncols, game->board ) / 2;
  192. game->score; // init final score
  193.  
  194. return;
  195. }
  196.  
  197. // ---------------------------------------------------------------------------------
  198. // Reset the game
  199. // It differs than init_game() in that this one leaves intact the playername and the
  200. // filename. It resets all moves and points though, and it re-populates the cell values
  201. //
  202. void reset_game( int nrows, int ncols, GameStatus *game )
  203. {
  204. game->move = 0; // init current move
  205. game->maxmoves = (nrows * ncols) / 2; // init maxmoves
  206. game->points = 0; // init current points
  207. // init both maxpoints and board cells
  208. game->maxpoints = init_board( nrows, ncols, game->board ) / 2;
  209. game->score; // init final score
  210.  
  211. return;
  212. }
  213.  
  214. // ---------------------------------------------------------------------------------
  215. // Save the game
  216. //
  217. ErrCode save_game( GameStatus *game )
  218. {
  219. if ( !game->fname )
  220. return ERR_FNAME;
  221.  
  222. FILE *fp = fopen(game->fname, "wb");
  223. if ( !fp )
  224. return ERR_FFILE;
  225.  
  226. if ( fwrite( game, sizeof( GameStatus ), 1, fp ) != 1 )
  227. return ERR_FWRITE;
  228.  
  229. fclose( fp );
  230.  
  231. return ERR_NOERROR;
  232. }
  233.  
  234. // ---------------------------------------------------------------------------------
  235. // Load a game
  236. //
  237. ErrCode load_game( GameStatus *game )
  238. {
  239. if ( !game->fname )
  240. return ERR_FNAME;
  241.  
  242. FILE *fp = fopen(game->fname, "rb");
  243. if ( !fp )
  244. return ERR_FFILE;
  245.  
  246. if ( fread( game, sizeof( GameStatus ), 1, fp ) != 1 )
  247. return ERR_FREAD;
  248.  
  249. fclose( fp );
  250.  
  251. return ERR_NOERROR;
  252. }
  253.  
  254. // ---------------------------------------------------------------------------------
  255. /*** DISABLED: UNUSED
  256.  
  257. void draw_cells( int nrows, int ncols, Cell board[nrows][ncols] )
  258. {
  259. register int i,j;
  260.  
  261. for (i=0; i<nrows; i++)
  262. {
  263. for (j=0; j < ncols; j++)
  264. printf("+---");
  265. puts("+");
  266. for (j=0; j < ncols; j++)
  267. printf("| %2d ", board[i][j].val);
  268. puts("|");
  269. }
  270. for (j=0; j < ncols; j++)
  271. printf("+---");
  272. puts("+");
  273.  
  274. return;
  275. }
  276. ***/
  277.  
  278. // ---------------------------------------------------------------------------------
  279. // Draw the board diagrams on the screen, the left one with available posistions,
  280. // the right one with already selected cell-values (if cheat is TRUE, the right
  281. // diagram shows ALL cell-values, instead).
  282. //
  283. void show_board( int nrows, int ncols, Cell board[nrows][ncols], Bool cheat )
  284. {
  285. register int i,j;
  286.  
  287. for (i=0; i < nrows; i++)
  288. {
  289. for (j=0; j < ncols; j++)
  290. printf("|----");
  291. printf("|\t");
  292. for (j=0; j < ncols; j++)
  293. printf("-----");
  294. puts("-");
  295.  
  296. for (j=0; j < ncols; j++)
  297. {
  298. if ( board[i][j].avail )
  299. printf("| %2d ", board[i][j].pos);
  300. else
  301. printf("| ## ");
  302. }
  303. printf("|\t");
  304. for (j=0; j < ncols; j++)
  305. {
  306. if ( board[i][j].avail && !cheat)
  307. printf("| ");
  308. else
  309. printf("| %2d ", board[i][j].val);
  310. }
  311. puts("|");
  312. }
  313. for (j=0; j < ncols; j++)
  314. printf("|----");
  315. printf("|\t");
  316. for (j=0; j < ncols; j++)
  317. printf("-----");
  318. puts("-");
  319.  
  320. return;
  321. }
  322.  
  323. // ---------------------------------------------------------------------------------
  324. // Handle the "help" command: h
  325. //
  326. void do_help( void )
  327. {
  328. puts("\n\tThis is a little gambling game written in ANSI C, by migf1.");
  329.  
  330. puts("\n\tYour task is to collect the target amount of points in as many");
  331. puts("\tmoves as the half of the total cells of the board.");
  332.  
  333. puts("\n\tCells are initially populated with random values ranging from");
  334. puts("\t0 to the total number of cells - 1 (e.g.: from 0 to 48 for a");
  335. puts("\t7x7 board). Duplicated values may appear in two or more cells.");
  336. puts("\tThe target amount of points you are after, equals to the half");
  337. puts("\tof the sum of all those values (sum of values / 2)");
  338.  
  339. puts("\n\tTo choose a cell you enter its positioning number, as shown");
  340. puts("\tin the left diagram. The value it holds will appear in the");
  341. puts("\tright diagram and it will be added to your current points.");
  342.  
  343. puts("\n\tIf you run out of moves before collecting the target amount");
  344. puts("\tof points, the game ends and you get 0 score. If you manage");
  345. puts("\tto collect the points before running out of moves, then the");
  346. puts("\tpoints collected beyond the targeted amount are multiplied");
  347. puts("\tby the moves you had left, and the result is your score");
  348.  
  349. puts("\n\tThe prompt always shows your current move and points, along");
  350.  
  351. PAUSE();
  352.  
  353. puts("\n\twith the moves you have left, and the targeted amount of");
  354. puts("\tpoints to be gathered. So make sure you check it regularly");
  355. puts("\t(M stands for move, P for points).");
  356.  
  357. puts("\n\tYou can also use the following commands at the prompt:");
  358. puts("");
  359. puts("\th\tthis help you are reading right now");
  360. puts("\tt\tshow current stats");
  361. puts("\ts\tsave current game");
  362. puts("\tl\tload previously saved game (current game data get erased!)");
  363. puts("\tr\treset game (everything but the playername are reset)");
  364. puts("\tx\texit the game (note: you'll get 0 score!)");
  365.  
  366. puts("\n\tThe game ends either when you run out of moves, you enter");
  367. puts("\tx at the prompt, or you gather more points than the targeted");
  368. puts("\tamount (equal or more). In any case, all the cell-values will");
  369. puts("\tbe revealed in the right diagram, just before the termination");
  370. puts("\tof the program.");
  371.  
  372. puts("\n\tHave fun!");
  373.  
  374. PAUSE();
  375.  
  376. return;
  377. }
  378.  
  379. // ---------------------------------------------------------------------------------
  380. // Handle the "stats" command: t
  381. //
  382. void do_stats( GameStatus *game )
  383. {
  384. printf( "\n\tyou've made %d move(s), having collected %d points\n",
  385. game->move, game->points );
  386. printf( "\tyou have %d move(s) left, to collect %d more point(s)\n\n",
  387. game->maxmoves - game->move, game->maxpoints - game->points
  388. );
  389.  
  390. return;
  391. }
  392.  
  393. // ---------------------------------------------------------------------------------
  394. // Handle the "save game" command: s
  395. //
  396. Bool do_savegame( GameStatus *game )
  397. {
  398. char inbuf[MAXINBUF] = "";
  399. ErrCode errcode;
  400.  
  401. printf("you are about to save the game, please confirm (y/) ");
  402. get_command(inbuf, MAXINBUF);
  403. if ( !*inbuf || *inbuf != 'y' ) {
  404. puts("\n\tsaving the game aborted by the user\n");
  405. return FALSE;
  406. }
  407.  
  408. printf("\n\tsaving the game... ");
  409.  
  410. errcode = save_game( game );
  411.  
  412. if ( errcode == ERR_NOERROR ) {
  413. printf("succeeded\n\n");
  414. return TRUE;
  415. }
  416.  
  417. if ( errcode == ERR_FNAME )
  418. printf("failed (internal error)\n\n");
  419. else if ( errcode == ERR_FFILE )
  420. printf("failed (file not found)\n\n");
  421. else if ( errcode == ERR_FWRITE)
  422. printf("failed (write error)\n\n");
  423. else
  424. printf("failed (uknown error)\n\n");
  425.  
  426. return FALSE;
  427. }
  428.  
  429. // ---------------------------------------------------------------------------------
  430. // Handle the "load game" command: l
  431. //
  432. Bool do_loadgame( GameStatus *game )
  433. {
  434. char inbuf[MAXINBUF] = "";
  435. ErrCode errcode;
  436.  
  437. printf("loading a previous game will delete this one, please confirm (y/) ");
  438. get_command(inbuf, MAXINBUF);
  439. if ( !*inbuf || *inbuf != 'y' ) {
  440. puts("\n\tloading a previously saved game aborted by the user\n");
  441. return FALSE;
  442. }
  443.  
  444. printf("\n\tloading game... ");
  445.  
  446. errcode = load_game( game );
  447.  
  448. if ( errcode == ERR_NOERROR ) {
  449. printf("succeeded\n\n");
  450. return TRUE;
  451. }
  452.  
  453. if ( errcode == ERR_FNAME )
  454. printf("failed (internal error)\n\n");
  455. else if ( errcode == ERR_FFILE )
  456. printf("failed (file not found)\n\n");
  457. else if ( errcode == ERR_FREAD)
  458. printf("failed (read error)\n\n");
  459. else
  460. printf("failed (uknown error)\n\n");
  461.  
  462. return FALSE;
  463. }
  464.  
  465. // ---------------------------------------------------------------------------------
  466. // Handle the "reset game" command: r
  467. //
  468. Bool do_resetgame( int nrows, int ncols, GameStatus *game )
  469. {
  470. char inbuf[MAXINBUF] = "";
  471.  
  472. printf("reset game will erase all moves & cell-values, please confirm (y/) ");
  473. get_command(inbuf, MAXINBUF);
  474. if ( !*inbuf || *inbuf != 'y' ) {
  475. puts("\n\treset game aborted by the user\n");
  476. return FALSE;
  477. }
  478.  
  479. reset_game( nrows, ncols, game );
  480. puts("\n\tgame was reset\n");
  481.  
  482. return TRUE;
  483. }
  484.  
  485. // ---------------------------------------------------------------------------------
  486. // Handle non-numerical commands
  487. //
  488. void exec_charcmd( char c, int nrows, int ncols, GameStatus *game)
  489. {
  490. switch ( c )
  491. {
  492. case 'x':
  493. case '\0':
  494. break;
  495.  
  496. case 'h':
  497. do_help();
  498. break;
  499.  
  500. case 't':
  501. do_stats( game );
  502. break;
  503.  
  504. case 'r':
  505. do_resetgame( nrows, ncols, game );
  506. break;
  507. case 's':
  508. do_savegame( game );
  509. break;
  510.  
  511. case 'l':
  512. do_loadgame( game );
  513. break;
  514.  
  515. default:
  516. puts("\a\n\tinvalid command, type h for help\n");
  517. break;
  518. }
  519.  
  520. return;
  521. }
  522.  
  523. // ---------------------------------------------------------------------------------
  524. // Handle numerical commands (expecting them to be board positions)
  525. //
  526. Bool exec_poscmd( char *inbuf, int nrows, int ncols, GameStatus *game)
  527. {
  528. int i, j, pos;
  529.  
  530. pos = atoi( inbuf );
  531. if ( pos < 0 || pos > nrows*ncols-1 ) {
  532. puts("\a\n\tinvalid position\n");
  533. return FALSE;
  534. }
  535.  
  536. i = pos / ncols;
  537. j = pos % ncols;
  538. if ( !game->board[ i ][ j ].avail ) {
  539. puts("\a\n\tyou have already played that position\n");
  540. return FALSE;
  541. }
  542.  
  543. game->board[ i ][ j ].avail = FALSE;
  544. (game->move)++;
  545. (game->points) += game->board[ i ][ j ].val;
  546. printf("\n\t%d points added to your bucket!\n\n", game->board[ i ][ j ].val);
  547.  
  548. return TRUE;
  549. }
  550.  
  551. // ---------------------------------------------------------------------------------
  552. // Calculate and show the final score on the screen
  553. //
  554. void show_finalscore( GameStatus *game )
  555. {
  556. if ( game->points >= game->maxpoints ) {
  557. printf("\t\a\a*** YES, YOU MADE IT! You collected %d points in %d moves(s)\n", game->points, game->move);
  558. game->score = (game->points - game->maxpoints + 1) * (game->maxmoves - game->move + 1);
  559. printf("\t*** YOUR SCORE IS: %d\n", game->score);
  560. }
  561. else {
  562. game->score = 0;
  563. puts("\n\tSORRY, YOU FAILED! NO SCORE THIS TIME!");
  564. }
  565.  
  566. return;
  567. }
  568.  
  569. // ---------------------------------------------------------------------------------
  570. int main( void )
  571. {
  572. char inbuf[MAXINBUF] = ""; // for reading commands
  573. GameStatus game; // initialization is done below
  574.  
  575. srand( time(NULL) ); // init the random generator
  576. // initialize the game (it also
  577. init_game(MAXROWS, MAXCOLS, MAXFNAME, MAXPNAME, &game); // reads the player name)
  578. putchar('\n');
  579.  
  580. do // the main loop of the game
  581. {
  582. // display the board diagrams
  583. show_board( MAXROWS, MAXCOLS, game.board, FALSE );
  584.  
  585. CREDITS(); // display the credits line
  586. PROMPT( game.player, game.move, // display the prompt
  587. game.maxmoves, game.points, game.maxpoints );
  588.  
  589. get_command( inbuf, MAXINBUF ); // get user command
  590. if ( isdigit(*inbuf) ) // handle numerical command
  591. exec_poscmd( inbuf, MAXROWS, MAXCOLS, &game );
  592. else // handle non-numerical command
  593. exec_charcmd( *inbuf, MAXROWS, MAXCOLS, &game );
  594.  
  595. } while( *inbuf != 'x' // game ends either when x
  596. && game.move < game.maxmoves // or run out of moves
  597. && game.points < game.maxpoints ); // or maxpoints have been reached
  598.  
  599. show_finalscore( &game ); // calc & display the final score
  600.  
  601. // reveal all the board cells
  602. puts("\n\thave a look at the full board, before exiting\n");
  603. show_board( MAXROWS, MAXCOLS, game.board, TRUE );
  604. PAUSE();
  605.  
  606. exit( EXIT_SUCCESS );
  607. }
  608.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty