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 /* convert the hashed binary password to hexadecimal representation and
107 free the hashed binary password */
108 void to_hexadecimal (char *hex
, unsigned char *binary_pwd
, unsigned int len
)
111 /* keep reference to beginning of the hashed password */
112 unsigned char *binary_pw_begin
= binary_pwd
;
113 for (int i
= 0; i
< len
; i
++)
115 sprintf (buf
, "%02x", (*binary_pwd
)&0xff);
119 /* free the hashed password */
120 OPENSSL_free (binary_pw_begin
);
123 /* get a number from the user */
132 /* verify solution password */
133 bool verify_solution_password (Puzzle
*p
, const char* password
)
135 /* no password set */
136 if (strcmp (p
->hashed_solution_password
, "\0") == 0)
139 /* hash the user input password and compare it with the stored password */
140 unsigned char* hashed_sol_password
;
142 digest_message ((const unsigned char *)password
, strlen(password
),
143 &hashed_sol_password
, &len
);
144 char hashed_hex_pwd
[256] = { (char) NULL
};
145 to_hexadecimal (hashed_hex_pwd
, hashed_sol_password
, len
);
147 if (strcmp (p
->hashed_solution_password
, hashed_hex_pwd
) == 0)
154 /* verify master password */
155 bool verify_master_password (Puzzle
*p
, const char* password
)
157 /* no password set */
158 if (strcmp (p
->hashed_master_password
, "\0") == 0)
161 /* hash the user input password and compare it with the stored password */
162 unsigned char* hashed_mas_password
;
164 digest_message ((const unsigned char *)password
, strlen(password
),
165 &hashed_mas_password
, &len
);
166 char hashed_hex_pwd
[256] = { (char) NULL
};
167 to_hexadecimal (hashed_hex_pwd
, hashed_mas_password
, len
);
169 if (strcmp (p
->hashed_master_password
, hashed_hex_pwd
) == 0)
175 /* Set or reset solution password for puzzle */
176 void set_solution_password (Puzzle
*p
, const char *password
)
178 /* if it is a null string, reset the password */
179 if (strcmp (password
, "\0") == 0)
180 strcpy (p
->hashed_solution_password
, "\0");
184 unsigned char* hashedpwd
;
186 digest_message ((const unsigned char *)password
, strlen(password
),
188 /* the hashedpwd contains binary data - we will convert it to
189 hexadecimal data and store in file */
191 to_hexadecimal (p
->hashed_solution_password
, hashedpwd
, len
);
195 /* Set or reset master password for puzzle */
196 void set_master_password (Puzzle
*p
, const char *password
)
198 /* if it is a null string, reset the password */
199 if (strcmp (password
, "\0") == 0)
200 strcpy (p
->hashed_master_password
, "\0");
204 unsigned char* hashedpwd
;
206 digest_message ((const unsigned char *)password
, strlen(password
),
208 /* the hashedpwd contains binary data - we will convert it to
209 hexadecimal data and store in file */
211 to_hexadecimal (p
->hashed_master_password
, hashedpwd
, len
);
215 /* Output the clues to text file */
216 void export_clues (Puzzle
*p
, const char *filename
)
218 FILE *outfile
= fopen (filename
, "w");
221 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
224 /* first the across clues */
225 fprintf (outfile
, "ACROSS CLUES\n");
226 for (int i
= 0; i
< p
->grid_size
; i
++)
228 for (int j
= 0; j
< p
->grid_size
; j
++)
230 if (p
->start_across_word
[i
][j
] != -1)
231 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
232 p
->clue_across
[i
][j
]);
235 /* now the down clues */
236 fprintf (outfile
, "DOWN CLUES\n");
237 for (int i
= 0; i
< p
->grid_size
; i
++)
239 for (int j
= 0; j
< p
->grid_size
; j
++)
241 if (p
->start_down_word
[i
][j
] != -1)
242 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
249 /* Output the grid to image - if answerkey is true export filled grid */
250 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
252 int img_size
= p
->grid_size
* GRID_PIXELS
;
253 FILE * outfile
= fopen (filename
, "wb");
256 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
260 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
261 gdImageColorAllocate (img
, 255,255,255);
262 int black
= gdImageColorAllocate (img
, 0, 0, 0);
263 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
264 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
265 gdFontPtr lg_fnt
= gdFontGetGiant ();
267 for (int i
= 0; i
< p
->grid_size
; i
++)
269 for (int j
= 0; j
< p
->grid_size
; j
++)
271 /* if it is a block, draw the black square */
272 if (p
->chars
[i
][j
] == '#')
273 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
274 j
*GRID_PIXELS
+GRID_PIXELS
,
275 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
278 /* draw a regular square */
279 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
280 j
*GRID_PIXELS
+GRID_PIXELS
,
281 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
283 /* print the numers, if it is either start across word or
285 if (p
->start_across_word
[i
][j
] != -1 ||
286 p
->start_down_word
[i
][j
] != -1)
288 if (p
->start_across_word
[i
][j
] != -1)
291 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
292 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
294 (unsigned char *)str
, blue
);
299 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
300 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
302 (unsigned char *)str
, blue
);
305 /* if answerkey is true, draw the character in the cell */
308 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
309 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
315 gdImagePng (img
, outfile
);
316 gdImageDestroy (img
);
320 /* Set the terminal colour */
321 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
322 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
325 /* Reset the terminal colour */
326 void reset_color () {
330 /* check if the prev row has a block or not */
331 bool prev_row_block (Puzzle
*p
, int r
, int c
)
335 if (p
->chars
[r
-1][c
] == '#')
340 /* check if the next row has a block or not */
341 bool next_row_block (Puzzle
*p
, int r
, int c
)
343 if (r
== p
->grid_size
-1)
345 if (p
->chars
[r
+1][c
] == '#')
350 /* check if the prev col has a block or not */
351 bool prev_col_block (Puzzle
*p
, int r
, int c
)
355 if (p
->chars
[r
][c
-1] == '#')
360 /* check if the next col has a block or not */
361 bool next_col_block (Puzzle
*p
, int r
, int c
)
363 if (c
== p
->grid_size
- 1)
365 if (p
->chars
[r
][c
+1] == '#')
370 /* check if previous row is blank or not */
371 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
373 if (r
== 0) return true;
374 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
377 /* check if next row is blank or not */
378 bool next_row_blank (Puzzle
*p
, int r
, int c
)
380 if (r
== p
->grid_size
- 1) return true;
381 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
384 /* check if previous col is blank or not */
385 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
387 if (c
== 0) return true;
388 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
391 /* check if the next col is blank or not */
392 bool next_col_blank (Puzzle
*p
, int r
, int c
)
394 if (c
== p
->grid_size
-1) return true;
395 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
399 /* set the current row/col to the beginning of word index (across or down) */
400 void set_selection_to_word_start (MainPlayerData
*app_data
,
401 enum ORIENTATION orient
, int word_index
)
403 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
405 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
407 if (orient
== ACROSS
&&
408 app_data
->puzzle
.start_across_word
[i
][j
] == word_index
)
410 app_data
->current_movement
= ACROSS
;
411 app_data
->cur_row
= i
;
412 app_data
->cur_col
= j
;
415 else if (orient
== DOWN
&&
416 app_data
->puzzle
.start_down_word
[i
][j
] == word_index
)
418 app_data
->current_movement
= DOWN
;
419 app_data
->cur_row
= i
;
420 app_data
->cur_col
= j
;
427 /* unfreeze the grid - make editing possible to change words */
428 void unfreeze_puzzle (Puzzle
*p
)
430 for (int i
= 0; i
< p
->grid_size
; i
++)
432 for (int j
= 0; j
< p
->grid_size
; j
++)
434 if (p
->chars
[i
][j
] == '#')
435 p
->chars
[i
][j
] = ' ';
437 p
->start_across_word
[i
][j
] = -1;
438 p
->start_down_word
[i
][j
] = -1;
441 p
->grid_frozen
= false;
444 /* freeze the grid - make editing impossible because it finalizes the
445 across and down words in the grid */
446 void freeze_puzzle (Puzzle
*p
)
449 bool across_word_start
, down_word_start
;
450 for (int i
= 0; i
< p
->grid_size
; i
++)
452 for (int j
= 0; j
< p
->grid_size
; j
++)
454 across_word_start
= false;
455 down_word_start
= false;
456 /* if it is a blank cell - cover it with a block */
457 if (p
->chars
[i
][j
] == ' ' || p
->chars
[i
][j
] == '#')
458 p
->chars
[i
][j
] = '#';
459 /* it is not a blank cell - check all possibilities */
462 bool prev_row
= prev_row_blank (p
, i
, j
);
463 bool next_row
= next_row_blank (p
, i
, j
);
464 bool prev_col
= prev_col_blank (p
, i
, j
);
465 bool next_col
= next_col_blank (p
, i
, j
);
466 if (prev_row
&& ! next_row
)
467 down_word_start
= true;
468 if (prev_col
&& ! next_col
)
469 across_word_start
= true;
472 if (across_word_start
== true)
473 p
->start_across_word
[i
][j
] = word_num
;
475 p
->start_across_word
[i
][j
] = -1;
476 if (down_word_start
== true)
477 p
->start_down_word
[i
][j
] = word_num
;
479 p
->start_down_word
[i
][j
] = -1;
480 if (across_word_start
== true || down_word_start
== true)
484 p
->grid_frozen
= true;
487 /* reset the entire grid */
488 void init_puzzle (Puzzle
*p
, int grid_size
)
490 p
->grid_size
= grid_size
;
491 p
->grid_frozen
= false;
492 for (int i
= 0; i
< p
->grid_size
; i
++)
494 for (int j
= 0; j
< p
->grid_size
; j
++)
496 p
->chars
[i
][j
] = ' ';
497 p
->start_across_word
[i
][j
] = -1;
498 p
->start_down_word
[i
][j
] = -1;
499 strcpy (p
->clue_across
[i
][j
], "");
500 strcpy (p
->clue_down
[i
][j
], "");
503 strcpy (p
->hashed_master_password
, "\0");
504 strcpy (p
->hashed_solution_password
, "\0");
508 /* save the puzzle to a file */
509 void save_puzzle (Puzzle
*puzzle
, const char* file
)
512 /* First output the uncompressed contents to a temp file */
513 outfile
= tmpfile ();
516 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
519 /* grid size is the first field */
520 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
521 /* whether grid is frozen or not */
522 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
523 /* the hashed password */
524 fprintf (outfile
, "%s\n", puzzle
->hashed_master_password
);
525 /* the hashed_solution_password */
526 fprintf (outfile
, "%s\n", puzzle
->hashed_solution_password
);
528 /* First output the grid characters columns/rows */
529 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
531 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
532 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
533 fprintf (outfile
, "\n");
536 /* Next output the start across/down numbers */
537 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
539 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
541 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
542 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
544 fprintf (outfile
, "\n");
547 /* Output the across clues */
548 fprintf (outfile
, "ACROSS\n");
549 /* Search the grid for across words */
550 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
552 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
554 /* if it is an across word, then put the word index followed by
555 tab character (as separator) and the clue */
556 if (puzzle
->start_across_word
[i
][j
] != -1)
557 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
558 puzzle
->clue_across
[i
][j
]);
562 /* Output the down clues */
563 fprintf (outfile
, "DOWN\n");
564 /* Search the grid for down words */
565 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
567 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
569 /* same as across word, put the word index followed by the tab
570 character and then the clue */
571 if (puzzle
->start_down_word
[i
][j
] != -1)
572 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
573 puzzle
->clue_down
[i
][j
]);
577 /* Flush the buffer and rewind to beginning - to read and save into
578 gzip compressed file */
580 fseek (outfile
, 0, 0);
582 /* now compress the file and save it to destination file */
583 gzFile outdestfile
= gzopen (file
, "wb");
584 if (outdestfile
== NULL
)
586 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
591 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
594 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
597 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
601 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
603 gzclose (outdestfile
);
608 /* read the puzzle from a file */
609 Puzzle
load_puzzle (const char* file
)
611 /* First open the GZip file */
612 gzFile insourcefile
= gzopen (file
, "rb");
613 if (insourcefile
== NULL
)
615 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
618 /* Open a temporary file to uncompress the contents */
619 FILE *infile
= tmpfile ();
622 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
625 /* Put the uncompressed content to the temp file */
628 num
= gzread (insourcefile
, buf
, 128);
631 int res
= fwrite (buf
, 1, num
, infile
);
634 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
636 gzclose (insourcefile
);
639 num
= gzread (insourcefile
, buf
, 128);
641 /* Close the gzip file */
642 gzclose (insourcefile
);
643 /* Flush the temp file buffer and rewind to beginning */
645 fseek (infile
, 0, 0);
647 /* Read the temporary file contents to the structure Puzzle */
649 char line
[MAX_CLUE_LENGTH
+10];
650 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
651 p
.grid_size
= atoi (line
);
652 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
653 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
654 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
655 if (strlen (line
) != 1)
656 strcpy (p
.hashed_master_password
, strtok (line
, "\n"));
658 strcpy (p
.hashed_master_password
, "\0");
659 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
660 if (strlen (line
) != 1)
661 strcpy (p
.hashed_solution_password
, strtok (line
, "\n"));
663 strcpy (p
.hashed_solution_password
, "\0");
665 /* read each character of the grid */
666 for (int i
= 0; i
< p
.grid_size
; i
++ )
668 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
669 for (int j
= 0; j
< p
.grid_size
; j
++)
670 p
.chars
[i
][j
] = line
[j
];
672 /* read the word numbers */
673 for (int i
= 0; i
< p
.grid_size
; i
++)
675 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
676 char *token
= strtok (line
, " ");
677 for (int j
= 0; j
< p
.grid_size
; j
++)
680 p
.start_across_word
[i
][j
] = atoi (token
);
681 token
= strtok (NULL
, " ");
683 p
.start_down_word
[i
][j
] = atoi (token
);
684 token
= strtok (NULL
, " ");
688 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
691 char clues
[100][MAX_CLUE_LENGTH
];
694 /* first read the across clues from file */
697 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
698 /* if reached the end of across clues */
699 if (strcmp (line
, "DOWN\n") == 0)
701 word_num
[c
] = atoi (strtok (line
, "\t"));
702 char *cl
= strtok (NULL
, "\n");
704 strcpy (clues
[c
], cl
);
706 strcpy (clues
[c
], "\0");
709 /* set the clue to the correct cell in grid */
710 for (int i
= 0; i
< p
.grid_size
; i
++)
712 for (int j
= 0; j
< p
.grid_size
; j
++)
714 for (int r
= 0; r
< c
; r
++)
715 if (p
.start_across_word
[i
][j
] == word_num
[r
])
716 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
722 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
724 word_num
[c
] = atoi (strtok (line
, "\t"));
725 char* cl
= strtok (NULL
, "\n");
727 strcpy (clues
[c
], cl
);
729 strcpy (clues
[c
], "\0");
732 for (int i
= 0; i
< p
.grid_size
; i
++)
734 for (int j
= 0; j
< p
.grid_size
; j
++)
736 for (int r
= 0; r
< c
; r
++)
737 if (p
.start_down_word
[i
][j
] == word_num
[r
])
738 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
746 /* display the puzzle */
747 void print_puzzle (Puzzle
*p
)
750 set_color (WHITE
, CYAN
, NORMAL
);
752 for (int i
= 0; i
< p
->grid_size
; i
++)
756 for (int i
= 0; i
< p
->grid_size
; i
++)
758 set_color (WHITE
, CYAN
, NORMAL
);
760 for (int j
= 0; j
< p
->grid_size
; j
++)
762 if (p
->chars
[i
][j
] == '#') {
763 set_color (WHITE
, BLACK
, NORMAL
);
768 if (p
->start_across_word
[i
][j
] != -1 ||
769 p
->start_down_word
[i
][j
] != -1)
771 set_color (BLUE
, WHITE
, NORMAL
);
772 if (p
->start_across_word
[i
][j
] != -1)
773 printf ("%-2d", p
->start_across_word
[i
][j
]);
775 printf ("%-2d", p
->start_down_word
[i
][j
]);
779 set_color (BLACK
, WHITE
,NORMAL
);
783 set_color (BLACK
, WHITE
, BOLD
);
784 printf ("%c", p
->chars
[i
][j
]);
790 /* print the clues if set */
791 if (p
->grid_frozen
== true)
793 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
794 for (int i
= 0; i
< p
->grid_size
; i
++)
796 for (int j
= 0; j
< p
->grid_size
; j
++)
798 if (p
->start_across_word
[i
][j
] != -1)
800 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
801 p
->clue_across
[i
][j
]);
805 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
806 for (int i
= 0; i
< p
->grid_size
; i
++)
808 for (int j
= 0; j
< p
->grid_size
; j
++)
810 if (p
->start_down_word
[i
][j
] != -1)
812 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
821 /* function to check if a word is valid or not */
822 char* is_valid_word (char *word
)
824 if (word
== NULL
|| strlen(word
) == 0)
826 for (int i
= 0; i
< strlen (word
) - 1; i
++)
827 if (! isalpha (word
[i
]))
830 return strtok (word
, "\n");
834 /* function to set a clue for an across word */
835 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
837 for (int i
= 0; i
< p
->grid_size
; i
++)
839 for (int j
= 0; j
< p
->grid_size
; j
++)
843 if (p
->start_across_word
[i
][j
] == index
)
845 strcpy (p
->clue_across
[i
][j
], clue
);
849 else if (order
== DOWN
)
851 if (p
->start_down_word
[i
][j
] == index
)
853 strcpy (p
->clue_down
[i
][j
], clue
);
862 /* function to print a menu */
863 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
864 char **items
, int num_items
, int padding
)
867 printf ("\e[1;1H\e[2J");
868 set_color (fg
, bg
, NORMAL
);
870 for (int i
= 0; i
< padding
; i
++)
873 reset_color (); printf ("\n");
874 set_color (fg
, bg
, NORMAL
);
876 set_color (fg
, bg
, BOLD
);
877 printf ("%-*s", padding
, title
);
879 set_color (fg
, bg
, NORMAL
);
881 reset_color (); printf ("\n");
882 set_color (fg
, bg
, NORMAL
);
884 for (int i
= 0; i
< padding
; i
++)
887 reset_color (); printf ("\n");
888 for (int i
= 0; i
< num_items
; i
++)
890 set_color (fg
, bg
, NORMAL
);
891 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
892 reset_color (); printf ("\n");
894 set_color (fg
, bg
, NORMAL
);
896 for (int i
= 0; i
< padding
; i
++)
899 reset_color (); printf ("\n");
902 /* reset the player data, loading from the puzzle file */
903 void reset_player_data (MainPlayerData
*app_data
, const char *filename
)
905 app_data
->puzzle
= load_puzzle (filename
);
907 app_data
->is_loaded
= app_data
->puzzle
.grid_frozen
;
908 app_data
->cur_col
= -1;
909 app_data
->cur_row
= -1;
910 app_data
->solution_revealed
= false;
911 strcpy (app_data
->filename
, filename
);
912 /* reset the answer keys */
913 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
914 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
915 app_data
->char_ans
[i
][j
] = ' ';
919 /* save the user grid to a file */
920 void save_user_data (MainPlayerData
*app_data
, const char *filename
)
923 outfile
= fopen (filename
, "wb");
926 fprintf (stderr
, ERROR_WRITING_FILE
);
929 fprintf (outfile
, "%s\n", app_data
->filename
);
930 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
932 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
933 fprintf (outfile
, "%c", app_data
->char_ans
[i
][j
]);
934 fprintf (outfile
, "\n");
940 /* load the user grid from a file */
941 void load_user_data (MainPlayerData
*app_data
, const char *filename
)
944 infile
= fopen (filename
, "rb");
947 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
951 char puzzle_file_name
[65535];
952 fgets (puzzle_file_name
, 65535, infile
);
953 reset_player_data (app_data
, strtok (puzzle_file_name
, "\n"));
955 char line
[MAX_PUZZLE_SIZE
+10];
956 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
958 fgets (line
, MAX_PUZZLE_SIZE
+10, infile
);
959 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
960 app_data
->char_ans
[i
][j
] = line
[j
];
966 /* in the player app, move the current selection index left or right */
967 void move_current_col (MainPlayerData
*app_data
, enum DIRECTION dir
)
969 int r
= app_data
->cur_row
;
970 int c
= app_data
->cur_col
;
971 if (dir
== DIR_FORWARD
)
974 while (c
< app_data
->puzzle
.grid_size
)
976 if (app_data
->puzzle
.chars
[r
][c
] == '#')
981 if (c
< app_data
->puzzle
.grid_size
)
982 app_data
->cur_col
= c
;
989 if (app_data
->puzzle
.chars
[r
][c
] == '#')
995 app_data
->cur_col
= c
;
999 /* in the player app move the current selection index up or down */
1000 void move_current_row (MainPlayerData
*app_data
, enum DIRECTION dir
)
1002 int r
= app_data
->cur_row
;
1003 int c
= app_data
->cur_col
;
1004 if (dir
== DIR_FORWARD
)
1007 while (r
< app_data
->puzzle
.grid_size
)
1009 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1014 if (r
< app_data
->puzzle
.grid_size
)
1015 app_data
->cur_row
= r
;
1022 if (app_data
->puzzle
.chars
[r
][c
] == '#')
1028 app_data
->cur_row
= r
;