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