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