9a5c27ce2c6fc32edc54ba937906cad0e3bfbaae
12 #include "constantstrings.h"
14 #define MAX_PUZZLE_SIZE 20
15 #define MAX_CLUE_LENGTH 150
17 /* Enum to define terminal colours */
39 typedef char String
[MAX_CLUE_LENGTH
];
41 /* The main puzzle struct type */
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
];
50 char hashed_password
[256];
54 /* get a number from the user */
64 bool verify_password (Puzzle
*p
, const char* password
)
67 if (strcmp (p
->hashed_password
, "\0") == 0)
70 /* hash the user input password and compare it with the stored password */
71 char* hashed_password
= crypt (password
, (const char *)p
->salt
);
73 if (strcmp (p
->hashed_password
, hashed_password
) == 0)
79 /* Set or reset password for puzzle */
80 void set_puzzle_password (Puzzle
*p
, const char *password
)
82 /* if it is a null string, reset the password */
83 if (strcmp (password
, "\0") == 0)
85 strcpy (p
->hashed_password
, "\0");
86 strcpy (p
->salt
, "\0");
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
);
99 /* Output the clues to text file */
100 void export_clues (Puzzle
*p
, const char *filename
)
102 FILE *outfile
= fopen (filename
, "w");
105 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
108 /* first the across clues */
109 fprintf (outfile
, "ACROSS CLUES\n");
110 for (int i
= 0; i
< p
->grid_size
; i
++)
112 for (int j
= 0; j
< p
->grid_size
; j
++)
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
]);
119 /* now the down clues */
120 fprintf (outfile
, "DOWN CLUES\n");
121 for (int i
= 0; i
< p
->grid_size
; i
++)
123 for (int j
= 0; j
< p
->grid_size
; j
++)
125 if (p
->start_down_word
[i
][j
] != -1)
126 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
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
)
136 int img_size
= p
->grid_size
* 40;
137 FILE * outfile
= fopen (filename
, "wb");
140 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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 ();
151 for (int i
= 0; i
< p
->grid_size
; i
++)
153 for (int j
= 0; j
< p
->grid_size
; j
++)
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,
161 /* draw a regular square */
162 gdImageRectangle (img
, j
*40, i
*40, j
*40+40,
165 /* print the numers, if it is either start across word or
167 if (p
->start_across_word
[i
][j
] != -1 ||
168 p
->start_down_word
[i
][j
] != -1)
170 if (p
->start_across_word
[i
][j
] != -1)
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
);
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
);
185 /* if answerkey is true, draw the character in the cell */
188 gdImageChar (img
, lg_fnt
, j
*40+15, i
*40+15,
189 p
->chars
[i
][j
], black
);
195 gdImagePng (img
, outfile
);
196 gdImageDestroy (img
);
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
);
205 /* Reset the terminal colour */
206 void reset_color () {
210 /* check if previous row is blank or not */
211 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
213 if (r
== 0) return true;
214 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
217 /* check if next row is blank or not */
218 bool next_row_blank (Puzzle
*p
, int r
, int c
)
220 if (r
== p
->grid_size
- 1) return true;
221 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
224 /* check if previous col is blank or not */
225 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
227 if (c
== 0) return true;
228 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
231 /* check if the next col is blank or not */
232 bool next_col_blank (Puzzle
*p
, int r
, int c
)
234 if (c
== p
->grid_size
-1) return true;
235 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
239 /* unfreeze the grid - make editing possible to change words */
240 void unfreeze_puzzle (Puzzle
*p
)
242 for (int i
= 0; i
< p
->grid_size
; i
++)
244 for (int j
= 0; j
< p
->grid_size
; j
++)
246 if (p
->chars
[i
][j
] == '#')
247 p
->chars
[i
][j
] = ' ';
249 p
->start_across_word
[i
][j
] = -1;
250 p
->start_down_word
[i
][j
] = -1;
253 p
->grid_frozen
= false;
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
)
261 bool across_word_start
, down_word_start
;
262 for (int i
= 0; i
< p
->grid_size
; i
++)
264 for (int j
= 0; j
< p
->grid_size
; j
++)
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 */
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;
284 if (across_word_start
== true)
285 p
->start_across_word
[i
][j
] = word_num
;
287 p
->start_across_word
[i
][j
] = -1;
288 if (down_word_start
== true)
289 p
->start_down_word
[i
][j
] = word_num
;
291 p
->start_down_word
[i
][j
] = -1;
292 if (across_word_start
== true || down_word_start
== true)
296 p
->grid_frozen
= true;
299 /* reset the entire grid */
300 void init_puzzle (Puzzle
*p
, int grid_size
)
302 p
->grid_size
= grid_size
;
303 p
->grid_frozen
= false;
304 for (int i
= 0; i
< p
->grid_size
; i
++)
306 for (int j
= 0; j
< p
->grid_size
; j
++)
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
], "");
315 strcpy (p
->hashed_password
, "\0");
316 strcpy (p
->salt
, "\0");
320 /* save the puzzle to a file */
321 void save_puzzle (Puzzle
*puzzle
, const char* file
) {
323 /* First output the uncompressed contents to a temp file */
324 outfile
= tmpfile ();
327 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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
);
337 fprintf (outfile
, "%s\n", puzzle
->salt
);
339 /* First output the grid characters columns/rows */
340 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
342 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
343 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
344 fprintf (outfile
, "\n");
347 /* Next output the start across/down numbers */
348 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
350 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
352 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
353 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
355 fprintf (outfile
, "\n");
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
++)
363 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
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
++)
378 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
388 /* Flush the buffer and rewind to beginning - to read and save into
389 gzip compressed file */
391 fseek (outfile
, 0, 0);
393 /* now compress the file and save it to destination file */
394 gzFile outdestfile
= gzopen (file
, "wb");
395 if (outdestfile
== NULL
)
397 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
402 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
405 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
408 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
412 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
414 gzclose (outdestfile
);
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
)
425 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
428 /* Open a temporary file to uncompress the contents */
429 FILE *infile
= tmpfile ();
432 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
435 /* Put the uncompressed content to the temp file */
438 num
= gzread (insourcefile
, buf
, 128);
441 int res
= fwrite (buf
, 1, num
, infile
);
444 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
446 gzclose (insourcefile
);
449 num
= gzread (insourcefile
, buf
, 128);
451 /* Close the gzip file */
452 gzclose (insourcefile
);
453 /* Flush the temp file buffer and rewind to beginning */
455 fseek (infile
, 0, 0);
457 /* Read the temporary file contents to the structure Puzzle */
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"));
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"));
473 strcpy (p
.salt
, "\0");
475 /* read each character of the grid */
476 for (int i
= 0; i
< p
.grid_size
; i
++ )
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
];
482 /* read the word numbers */
483 for (int i
= 0; i
< p
.grid_size
; i
++)
485 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
486 char *token
= strtok (line
, " ");
487 for (int j
= 0; j
< p
.grid_size
; j
++)
490 p
.start_across_word
[i
][j
] = atoi (token
);
491 token
= strtok (NULL
, " ");
493 p
.start_down_word
[i
][j
] = atoi (token
);
494 token
= strtok (NULL
, " ");
498 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
501 char clues
[100][MAX_CLUE_LENGTH
];
504 /* first read the across clues from file */
507 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
508 /* if reached the end of across clues */
509 if (strcmp (line
, "DOWN\n") == 0)
511 word_num
[c
] = atoi (strtok (line
, "\t"));
512 char *cl
= strtok (NULL
, "\n");
514 strcpy (clues
[c
], cl
);
516 strcpy (clues
[c
], "\0");
519 /* set the clue to the correct cell in grid */
520 for (int i
= 0; i
< p
.grid_size
; i
++)
522 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
532 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
534 word_num
[c
] = atoi (strtok (line
, "\t"));
535 char* cl
= strtok (NULL
, "\n");
537 strcpy (clues
[c
], cl
);
539 strcpy (clues
[c
], "\0");
542 for (int i
= 0; i
< p
.grid_size
; i
++)
544 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
556 /* display the puzzle */
557 void print_puzzle (Puzzle
*p
)
560 set_color (WHITE
, CYAN
, NORMAL
);
562 for (int i
= 0; i
< p
->grid_size
; i
++)
566 for (int i
= 0; i
< p
->grid_size
; i
++)
568 set_color (WHITE
, CYAN
, NORMAL
);
570 for (int j
= 0; j
< p
->grid_size
; j
++)
572 if (p
->chars
[i
][j
] == '#') {
573 set_color (WHITE
, BLACK
, NORMAL
);
578 if (p
->start_across_word
[i
][j
] != -1 ||
579 p
->start_down_word
[i
][j
] != -1)
581 set_color (BLUE
, WHITE
, NORMAL
);
582 if (p
->start_across_word
[i
][j
] != -1)
583 printf ("%-2d", p
->start_across_word
[i
][j
]);
585 printf ("%-2d", p
->start_down_word
[i
][j
]);
589 set_color (BLACK
, WHITE
,NORMAL
);
593 set_color (BLACK
, WHITE
, BOLD
);
594 printf ("%c", p
->chars
[i
][j
]);
600 /* print the clues if set */
601 if (p
->grid_frozen
== true)
603 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
604 for (int i
= 0; i
< p
->grid_size
; i
++)
606 for (int j
= 0; j
< p
->grid_size
; j
++)
608 if (p
->start_across_word
[i
][j
] != -1)
610 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
611 p
->clue_across
[i
][j
]);
615 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
616 for (int i
= 0; i
< p
->grid_size
; i
++)
618 for (int j
= 0; j
< p
->grid_size
; j
++)
620 if (p
->start_down_word
[i
][j
] != -1)
622 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
631 /* function to check if a word is valid or not */
632 char* is_valid_word (char *word
)
634 if (word
== NULL
|| strlen(word
) == 0)
636 for (int i
= 0; i
< strlen (word
) - 1; i
++)
637 if (! isalpha (word
[i
]))
640 return strtok (word
, "\n");
644 /* function to set a clue for an across word */
645 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
647 for (int i
= 0; i
< p
->grid_size
; i
++)
649 for (int j
= 0; j
< p
->grid_size
; j
++)
653 if (p
->start_across_word
[i
][j
] == index
)
655 strcpy (p
->clue_across
[i
][j
], clue
);
659 else if (order
== DOWN
)
661 if (p
->start_down_word
[i
][j
] == index
)
663 strcpy (p
->clue_down
[i
][j
], clue
);
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
)
677 printf ("\e[1;1H\e[2J");
678 set_color (fg
, bg
, NORMAL
);
680 for (int i
= 0; i
< padding
; i
++)
683 reset_color (); printf ("\n");
685 set_color (fg
, bg
, BOLD
);
686 printf ("%-*s", padding
, title
);
688 set_color (fg
, bg
, NORMAL
);
690 reset_color (); printf ("\n");
691 set_color (fg
, bg
, NORMAL
);
693 for (int i
= 0; i
< padding
; i
++)
696 reset_color (); printf ("\n");
697 for (int i
= 0; i
< num_items
; i
++)
699 set_color (fg
, bg
, NORMAL
);
700 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
701 reset_color (); printf ("\n");
703 set_color (fg
, bg
, NORMAL
);
705 for (int i
= 0; i
< padding
; i
++)
708 reset_color (); printf ("\n");