fork(1) download
  1. /* -------------------------------------------------------------------------
  2.  * Title: Hangman v1.0 (bilingual)
  3.  *
  4.  * From: migf1 (mig_f1@hotmail.com)
  5.  *
  6.  * Description:
  7.  * A rather simplistic implementation of the Hangman game, written in C (C99)
  8.  * This is NOT AI! The human player makes the guesses!
  9.  *
  10.  * Notes:
  11.  * The program WILL NOT run without its accompanying world-list files:
  12.  * - en_scrabble.dic: SCRABBLE tournament Englih words (172804 words)
  13.  * - el_ispell_small.dic: small version of iSPELL Greek words (76496 words)
  14.  *
  15.  * To play with Greek words you MUST USE Windows1253 8-BIT FONTS at your terminal
  16.  * AND compile the source code with GCC 4.x
  17.  *
  18.  * Implementation:
  19.  * When the user specifies the desired length for the secret word,
  20.  * the program loads from the dictionary all the words with the specified
  21.  * length into a singly linked-list. It then picks a word randomly and
  22.  * destroys the list.
  23.  *
  24.  * At the guessing prompt, the user may enter either a single letter or
  25.  * a whole word. In the latter case, if the entered word MATCHES the
  26.  * secret word, we have a winner and the moves loop stops. Otherwse the
  27.  * 1st letter of the entered word is checked against the secret letters.
  28.  *
  29.  * The program keeps track of all the letters already typed in by the user
  30.  * and it doesn't let him re-enter them. It also keeps track of the number
  31.  * of letters remained to be found, using it as a condition to decide whether
  32.  * the user has found the secret word or not (missing == 0).
  33.  *
  34.  * In general, all input is validated in real time, keep prompting the user
  35.  * until he enters valid input.
  36.  *
  37.  * You can play an infinite number of rounds (new words of the same length)
  38.  * as long as you answer positively when you are asked for one more round.
  39.  * Each round consists of a user-defined number of moves (9 to 13)
  40.  *
  41.  * -------------------------------------------------------------------------
  42.  */
  43.  
  44. #include <stdio.h>
  45. #include <stdlib.h> // for atoi(), exit()
  46. #include <string.h> // for strlen()
  47. #include <time.h> // for time()
  48. #include <ctype.h> // for toupper(), isblank()
  49.  
  50. #define MAXINPUT 255+1 // max # of chars read from user input
  51.  
  52. #define MINMOVES 9 // minimum allowed guesses
  53. #define MAXMOVES 13 // maximum allowed guesses
  54.  
  55. #define DIC_FNAME_EN "en_scrabble.dic" // filename of english wordlist
  56. #define DIC_FNAME_EL "el_ispell_small.dic" // filename of greek wordlist
  57.  
  58. #define MAX_WLEN 20+1 // maximum allowed lebgth of secret word
  59. #define MIN_WLEN 4+1 // minimum allowed lebgth of secret word
  60.  
  61. // our alphabet of allowed letters
  62. #define ALPHABET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ"
  63.  
  64. #define MIX(x,y) (x) < (y) ? (x) : (y)
  65.  
  66. #define CREDITS() printf("\n%78s", "H A N G M A N 1.0 (by migf1) \n")
  67.  
  68. #define PAUSE() \
  69. do{ \
  70. char myinbufr[255]=""; \
  71. printf("\npress ENTER to continue..."); \
  72. fgets(myinbufr, 255, stdin); \
  73. }while(0)
  74.  
  75. typedef enum { FALSE=0, TRUE } Bool; // our custom boolean type
  76.  
  77. typedef struct node { // node struct for the words linked-list
  78. char word[ MAX_WLEN ];
  79. struct node *next;
  80. } Node;
  81.  
  82. typedef struct { // struct for the words linked-list
  83. Node *head, *tail;
  84. long int len;
  85. } List;
  86.  
  87. /* ------------------------------------------------------------------
  88.  * Read up to len-1 chars from stdin into string s & null terminate it
  89.  */
  90. char *s_get(char *s, size_t len)
  91. {
  92. char *cp;
  93. for (cp=s; (*cp=getc(stdin)) != '\n' && (cp-s) < len-1; cp++ )
  94. ; // for-loop with empty body
  95. *cp = '\0'; // null-terminate
  96.  
  97. return s;
  98. }
  99.  
  100. /* ------------------------------------------------------------------
  101.  * Convert char c to uppercase (explicit care is taken for Greek 8-bit chars)
  102.  */
  103. char to_upper( int c )
  104. {
  105. switch( c )
  106. {
  107. case 'α':
  108. case 'ά':
  109. c = 'Α';
  110. break;
  111. case 'β':
  112. c = 'Β';
  113. break;
  114. case 'γ':
  115. c = 'Γ';
  116. break;
  117. case 'δ':
  118. c = 'Δ';
  119. break;
  120. case 'ε':
  121. case 'έ':
  122. c = 'Ε';
  123. break;
  124. case 'ζ':
  125. c = 'Ζ';
  126. break;
  127. case 'η':
  128. case 'ή':
  129. c = 'Η';
  130. break;
  131. case 'θ':
  132. c = 'Θ';
  133. break;
  134. case 'ι':
  135. case 'ί':
  136. c = 'Ι';
  137. break;
  138. case 'ϊ':
  139. case 'ΐ':
  140. c = 'Ϊ';
  141. break;
  142. case 'κ':
  143. c = 'Κ';
  144. break;
  145. case 'λ':
  146. c = 'Λ';
  147. break;
  148. case 'μ':
  149. c = 'Μ';
  150. break;
  151. case 'ν':
  152. c = 'Ν';
  153. break;
  154. case 'ξ':
  155. c = 'Ξ';
  156. break;
  157. case 'ο':
  158. case 'ό':
  159. c = 'Ο';
  160. break;
  161. case 'π':
  162. c = 'Π';
  163. break;
  164. case 'ρ':
  165. c = 'Ρ';
  166. break;
  167. case 'σ':
  168. case 'ς':
  169. c = 'Σ';
  170. break;
  171. case 'τ':
  172. c = 'Τ';
  173. break;
  174. case 'υ':
  175. case 'ύ':
  176. c = 'Υ';
  177. break;
  178. case 'ϋ':
  179. case 'ΰ':
  180. c = 'Ϋ';
  181. break;
  182. case 'φ':
  183. c = 'Φ';
  184. break;
  185. case 'χ':
  186. c = 'Χ';
  187. break;
  188. case 'ψ':
  189. c = 'Ψ';
  190. break;
  191. case 'ω':
  192. case 'ώ':
  193. c = 'Ω';
  194. break;
  195.  
  196. default:
  197. c = toupper( c );
  198. break;
  199. }
  200.  
  201. return c;
  202. }
  203.  
  204. /* ------------------------------------------------------------------
  205.  * Convert to uppercase all chars of the null-terminated string s
  206.  */
  207. char *s_toupper(char *s)
  208. {
  209. char *ret;
  210.  
  211. for ( ret=s; (*s=to_upper(*s)); s++ )
  212. ;
  213. return ret;
  214. }
  215.  
  216. /* ------------------------------------------------------------------
  217.  * Copy up to n-1 chars of string src into string dst (both strings are null-terminated)
  218.  */
  219. char *s_ncopy( char *dst, const char *src, int n )
  220. {
  221. char *ret = dst;
  222. while ( (dst-ret) < n-1 && (*dst=*src) != '\0' )
  223. dst++, src++;
  224.  
  225. if ( *dst )
  226. *dst = 0;
  227.  
  228. return ret;
  229. }
  230.  
  231. /* ------------------------------------------------------------------
  232.  * Trim leading & trailing spaces from a null-termintated string s
  233.  */
  234. char *s_trim( char *s )
  235. {
  236. char *cp1; // for parsing the whole s
  237. char *cp2; // for shifting & padding
  238.  
  239. // trim leading & shift left remaining
  240. for (cp1=s; isblank(*cp1); cp1++ ) // skip leading spaces, via cp1
  241. ;
  242. for (cp2=s; *cp1; cp1++, cp2++) // shift left remaining chars, via cp2
  243. *cp2 = *cp1;
  244. *cp2-- = '\0'; // mark end of left trimmed s
  245.  
  246. // replace trailing spaces with '\0's
  247. while ( cp2 > s && isblank(*cp2) )
  248. *cp2-- = '\0'; // pad with '\0'
  249.  
  250. return s;
  251. }
  252.  
  253. /* ------------------------------------------------------------------
  254.  * Append char c into null-terminated string s (if there's no room, s is returned intact)
  255.  */
  256. char *s_cappend( char *s, const char c, const int slen )
  257. {
  258. if ( !s )
  259. return NULL;
  260.  
  261. char *cp;
  262. for (cp=s; *cp && (cp-s) < slen-1; cp++)
  263. ;
  264. if ( cp-s < slen-1 ) {
  265. *cp = c;
  266. *++cp = '\0';
  267. }
  268.  
  269. return s;
  270. }
  271.  
  272.  
  273. /* ------------------------------------------------------------------
  274.  * Insert data as LAST node of list
  275.  */
  276. Bool list_append( List *list, char *data )
  277. {
  278. Node *new = calloc(1, sizeof(Node) );
  279. if ( !new )
  280. return FALSE;
  281.  
  282. s_ncopy(new->word, data, MAX_WLEN);
  283. new->next = NULL;
  284.  
  285. if ( list->head == NULL ) {
  286. list->head = list->tail = new;
  287. (list->len)++;
  288. return TRUE;
  289. }
  290.  
  291. list->tail->next = new;
  292. list->tail = new;
  293. (list->len)++;
  294.  
  295. return TRUE;
  296. }
  297.  
  298. /* ------------------------------------------------------------------
  299.  * Return a pointer to the ith node of list
  300.  */
  301. Node *list_getinode( List list, long int i )
  302. {
  303. long int j;
  304. for (j=0; j < i && list.head; j++)
  305. list.head = list.head->next;
  306.  
  307. return list.head;
  308. }
  309.  
  310. /* ------------------------------------------------------------------
  311.  * Print list contents
  312.  */
  313. void list_print( List list )
  314. {
  315. while ( list.head ) {
  316. printf("\t%s\n", list.head->word );
  317. list.head = list.head->next;
  318. }
  319. putchar('\n'); // αλλαγή γραμμής
  320.  
  321. return;
  322. }
  323.  
  324. /* ------------------------------------------------------------------
  325.  * Free memory reserved for list
  326.  */
  327. void list_destroy( List *list )
  328. {
  329. if ( list->head == NULL )
  330. return;
  331.  
  332. Node *dummy = NULL;
  333. while ( list->head ) {
  334. dummy = list->head->next;
  335. free( list->head );
  336. list->head = dummy;
  337. }
  338.  
  339. return;
  340. }
  341.  
  342. /* ------------------------------------------------------------------
  343.  * Helper function, for reading the input buffer, trimming leading & trailing blanks
  344.  * and convert it to uppercase
  345.  */
  346. char *read_input( char *input, const int maxinput )
  347. {
  348. if ( !input )
  349. return NULL;
  350.  
  351. s_get(input, maxinput);
  352. s_trim( s_toupper(input) );
  353.  
  354. return input;
  355. }
  356.  
  357. /* ------------------------------------------------------------------
  358.  * Print the secret word.
  359.  *
  360.  * Already found letters must be contained inside the null-terminated
  361.  * string: found. If so, they get printed at their correct positions
  362.  * inside the secret word, otherwise a dash is printed instead of the letter.
  363.  *
  364.  * IMPORTANT:
  365.  * The function returns the number of letters that have NOT been found
  366.  * yet! We use this information to control the main loop of the game.
  367.  * Specidically, we stop the loop when this number gets equal to 0
  368.  * because that means ALL the letters of the secret word have been found!
  369.  */
  370. int print_secret( char *secret, char *found )
  371. {
  372. register int i, ret=0;
  373. for (i=0; secret[i] != '\0'; i++)
  374. {
  375. if ( strchr(found, secret[i]) ) {
  376. putchar( to_upper(secret[i]) );
  377. ret++;
  378. }
  379. else
  380. putchar('-');
  381. }
  382.  
  383. return i-ret;
  384. }
  385.  
  386. /* ------------------------------------------------------------------
  387.  * Ask the user to choose which dictionary will the program use for pickicng
  388.  * later on the secret word.
  389.  */
  390. void get_dic( char *dic_fname, const char *en_fname, const char *el_fname )
  391. {
  392. char input[MAXINPUT] = "";
  393.  
  394. CREDITS();
  395. printf("%76s\n", "------------------------------");
  396. printf("%70s\n", "en\tEnglish dictionary");
  397. printf("%70s\n", "el\tΕλληνικό λεξιλόγιο");
  398. printf("%76s\n", "------------------------------");
  399. do {
  400. printf("%62s","Select (en/el): ");
  401. read_input(input, MAXINPUT);
  402. }while ( strncmp("EN", input, MAXINPUT) && strncmp("EL", input, MAXINPUT) );
  403. if ( strncmp(input, "EN", MAXINPUT) == 0 )
  404. s_ncopy(dic_fname, en_fname, MAXINPUT);
  405. else
  406. s_ncopy(dic_fname, el_fname, MAXINPUT);
  407. printf( "\n%76s\n",
  408. strncmp(input, "EN", MAXINPUT) == 0 ?
  409. "OFFICIAL SCRABBLE WORD LIST" :
  410. "με χρήση ΕΛΛΗΝΙΚΟΥ λεξιλογίου"
  411. );
  412. if ( strncmp(input, "EL", MAXINPUT) == 0 ) {
  413. printf("%76s\n\n", "γυρίστε το πληκτρολόγιό σας");
  414. printf("%76s\n\n", "Win1253 (8-BIT) FONTS REQUIRED");
  415. }
  416. else
  417. putchar('\n');
  418.  
  419. return;
  420. }
  421.  
  422. /* ------------------------------------------------------------------
  423.  * Ask user for the desired length of the secret word
  424.  */
  425. int get_wlen( const int minwlen, const int maxwlen )
  426. {
  427. int wlen = 0;
  428. char input[MAXINPUT] = "";
  429.  
  430. do {
  431. printf("How many letters (%d - %d)? ", minwlen-1, maxwlen-1);
  432. read_input(input, MAXINPUT);
  433. wlen = atoi( input );
  434. } while (wlen < minwlen-1 || wlen > maxwlen-1 );
  435.  
  436. return wlen;
  437. }
  438.  
  439. /* ------------------------------------------------------------------
  440.  * Ask user for the number of moves he''l be allowed to use for guessing the secret word
  441.  */
  442. int get_maxmoves( const int minturns, int maxturns )
  443. {
  444. int maxmoves = 0;
  445. char input[MAXINPUT] = "";
  446.  
  447. do {
  448. printf("How many moves (%d-%d)? ", minturns, maxturns);
  449. read_input(input, MAXINPUT);
  450. maxmoves = atoi(input);
  451. } while ( maxmoves < minturns || maxmoves > maxturns );
  452.  
  453. return maxmoves;
  454. }
  455.  
  456. /* ------------------------------------------------------------------
  457.  * Open dictionary: dic_fname, load all words of length: lensecret into a
  458.  * linked list, pick randomly a word from that list and save it into the
  459.  * null-terminated string: secret
  460.  */
  461. void pick_word(
  462. char *secret, const int lensecret, const char *dic_fname, const int maxwlen )
  463. {
  464. FILE *fp;
  465. List wlist = { .head=NULL, .tail=NULL, .len=0L };
  466. long int pick = 0L;
  467. char dummy[MAXINPUT] = "";
  468.  
  469. // open the dictionary file
  470. fp = fopen(dic_fname, "rb");
  471. if ( !fp ) {
  472. puts("\t*** error: could not read dictionary");
  473. exit( EXIT_FAILURE );
  474. }
  475.  
  476. // create a list of all words of length lensecret found in the dictionary
  477. while ( !feof(fp) ) {
  478. fscanf(fp, "%s", dummy);
  479. s_toupper(dummy);
  480. if ( strlen(dummy) == lensecret )
  481. list_append( &wlist, dummy );
  482. }
  483. fclose(fp);
  484.  
  485. // a random word fron the list
  486. pick = rand() % wlist.len;
  487. Node *p = list_getinode( wlist, pick );
  488. if ( p )
  489. s_ncopy(secret, p->word, maxwlen);
  490.  
  491. // destroy the list
  492. list_destroy( &wlist );
  493.  
  494. return;
  495. }
  496.  
  497. /* ------------------------------------------------------------------
  498.  * Our main function...
  499.  * secret: null-terminated string for the secret word
  500.  * found: null-terminated string holding letters found correctly by the user
  501.  * played: null-terminated string holding all the letters typed by the user
  502.  * lensecret: length of the secret word (provided by the user)
  503.  * missing: number of letters which have not been found yet
  504.  */
  505. int main( void )
  506. {
  507. char input[ MAXINPUT ] = ""; // our input buffer
  508. char dic_fname[ MAXINPUT ] = ""; // dictionary filename
  509. char secret[ MAX_WLEN ] = "", found[ MAX_WLEN ] = "", played[ MAX_WLEN ] = "";
  510. int round=1, lensecret = 0, move=0, maxmoves=0, missing=0;
  511.  
  512. srand( time(NULL) ); // init random generator
  513.  
  514. get_dic( dic_fname, DIC_FNAME_EN, DIC_FNAME_EL ); // ask for dictionary
  515. lensecret = get_wlen( MIN_WLEN, MAX_WLEN ); // ask for word length
  516. maxmoves = get_maxmoves( MINMOVES, MAXMOVES ); // ask for max moves
  517.  
  518. do { // rounds loop
  519. printf("\n____________________________________________________________________ ROUND %d\n\n", round++);
  520. // pick the secret word
  521. pick_word( secret, lensecret, dic_fname, MAX_WLEN );
  522. move = 0;
  523. do { // moves loop
  524. printf("\n\t");
  525. missing = print_secret(secret, found); // # of missing letters
  526. if ( missing == 0) // no missinng letters
  527. break; // word found
  528.  
  529. printf("\t(%d missing) played: %s\n\n", missing, played);
  530.  
  531. do // ask user for a guess
  532. { // (allow letter or word)
  533. printf("Guess #%d/%d: ", move+1, maxmoves);
  534. read_input(input, MAXINPUT);
  535. // correct word entered
  536. if ( strncmp(input, secret, MAX_WLEN) == 0 )
  537. break;
  538. // check letter validity
  539. } while ( *input == '\0' || !strchr(ALPHABET_UPPER, *input) || strchr(played, *input) );
  540.  
  541. // valid letter was entered
  542. s_cappend( played, *input, MAX_WLEN ); // append it to played
  543. if ( strchr(secret, *input) ) // correct letter...
  544. s_cappend( found, *input, MAX_WLEN );// append to found
  545.  
  546. move++; // increase moves counter
  547.  
  548. } while( strncmp(input,secret,MAX_WLEN) != 0 && move < maxmoves && missing > 0 );
  549.  
  550. printf("\n\tthe word was: %s\n", secret); // reveal secret word
  551.  
  552. printf("\nplay again with another word (/n)? ");// ask for another round
  553. read_input(input, MAXINPUT);
  554. *secret = *found = *played = '\0'; // initializations
  555.  
  556. } while ( *input != 'N' && *input != 'Ο' );
  557.  
  558. exit( EXIT_SUCCESS );
  559. }
  560.  
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty