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 */
43 /* for use with the player */
49 typedef char String
[MAX_CLUE_LENGTH
];
51 /* The main puzzle struct type */
53 char chars
[MAX_PUZZLE_SIZE
+1][MAX_PUZZLE_SIZE
+1];
54 int start_across_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
55 int start_down_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
56 String clue_across
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
57 String clue_down
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
60 char hashed_master_password
[256];
61 char hashed_solution_password
[256];
64 /* The player data struct type - for the player app */
69 char char_ans
[MAX_PUZZLE_SIZE
+1][MAX_PUZZLE_SIZE
+1];
72 bool solution_revealed
;
73 enum ORIENTATION current_movement
;
76 /* compute the hash of a password */
77 void digest_message(const unsigned char *message
,
78 size_t message_len
, unsigned char **digest
, unsigned int *digest_len
)
82 if((mdctx
= EVP_MD_CTX_new()) == NULL
)
85 if(1 != EVP_DigestInit_ex(mdctx
, EVP_sha256(), NULL
))
88 if(1 != EVP_DigestUpdate(mdctx
, message
, message_len
))
91 if((*digest
= (unsigned char *)
92 OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL
)
95 if(1 != EVP_DigestFinal_ex(mdctx
, *digest
, digest_len
))
98 EVP_MD_CTX_free(mdctx
);
101 EVP_MD_CTX_free(mdctx
);
102 ERR_print_errors_fp(stderr
);
106 /* encode the binary data to readable text format using OpenSSL - also call
107 OPENSSL_free if the binary data was allocated by OpenSSL */
108 void encode_binary (char *encoded
, unsigned char *binary_data
, unsigned int len
,
109 bool free_openssl_data
)
112 EVP_EncodeBlock ((unsigned char*)encoded
,
113 (const unsigned char*)binary_data
, len
);
114 if (free_openssl_data
)
115 OPENSSL_free (binary_data
);
118 /* decode the binary data from the textual representation using OpenSSL */
119 void decode_binary (char *bin_data
, char *encoded
)
121 EVP_DecodeBlock ((unsigned char*)bin_data
,
122 (const unsigned char*)encoded
, strlen (encoded
));
125 /* encrypt a block of text using password/passphrase with OpenSSL and
126 also encode it to textual representation */
127 void encrypt_data (char *enc_data
, const char *data
, const char *password
)
131 ctx
= EVP_CIPHER_CTX_new ();
134 unsigned char encrypted
[256] = { '\0' };
136 unsigned char key
[EVP_MAX_KEY_LENGTH
] = { '\0'};
137 unsigned char iv
[EVP_MAX_IV_LENGTH
] = { '\0' };
139 if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL
,
140 (unsigned char*)password
, strlen(password
),
144 if (1 != EVP_EncryptInit_ex (ctx
, EVP_aes_256_cbc(), NULL
, key
, iv
))
147 if (1 != EVP_EncryptUpdate (ctx
, (unsigned char*) encrypted
, &len
,
148 (unsigned char*) data
, strlen (data
) ))
152 if (1 != EVP_EncryptFinal_ex (ctx
, encrypted
+ len
, &len
))
156 EVP_CIPHER_CTX_free (ctx
);
158 EVP_EncodeBlock ((unsigned char*) enc_data
, (unsigned char*) encrypted
,
162 ERR_print_errors_fp (stderr
);
163 EVP_CIPHER_CTX_free (ctx
);
167 /* decrypt a block of text using password/passphrase with OpenSSL */
168 void decrypt_data (char *dec_data
, const char *data
, const char *password
)
172 ctx
= EVP_CIPHER_CTX_new ();
175 char enc_data
[256] = { '\0' };
177 unsigned char key
[EVP_MAX_KEY_LENGTH
] = { '\0'};
178 unsigned char iv
[EVP_MAX_IV_LENGTH
] = { '\0' };
181 if (! EVP_BytesToKey (EVP_aes_256_cbc(), EVP_md5(), NULL
,
182 (unsigned char*)password
, strlen(password
),
186 int r
= EVP_DecodeBlock ((unsigned char*)enc_data
,
187 (const unsigned char*) data
, strlen (data
));
192 if (1 != EVP_DecryptInit_ex (ctx
, EVP_aes_256_cbc(), NULL
, key
, iv
))
195 if (1 != EVP_DecryptUpdate (ctx
, (unsigned char*) dec_data
, &len
,
196 (unsigned char*) enc_data
, r
- (r
% 16) ))
200 if (1 != EVP_DecryptFinal_ex (ctx
, (unsigned char *)dec_data
+ len
, &len
))
204 EVP_CIPHER_CTX_free (ctx
);
206 dec_data
[text_len
] = '\0';
210 ERR_print_errors_fp (stderr
);
211 EVP_CIPHER_CTX_free (ctx
);
215 /* get a number from the user */
224 /* verify solution password */
225 bool verify_solution_password (Puzzle
*p
, const char* password
)
227 /* no password set */
228 if (strcmp (p
->hashed_solution_password
, "\0") == 0)
231 /* hash the user input password and compare it with the stored password */
232 unsigned char* hashed_sol_password
;
234 digest_message ((const unsigned char *)password
, strlen(password
),
235 &hashed_sol_password
, &len
);
236 char hashed_hex_pwd
[256] = { '\0' };
237 encode_binary (hashed_hex_pwd
, hashed_sol_password
, len
, true);
239 if (strcmp (p
->hashed_solution_password
, hashed_hex_pwd
) == 0)
246 /* verify master password */
247 bool verify_master_password (Puzzle
*p
, const char* password
)
249 /* no password set */
250 if (strcmp (p
->hashed_master_password
, "\0") == 0)
253 /* hash the user input password and compare it with the stored password */
254 unsigned char* hashed_mas_password
;
256 digest_message ((const unsigned char *)password
, strlen(password
),
257 &hashed_mas_password
, &len
);
258 char hashed_hex_pwd
[256] = { '\0' };
259 encode_binary (hashed_hex_pwd
, hashed_mas_password
, len
, true);
261 if (strcmp (p
->hashed_master_password
, hashed_hex_pwd
) == 0)
267 /* Set or reset solution password for puzzle */
268 void set_solution_password (Puzzle
*p
, const char *password
)
270 /* if it is a null string, reset the password */
271 if (strcmp (password
, "\0") == 0)
272 strcpy (p
->hashed_solution_password
, "\0");
276 unsigned char* hashedpwd
;
278 digest_message ((const unsigned char *)password
, strlen(password
),
280 /* the hashedpwd contains binary data - we will convert it to
281 hexadecimal data and store in file */
283 encode_binary (p
->hashed_solution_password
, hashedpwd
, len
, true);
287 /* Set or reset master password for puzzle */
288 void set_master_password (Puzzle
*p
, const char *password
)
290 /* if it is a null string, reset the password */
291 if (strcmp (password
, "\0") == 0)
292 strcpy (p
->hashed_master_password
, "\0");
296 unsigned char* hashedpwd
;
298 digest_message ((const unsigned char *)password
, strlen(password
),
300 /* the hashedpwd contains binary data - we will convert it to
301 hexadecimal data and store in file */
303 encode_binary (p
->hashed_master_password
, hashedpwd
, len
, true);
307 /* Output the clues to text file */
308 void export_clues (Puzzle
*p
, const char *filename
)
310 FILE *outfile
= fopen (filename
, "w");
313 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
316 /* first the across clues */
317 fprintf (outfile
, "ACROSS CLUES\n");
318 for (int i
= 0; i
< p
->grid_size
; i
++)
320 for (int j
= 0; j
< p
->grid_size
; j
++)
322 if (p
->start_across_word
[i
][j
] != -1)
323 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
324 p
->clue_across
[i
][j
]);
327 /* now the down clues */
328 fprintf (outfile
, "DOWN CLUES\n");
329 for (int i
= 0; i
< p
->grid_size
; i
++)
331 for (int j
= 0; j
< p
->grid_size
; j
++)
333 if (p
->start_down_word
[i
][j
] != -1)
334 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
341 /* Output the grid to image - if answerkey is true export filled grid */
342 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
344 int img_size
= p
->grid_size
* GRID_PIXELS
;
345 FILE * outfile
= fopen (filename
, "wb");
348 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
352 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
353 gdImageColorAllocate (img
, 255,255,255);
354 int black
= gdImageColorAllocate (img
, 0, 0, 0);
355 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
356 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
357 gdFontPtr lg_fnt
= gdFontGetGiant ();
359 for (int i
= 0; i
< p
->grid_size
; i
++)
361 for (int j
= 0; j
< p
->grid_size
; j
++)
363 /* if it is a block, draw the black square */
364 if (p
->chars
[i
][j
] == '#')
365 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
366 j
*GRID_PIXELS
+GRID_PIXELS
,
367 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
370 /* draw a regular square */
371 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
372 j
*GRID_PIXELS
+GRID_PIXELS
,
373 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
375 /* print the numers, if it is either start across word or
377 if (p
->start_across_word
[i
][j
] != -1 ||
378 p
->start_down_word
[i
][j
] != -1)
380 if (p
->start_across_word
[i
][j
] != -1)
383 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
384 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
386 (unsigned char *)str
, blue
);
391 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
392 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
394 (unsigned char *)str
, blue
);
397 /* if answerkey is true, draw the character in the cell */
400 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
401 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
407 gdImagePng (img
, outfile
);
408 gdImageDestroy (img
);
412 /* Set the terminal colour */
413 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
414 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
417 /* Reset the terminal colour */
418 void reset_color () {
422 /* check if the prev row has a block or not */
423 bool prev_row_block (Puzzle
*p
, int r
, int c
)
427 if (p
->chars
[r
-1][c
] == '#')
432 /* check if the next row has a block or not */
433 bool next_row_block (Puzzle
*p
, int r
, int c
)
435 if (r
== p
->grid_size
-1)
437 if (p
->chars
[r
+1][c
] == '#')
442 /* check if the prev col has a block or not */
443 bool prev_col_block (Puzzle
*p
, int r
, int c
)
447 if (p
->chars
[r
][c
-1] == '#')
452 /* check if the next col has a block or not */
453 bool next_col_block (Puzzle
*p
, int r
, int c
)
455 if (c
== p
->grid_size
- 1)
457 if (p
->chars
[r
][c
+1] == '#')
462 /* check if previous row is blank or not */
463 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
465 if (r
== 0) return true;
466 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
469 /* check if next row is blank or not */
470 bool next_row_blank (Puzzle
*p
, int r
, int c
)
472 if (r
== p
->grid_size
- 1) return true;
473 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
476 /* check if previous col is blank or not */
477 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
479 if (c
== 0) return true;
480 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
483 /* check if the next col is blank or not */
484 bool next_col_blank (Puzzle
*p
, int r
, int c
)
486 if (c
== p
->grid_size
-1) return true;
487 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
491 /* set the current row/col to the beginning of word index (across or down) */
492 void set_selection_to_word_start (MainPlayerData
*app_data
,
493 enum ORIENTATION orient
, int word_index
)
495 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
497 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
499 if (orient
== ACROSS
&&
500 app_data
->puzzle
.start_across_word
[i
][j
] == word_index
)
502 app_data
->current_movement
= ACROSS
;
503 app_data
->cur_row
= i
;
504 app_data
->cur_col
= j
;
507 else if (orient
== DOWN
&&
508 app_data
->puzzle
.start_down_word
[i
][j
] == word_index
)
510 app_data
->current_movement
= DOWN
;
511 app_data
->cur_row
= i
;
512 app_data
->cur_col
= j
;
519 /* unfreeze the grid - make editing possible to change words */
520 void unfreeze_puzzle (Puzzle
*p
)
522 for (int i
= 0; i
< p
->grid_size
; i
++)
524 for (int j
= 0; j
< p
->grid_size
; j
++)
526 if (p
->chars
[i
][j
] == '#')
527 p
->chars
[i
][j
] = ' ';
529 p
->start_across_word
[i
][j
] = -1;
530 p
->start_down_word
[i
][j
] = -1;
533 p
->grid_frozen
= false;
536 /* freeze the grid - make editing impossible because it finalizes the
537 across and down words in the grid */
538 void freeze_puzzle (Puzzle
*p
)
541 bool across_word_start
, down_word_start
;
542 for (int i
= 0; i
< p
->grid_size
; i
++)
544 for (int j
= 0; j
< p
->grid_size
; j
++)
546 across_word_start
= false;
547 down_word_start
= false;
548 /* if it is a blank cell - cover it with a block */
549 if (p
->chars
[i
][j
] == ' ' || p
->chars
[i
][j
] == '#')
550 p
->chars
[i
][j
] = '#';
551 /* it is not a blank cell - check all possibilities */
554 bool prev_row
= prev_row_blank (p
, i
, j
);
555 bool next_row
= next_row_blank (p
, i
, j
);
556 bool prev_col
= prev_col_blank (p
, i
, j
);
557 bool next_col
= next_col_blank (p
, i
, j
);
558 if (prev_row
&& ! next_row
)
559 down_word_start
= true;
560 if (prev_col
&& ! next_col
)
561 across_word_start
= true;
564 if (across_word_start
== true)
565 p
->start_across_word
[i
][j
] = word_num
;
567 p
->start_across_word
[i
][j
] = -1;
568 if (down_word_start
== true)
569 p
->start_down_word
[i
][j
] = word_num
;
571 p
->start_down_word
[i
][j
] = -1;
572 if (across_word_start
== true || down_word_start
== true)
576 p
->grid_frozen
= true;
579 /* reset the entire grid */
580 void init_puzzle (Puzzle
*p
, int grid_size
)
582 p
->grid_size
= grid_size
;
583 p
->grid_frozen
= false;
584 for (int i
= 0; i
< p
->grid_size
; i
++)
586 for (int j
= 0; j
< p
->grid_size
; j
++)
588 p
->chars
[i
][j
] = ' ';
589 p
->start_across_word
[i
][j
] = -1;
590 p
->start_down_word
[i
][j
] = -1;
591 strcpy (p
->clue_across
[i
][j
], "");
592 strcpy (p
->clue_down
[i
][j
], "");
595 strcpy (p
->hashed_master_password
, "\0");
596 strcpy (p
->hashed_solution_password
, "\0");
600 /* save the puzzle to a file */
601 void save_puzzle (Puzzle
*puzzle
, const char* file
)
604 /* First output the uncompressed contents to a temp file */
605 outfile
= tmpfile ();
608 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
611 /* grid size is the first field */
612 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
613 /* whether grid is frozen or not */
614 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
615 /* the hashed password */
616 fprintf (outfile
, "%s\n", puzzle
->hashed_master_password
);
617 /* the hashed_solution_password */
618 fprintf (outfile
, "%s\n", puzzle
->hashed_solution_password
);
620 /* First output the grid characters columns/rows as encrypted */
621 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
623 char encrypted
[256] = { '\0' };
624 encrypt_data (encrypted
, puzzle
->chars
[i
],
625 puzzle
->hashed_master_password
);
627 fprintf (outfile
, "%s", encrypted
);
628 fprintf (outfile
, "\n");
631 /* Next output the start across/down numbers */
632 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
634 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
636 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
637 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
639 fprintf (outfile
, "\n");
642 /* Output the across clues */
643 fprintf (outfile
, "ACROSS\n");
644 /* Search the grid for across words */
645 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
647 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
649 /* if it is an across word, then put the word index followed by
650 tab character (as separator) and the clue */
651 if (puzzle
->start_across_word
[i
][j
] != -1)
652 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
653 puzzle
->clue_across
[i
][j
]);
657 /* Output the down clues */
658 fprintf (outfile
, "DOWN\n");
659 /* Search the grid for down words */
660 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
662 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
664 /* same as across word, put the word index followed by the tab
665 character and then the clue */
666 if (puzzle
->start_down_word
[i
][j
] != -1)
667 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
668 puzzle
->clue_down
[i
][j
]);
672 /* Flush the buffer and rewind to beginning - to read and save into
673 gzip compressed file */
675 fseek (outfile
, 0, 0);
677 /* now compress the file and save it to destination file */
678 gzFile outdestfile
= gzopen (file
, "wb");
679 if (outdestfile
== NULL
)
681 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
686 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
689 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
692 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
696 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
698 gzclose (outdestfile
);
703 /* read the puzzle from a file */
704 Puzzle
load_puzzle (const char* file
)
707 /* First open the GZip file */
708 gzFile insourcefile
= gzopen (file
, "rb");
709 if (insourcefile
== NULL
)
711 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
712 /* return an invalid puzzle */
716 /* Open a temporary file to uncompress the contents */
717 FILE *infile
= tmpfile ();
720 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
724 /* Put the uncompressed content to the temp file */
727 num
= gzread (insourcefile
, buf
, 128);
730 int res
= fwrite (buf
, 1, num
, infile
);
733 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
735 gzclose (insourcefile
);
736 /* return an invalid puzzle */
740 num
= gzread (insourcefile
, buf
, 128);
742 /* Close the gzip file */
743 gzclose (insourcefile
);
744 /* Flush the temp file buffer and rewind to beginning */
746 fseek (infile
, 0, 0);
748 /* Read the temporary file contents to the structure Puzzle */
749 char line
[MAX_CLUE_LENGTH
+10];
750 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
751 p
.grid_size
= atoi (line
);
752 /* if puzzle is invalid or otherwise not proper grid, return an invalid
754 if (p
.grid_size
== 0)
756 fprintf (stderr
, "%s\n", INVALID_PUZZLE
);
760 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
761 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
762 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
763 if (strlen (line
) != 1)
764 strcpy (p
.hashed_master_password
, strtok (line
, "\n"));
766 strcpy (p
.hashed_master_password
, "\0");
767 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
768 if (strlen (line
) != 1)
769 strcpy (p
.hashed_solution_password
, strtok (line
, "\n"));
771 strcpy (p
.hashed_solution_password
, "\0");
773 /* read each character of the grid */
774 for (int i
= 0; i
< p
.grid_size
; i
++ )
777 fgets (encoded
, MAX_CLUE_LENGTH
+ 10, infile
);
778 decrypt_data (line
, encoded
, p
.hashed_master_password
);
779 for (int j
= 0; j
< p
.grid_size
; j
++)
780 p
.chars
[i
][j
] = line
[j
];
783 /* read the word numbers */
784 for (int i
= 0; i
< p
.grid_size
; i
++)
786 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
787 char *token
= strtok (line
, " ");
788 for (int j
= 0; j
< p
.grid_size
; j
++)
791 p
.start_across_word
[i
][j
] = atoi (token
);
792 token
= strtok (NULL
, " ");
794 p
.start_down_word
[i
][j
] = atoi (token
);
795 token
= strtok (NULL
, " ");
799 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
802 char clues
[100][MAX_CLUE_LENGTH
];
805 /* first read the across clues from file */
808 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
809 /* if reached the end of across clues */
810 if (strcmp (line
, "DOWN\n") == 0)
812 word_num
[c
] = atoi (strtok (line
, "\t"));
813 char *cl
= strtok (NULL
, "\n");
815 strcpy (clues
[c
], cl
);
817 strcpy (clues
[c
], "\0");
820 /* set the clue to the correct cell in grid */
821 for (int i
= 0; i
< p
.grid_size
; i
++)
823 for (int j
= 0; j
< p
.grid_size
; j
++)
825 for (int r
= 0; r
< c
; r
++)
826 if (p
.start_across_word
[i
][j
] == word_num
[r
])
827 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
833 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
835 word_num
[c
] = atoi (strtok (line
, "\t"));
836 char* cl
= strtok (NULL
, "\n");
838 strcpy (clues
[c
], cl
);
840 strcpy (clues
[c
], "\0");
843 for (int i
= 0; i
< p
.grid_size
; i
++)
845 for (int j
= 0; j
< p
.grid_size
; j
++)
847 for (int r
= 0; r
< c
; r
++)
848 if (p
.start_down_word
[i
][j
] == word_num
[r
])
849 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
857 /* display the puzzle */
858 void print_puzzle (Puzzle
*p
)
861 set_color (WHITE
, CYAN
, NORMAL
);
863 for (int i
= 0; i
< p
->grid_size
; i
++)
867 for (int i
= 0; i
< p
->grid_size
; i
++)
869 set_color (WHITE
, CYAN
, NORMAL
);
871 for (int j
= 0; j
< p
->grid_size
; j
++)
873 if (p
->chars
[i
][j
] == '#') {
874 set_color (WHITE
, BLACK
, NORMAL
);
879 if (p
->start_across_word
[i
][j
] != -1 ||
880 p
->start_down_word
[i
][j
] != -1)
882 set_color (BLUE
, WHITE
, NORMAL
);
883 if (p
->start_across_word
[i
][j
] != -1)
884 printf ("%-2d", p
->start_across_word
[i
][j
]);
886 printf ("%-2d", p
->start_down_word
[i
][j
]);
890 set_color (BLACK
, WHITE
,NORMAL
);
894 set_color (BLACK
, WHITE
, BOLD
);
895 printf ("%c", p
->chars
[i
][j
]);
901 /* print the clues if set */
902 if (p
->grid_frozen
== true)
904 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
905 for (int i
= 0; i
< p
->grid_size
; i
++)
907 for (int j
= 0; j
< p
->grid_size
; j
++)
909 if (p
->start_across_word
[i
][j
] != -1)
911 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
912 p
->clue_across
[i
][j
]);
916 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
917 for (int i
= 0; i
< p
->grid_size
; i
++)
919 for (int j
= 0; j
< p
->grid_size
; j
++)
921 if (p
->start_down_word
[i
][j
] != -1)
923 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
932 /* function to check if a word is valid or not */
933 char* is_valid_word (char *word
)
935 if (word
== NULL
|| strlen(word
) == 0)
937 for (int i
= 0; i
< strlen (word
) - 1; i
++)
938 if (! isalpha (word
[i
]))
941 return strtok (word
, "\n");
945 /* function to set a clue for an across word */
946 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
948 for (int i
= 0; i
< p
->grid_size
; i
++)
950 for (int j
= 0; j
< p
->grid_size
; j
++)
954 if (p
->start_across_word
[i
][j
] == index
)
956 strcpy (p
->clue_across
[i
][j
], clue
);
960 else if (order
== DOWN
)
962 if (p
->start_down_word
[i
][j
] == index
)
964 strcpy (p
->clue_down
[i
][j
], clue
);
973 /* function to print a menu */
974 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
975 char **items
, int num_items
, int padding
)
978 printf ("\e[1;1H\e[2J");
979 set_color (fg
, bg
, NORMAL
);
981 for (int i
= 0; i
< padding
; i
++)
984 reset_color (); printf ("\n");
985 set_color (fg
, bg
, NORMAL
);
987 set_color (fg
, bg
, BOLD
);
988 printf ("%-*s", padding
, title
);
990 set_color (fg
, bg
, NORMAL
);
992 reset_color (); printf ("\n");
993 set_color (fg
, bg
, NORMAL
);
995 for (int i
= 0; i
< padding
; i
++)
998 reset_color (); printf ("\n");
999 for (int i
= 0; i
< num_items
; i
++)
1001 set_color (fg
, bg
, NORMAL
);
1002 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
1003 reset_color (); printf ("\n");
1005 set_color (fg
, bg
, NORMAL
);
1007 for (int i
= 0; i
< padding
; i
++)
1010 reset_color (); printf ("\n");
1013 /* reset the player data, loading from the puzzle file */
1014 void reset_player_data (MainPlayerData
*app_data
, const char *filename
)
1016 app_data
->puzzle
= load_puzzle (filename
);
1018 app_data
->is_loaded
= app_data
->puzzle
.grid_frozen
;
1019 app_data
->cur_col
= -1;
1020 app_data
->cur_row
= -1;
1021 app_data
->solution_revealed
= false;
1022 strcpy (app_data
->filename
, filename
);
1023 /* reset the answer keys */
1024 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1025 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1026 app_data
->char_ans
[i
][j
] = ' ';
1030 /* save the user grid to a file */
1031 void save_user_data (MainPlayerData
*app_data
, const char *filename
)
1034 outfile
= fopen (filename
, "wb");
1035 if (outfile
== NULL
)
1037 fprintf (stderr
, ERROR_WRITING_FILE
);
1040 fprintf (outfile
, "%s\n", app_data
->filename
);
1041 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1043 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1044 fprintf (outfile
, "%c", app_data
->char_ans
[i
][j
]);
1045 fprintf (outfile
, "\n");
1051 /* load the user grid from a file */
1052 void load_user_data (MainPlayerData
*app_data
, const char *filename
)
1055 infile
= fopen (filename
, "rb");
1058 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
1062 char puzzle_file_name
[65535];
1063 fgets (puzzle_file_name
, 65535, infile
);
1064 reset_player_data (app_data
, strtok (puzzle_file_name
, "\n"));
1066 char line
[MAX_PUZZLE_SIZE
+10];
1067 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
1069 fgets (line
, MAX_PUZZLE_SIZE
+10, infile
);
1070 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
1071 app_data
->char_ans
[i
][j
] = line
[j
];
1077 /* in the player app, move the current selection index left or right */
1078 void move_current_col (MainPlayerData
*app_data
, enum DIRECTION dir
)
1080 int r
= app_data
->cur_row
;
1081 int c
= app_data
->cur_col
;
1082 if (dir
== DIR_FORWARD
)
1085 while (c
< app_data
->puzzle
.grid_size
)
1087 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1092 if (c
< app_data
->puzzle
.grid_size
)
1093 app_data
->cur_col
= c
;
1100 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1106 app_data
->cur_col
= c
;
1110 /* in the player app move the current selection index up or down */
1111 void move_current_row (MainPlayerData
*app_data
, enum DIRECTION dir
)
1113 int r
= app_data
->cur_row
;
1114 int c
= app_data
->cur_col
;
1115 if (dir
== DIR_FORWARD
)
1118 while (r
< app_data
->puzzle
.grid_size
)
1120 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1125 if (r
< app_data
->puzzle
.grid_size
)
1126 app_data
->cur_row
= r
;
1133 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1139 app_data
->cur_row
= r
;