12 #include <openssl/conf.h>
13 #include <openssl/evp.h>
14 #include <openssl/err.h>
15 #include "constantstrings.h"
17 #define MAX_PUZZLE_SIZE 25
18 #define MAX_CLUE_LENGTH 150
19 #define GRID_PIXELS 37
21 /* Enum to define terminal colours */
33 /* Enum to define terminal attributes */
39 /* Enum to describe current movement orientation in puzzle grid */
45 /* for use with the player */
51 typedef char String
[MAX_CLUE_LENGTH
];
53 /* The main puzzle struct type */
55 char chars
[MAX_PUZZLE_SIZE
+1][MAX_PUZZLE_SIZE
+1];
56 int start_across_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
57 int start_down_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
58 String clue_across
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
59 String clue_down
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
62 char hashed_master_password
[256];
63 char hashed_solution_password
[256];
66 /* The player data struct type - for the player app */
71 char char_ans
[MAX_PUZZLE_SIZE
+1][MAX_PUZZLE_SIZE
+1];
74 bool solution_revealed
;
75 enum ORIENTATION current_movement
;
78 /* compute the hash of a password */
79 void digest_message(const unsigned char *message
,
80 size_t message_len
, unsigned char **digest
, unsigned int *digest_len
)
84 if((mdctx
= EVP_MD_CTX_new()) == NULL
)
87 if(1 != EVP_DigestInit_ex(mdctx
, EVP_sha256(), NULL
))
90 if(1 != EVP_DigestUpdate(mdctx
, message
, message_len
))
93 if((*digest
= (unsigned char *)
94 OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL
)
97 if(1 != EVP_DigestFinal_ex(mdctx
, *digest
, digest_len
))
100 EVP_MD_CTX_free(mdctx
);
103 EVP_MD_CTX_free(mdctx
);
104 ERR_print_errors_fp(stderr
);
108 /* encode the binary data to readable text format using OpenSSL - also call
109 OPENSSL_free if the binary data was allocated by OpenSSL */
110 void encode_binary (char *encoded
, unsigned char *binary_data
, unsigned int len
,
111 bool free_openssl_data
)
114 EVP_EncodeBlock ((unsigned char*)encoded
,
115 (const unsigned char*)binary_data
, len
);
116 if (free_openssl_data
)
117 OPENSSL_free (binary_data
);
120 /* decode the binary data from the textual representation using OpenSSL */
121 void decode_binary (char *bin_data
, char *encoded
)
123 EVP_DecodeBlock ((unsigned char*)bin_data
,
124 (const unsigned char*)encoded
, strlen (encoded
));
127 /* encrypt a block of text using password/passphrase with OpenSSL and
128 also encode it to textual representation */
129 void encrypt_data (char *enc_data
, const char *data
, const char *password
)
133 ctx
= EVP_CIPHER_CTX_new ();
136 unsigned char encrypted
[256] = { '\0' };
138 unsigned char key
[EVP_MAX_KEY_LENGTH
] = { '\0'};
139 unsigned char iv
[EVP_MAX_IV_LENGTH
] = { '\0' };
141 if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL
,
142 (unsigned char*)password
, strlen(password
),
146 if (1 != EVP_EncryptInit_ex (ctx
, EVP_aes_256_cbc(), NULL
, key
, iv
))
149 if (1 != EVP_EncryptUpdate (ctx
, (unsigned char*) encrypted
, &len
,
150 (unsigned char*) data
, strlen (data
) ))
154 if (1 != EVP_EncryptFinal_ex (ctx
, encrypted
+ len
, &len
))
158 EVP_CIPHER_CTX_free (ctx
);
160 EVP_EncodeBlock ((unsigned char*) enc_data
, (unsigned char*) encrypted
,
164 ERR_print_errors_fp (stderr
);
165 EVP_CIPHER_CTX_free (ctx
);
169 /* decrypt a block of text using password/passphrase with OpenSSL */
170 void decrypt_data (char *dec_data
, const char *data
, const char *password
)
174 ctx
= EVP_CIPHER_CTX_new ();
177 char enc_data
[256] = { '\0' };
179 unsigned char key
[EVP_MAX_KEY_LENGTH
] = { '\0'};
180 unsigned char iv
[EVP_MAX_IV_LENGTH
] = { '\0' };
183 if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL
,
184 (unsigned char*)password
, strlen(password
),
188 int r
= EVP_DecodeBlock ((unsigned char*)enc_data
,
189 (const unsigned char*) data
, strlen (data
));
194 if (1 != EVP_DecryptInit_ex (ctx
, EVP_aes_256_cbc(), NULL
, key
, iv
))
197 if (1 != EVP_DecryptUpdate (ctx
, (unsigned char*) dec_data
, &len
,
198 (unsigned char*) enc_data
, r
- (r
% 16) ))
202 if (1 != EVP_DecryptFinal_ex (ctx
, (unsigned char *)dec_data
+ len
, &len
))
206 EVP_CIPHER_CTX_free (ctx
);
208 dec_data
[text_len
] = '\0';
212 ERR_print_errors_fp (stderr
);
213 EVP_CIPHER_CTX_free (ctx
);
217 /* get a number from the user */
226 /* verify solution password */
227 bool verify_solution_password (Puzzle
*p
, const char* password
)
229 /* no password set */
230 if (strcmp (p
->hashed_solution_password
, "\0") == 0)
233 /* hash the user input password and compare it with the stored password */
234 unsigned char* hashed_sol_password
;
236 digest_message ((const unsigned char *)password
, strlen(password
),
237 &hashed_sol_password
, &len
);
238 char hashed_hex_pwd
[256] = { '\0' };
239 encode_binary (hashed_hex_pwd
, hashed_sol_password
, len
, true);
241 if (strcmp (p
->hashed_solution_password
, hashed_hex_pwd
) == 0)
248 /* verify master password */
249 bool verify_master_password (Puzzle
*p
, const char* password
)
251 /* no password set */
252 if (strcmp (p
->hashed_master_password
, "\0") == 0)
255 /* hash the user input password and compare it with the stored password */
256 unsigned char* hashed_mas_password
;
258 digest_message ((const unsigned char *)password
, strlen(password
),
259 &hashed_mas_password
, &len
);
260 char hashed_hex_pwd
[256] = { '\0' };
261 encode_binary (hashed_hex_pwd
, hashed_mas_password
, len
, true);
263 if (strcmp (p
->hashed_master_password
, hashed_hex_pwd
) == 0)
269 /* Set or reset solution password for puzzle */
270 void set_solution_password (Puzzle
*p
, const char *password
)
272 /* if it is a null string, reset the password */
273 if (strcmp (password
, "\0") == 0)
274 strcpy (p
->hashed_solution_password
, "\0");
278 unsigned char* hashedpwd
;
280 digest_message ((const unsigned char *)password
, strlen(password
),
282 /* the hashedpwd contains binary data - we will convert it to
283 hexadecimal data and store in file */
285 encode_binary (p
->hashed_solution_password
, hashedpwd
, len
, true);
289 /* Set or reset master password for puzzle */
290 void set_master_password (Puzzle
*p
, const char *password
)
292 /* if it is a null string, reset the password */
293 if (strcmp (password
, "\0") == 0)
294 strcpy (p
->hashed_master_password
, "\0");
298 unsigned char* hashedpwd
;
300 digest_message ((const unsigned char *)password
, strlen(password
),
302 /* the hashedpwd contains binary data - we will convert it to
303 hexadecimal data and store in file */
305 encode_binary (p
->hashed_master_password
, hashedpwd
, len
, true);
309 /* Output the clues to text file */
310 void export_clues (Puzzle
*p
, const char *filename
)
312 FILE *outfile
= fopen (filename
, "w");
315 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
318 /* first the across clues */
319 fprintf (outfile
, "ACROSS CLUES\n");
320 for (int i
= 0; i
< p
->grid_size
; i
++)
322 for (int j
= 0; j
< p
->grid_size
; j
++)
324 if (p
->start_across_word
[i
][j
] != -1)
325 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
326 p
->clue_across
[i
][j
]);
329 /* now the down clues */
330 fprintf (outfile
, "DOWN CLUES\n");
331 for (int i
= 0; i
< p
->grid_size
; i
++)
333 for (int j
= 0; j
< p
->grid_size
; j
++)
335 if (p
->start_down_word
[i
][j
] != -1)
336 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
343 /* Output the grid to image - if answerkey is true export filled grid */
344 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
346 int img_size
= p
->grid_size
* GRID_PIXELS
;
347 FILE * outfile
= fopen (filename
, "wb");
350 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
354 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
355 gdImageColorAllocate (img
, 255,255,255);
356 int black
= gdImageColorAllocate (img
, 0, 0, 0);
357 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
358 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
359 gdFontPtr lg_fnt
= gdFontGetGiant ();
361 for (int i
= 0; i
< p
->grid_size
; i
++)
363 for (int j
= 0; j
< p
->grid_size
; j
++)
365 /* if it is a block, draw the black square */
366 if (p
->chars
[i
][j
] == '#')
367 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
368 j
*GRID_PIXELS
+GRID_PIXELS
,
369 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
372 /* draw a regular square */
373 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
374 j
*GRID_PIXELS
+GRID_PIXELS
,
375 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
377 /* print the numers, if it is either start across word or
379 if (p
->start_across_word
[i
][j
] != -1 ||
380 p
->start_down_word
[i
][j
] != -1)
382 if (p
->start_across_word
[i
][j
] != -1)
385 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
386 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
388 (unsigned char *)str
, blue
);
393 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
394 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
396 (unsigned char *)str
, blue
);
399 /* if answerkey is true, draw the character in the cell */
402 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
403 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
409 gdImagePng (img
, outfile
);
410 gdImageDestroy (img
);
414 /* Set the terminal colour */
415 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
416 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
419 /* Reset the terminal colour */
420 void reset_color () {
424 /* check if the prev row has a block or not */
425 bool prev_row_block (Puzzle
*p
, int r
, int c
)
429 if (p
->chars
[r
-1][c
] == '#')
434 /* check if the next row has a block or not */
435 bool next_row_block (Puzzle
*p
, int r
, int c
)
437 if (r
== p
->grid_size
-1)
439 if (p
->chars
[r
+1][c
] == '#')
444 /* check if the prev col has a block or not */
445 bool prev_col_block (Puzzle
*p
, int r
, int c
)
449 if (p
->chars
[r
][c
-1] == '#')
454 /* check if the next col has a block or not */
455 bool next_col_block (Puzzle
*p
, int r
, int c
)
457 if (c
== p
->grid_size
- 1)
459 if (p
->chars
[r
][c
+1] == '#')
464 /* check if previous row is blank or not */
465 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
467 if (r
== 0) return true;
468 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
471 /* check if next row is blank or not */
472 bool next_row_blank (Puzzle
*p
, int r
, int c
)
474 if (r
== p
->grid_size
- 1) return true;
475 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
478 /* check if previous col is blank or not */
479 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
481 if (c
== 0) return true;
482 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
485 /* check if the next col is blank or not */
486 bool next_col_blank (Puzzle
*p
, int r
, int c
)
488 if (c
== p
->grid_size
-1) return true;
489 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
493 /* set the current row/col to the beginning of word index (across or down) */
494 void set_selection_to_word_start (MainPlayerData
*app_data
,
495 enum ORIENTATION orient
, int word_index
)
497 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
499 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
501 if (orient
== ACROSS
&&
502 app_data
->puzzle
.start_across_word
[i
][j
] == word_index
)
504 app_data
->current_movement
= ACROSS
;
505 app_data
->cur_row
= i
;
506 app_data
->cur_col
= j
;
509 else if (orient
== DOWN
&&
510 app_data
->puzzle
.start_down_word
[i
][j
] == word_index
)
512 app_data
->current_movement
= DOWN
;
513 app_data
->cur_row
= i
;
514 app_data
->cur_col
= j
;
521 /* unfreeze the grid - make editing possible to change words */
522 void unfreeze_puzzle (Puzzle
*p
)
524 for (int i
= 0; i
< p
->grid_size
; i
++)
526 for (int j
= 0; j
< p
->grid_size
; j
++)
528 if (p
->chars
[i
][j
] == '#')
529 p
->chars
[i
][j
] = ' ';
531 p
->start_across_word
[i
][j
] = -1;
532 p
->start_down_word
[i
][j
] = -1;
535 p
->grid_frozen
= false;
538 /* freeze the grid - make editing impossible because it finalizes the
539 across and down words in the grid */
540 void freeze_puzzle (Puzzle
*p
)
543 bool across_word_start
, down_word_start
;
544 for (int i
= 0; i
< p
->grid_size
; i
++)
546 for (int j
= 0; j
< p
->grid_size
; j
++)
548 across_word_start
= false;
549 down_word_start
= false;
550 /* if it is a blank cell - cover it with a block */
551 if (p
->chars
[i
][j
] == ' ' || p
->chars
[i
][j
] == '#')
552 p
->chars
[i
][j
] = '#';
553 /* it is not a blank cell - check all possibilities */
556 bool prev_row
= prev_row_blank (p
, i
, j
);
557 bool next_row
= next_row_blank (p
, i
, j
);
558 bool prev_col
= prev_col_blank (p
, i
, j
);
559 bool next_col
= next_col_blank (p
, i
, j
);
560 if (prev_row
&& ! next_row
)
561 down_word_start
= true;
562 if (prev_col
&& ! next_col
)
563 across_word_start
= true;
566 if (across_word_start
== true)
567 p
->start_across_word
[i
][j
] = word_num
;
569 p
->start_across_word
[i
][j
] = -1;
570 if (down_word_start
== true)
571 p
->start_down_word
[i
][j
] = word_num
;
573 p
->start_down_word
[i
][j
] = -1;
574 if (across_word_start
== true || down_word_start
== true)
578 p
->grid_frozen
= true;
581 /* reset the entire grid */
582 void init_puzzle (Puzzle
*p
, int grid_size
)
584 /* check for bounds */
585 if (p
->grid_size
> MAX_PUZZLE_SIZE
)
586 p
->grid_size
= MAX_PUZZLE_SIZE
;
588 p
->grid_size
= grid_size
;
590 /* grid is always unfrozen for a new puzzle */
591 p
->grid_frozen
= false;
593 /* initialize all the puzzle data - characters, start of words (across/down)
594 and the clues to null */
595 for (int i
= 0; i
< p
->grid_size
; i
++)
597 for (int j
= 0; j
< p
->grid_size
; j
++)
599 p
->chars
[i
][j
] = ' ';
600 p
->start_across_word
[i
][j
] = -1;
601 p
->start_down_word
[i
][j
] = -1;
602 strcpy (p
->clue_across
[i
][j
], "\0");
603 strcpy (p
->clue_down
[i
][j
], "\0");
606 /* reset the master password and solution password */
607 strcpy (p
->hashed_master_password
, "\0");
608 strcpy (p
->hashed_solution_password
, "\0");
612 /* save the puzzle to a file */
613 void save_puzzle (Puzzle
*puzzle
, const char* file
)
616 /* First output the uncompressed contents to a temp file */
617 outfile
= tmpfile ();
620 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
623 /* grid size is the first field */
624 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
625 /* whether grid is frozen or not */
626 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
627 /* the hashed password */
628 fprintf (outfile
, "%s\n", puzzle
->hashed_master_password
);
629 /* the hashed_solution_password */
630 fprintf (outfile
, "%s\n", puzzle
->hashed_solution_password
);
632 /* First output the grid characters columns/rows as encrypted */
633 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
635 char encrypted
[256] = { '\0' };
636 /* encrypt the grid characters at row i with master password to
637 generate the key and iv */
638 encrypt_data (encrypted
, puzzle
->chars
[i
],
639 puzzle
->hashed_master_password
);
641 fprintf (outfile
, "%s", encrypted
);
642 fprintf (outfile
, "\n");
645 /* Next output the start across/down numbers */
646 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
648 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
650 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
651 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
653 fprintf (outfile
, "\n");
656 /* Output the across clues */
657 fprintf (outfile
, "ACROSS\n");
658 /* Search the grid for across words */
659 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
661 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
663 /* if it is an across word, then put the word index followed by
664 tab character (as separator) and the clue */
665 if (puzzle
->start_across_word
[i
][j
] != -1)
666 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
667 puzzle
->clue_across
[i
][j
]);
671 /* Output the down clues */
672 fprintf (outfile
, "DOWN\n");
673 /* Search the grid for down words */
674 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
676 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
678 /* same as across word, put the word index followed by the tab
679 character and then the clue */
680 if (puzzle
->start_down_word
[i
][j
] != -1)
681 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
682 puzzle
->clue_down
[i
][j
]);
686 /* Flush the buffer and rewind to beginning - to read and save into
687 gzip compressed file */
689 fseek (outfile
, 0, 0);
691 /* now compress the file and save it to destination file */
692 gzFile outdestfile
= gzopen (file
, "wb");
693 if (outdestfile
== NULL
)
695 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
700 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
703 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
706 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
710 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
712 gzclose (outdestfile
);
717 /* read the puzzle from a file */
718 Puzzle
load_puzzle (const char* file
)
721 /* First open the GZip file */
722 gzFile insourcefile
= gzopen (file
, "rb");
723 if (insourcefile
== NULL
)
725 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
726 /* return an invalid puzzle */
730 /* Open a temporary file to uncompress the contents */
731 FILE *infile
= tmpfile ();
734 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
738 /* Put the uncompressed content to the temp file */
741 num
= gzread (insourcefile
, buf
, 128);
744 int res
= fwrite (buf
, 1, num
, infile
);
747 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
749 gzclose (insourcefile
);
750 /* return an invalid puzzle */
754 num
= gzread (insourcefile
, buf
, 128);
756 /* Close the gzip file */
757 gzclose (insourcefile
);
758 /* Flush the temp file buffer and rewind to beginning */
760 fseek (infile
, 0, 0);
762 /* Read the temporary file contents to the structure Puzzle */
763 char line
[MAX_CLUE_LENGTH
+10];
764 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
765 p
.grid_size
= atoi (line
);
766 /* if puzzle is invalid or otherwise not proper grid, return an invalid
768 if (p
.grid_size
== 0)
770 fprintf (stderr
, "%s\n", INVALID_PUZZLE
);
774 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
775 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
776 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
777 if (strlen (line
) != 1)
778 strcpy (p
.hashed_master_password
, strtok (line
, "\n"));
780 strcpy (p
.hashed_master_password
, "\0");
781 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
782 if (strlen (line
) != 1)
783 strcpy (p
.hashed_solution_password
, strtok (line
, "\n"));
785 strcpy (p
.hashed_solution_password
, "\0");
787 /* read each character of the grid */
788 for (int i
= 0; i
< p
.grid_size
; i
++ )
791 /* get a line from the file - each line is a grid row */
792 fgets (encrypted
, MAX_CLUE_LENGTH
+ 10, infile
);
793 /* decrypt each line from the file and put the decrypted chars
794 into the grid array */
795 decrypt_data (line
, encrypted
, p
.hashed_master_password
);
796 /* finally read the decrypted data into the array */
797 for (int j
= 0; j
< p
.grid_size
; j
++)
798 p
.chars
[i
][j
] = line
[j
];
801 /* read the word numbers */
802 for (int i
= 0; i
< p
.grid_size
; i
++)
804 /* get a line from the file - each file represents a row */
805 /* the word numbers are started as n1<space>n2 where n1 is
806 the across word number and n2 is the down word number.
807 Though both across and down word numbers will be the same
808 in a given cell, we use separate number to determine whether
809 there is an across or down word or both in a given cell. */
810 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
811 /* split the line into tokens with space as the separating character */
812 char *token
= strtok (line
, " ");
813 for (int j
= 0; j
< p
.grid_size
; j
++)
815 /* so long as token is valid, read the first token as across
818 p
.start_across_word
[i
][j
] = atoi (token
);
819 /* similarly get the next token as the down word number */
820 token
= strtok (NULL
, " ");
822 p
.start_down_word
[i
][j
] = atoi (token
);
823 /* get the next token, it should be the across word number format
824 the next cell or NULL if we have read all the tokens */
825 token
= strtok (NULL
, " ");
829 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
832 char clues
[100][MAX_CLUE_LENGTH
];
835 /* first read the across clues from file */
838 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
839 /* the word DOWN indicates that we reached the end of across clues */
840 if (strcmp (line
, "DOWN\n") == 0)
842 word_num
[c
] = atoi (strtok (line
, "\t"));
843 char *cl
= strtok (NULL
, "\n");
845 strcpy (clues
[c
], cl
);
847 strcpy (clues
[c
], "\0");
850 /* set the clue to the correct cell in grid */
851 for (int i
= 0; i
< p
.grid_size
; i
++)
853 for (int j
= 0; j
< p
.grid_size
; j
++)
855 for (int r
= 0; r
< c
; r
++)
856 if (p
.start_across_word
[i
][j
] == word_num
[r
])
857 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
863 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
865 word_num
[c
] = atoi (strtok (line
, "\t"));
866 char* cl
= strtok (NULL
, "\n");
868 strcpy (clues
[c
], cl
);
870 strcpy (clues
[c
], "\0");
873 for (int i
= 0; i
< p
.grid_size
; i
++)
875 for (int j
= 0; j
< p
.grid_size
; j
++)
877 for (int r
= 0; r
< c
; r
++)
878 if (p
.start_down_word
[i
][j
] == word_num
[r
])
879 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
887 /* display the puzzle */
888 void print_puzzle (Puzzle
*p
)
891 set_color (WHITE
, CYAN
, NORMAL
);
893 for (int i
= 0; i
< p
->grid_size
; i
++)
897 for (int i
= 0; i
< p
->grid_size
; i
++)
899 set_color (WHITE
, CYAN
, NORMAL
);
901 for (int j
= 0; j
< p
->grid_size
; j
++)
903 if (p
->chars
[i
][j
] == '#') {
904 set_color (WHITE
, BLACK
, NORMAL
);
909 if (p
->start_across_word
[i
][j
] != -1 ||
910 p
->start_down_word
[i
][j
] != -1)
912 set_color (BLUE
, WHITE
, NORMAL
);
913 if (p
->start_across_word
[i
][j
] != -1)
914 printf ("%-2d", p
->start_across_word
[i
][j
]);
916 printf ("%-2d", p
->start_down_word
[i
][j
]);
920 set_color (BLACK
, WHITE
,NORMAL
);
924 set_color (BLACK
, WHITE
, BOLD
);
925 printf ("%c", p
->chars
[i
][j
]);
931 /* print the clues if set */
932 if (p
->grid_frozen
== true)
934 printf ("\x1B[1m%s\x1B[0m\n", ACROSS_CLUES
);
935 for (int i
= 0; i
< p
->grid_size
; i
++)
937 for (int j
= 0; j
< p
->grid_size
; j
++)
939 if (p
->start_across_word
[i
][j
] != -1)
941 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
942 p
->clue_across
[i
][j
]);
946 printf ("\n\x1B[1m%s\x1B[0m\n", DOWN_CLUES
);
947 for (int i
= 0; i
< p
->grid_size
; i
++)
949 for (int j
= 0; j
< p
->grid_size
; j
++)
951 if (p
->start_down_word
[i
][j
] != -1)
953 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
962 /* function to check if a word is valid or not */
963 char* is_valid_word (char *word
)
965 if (word
== NULL
|| strlen(word
) == 0)
967 for (int i
= 0; i
< strlen (word
) - 1; i
++)
968 if (! isalpha (word
[i
]))
971 return strtok (word
, "\n");
975 /* function to set a clue for an across word */
976 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
978 for (int i
= 0; i
< p
->grid_size
; i
++)
980 for (int j
= 0; j
< p
->grid_size
; j
++)
984 if (p
->start_across_word
[i
][j
] == index
)
986 strcpy (p
->clue_across
[i
][j
], clue
);
990 else if (order
== DOWN
)
992 if (p
->start_down_word
[i
][j
] == index
)
994 strcpy (p
->clue_down
[i
][j
], clue
);
1003 /* function to print a menu */
1004 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
1005 char **items
, int num_items
, int padding
)
1008 printf ("\e[1;1H\e[2J");
1009 set_color (fg
, bg
, NORMAL
);
1011 for (int i
= 0; i
< padding
; i
++)
1014 reset_color (); printf ("\n");
1015 set_color (fg
, bg
, NORMAL
);
1017 set_color (fg
, bg
, BOLD
);
1018 printf ("%-*s", padding
, title
);
1020 set_color (fg
, bg
, NORMAL
);
1022 reset_color (); printf ("\n");
1023 set_color (fg
, bg
, NORMAL
);
1025 for (int i
= 0; i
< padding
; i
++)
1028 reset_color (); printf ("\n");
1029 for (int i
= 0; i
< num_items
; i
++)
1031 set_color (fg
, bg
, NORMAL
);
1032 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
1033 reset_color (); printf ("\n");
1035 set_color (fg
, bg
, NORMAL
);
1037 for (int i
= 0; i
< padding
; i
++)
1040 reset_color (); printf ("\n");
1043 /* reset the player data, loading from the puzzle file */
1044 void reset_player_data (MainPlayerData
*app_data
, const char *filename
)
1046 app_data
->puzzle
= load_puzzle (filename
);
1048 app_data
->is_loaded
= app_data
->puzzle
.grid_frozen
;
1049 app_data
->cur_col
= -1;
1050 app_data
->cur_row
= -1;
1051 app_data
->solution_revealed
= false;
1052 strcpy (app_data
->filename
, filename
);
1053 /* reset the answer keys */
1054 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1055 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1056 app_data
->char_ans
[i
][j
] = ' ';
1060 /* save the user grid to a file */
1061 void save_user_data (MainPlayerData
*app_data
, const char *filename
)
1064 outfile
= fopen (filename
, "wb");
1065 if (outfile
== NULL
)
1067 fprintf (stderr
, ERROR_WRITING_FILE
);
1070 fprintf (outfile
, "%s\n", app_data
->filename
);
1071 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1073 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1074 fprintf (outfile
, "%c", app_data
->char_ans
[i
][j
]);
1075 fprintf (outfile
, "\n");
1081 /* load the user grid from a file */
1082 void load_user_data (MainPlayerData
*app_data
, const char *filename
)
1085 infile
= fopen (filename
, "rb");
1088 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
1092 char puzzle_file_name
[65535];
1093 fgets (puzzle_file_name
, 65535, infile
);
1094 reset_player_data (app_data
, strtok (puzzle_file_name
, "\n"));
1096 char line
[MAX_PUZZLE_SIZE
+10];
1097 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1099 fgets (line
, MAX_PUZZLE_SIZE
+10, infile
);
1100 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1101 app_data
->char_ans
[i
][j
] = line
[j
];
1107 /* in the player app, move the current selection index left or right */
1108 void move_current_col (MainPlayerData
*app_data
, enum DIRECTION dir
)
1110 int r
= app_data
->cur_row
;
1111 int c
= app_data
->cur_col
;
1112 if (dir
== DIR_FORWARD
)
1115 while (c
< app_data
->puzzle
.grid_size
)
1117 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1122 if (c
< app_data
->puzzle
.grid_size
)
1123 app_data
->cur_col
= c
;
1130 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1136 app_data
->cur_col
= c
;
1140 /* in the player app move the current selection index up or down */
1141 void move_current_row (MainPlayerData
*app_data
, enum DIRECTION dir
)
1143 int r
= app_data
->cur_row
;
1144 int c
= app_data
->cur_col
;
1145 if (dir
== DIR_FORWARD
)
1148 while (r
< app_data
->puzzle
.grid_size
)
1150 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1155 if (r
< app_data
->puzzle
.grid_size
)
1156 app_data
->cur_row
= r
;
1163 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1169 app_data
->cur_row
= r
;