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
][MAX_PUZZLE_SIZE
];
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
][MAX_PUZZLE_SIZE
];
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 */
107 void encode_binary (char *encoded
, unsigned char *binary_data
, unsigned int len
)
110 EVP_EncodeBlock ((unsigned char*)encoded
,
111 (const unsigned char*)binary_data
, len
);
112 OPENSSL_free (binary_data
);
115 /* get a number from the user */
124 /* verify solution password */
125 bool verify_solution_password (Puzzle
*p
, const char* password
)
127 /* no password set */
128 if (strcmp (p
->hashed_solution_password
, "\0") == 0)
131 /* hash the user input password and compare it with the stored password */
132 unsigned char* hashed_sol_password
;
134 digest_message ((const unsigned char *)password
, strlen(password
),
135 &hashed_sol_password
, &len
);
136 char hashed_hex_pwd
[256] = { '\0' };
137 encode_binary (hashed_hex_pwd
, hashed_sol_password
, len
);
139 if (strcmp (p
->hashed_solution_password
, hashed_hex_pwd
) == 0)
146 /* verify master password */
147 bool verify_master_password (Puzzle
*p
, const char* password
)
149 /* no password set */
150 if (strcmp (p
->hashed_master_password
, "\0") == 0)
153 /* hash the user input password and compare it with the stored password */
154 unsigned char* hashed_mas_password
;
156 digest_message ((const unsigned char *)password
, strlen(password
),
157 &hashed_mas_password
, &len
);
158 char hashed_hex_pwd
[256] = { '\0' };
159 encode_binary (hashed_hex_pwd
, hashed_mas_password
, len
);
161 if (strcmp (p
->hashed_master_password
, hashed_hex_pwd
) == 0)
167 /* Set or reset solution password for puzzle */
168 void set_solution_password (Puzzle
*p
, const char *password
)
170 /* if it is a null string, reset the password */
171 if (strcmp (password
, "\0") == 0)
172 strcpy (p
->hashed_solution_password
, "\0");
176 unsigned char* hashedpwd
;
178 digest_message ((const unsigned char *)password
, strlen(password
),
180 /* the hashedpwd contains binary data - we will convert it to
181 hexadecimal data and store in file */
183 encode_binary (p
->hashed_solution_password
, hashedpwd
, len
);
187 /* Set or reset master password for puzzle */
188 void set_master_password (Puzzle
*p
, const char *password
)
190 /* if it is a null string, reset the password */
191 if (strcmp (password
, "\0") == 0)
192 strcpy (p
->hashed_master_password
, "\0");
196 unsigned char* hashedpwd
;
198 digest_message ((const unsigned char *)password
, strlen(password
),
200 /* the hashedpwd contains binary data - we will convert it to
201 hexadecimal data and store in file */
203 encode_binary (p
->hashed_master_password
, hashedpwd
, len
);
207 /* Output the clues to text file */
208 void export_clues (Puzzle
*p
, const char *filename
)
210 FILE *outfile
= fopen (filename
, "w");
213 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
216 /* first the across clues */
217 fprintf (outfile
, "ACROSS CLUES\n");
218 for (int i
= 0; i
< p
->grid_size
; i
++)
220 for (int j
= 0; j
< p
->grid_size
; j
++)
222 if (p
->start_across_word
[i
][j
] != -1)
223 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
224 p
->clue_across
[i
][j
]);
227 /* now the down clues */
228 fprintf (outfile
, "DOWN CLUES\n");
229 for (int i
= 0; i
< p
->grid_size
; i
++)
231 for (int j
= 0; j
< p
->grid_size
; j
++)
233 if (p
->start_down_word
[i
][j
] != -1)
234 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
241 /* Output the grid to image - if answerkey is true export filled grid */
242 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
244 int img_size
= p
->grid_size
* GRID_PIXELS
;
245 FILE * outfile
= fopen (filename
, "wb");
248 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
252 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
253 gdImageColorAllocate (img
, 255,255,255);
254 int black
= gdImageColorAllocate (img
, 0, 0, 0);
255 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
256 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
257 gdFontPtr lg_fnt
= gdFontGetGiant ();
259 for (int i
= 0; i
< p
->grid_size
; i
++)
261 for (int j
= 0; j
< p
->grid_size
; j
++)
263 /* if it is a block, draw the black square */
264 if (p
->chars
[i
][j
] == '#')
265 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
266 j
*GRID_PIXELS
+GRID_PIXELS
,
267 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
270 /* draw a regular square */
271 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
272 j
*GRID_PIXELS
+GRID_PIXELS
,
273 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
275 /* print the numers, if it is either start across word or
277 if (p
->start_across_word
[i
][j
] != -1 ||
278 p
->start_down_word
[i
][j
] != -1)
280 if (p
->start_across_word
[i
][j
] != -1)
283 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
284 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
286 (unsigned char *)str
, blue
);
291 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
292 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
294 (unsigned char *)str
, blue
);
297 /* if answerkey is true, draw the character in the cell */
300 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
301 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
307 gdImagePng (img
, outfile
);
308 gdImageDestroy (img
);
312 /* Set the terminal colour */
313 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
314 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
317 /* Reset the terminal colour */
318 void reset_color () {
322 /* check if the prev row has a block or not */
323 bool prev_row_block (Puzzle
*p
, int r
, int c
)
327 if (p
->chars
[r
-1][c
] == '#')
332 /* check if the next row has a block or not */
333 bool next_row_block (Puzzle
*p
, int r
, int c
)
335 if (r
== p
->grid_size
-1)
337 if (p
->chars
[r
+1][c
] == '#')
342 /* check if the prev col has a block or not */
343 bool prev_col_block (Puzzle
*p
, int r
, int c
)
347 if (p
->chars
[r
][c
-1] == '#')
352 /* check if the next col has a block or not */
353 bool next_col_block (Puzzle
*p
, int r
, int c
)
355 if (c
== p
->grid_size
- 1)
357 if (p
->chars
[r
][c
+1] == '#')
362 /* check if previous row is blank or not */
363 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
365 if (r
== 0) return true;
366 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
369 /* check if next row is blank or not */
370 bool next_row_blank (Puzzle
*p
, int r
, int c
)
372 if (r
== p
->grid_size
- 1) return true;
373 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
376 /* check if previous col is blank or not */
377 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
379 if (c
== 0) return true;
380 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
383 /* check if the next col is blank or not */
384 bool next_col_blank (Puzzle
*p
, int r
, int c
)
386 if (c
== p
->grid_size
-1) return true;
387 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
391 /* set the current row/col to the beginning of word index (across or down) */
392 void set_selection_to_word_start (MainPlayerData
*app_data
,
393 enum ORIENTATION orient
, int word_index
)
395 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
397 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
399 if (orient
== ACROSS
&&
400 app_data
->puzzle
.start_across_word
[i
][j
] == word_index
)
402 app_data
->current_movement
= ACROSS
;
403 app_data
->cur_row
= i
;
404 app_data
->cur_col
= j
;
407 else if (orient
== DOWN
&&
408 app_data
->puzzle
.start_down_word
[i
][j
] == word_index
)
410 app_data
->current_movement
= DOWN
;
411 app_data
->cur_row
= i
;
412 app_data
->cur_col
= j
;
419 /* unfreeze the grid - make editing possible to change words */
420 void unfreeze_puzzle (Puzzle
*p
)
422 for (int i
= 0; i
< p
->grid_size
; i
++)
424 for (int j
= 0; j
< p
->grid_size
; j
++)
426 if (p
->chars
[i
][j
] == '#')
427 p
->chars
[i
][j
] = ' ';
429 p
->start_across_word
[i
][j
] = -1;
430 p
->start_down_word
[i
][j
] = -1;
433 p
->grid_frozen
= false;
436 /* freeze the grid - make editing impossible because it finalizes the
437 across and down words in the grid */
438 void freeze_puzzle (Puzzle
*p
)
441 bool across_word_start
, down_word_start
;
442 for (int i
= 0; i
< p
->grid_size
; i
++)
444 for (int j
= 0; j
< p
->grid_size
; j
++)
446 across_word_start
= false;
447 down_word_start
= false;
448 /* if it is a blank cell - cover it with a block */
449 if (p
->chars
[i
][j
] == ' ' || p
->chars
[i
][j
] == '#')
450 p
->chars
[i
][j
] = '#';
451 /* it is not a blank cell - check all possibilities */
454 bool prev_row
= prev_row_blank (p
, i
, j
);
455 bool next_row
= next_row_blank (p
, i
, j
);
456 bool prev_col
= prev_col_blank (p
, i
, j
);
457 bool next_col
= next_col_blank (p
, i
, j
);
458 if (prev_row
&& ! next_row
)
459 down_word_start
= true;
460 if (prev_col
&& ! next_col
)
461 across_word_start
= true;
464 if (across_word_start
== true)
465 p
->start_across_word
[i
][j
] = word_num
;
467 p
->start_across_word
[i
][j
] = -1;
468 if (down_word_start
== true)
469 p
->start_down_word
[i
][j
] = word_num
;
471 p
->start_down_word
[i
][j
] = -1;
472 if (across_word_start
== true || down_word_start
== true)
476 p
->grid_frozen
= true;
479 /* reset the entire grid */
480 void init_puzzle (Puzzle
*p
, int grid_size
)
482 p
->grid_size
= grid_size
;
483 p
->grid_frozen
= false;
484 for (int i
= 0; i
< p
->grid_size
; i
++)
486 for (int j
= 0; j
< p
->grid_size
; j
++)
488 p
->chars
[i
][j
] = ' ';
489 p
->start_across_word
[i
][j
] = -1;
490 p
->start_down_word
[i
][j
] = -1;
491 strcpy (p
->clue_across
[i
][j
], "");
492 strcpy (p
->clue_down
[i
][j
], "");
495 strcpy (p
->hashed_master_password
, "\0");
496 strcpy (p
->hashed_solution_password
, "\0");
500 /* save the puzzle to a file */
501 void save_puzzle (Puzzle
*puzzle
, const char* file
)
504 /* First output the uncompressed contents to a temp file */
505 outfile
= tmpfile ();
508 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
511 /* grid size is the first field */
512 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
513 /* whether grid is frozen or not */
514 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
515 /* the hashed password */
516 fprintf (outfile
, "%s\n", puzzle
->hashed_master_password
);
517 /* the hashed_solution_password */
518 fprintf (outfile
, "%s\n", puzzle
->hashed_solution_password
);
520 /* First output the grid characters columns/rows */
521 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
523 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
524 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
525 fprintf (outfile
, "\n");
528 /* Next output the start across/down numbers */
529 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
531 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
533 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
534 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
536 fprintf (outfile
, "\n");
539 /* Output the across clues */
540 fprintf (outfile
, "ACROSS\n");
541 /* Search the grid for across words */
542 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
544 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
546 /* if it is an across word, then put the word index followed by
547 tab character (as separator) and the clue */
548 if (puzzle
->start_across_word
[i
][j
] != -1)
549 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
550 puzzle
->clue_across
[i
][j
]);
554 /* Output the down clues */
555 fprintf (outfile
, "DOWN\n");
556 /* Search the grid for down words */
557 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
559 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
561 /* same as across word, put the word index followed by the tab
562 character and then the clue */
563 if (puzzle
->start_down_word
[i
][j
] != -1)
564 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
565 puzzle
->clue_down
[i
][j
]);
569 /* Flush the buffer and rewind to beginning - to read and save into
570 gzip compressed file */
572 fseek (outfile
, 0, 0);
574 /* now compress the file and save it to destination file */
575 gzFile outdestfile
= gzopen (file
, "wb");
576 if (outdestfile
== NULL
)
578 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
583 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
586 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
589 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
593 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
595 gzclose (outdestfile
);
600 /* read the puzzle from a file */
601 Puzzle
load_puzzle (const char* file
)
604 /* First open the GZip file */
605 gzFile insourcefile
= gzopen (file
, "rb");
606 if (insourcefile
== NULL
)
608 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
609 /* return an invalid puzzle */
613 /* Open a temporary file to uncompress the contents */
614 FILE *infile
= tmpfile ();
617 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
621 /* Put the uncompressed content to the temp file */
624 num
= gzread (insourcefile
, buf
, 128);
627 int res
= fwrite (buf
, 1, num
, infile
);
630 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
632 gzclose (insourcefile
);
633 /* return an invalid puzzle */
637 num
= gzread (insourcefile
, buf
, 128);
639 /* Close the gzip file */
640 gzclose (insourcefile
);
641 /* Flush the temp file buffer and rewind to beginning */
643 fseek (infile
, 0, 0);
645 /* Read the temporary file contents to the structure Puzzle */
646 char line
[MAX_CLUE_LENGTH
+10];
647 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
648 p
.grid_size
= atoi (line
);
649 /* if puzzle is invalid or otherwise not proper grid, return an invalid
651 if (p
.grid_size
== 0)
653 fprintf (stderr
, "%s\n", INVALID_PUZZLE
);
657 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
658 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
659 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
660 if (strlen (line
) != 1)
661 strcpy (p
.hashed_master_password
, strtok (line
, "\n"));
663 strcpy (p
.hashed_master_password
, "\0");
664 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
665 if (strlen (line
) != 1)
666 strcpy (p
.hashed_solution_password
, strtok (line
, "\n"));
668 strcpy (p
.hashed_solution_password
, "\0");
670 /* read each character of the grid */
671 for (int i
= 0; i
< p
.grid_size
; i
++ )
673 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
674 for (int j
= 0; j
< p
.grid_size
; j
++)
675 p
.chars
[i
][j
] = line
[j
];
677 /* read the word numbers */
678 for (int i
= 0; i
< p
.grid_size
; i
++)
680 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
681 char *token
= strtok (line
, " ");
682 for (int j
= 0; j
< p
.grid_size
; j
++)
685 p
.start_across_word
[i
][j
] = atoi (token
);
686 token
= strtok (NULL
, " ");
688 p
.start_down_word
[i
][j
] = atoi (token
);
689 token
= strtok (NULL
, " ");
693 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
696 char clues
[100][MAX_CLUE_LENGTH
];
699 /* first read the across clues from file */
702 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
703 /* if reached the end of across clues */
704 if (strcmp (line
, "DOWN\n") == 0)
706 word_num
[c
] = atoi (strtok (line
, "\t"));
707 char *cl
= strtok (NULL
, "\n");
709 strcpy (clues
[c
], cl
);
711 strcpy (clues
[c
], "\0");
714 /* set the clue to the correct cell in grid */
715 for (int i
= 0; i
< p
.grid_size
; i
++)
717 for (int j
= 0; j
< p
.grid_size
; j
++)
719 for (int r
= 0; r
< c
; r
++)
720 if (p
.start_across_word
[i
][j
] == word_num
[r
])
721 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
727 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
729 word_num
[c
] = atoi (strtok (line
, "\t"));
730 char* cl
= strtok (NULL
, "\n");
732 strcpy (clues
[c
], cl
);
734 strcpy (clues
[c
], "\0");
737 for (int i
= 0; i
< p
.grid_size
; i
++)
739 for (int j
= 0; j
< p
.grid_size
; j
++)
741 for (int r
= 0; r
< c
; r
++)
742 if (p
.start_down_word
[i
][j
] == word_num
[r
])
743 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
751 /* display the puzzle */
752 void print_puzzle (Puzzle
*p
)
755 set_color (WHITE
, CYAN
, NORMAL
);
757 for (int i
= 0; i
< p
->grid_size
; i
++)
761 for (int i
= 0; i
< p
->grid_size
; i
++)
763 set_color (WHITE
, CYAN
, NORMAL
);
765 for (int j
= 0; j
< p
->grid_size
; j
++)
767 if (p
->chars
[i
][j
] == '#') {
768 set_color (WHITE
, BLACK
, NORMAL
);
773 if (p
->start_across_word
[i
][j
] != -1 ||
774 p
->start_down_word
[i
][j
] != -1)
776 set_color (BLUE
, WHITE
, NORMAL
);
777 if (p
->start_across_word
[i
][j
] != -1)
778 printf ("%-2d", p
->start_across_word
[i
][j
]);
780 printf ("%-2d", p
->start_down_word
[i
][j
]);
784 set_color (BLACK
, WHITE
,NORMAL
);
788 set_color (BLACK
, WHITE
, BOLD
);
789 printf ("%c", p
->chars
[i
][j
]);
795 /* print the clues if set */
796 if (p
->grid_frozen
== true)
798 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
799 for (int i
= 0; i
< p
->grid_size
; i
++)
801 for (int j
= 0; j
< p
->grid_size
; j
++)
803 if (p
->start_across_word
[i
][j
] != -1)
805 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
806 p
->clue_across
[i
][j
]);
810 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
811 for (int i
= 0; i
< p
->grid_size
; i
++)
813 for (int j
= 0; j
< p
->grid_size
; j
++)
815 if (p
->start_down_word
[i
][j
] != -1)
817 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
826 /* function to check if a word is valid or not */
827 char* is_valid_word (char *word
)
829 if (word
== NULL
|| strlen(word
) == 0)
831 for (int i
= 0; i
< strlen (word
) - 1; i
++)
832 if (! isalpha (word
[i
]))
835 return strtok (word
, "\n");
839 /* function to set a clue for an across word */
840 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
842 for (int i
= 0; i
< p
->grid_size
; i
++)
844 for (int j
= 0; j
< p
->grid_size
; j
++)
848 if (p
->start_across_word
[i
][j
] == index
)
850 strcpy (p
->clue_across
[i
][j
], clue
);
854 else if (order
== DOWN
)
856 if (p
->start_down_word
[i
][j
] == index
)
858 strcpy (p
->clue_down
[i
][j
], clue
);
867 /* function to print a menu */
868 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
869 char **items
, int num_items
, int padding
)
872 printf ("\e[1;1H\e[2J");
873 set_color (fg
, bg
, NORMAL
);
875 for (int i
= 0; i
< padding
; i
++)
878 reset_color (); printf ("\n");
879 set_color (fg
, bg
, NORMAL
);
881 set_color (fg
, bg
, BOLD
);
882 printf ("%-*s", padding
, title
);
884 set_color (fg
, bg
, NORMAL
);
886 reset_color (); printf ("\n");
887 set_color (fg
, bg
, NORMAL
);
889 for (int i
= 0; i
< padding
; i
++)
892 reset_color (); printf ("\n");
893 for (int i
= 0; i
< num_items
; i
++)
895 set_color (fg
, bg
, NORMAL
);
896 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
897 reset_color (); printf ("\n");
899 set_color (fg
, bg
, NORMAL
);
901 for (int i
= 0; i
< padding
; i
++)
904 reset_color (); printf ("\n");
907 /* reset the player data, loading from the puzzle file */
908 void reset_player_data (MainPlayerData
*app_data
, const char *filename
)
910 app_data
->puzzle
= load_puzzle (filename
);
912 app_data
->is_loaded
= app_data
->puzzle
.grid_frozen
;
913 app_data
->cur_col
= -1;
914 app_data
->cur_row
= -1;
915 app_data
->solution_revealed
= false;
916 strcpy (app_data
->filename
, filename
);
917 /* reset the answer keys */
918 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
919 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
920 app_data
->char_ans
[i
][j
] = ' ';
924 /* save the user grid to a file */
925 void save_user_data (MainPlayerData
*app_data
, const char *filename
)
928 outfile
= fopen (filename
, "wb");
931 fprintf (stderr
, ERROR_WRITING_FILE
);
934 fprintf (outfile
, "%s\n", app_data
->filename
);
935 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
937 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
938 fprintf (outfile
, "%c", app_data
->char_ans
[i
][j
]);
939 fprintf (outfile
, "\n");
945 /* load the user grid from a file */
946 void load_user_data (MainPlayerData
*app_data
, const char *filename
)
949 infile
= fopen (filename
, "rb");
952 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
956 char puzzle_file_name
[65535];
957 fgets (puzzle_file_name
, 65535, infile
);
958 reset_player_data (app_data
, strtok (puzzle_file_name
, "\n"));
960 char line
[MAX_PUZZLE_SIZE
+10];
961 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
963 fgets (line
, MAX_PUZZLE_SIZE
+10, infile
);
964 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
965 app_data
->char_ans
[i
][j
] = line
[j
];
971 /* in the player app, move the current selection index left or right */
972 void move_current_col (MainPlayerData
*app_data
, enum DIRECTION dir
)
974 int r
= app_data
->cur_row
;
975 int c
= app_data
->cur_col
;
976 if (dir
== DIR_FORWARD
)
979 while (c
< app_data
->puzzle
.grid_size
)
981 if (app_data
->puzzle
.chars
[r
][c
] == '#')
986 if (c
< app_data
->puzzle
.grid_size
)
987 app_data
->cur_col
= c
;
994 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1000 app_data
->cur_col
= c
;
1004 /* in the player app move the current selection index up or down */
1005 void move_current_row (MainPlayerData
*app_data
, enum DIRECTION dir
)
1007 int r
= app_data
->cur_row
;
1008 int c
= app_data
->cur_col
;
1009 if (dir
== DIR_FORWARD
)
1012 while (r
< app_data
->puzzle
.grid_size
)
1014 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1019 if (r
< app_data
->puzzle
.grid_size
)
1020 app_data
->cur_row
= r
;
1027 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1033 app_data
->cur_row
= r
;