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