Fixed bug in hashing and represented hash as a hexadecimal
[wordblah.git] / wordblox.h
1 #ifndef __WORDBLOX_H
2 #define __WORDBLOX_H
3 #define _XOPEN_SOURCE
4 #include <unistd.h>
5 #include <stdbool.h>
6 #include <string.h>
7 #include <ctype.h>
8 #include <gd.h>
9 #include <gdfontmb.h>
10 #include <gdfontg.h>
11 #include <zlib.h>
12 #include <openssl/conf.h>
13 #include <openssl/evp.h>
14 #include <openssl/err.h>
15 #include "constantstrings.h"
16
17 #define MAX_PUZZLE_SIZE 25
18 #define MAX_CLUE_LENGTH 150
19 #define GRID_PIXELS 37
20
21 /* Enum to define terminal colours */
22 enum COLOR {
23 BLACK = 0,
24 RED= 1,
25 GREEN=2,
26 YELLOW=3,
27 BLUE=4,
28 MAGENTA=5,
29 CYAN=6,
30 WHITE=7
31 };
32
33 enum ATTR {
34 NORMAL = 23,
35 BOLD=1
36 };
37
38 enum ORIENTATION {
39 ACROSS=1,
40 DOWN=2
41 };
42
43 /* for use with the player */
44 enum DIRECTION {
45 DIR_FORWARD = 1,
46 DIR_BACK = -1
47 };
48
49 typedef char String[MAX_CLUE_LENGTH];
50
51 /* The main puzzle struct type */
52 typedef struct {
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];
58 int grid_size;
59 bool grid_frozen;
60 char hashed_password[256];
61 char salt[256];
62 } Puzzle;
63
64 /* The player data struct type - for the player app */
65 typedef struct {
66 Puzzle puzzle;
67 char filename[65535];
68 bool is_loaded;
69 char char_ans[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
70 int cur_row;
71 int cur_col;
72 } MainPlayerData;
73
74 /* compute the hash of a password */
75 void digest_message(const unsigned char *message,
76 size_t message_len, unsigned char **digest, unsigned int *digest_len)
77 {
78 EVP_MD_CTX *mdctx;
79
80 if((mdctx = EVP_MD_CTX_new()) == NULL)
81 goto err;
82
83 if(1 != EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL))
84 goto err;
85
86 if(1 != EVP_DigestUpdate(mdctx, message, message_len))
87 goto err;
88
89 if((*digest = (unsigned char *)
90 OPENSSL_malloc(EVP_MD_size(EVP_sha256()))) == NULL)
91 goto err;
92
93 if(1 != EVP_DigestFinal_ex(mdctx, *digest, digest_len))
94 goto err;
95
96 EVP_MD_CTX_free(mdctx);
97 return;
98 err:
99 EVP_MD_CTX_free(mdctx);
100 ERR_print_errors_fp(stderr);
101 exit (2);
102 }
103
104 /* convert the hashed binary password to hexadecimal representation and
105 free the hashed binary password */
106 void to_hexadecimal (char *hex, unsigned char *binary_pwd, unsigned int len)
107 {
108 char buf[3];
109 /* keep reference to beginning of the hashed password */
110 unsigned char *binary_pw_begin = binary_pwd;
111 for (int i = 0; i < len; i ++)
112 {
113 sprintf (buf, "%02x", (*binary_pwd)&0xff);
114 strcat (hex, buf);
115 binary_pwd ++;
116 }
117 /* free the hashed password */
118 OPENSSL_free (binary_pw_begin);
119 }
120
121 /* get a number from the user */
122 int get_num ()
123 {
124 char s[5];
125 fgets (s, 5, stdin);
126 int n = atoi (s);
127 return n;
128 }
129
130 /* verify password */
131 bool verify_password (Puzzle *p, const char* password)
132 {
133 /* no password set */
134 if (strcmp (p->hashed_password, "\0") == 0)
135 return true;
136
137 /* hash the user input password and compare it with the stored password */
138 unsigned char* hashed_password;
139 unsigned int len;
140 digest_message ((const unsigned char *)password, strlen(password),
141 &hashed_password, &len);
142 char hashed_hex_pwd[256] = { (char) NULL };
143 to_hexadecimal (hashed_hex_pwd, hashed_password, len);
144
145 if (strcmp (p->hashed_password, hashed_hex_pwd) == 0)
146 return true;
147
148 return false;
149 }
150
151 /* Set or reset password for puzzle */
152 void set_puzzle_password (Puzzle *p, const char *password)
153 {
154 /* if it is a null string, reset the password */
155 if (strcmp (password, "\0") == 0)
156 {
157 strcpy (p->hashed_password, "\0");
158 strcpy (p->salt, "\0");
159 }
160 else
161 {
162
163 unsigned char* hashedpwd;
164 unsigned int len;
165 digest_message ((const unsigned char *)password, strlen(password),
166 &hashedpwd, &len);
167 /* the hashedpwd contains binary data - we will convert it to
168 hexadecimal data and store in file */
169
170 to_hexadecimal (p->hashed_password, hashedpwd, len);
171 printf ("%s\n", p->hashed_password);
172
173 strcpy (p->salt, "\0");
174
175 }
176 }
177
178 /* Output the clues to text file */
179 void export_clues (Puzzle *p, const char *filename)
180 {
181 FILE *outfile = fopen (filename, "w");
182 if (outfile == NULL)
183 {
184 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
185 exit (1);
186 }
187 /* first the across clues */
188 fprintf (outfile, "ACROSS CLUES\n");
189 for (int i = 0; i < p->grid_size; i ++)
190 {
191 for (int j = 0; j < p->grid_size; j ++)
192 {
193 if (p->start_across_word[i][j] != -1)
194 fprintf (outfile, "%d - %s\n", p->start_across_word[i][j],
195 p->clue_across[i][j]);
196 }
197 }
198 /* now the down clues */
199 fprintf (outfile, "DOWN CLUES\n");
200 for (int i = 0; i < p->grid_size; i ++)
201 {
202 for (int j = 0; j < p->grid_size; j ++)
203 {
204 if (p->start_down_word[i][j] != -1)
205 fprintf (outfile, "%d - %s\n", p->start_down_word[i][j],
206 p->clue_down[i][j]);
207 }
208 }
209 fclose (outfile);
210 }
211
212 /* Output the grid to image - if answerkey is true export filled grid */
213 void export_grid_image (Puzzle *p, const char *filename, bool answerkey)
214 {
215 int img_size = p->grid_size * GRID_PIXELS;
216 FILE * outfile = fopen (filename, "wb");
217 if (outfile == NULL)
218 {
219 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
220 exit (1);
221 }
222
223 gdImagePtr img = gdImageCreate (img_size, img_size);
224 gdImageColorAllocate (img, 255,255,255);
225 int black = gdImageColorAllocate (img, 0, 0, 0);
226 int blue = gdImageColorAllocate (img, 0, 0, 216);
227 gdFontPtr sm_fnt = gdFontGetMediumBold ();
228 gdFontPtr lg_fnt = gdFontGetGiant ();
229
230 for (int i = 0; i < p->grid_size; i ++)
231 {
232 for (int j = 0; j < p->grid_size; j++)
233 {
234 /* if it is a block, draw the black square */
235 if (p->chars[i][j] == '#')
236 gdImageFilledRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
237 j*GRID_PIXELS+GRID_PIXELS,
238 i*GRID_PIXELS+GRID_PIXELS,black);
239 else
240 {
241 /* draw a regular square */
242 gdImageRectangle (img, j*GRID_PIXELS, i*GRID_PIXELS,
243 j*GRID_PIXELS+GRID_PIXELS,
244 i*GRID_PIXELS+GRID_PIXELS, black);
245
246 /* print the numers, if it is either start across word or
247 a down word */
248 if (p->start_across_word[i][j] != -1 ||
249 p->start_down_word[i][j] != -1)
250 {
251 if (p->start_across_word[i][j] != -1)
252 {
253 char str[5];
254 sprintf (str, "%d", p->start_across_word[i][j]);
255 gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
256 i*GRID_PIXELS+2,
257 (unsigned char *)str, blue);
258 }
259 else
260 {
261 char str[5];
262 sprintf (str, "%d", p->start_down_word[i][j]);
263 gdImageString (img, sm_fnt, j*GRID_PIXELS+2,
264 i*GRID_PIXELS+2,
265 (unsigned char *)str, blue);
266 }
267 }
268 /* if answerkey is true, draw the character in the cell */
269 if (answerkey)
270 {
271 gdImageChar (img, lg_fnt, j*GRID_PIXELS+15,
272 i*GRID_PIXELS+10, p->chars[i][j], black);
273 }
274 }
275 }
276 }
277
278 gdImagePng (img, outfile);
279 gdImageDestroy (img);
280 fclose (outfile);
281 }
282
283 /* Set the terminal colour */
284 void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) {
285 printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at);
286 }
287
288 /* Reset the terminal colour */
289 void reset_color () {
290 printf ("\x1B[0m");
291 }
292
293 /* check if previous row is blank or not */
294 bool prev_row_blank (Puzzle *p, int r, int c)
295 {
296 if (r == 0) return true;
297 if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true;
298 return false;
299 }
300 /* check if next row is blank or not */
301 bool next_row_blank (Puzzle *p, int r, int c)
302 {
303 if (r == p->grid_size - 1) return true;
304 if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true;
305 return false;
306 }
307 /* check if previous col is blank or not */
308 bool prev_col_blank (Puzzle *p, int r, int c)
309 {
310 if (c == 0) return true;
311 if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true;
312 return false;
313 }
314 /* check if the next col is blank or not */
315 bool next_col_blank (Puzzle *p, int r, int c)
316 {
317 if (c == p->grid_size -1) return true;
318 if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true;
319 return false;
320 }
321
322 /* unfreeze the grid - make editing possible to change words */
323 void unfreeze_puzzle (Puzzle *p)
324 {
325 for (int i = 0; i < p->grid_size; i ++)
326 {
327 for (int j = 0; j < p->grid_size; j ++)
328 {
329 if (p->chars[i][j] == '#')
330 p->chars[i][j] = ' ';
331
332 p->start_across_word[i][j] = -1;
333 p->start_down_word[i][j] = -1;
334 }
335 }
336 p->grid_frozen = false;
337 }
338
339 /* freeze the grid - make editing impossible because it finalizes the
340 across and down words in the grid */
341 void freeze_puzzle (Puzzle *p)
342 {
343 int word_num = 1;
344 bool across_word_start, down_word_start;
345 for (int i = 0; i < p->grid_size; i ++)
346 {
347 for (int j = 0; j < p->grid_size; j++)
348 {
349 across_word_start = false;
350 down_word_start = false;
351 /* if it is a blank cell - cover it with a block */
352 if (p->chars[i][j] == ' ' || p->chars[i][j] == '#')
353 p->chars[i][j] = '#';
354 /* it is not a blank cell - check all possibilities */
355 else
356 {
357 bool prev_row = prev_row_blank (p, i, j);
358 bool next_row = next_row_blank (p, i, j);
359 bool prev_col = prev_col_blank (p, i, j);
360 bool next_col = next_col_blank (p, i, j);
361 if (prev_row && ! next_row)
362 down_word_start = true;
363 if (prev_col && ! next_col)
364 across_word_start = true;
365 }
366
367 if (across_word_start == true)
368 p->start_across_word[i][j] = word_num;
369 else
370 p->start_across_word[i][j] = -1;
371 if (down_word_start == true)
372 p->start_down_word[i][j] = word_num;
373 else
374 p->start_down_word[i][j] = -1;
375 if (across_word_start == true || down_word_start == true)
376 word_num ++;
377 }
378 }
379 p->grid_frozen = true;
380 }
381
382 /* reset the entire grid */
383 void init_puzzle (Puzzle *p, int grid_size)
384 {
385 p->grid_size = grid_size;
386 p->grid_frozen = false;
387 for (int i = 0; i < p->grid_size; i ++)
388 {
389 for (int j = 0; j < p->grid_size; j ++)
390 {
391 p->chars[i][j] = ' ';
392 p->start_across_word[i][j] = -1;
393 p->start_down_word[i][j] = -1;
394 strcpy (p->clue_across[i][j], "");
395 strcpy (p->clue_down[i][j], "");
396 }
397 }
398 strcpy (p->hashed_password, "\0");
399 strcpy (p->salt, "\0");
400
401 }
402
403 /* save the puzzle to a file */
404 void save_puzzle (Puzzle *puzzle, const char* file) {
405 FILE *outfile;
406 /* First output the uncompressed contents to a temp file */
407 outfile = tmpfile ();
408 if (outfile == NULL)
409 {
410 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
411 exit (1);
412 }
413 /* grid size is the first field */
414 fprintf (outfile, "%d\n", puzzle->grid_size);
415 /* whether grid is frozen or not */
416 fprintf (outfile, "%d\n", puzzle->grid_frozen);
417 /* the hashed password */
418 fprintf (outfile, "%s\n", puzzle->hashed_password);
419 /* the salt */
420 fprintf (outfile, "%s\n", puzzle->salt);
421
422 /* First output the grid characters columns/rows */
423 for (int i = 0; i < puzzle->grid_size; i ++)
424 {
425 for (int j = 0; j < puzzle->grid_size; j ++)
426 fprintf (outfile, "%c", puzzle->chars[i][j]);
427 fprintf (outfile, "\n");
428 }
429
430 /* Next output the start across/down numbers */
431 for (int i = 0; i < puzzle->grid_size; i ++)
432 {
433 for (int j = 0; j < puzzle->grid_size; j++)
434 {
435 fprintf (outfile, "%d ", puzzle->start_across_word[i][j]);
436 fprintf (outfile, "%d ", puzzle->start_down_word[i][j]);
437 }
438 fprintf (outfile, "\n");
439 }
440
441 /* Output the across clues */
442 fprintf (outfile, "ACROSS\n");
443 /* Search the grid for across words */
444 for (int i = 0; i < puzzle->grid_size; i ++)
445 {
446 for (int j = 0; j < puzzle->grid_size; j++)
447 {
448 /* if it is an across word, then put the word index followed by
449 tab character (as separator) and the clue */
450 if (puzzle->start_across_word[i][j] != -1)
451 fprintf (outfile, "%d\t%s\n", puzzle->start_across_word[i][j],
452 puzzle->clue_across[i][j]);
453 }
454 }
455
456 /* Output the down clues */
457 fprintf (outfile, "DOWN\n");
458 /* Search the grid for down words */
459 for (int i = 0; i < puzzle->grid_size; i ++)
460 {
461 for (int j = 0; j < puzzle->grid_size; j++)
462 {
463 /* same as across word, put the word index followed by the tab
464 character and then the clue */
465 if (puzzle->start_down_word[i][j] != -1)
466 fprintf (outfile, "%d\t%s\n", puzzle->start_down_word[i][j],
467 puzzle->clue_down[i][j]);
468 }
469 }
470
471 /* Flush the buffer and rewind to beginning - to read and save into
472 gzip compressed file */
473 fflush (outfile);
474 fseek (outfile, 0, 0);
475
476 /* now compress the file and save it to destination file */
477 gzFile outdestfile = gzopen (file, "wb");
478 if (outdestfile == NULL)
479 {
480 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
481 fclose (outfile);
482 exit (1);
483 }
484 char buf[128];
485 int num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
486 while (num > 0)
487 {
488 int res = gzwrite (outdestfile, buf, num*sizeof(char) );
489 if (res == 0)
490 {
491 fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED);
492 fclose (outfile);
493 exit (1);
494 }
495 num = fread (buf, sizeof(char), sizeof(char)*128, outfile);
496 }
497 gzclose (outdestfile);
498 fclose (outfile);
499
500 }
501
502 /* read the puzzle from a file */
503 Puzzle load_puzzle (const char* file) {
504 /* First open the GZip file */
505 gzFile insourcefile = gzopen (file, "rb");
506 if (insourcefile == NULL)
507 {
508 fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED);
509 exit (1);
510 }
511 /* Open a temporary file to uncompress the contents */
512 FILE *infile = tmpfile ();
513 if (infile == NULL)
514 {
515 fprintf (stderr, "%s\n", ERROR_READING_FILE);
516 exit (1);
517 }
518 /* Put the uncompressed content to the temp file */
519 char buf[128];
520 int num = 0;
521 num = gzread (insourcefile, buf, 128);
522 while (num > 0)
523 {
524 int res = fwrite (buf, 1, num, infile);
525 if (res == 0)
526 {
527 fprintf (stderr, "%s\n", ERROR_READING_FILE);
528 fclose (infile);
529 gzclose (insourcefile);
530 exit (1);
531 }
532 num = gzread (insourcefile, buf, 128);
533 }
534 /* Close the gzip file */
535 gzclose (insourcefile);
536 /* Flush the temp file buffer and rewind to beginning */
537 fflush (infile);
538 fseek (infile, 0, 0);
539
540 /* Read the temporary file contents to the structure Puzzle */
541 Puzzle p;
542 char line[MAX_CLUE_LENGTH+10];
543 fgets (line, MAX_CLUE_LENGTH + 10, infile);
544 p.grid_size = atoi (line);
545 fgets (line, MAX_CLUE_LENGTH + 10, infile);
546 p.grid_frozen = atoi (line) == 0 ? false : true ;
547 fgets (line, MAX_CLUE_LENGTH + 10, infile);
548 if (strlen (line) != 1)
549 strcpy (p.hashed_password, strtok (line, "\n"));
550 else
551 strcpy (p.hashed_password, "\0");
552 fgets (line, MAX_CLUE_LENGTH + 10, infile);
553 if (strlen (line) != 1)
554 strcpy (p.salt, strtok (line, "\n"));
555 else
556 strcpy (p.salt, "\0");
557
558 /* read each character of the grid */
559 for (int i = 0; i < p.grid_size; i ++ )
560 {
561 fgets (line, MAX_CLUE_LENGTH + 10, infile);
562 for (int j = 0; j < p.grid_size; j ++)
563 p.chars[i][j] = line[j];
564 }
565 /* read the word numbers */
566 for (int i = 0; i < p.grid_size; i ++)
567 {
568 fgets (line, MAX_CLUE_LENGTH + 10, infile);
569 char *token = strtok (line, " ");
570 for (int j = 0; j < p.grid_size; j ++)
571 {
572 if (token != NULL)
573 p.start_across_word[i][j] = atoi (token);
574 token = strtok (NULL, " ");
575 if (token != NULL)
576 p.start_down_word[i][j] = atoi (token);
577 token = strtok (NULL, " ");
578 }
579 }
580 /* read the clues */
581 fgets (line, MAX_CLUE_LENGTH + 10, infile);
582
583 /* across clues */
584 char clues[100][MAX_CLUE_LENGTH];
585 int word_num[100];
586 int c = 0;
587 /* first read the across clues from file */
588 while (1)
589 {
590 fgets (line, MAX_CLUE_LENGTH + 10, infile);
591 /* if reached the end of across clues */
592 if (strcmp (line, "DOWN\n") == 0)
593 break;
594 word_num[c] = atoi (strtok (line, "\t"));
595 char *cl = strtok (NULL, "\n");
596 if (cl != NULL)
597 strcpy (clues[c], cl);
598 else
599 strcpy (clues[c], "\0");
600 c++;
601 }
602 /* set the clue to the correct cell in grid */
603 for (int i = 0; i < p.grid_size; i ++)
604 {
605 for (int j = 0; j < p.grid_size; j ++)
606 {
607 for (int r = 0; r < c; r ++)
608 if (p.start_across_word[i][j] == word_num[r])
609 strcpy (p.clue_across[i][j], clues[r]);
610 }
611 }
612
613 /* down clues */
614 c = 0;
615 while (fgets (line, MAX_CLUE_LENGTH + 10, infile))
616 {
617 word_num[c] = atoi (strtok (line, "\t"));
618 char* cl = strtok (NULL, "\n");
619 if (cl != NULL)
620 strcpy (clues[c], cl);
621 else
622 strcpy (clues[c], "\0");
623 c++;
624 }
625 for (int i = 0; i < p.grid_size; i ++)
626 {
627 for (int j = 0; j < p.grid_size; j ++)
628 {
629 for (int r = 0; r < c; r ++)
630 if (p.start_down_word[i][j] == word_num[r])
631 strcpy (p.clue_down[i][j], clues[r]);
632 }
633 }
634
635 fclose (infile);
636 return p;
637 }
638
639 /* display the puzzle */
640 void print_puzzle (Puzzle *p)
641 {
642 printf ("\n");
643 set_color (WHITE, CYAN, NORMAL);
644 printf (" ");
645 for (int i = 0; i < p->grid_size; i ++)
646 printf ("%3d", i);
647 reset_color ();
648 printf("\n");
649 for (int i = 0; i < p->grid_size; i ++)
650 {
651 set_color (WHITE, CYAN, NORMAL);
652 printf ("%3d ", i);
653 for (int j = 0; j < p->grid_size; j ++)
654 {
655 if (p->chars[i][j] == '#') {
656 set_color (WHITE, BLACK, NORMAL);
657 printf (" ");
658 }
659 else
660 {
661 if (p->start_across_word[i][j] != -1 ||
662 p->start_down_word[i][j] != -1)
663 {
664 set_color (BLUE, WHITE, NORMAL);
665 if (p->start_across_word[i][j] != -1)
666 printf ("%-2d", p->start_across_word[i][j]);
667 else
668 printf ("%-2d", p->start_down_word[i][j]);
669 }
670 else
671 {
672 set_color (BLACK, WHITE,NORMAL);
673 printf (" ");
674 }
675
676 set_color (BLACK, WHITE, BOLD);
677 printf ("%c", p->chars[i][j]);
678 }
679 reset_color ();
680 }
681 printf ("\n");
682 }
683 /* print the clues if set */
684 if (p->grid_frozen == true)
685 {
686 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
687 for (int i = 0; i < p->grid_size; i ++)
688 {
689 for (int j = 0; j < p->grid_size; j ++)
690 {
691 if (p->start_across_word[i][j] != -1)
692 {
693 printf ("%d - %s; ", p->start_across_word[i][j],
694 p->clue_across[i][j]);
695 }
696 }
697 }
698 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
699 for (int i = 0; i < p->grid_size; i ++)
700 {
701 for (int j = 0; j < p->grid_size; j ++)
702 {
703 if (p->start_down_word[i][j] != -1)
704 {
705 printf ("%d - %s; ", p->start_down_word[i][j],
706 p->clue_down[i][j]);
707 }
708 }
709 }
710 printf ("\n");
711 }
712 }
713
714 /* function to check if a word is valid or not */
715 char* is_valid_word (char *word)
716 {
717 if (word == NULL || strlen(word) == 0)
718 return NULL;
719 for (int i = 0; i < strlen (word) - 1; i ++)
720 if (! isalpha (word[i]))
721 return NULL;
722
723 return strtok (word, "\n");
724 }
725
726
727 /* function to set a clue for an across word */
728 bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
729 {
730 for (int i = 0; i < p->grid_size; i ++)
731 {
732 for (int j = 0; j < p->grid_size; j ++)
733 {
734 if (order == ACROSS)
735 {
736 if (p->start_across_word[i][j] == index)
737 {
738 strcpy (p->clue_across[i][j], clue);
739 return true;
740 }
741 }
742 else if (order == DOWN)
743 {
744 if (p->start_down_word[i][j] == index)
745 {
746 strcpy (p->clue_down[i][j], clue);
747 return true;
748 }
749 }
750 }
751 }
752 return false;
753 }
754
755 /* function to print a menu */
756 void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
757 char **items, int num_items, int padding)
758 {
759 /* clear screen */
760 printf ("\e[1;1H\e[2J");
761 set_color (fg, bg, NORMAL);
762 printf ("\u2554");
763 for (int i = 0; i < padding; i ++)
764 printf ("\u2550");
765 printf ("\u2557");
766 reset_color (); printf ("\n");
767 set_color (fg, bg, NORMAL);
768 printf ("\u2551");
769 set_color (fg, bg, BOLD);
770 printf ("%-*s", padding, title);
771 reset_color ();
772 set_color (fg, bg, NORMAL);
773 printf ("\u2551");
774 reset_color (); printf ("\n");
775 set_color (fg, bg, NORMAL);
776 printf ("\u2560");
777 for (int i = 0; i < padding; i ++)
778 printf ("\u2550");
779 printf ("\u2563");
780 reset_color (); printf ("\n");
781 for (int i = 0; i < num_items; i ++)
782 {
783 set_color (fg, bg, NORMAL);
784 printf ("\u2551%-*s\u2551", padding, items[i]);
785 reset_color (); printf ("\n");
786 }
787 set_color (fg, bg, NORMAL);
788 printf ("\u255A");
789 for (int i = 0; i < padding; i ++)
790 printf ("\u2550");
791 printf ("\u255D");
792 reset_color (); printf ("\n");
793 }
794
795 /* reset the player data, from the new file */
796 void reset_player_data (MainPlayerData *app_data, const char *filename)
797 {
798 app_data->puzzle = load_puzzle (filename);
799
800 app_data->is_loaded = app_data->puzzle.grid_frozen;
801 app_data->cur_col = -1;
802 app_data->cur_row = -1;
803 strcpy (app_data->filename, filename);
804 /* reset the answer keys */
805 for (int i = 0; i < app_data->puzzle.grid_size; i ++)
806 for (int j = 0; j < app_data->puzzle.grid_size; j ++)
807 app_data->char_ans[i][j] = ' ';
808
809 }
810
811 /* in the player app, move the current selection index left or right */
812 void move_current_col (MainPlayerData *app_data, enum DIRECTION dir)
813 {
814 int r = app_data->cur_row;
815 int c = app_data->cur_col;
816 if (dir == DIR_FORWARD)
817 {
818 c ++;
819 while (c < app_data->puzzle.grid_size)
820 {
821 if (app_data->puzzle.chars[r][c] == '#')
822 c ++;
823 else
824 break;
825 }
826 if (c < app_data->puzzle.grid_size)
827 app_data->cur_col = c;
828 }
829 else
830 {
831 c --;
832 while (c >= 0)
833 {
834 if (app_data->puzzle.chars[r][c] == '#')
835 c --;
836 else
837 break;
838 }
839 if (c >= 0)
840 app_data->cur_col = c;
841 }
842 }
843
844 /* in the player app move the current selection index up or down */
845 void move_current_row (MainPlayerData *app_data, enum DIRECTION dir)
846 {
847 int r = app_data->cur_row;
848 int c = app_data->cur_col;
849 if (dir == DIR_FORWARD)
850 {
851 r ++;
852 while (r < app_data->puzzle.grid_size)
853 {
854 if (app_data->puzzle.chars[r][c] == '#')
855 r ++;
856 else
857 break;
858 }
859 if (r < app_data->puzzle.grid_size)
860 app_data->cur_row = r;
861 }
862 else
863 {
864 r --;
865 while (r >= 0)
866 {
867 if (app_data->puzzle.chars[r][c] == '#')
868 r --;
869 else
870 break;
871 }
872 if (r >= 0)
873 app_data->cur_row = r;
874 }
875 }
876
877 #endif