Continue work on the GUI - added code for drawing
[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 int white = 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 - mak 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 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[4096];
402 while (fread (buf, sizeof(char), 4096, outfile))
403 {
404 int res = gzwrite (outdestfile, buf, strlen (buf) );
405 if (res == 0)
406 {
407 fprintf (stderr, "%s %s\n", ERROR_WRITING_FILE, COMPRESSED);
408 fclose (outfile);
409 exit (1);
410 }
411 }
412 gzclose (outdestfile);
413 fclose (outfile);
414
415 }
416
417 /* read the puzzle from a file */
418 Puzzle load_puzzle (const char* file) {
419 /* First open the GZip file */
420 gzFile insourcefile = gzopen (file, "rb");
421 if (insourcefile == NULL)
422 {
423 fprintf (stderr, "%s %s\n", ERROR_READING_FILE, COMPRESSED);
424 exit (1);
425 }
426 /* Open a temporary file to uncompress the contents */
427 FILE *infile = tmpfile ();
428 if (infile == NULL)
429 {
430 fprintf (stderr, "%s\n", ERROR_READING_FILE);
431 exit (1);
432 }
433 /* Put the uncompressed content to the temp file */
434 char buf[4096];
435 while (gzread (insourcefile, buf, 4096))
436 {
437 int res = fwrite (buf, sizeof(char), strlen (buf), infile);
438 if (res == 0)
439 {
440 fprintf (stderr, "%s\n", ERROR_READING_FILE);
441 fclose (infile);
442 gzclose (insourcefile);
443 exit (1);
444 }
445 }
446 /* Close the gzip file */
447 gzclose (insourcefile);
448 /* Flush the temp file buffer and rewind to beginning */
449 fflush (infile);
450 fseek (infile, 0, 0);
451
452 /* Read the temporary file contents to the structure Puzzle */
453 Puzzle p;
454 char line[MAX_CLUE_LENGTH+10];
455 fgets (line, MAX_CLUE_LENGTH + 10, infile);
456 p.grid_size = atoi (line);
457 fgets (line, MAX_CLUE_LENGTH + 10, infile);
458 p.grid_frozen = atoi (line) == 0 ? false : true ;
459 fgets (line, MAX_CLUE_LENGTH + 10, infile);
460 if (strlen (line) != 1)
461 strcpy (p.hashed_password, strtok (line, "\n"));
462 else
463 strcpy (p.hashed_password, "\0");
464 fgets (line, MAX_CLUE_LENGTH + 10, infile);
465 if (strlen (line) != 1)
466 strcpy (p.salt, strtok (line, "\n"));
467 else
468 strcpy (p.salt, "\0");
469
470 /* read each character of the grid */
471 for (int i = 0; i < p.grid_size; i ++ )
472 {
473 fgets (line, MAX_CLUE_LENGTH + 10, infile);
474 for (int j = 0; j < p.grid_size; j ++)
475 p.chars[i][j] = line[j];
476 }
477 /* read the word numbers */
478 for (int i = 0; i < p.grid_size; i ++)
479 {
480 fgets (line, MAX_CLUE_LENGTH + 10, infile);
481 char *token = strtok (line, " ");
482 for (int j = 0; j < p.grid_size; j ++)
483 {
484 if (token != NULL)
485 p.start_across_word[i][j] = atoi (token);
486 token = strtok (NULL, " ");
487 if (token != NULL)
488 p.start_down_word[i][j] = atoi (token);
489 token = strtok (NULL, " ");
490 }
491 }
492 /* read the clues */
493 fgets (line, MAX_CLUE_LENGTH + 10, infile);
494
495 /* across clues */
496 char clues[100][MAX_CLUE_LENGTH];
497 int word_num[100];
498 int c = 0;
499 /* first read the across clues from file */
500 while (1)
501 {
502 fgets (line, MAX_CLUE_LENGTH + 10, infile);
503 /* if reached the end of across clues */
504 if (strcmp (line, "DOWN\n") == 0)
505 break;
506 word_num[c] = atoi (strtok (line, "\t"));
507 char *cl = strtok (NULL, "\n");
508 if (cl != NULL)
509 strcpy (clues[c], cl);
510 else
511 strcpy (clues[c], "\0");
512 c++;
513 }
514 /* set the clue to the correct cell in grid */
515 for (int i = 0; i < p.grid_size; i ++)
516 {
517 for (int j = 0; j < p.grid_size; j ++)
518 {
519 for (int r = 0; r < c; r ++)
520 if (p.start_across_word[i][j] == word_num[r])
521 strcpy (p.clue_across[i][j], clues[r]);
522 }
523 }
524
525 /* down clues */
526 c = 0;
527 while (fgets (line, MAX_CLUE_LENGTH + 10, infile))
528 {
529 word_num[c] = atoi (strtok (line, "\t"));
530 char* cl = strtok (NULL, "\n");
531 if (cl != NULL)
532 strcpy (clues[c], cl);
533 else
534 strcpy (clues[c], "\0");
535 c++;
536 }
537 for (int i = 0; i < p.grid_size; i ++)
538 {
539 for (int j = 0; j < p.grid_size; j ++)
540 {
541 for (int r = 0; r < c; r ++)
542 if (p.start_down_word[i][j] == word_num[r])
543 strcpy (p.clue_down[i][j], clues[r]);
544 }
545 }
546
547 fclose (infile);
548 return p;
549 }
550
551 /* display the puzzle */
552 void print_puzzle (Puzzle *p)
553 {
554 printf ("\n");
555 set_color (WHITE, CYAN, NORMAL);
556 printf (" ");
557 for (int i = 0; i < p->grid_size; i ++)
558 printf ("%3d", i);
559 reset_color ();
560 printf("\n");
561 for (int i = 0; i < p->grid_size; i ++)
562 {
563 set_color (WHITE, CYAN, NORMAL);
564 printf ("%3d ", i);
565 for (int j = 0; j < p->grid_size; j ++)
566 {
567 if (p->chars[i][j] == '#') {
568 set_color (WHITE, BLACK, NORMAL);
569 printf (" ");
570 }
571 else
572 {
573 if (p->start_across_word[i][j] != -1 ||
574 p->start_down_word[i][j] != -1)
575 {
576 set_color (BLUE, WHITE, NORMAL);
577 if (p->start_across_word[i][j] != -1)
578 printf ("%-2d", p->start_across_word[i][j]);
579 else
580 printf ("%-2d", p->start_down_word[i][j]);
581 }
582 else
583 {
584 set_color (BLACK, WHITE,NORMAL);
585 printf (" ");
586 }
587
588 set_color (BLACK, WHITE, BOLD);
589 printf ("%c", p->chars[i][j]);
590 }
591 reset_color ();
592 }
593 printf ("\n");
594 }
595 /* print the clues if set */
596 if (p->grid_frozen == true)
597 {
598 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
599 for (int i = 0; i < p->grid_size; i ++)
600 {
601 for (int j = 0; j < p->grid_size; j ++)
602 {
603 if (p->start_across_word[i][j] != -1)
604 {
605 printf ("%d - %s; ", p->start_across_word[i][j],
606 p->clue_across[i][j]);
607 }
608 }
609 }
610 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
611 for (int i = 0; i < p->grid_size; i ++)
612 {
613 for (int j = 0; j < p->grid_size; j ++)
614 {
615 if (p->start_down_word[i][j] != -1)
616 {
617 printf ("%d - %s; ", p->start_down_word[i][j],
618 p->clue_down[i][j]);
619 }
620 }
621 }
622 printf ("\n");
623 }
624 }
625
626 /* function to check if a word is valid or not */
627 char* is_valid_word (char *word)
628 {
629 if (word == NULL || strlen(word) == 0)
630 return NULL;
631 for (int i = 0; i < strlen (word) - 1; i ++)
632 if (! isalpha (word[i]))
633 return NULL;
634
635 return strtok (word, "\n");
636 }
637
638
639 /* function to set a clue for an across word */
640 bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
641 {
642 for (int i = 0; i < p->grid_size; i ++)
643 {
644 for (int j = 0; j < p->grid_size; j ++)
645 {
646 if (order == ACROSS)
647 {
648 if (p->start_across_word[i][j] == index)
649 {
650 strcpy (p->clue_across[i][j], clue);
651 return true;
652 }
653 }
654 else if (order == DOWN)
655 {
656 if (p->start_down_word[i][j] == index)
657 {
658 strcpy (p->clue_down[i][j], clue);
659 return true;
660 }
661 }
662 }
663 }
664 return false;
665 }
666
667 /* function to print a menu */
668 void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
669 char **items, int num_items, int padding)
670 {
671 /* clear screen */
672 printf ("\e[1;1H\e[2J");
673 set_color (fg, bg, NORMAL);
674 printf ("\u2554");
675 for (int i = 0; i < padding; i ++)
676 printf ("\u2550");
677 printf ("\u2557");
678 reset_color (); printf ("\n");
679 printf ("\u2551");
680 set_color (fg, bg, BOLD);
681 printf ("%-*s", padding, title);
682 reset_color ();
683 set_color (fg, bg, NORMAL);
684 printf ("\u2551");
685 reset_color (); printf ("\n");
686 set_color (fg, bg, NORMAL);
687 printf ("\u2560");
688 for (int i = 0; i < padding; i ++)
689 printf ("\u2550");
690 printf ("\u2563");
691 reset_color (); printf ("\n");
692 for (int i = 0; i < num_items; i ++)
693 {
694 set_color (fg, bg, NORMAL);
695 printf ("\u2551%-*s\u2551", padding, items[i]);
696 reset_color (); printf ("\n");
697 }
698 set_color (fg, bg, NORMAL);
699 printf ("\u255A");
700 for (int i = 0; i < padding; i ++)
701 printf ("\u2550");
702 printf ("\u255D");
703 reset_color (); printf ("\n");
704 }
705
706 #endif