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