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