22963e4dba5d2ffb1b933fcc74de78e401916a75
9 #include "constantstrings.h"
11 #define MAX_PUZZLE_SIZE 20
12 #define MAX_CLUE_LENGTH 150
14 /* Enum to define terminal colours */
36 typedef char String
[MAX_CLUE_LENGTH
];
38 /* The main puzzle struct type */
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
];
47 char hashed_password
[256];
51 /* get a number from the user */
61 bool verify_password (Puzzle
*p
, const char* password
)
64 if (strcmp (p
->hashed_password
, "\0") == 0)
67 /* hash the user input password and compare it with the stored password */
68 char* hashed_password
= crypt (password
, (const char *)p
->salt
);
70 if (strcmp (p
->hashed_password
, hashed_password
) == 0)
76 /* Set or reset password for puzzle */
77 void set_puzzle_password (Puzzle
*p
, const char *password
)
79 /* if it is a null string, reset the password */
80 if (strcmp (password
, "\0") == 0)
82 strcpy (p
->hashed_password
, "\0");
83 strcpy (p
->salt
, "\0");
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
);
96 /* Output the clues to text file */
97 void export_clues (Puzzle
*p
, const char *filename
)
99 FILE *outfile
= fopen (filename
, "w");
102 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
105 /* first the across clues */
106 fprintf (outfile
, "ACROSS CLUES\n");
107 for (int i
= 0; i
< p
->grid_size
; i
++)
109 for (int j
= 0; j
< p
->grid_size
; j
++)
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
]);
116 /* now the down clues */
117 fprintf (outfile
, "DOWN CLUES\n");
118 for (int i
= 0; i
< p
->grid_size
; i
++)
120 for (int j
= 0; j
< p
->grid_size
; j
++)
122 if (p
->start_down_word
[i
][j
] != -1)
123 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
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
)
133 int img_size
= p
->grid_size
* 40;
134 FILE * outfile
= fopen (filename
, "wb");
137 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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 ();
148 for (int i
= 0; i
< p
->grid_size
; i
++)
150 for (int j
= 0; j
< p
->grid_size
; j
++)
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,
158 /* draw a regular square */
159 gdImageRectangle (img
, j
*40, i
*40, j
*40+40,
162 /* print the numers, if it is either start across word or
164 if (p
->start_across_word
[i
][j
] != -1 ||
165 p
->start_down_word
[i
][j
] != -1)
167 if (p
->start_across_word
[i
][j
] != -1)
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
);
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
);
182 /* if answerkey is true, draw the character in the cell */
185 gdImageChar (img
, lg_fnt
, j
*40+15, i
*40+15,
186 p
->chars
[i
][j
], black
);
192 gdImagePng (img
, outfile
);
193 gdImageDestroy (img
);
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
);
202 /* Reset the terminal colour */
203 void reset_color () {
207 /* check if previous row is blank or not */
208 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
210 if (r
== 0) return true;
211 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
214 /* check if next row is blank or not */
215 bool next_row_blank (Puzzle
*p
, int r
, int c
)
217 if (r
== p
->grid_size
- 1) return true;
218 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
221 /* check if previous col is blank or not */
222 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
224 if (c
== 0) return true;
225 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
228 /* check if the next col is blank or not */
229 bool next_col_blank (Puzzle
*p
, int r
, int c
)
231 if (c
== p
->grid_size
-1) return true;
232 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
236 /* unfreeze the grid - mak editing possible to change words */
237 void unfreeze_puzzle (Puzzle
*p
)
239 for (int i
= 0; i
< p
->grid_size
; i
++)
241 for (int j
= 0; j
< p
->grid_size
; j
++)
243 if (p
->chars
[i
][j
] == '#')
244 p
->chars
[i
][j
] = ' ';
246 p
->start_across_word
[i
][j
] = -1;
247 p
->start_down_word
[i
][j
] = -1;
250 p
->grid_frozen
= false;
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
)
258 bool across_word_start
, down_word_start
;
259 for (int i
= 0; i
< p
->grid_size
; i
++)
261 for (int j
= 0; j
< p
->grid_size
; j
++)
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 */
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;
281 if (across_word_start
== true)
282 p
->start_across_word
[i
][j
] = word_num
;
284 p
->start_across_word
[i
][j
] = -1;
285 if (down_word_start
== true)
286 p
->start_down_word
[i
][j
] = word_num
;
288 p
->start_down_word
[i
][j
] = -1;
289 if (across_word_start
== true || down_word_start
== true)
293 p
->grid_frozen
= true;
296 /* reset the entire grid */
297 void init_puzzle (Puzzle
*p
, int grid_size
)
299 p
->grid_size
= grid_size
;
300 p
->grid_frozen
= false;
301 for (int i
= 0; i
< p
->grid_size
; i
++)
303 for (int j
= 0; j
< p
->grid_size
; j
++)
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
], "");
312 strcpy (p
->hashed_password
, "\0");
313 strcpy (p
->salt
, "\0");
317 /* save the puzzle to a file */
318 void save_puzzle (Puzzle
*puzzle
, const char* file
) {
320 /* First output the uncompressed contents to temp file */
321 outfile
= tmpfile ();
324 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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
);
334 fprintf (outfile
, "%s\n", puzzle
->salt
);
336 /* First output the grid characters columns/rows */
337 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
339 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
340 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
341 fprintf (outfile
, "\n");
344 /* Next output the start across/down numbers */
345 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
347 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
349 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
350 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
352 fprintf (outfile
, "\n");
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
++)
360 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
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
++)
375 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
385 /* Flush the buffer and rewind to beginning - to read and save into
386 gzip compressed file */
388 fseek (outfile
, 0, 0);
390 /* now compress the file and save it to destination file */
391 gzFile outdestfile
= gzopen (file
, "wb");
392 if (outdestfile
== NULL
)
394 fprintf (stderr
, ERROR_WRITING_FILE
);
399 while (fread (buf
, sizeof(char), 4096, outfile
))
401 int res
= gzwrite (outdestfile
, buf
, strlen (buf
) );
404 fprintf (stderr
, "%s %s", ERROR_WRITING_FILE
, COMPRESSED
);
409 gzclose (outdestfile
);
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
)
420 fprintf (stderr
, "%s %s", ERROR_READING_FILE
, COMPRESSED
);
423 /* Open a temporary file to uncompress the contents */
424 FILE *infile
= tmpfile ();
427 fprintf (stderr
, ERROR_READING_FILE
);
430 /* Put the uncompressed content to the temp file */
432 while (gzread (insourcefile
, buf
, 4096))
434 int res
= fwrite (buf
, sizeof(char), strlen (buf
), infile
);
437 fprintf (stderr
, ERROR_READING_FILE
);
439 gzclose (insourcefile
);
443 /* Close the gzip file */
444 gzclose (insourcefile
);
445 /* Flush the temp file buffer and rewind to beginning */
447 fseek (infile
, 0, 0);
449 /* Read the temporary file contents to the structure Puzzle */
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"));
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"));
465 strcpy (p
.salt
, "\0");
467 /* read each character of the grid */
468 for (int i
= 0; i
< p
.grid_size
; i
++ )
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
];
474 /* read the word numbers */
475 for (int i
= 0; i
< p
.grid_size
; i
++)
477 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
478 char *token
= strtok (line
, " ");
479 for (int j
= 0; j
< p
.grid_size
; j
++)
482 p
.start_across_word
[i
][j
] = atoi (token
);
483 token
= strtok (NULL
, " ");
485 p
.start_down_word
[i
][j
] = atoi (token
);
486 token
= strtok (NULL
, " ");
490 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
493 char clues
[100][MAX_CLUE_LENGTH
];
496 /* first read the across clues from file */
499 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
500 /* if reached the end of across clues */
501 if (strcmp (line
, "DOWN\n") == 0)
503 word_num
[c
] = atoi (strtok (line
, "\t"));
504 char *cl
= strtok (NULL
, "\n");
506 strcpy (clues
[c
], cl
);
508 strcpy (clues
[c
], "\0");
511 /* set the clue to the correct cell in grid */
512 for (int i
= 0; i
< p
.grid_size
; i
++)
514 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
524 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
526 word_num
[c
] = atoi (strtok (line
, "\t"));
527 char* cl
= strtok (NULL
, "\n");
529 strcpy (clues
[c
], cl
);
531 strcpy (clues
[c
], "\0");
534 for (int i
= 0; i
< p
.grid_size
; i
++)
536 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
548 /* display the puzzle */
549 void print_puzzle (Puzzle
*p
)
552 set_color (WHITE
, CYAN
, NORMAL
);
554 for (int i
= 0; i
< p
->grid_size
; i
++)
558 for (int i
= 0; i
< p
->grid_size
; i
++)
560 set_color (WHITE
, CYAN
, NORMAL
);
562 for (int j
= 0; j
< p
->grid_size
; j
++)
564 if (p
->chars
[i
][j
] == '#') {
565 set_color (WHITE
, BLACK
, NORMAL
);
570 if (p
->start_across_word
[i
][j
] != -1 ||
571 p
->start_down_word
[i
][j
] != -1)
573 set_color (BLUE
, WHITE
, NORMAL
);
574 if (p
->start_across_word
[i
][j
] != -1)
575 printf ("%-2d", p
->start_across_word
[i
][j
]);
577 printf ("%-2d", p
->start_down_word
[i
][j
]);
581 set_color (BLACK
, WHITE
,NORMAL
);
585 set_color (BLACK
, WHITE
, BOLD
);
586 printf ("%c", p
->chars
[i
][j
]);
592 /* print the clues if set */
593 if (p
->grid_frozen
== true)
595 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
596 for (int i
= 0; i
< p
->grid_size
; i
++)
598 for (int j
= 0; j
< p
->grid_size
; j
++)
600 if (p
->start_across_word
[i
][j
] != -1)
602 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
603 p
->clue_across
[i
][j
]);
607 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
608 for (int i
= 0; i
< p
->grid_size
; i
++)
610 for (int j
= 0; j
< p
->grid_size
; j
++)
612 if (p
->start_down_word
[i
][j
] != -1)
614 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
623 /* function to check if a word is valid or not */
624 char* is_valid_word (char *word
)
626 if (word
== NULL
|| strlen(word
) == 0)
628 for (int i
= 0; i
< strlen (word
) - 1; i
++)
629 if (! isalpha (word
[i
]))
632 return strtok (word
, "\n");
636 /* function to set a clue for an across word */
637 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
639 for (int i
= 0; i
< p
->grid_size
; i
++)
641 for (int j
= 0; j
< p
->grid_size
; j
++)
645 if (p
->start_across_word
[i
][j
] == index
)
647 strcpy (p
->clue_across
[i
][j
], clue
);
651 else if (order
== DOWN
)
653 if (p
->start_down_word
[i
][j
] == index
)
655 strcpy (p
->clue_down
[i
][j
], clue
);
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
)
669 printf ("\e[1;1H\e[2J");
670 set_color (fg
, bg
, NORMAL
);
672 for (int i
= 0; i
< padding
; i
++)
675 reset_color (); printf ("\n");
677 set_color (fg
, bg
, BOLD
);
678 printf ("%-*s", padding
, title
);
680 set_color (fg
, bg
, NORMAL
);
682 reset_color (); printf ("\n");
683 set_color (fg
, bg
, NORMAL
);
685 for (int i
= 0; i
< padding
; i
++)
688 reset_color (); printf ("\n");
689 for (int i
= 0; i
< num_items
; i
++)
691 set_color (fg
, bg
, NORMAL
);
692 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
693 reset_color (); printf ("\n");
695 set_color (fg
, bg
, NORMAL
);
697 for (int i
= 0; i
< padding
; i
++)
700 reset_color (); printf ("\n");