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