2978e0da3df0858f1c3c73b0fd51cbc31cb3abf8
12 #include "constantstrings.h"
14 #define MAX_PUZZLE_SIZE 25
15 #define MAX_CLUE_LENGTH 150
16 #define GRID_PIXELS 37
18 /* Enum to define terminal colours */
40 /* for use with the player */
46 typedef char String
[MAX_CLUE_LENGTH
];
48 /* The main puzzle struct type */
50 char chars
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
51 int start_across_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
52 int start_down_word
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
53 String clue_across
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
54 String clue_down
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
57 char hashed_password
[256];
61 /* The player data struct type - for the player app */
66 char char_ans
[MAX_PUZZLE_SIZE
][MAX_PUZZLE_SIZE
];
72 /* get a number from the user */
82 bool verify_password (Puzzle
*p
, const char* password
)
85 if (strcmp (p
->hashed_password
, "\0") == 0)
88 /* hash the user input password and compare it with the stored password */
89 char* hashed_password
= crypt (password
, (const char *)p
->salt
);
91 if (strcmp (p
->hashed_password
, hashed_password
) == 0)
97 /* Set or reset password for puzzle */
98 void set_puzzle_password (Puzzle
*p
, const char *password
)
100 /* if it is a null string, reset the password */
101 if (strcmp (password
, "\0") == 0)
103 strcpy (p
->hashed_password
, "\0");
104 strcpy (p
->salt
, "\0");
110 sprintf (salt
, "puzzle%d", rand()%1000);
111 char* hashedpwd
= crypt (password
, (const char*)salt
);
112 strcpy (p
->hashed_password
, hashedpwd
);
113 strcpy (p
->salt
, salt
);
117 /* Output the clues to text file */
118 void export_clues (Puzzle
*p
, const char *filename
)
120 FILE *outfile
= fopen (filename
, "w");
123 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
126 /* first the across clues */
127 fprintf (outfile
, "ACROSS CLUES\n");
128 for (int i
= 0; i
< p
->grid_size
; i
++)
130 for (int j
= 0; j
< p
->grid_size
; j
++)
132 if (p
->start_across_word
[i
][j
] != -1)
133 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
134 p
->clue_across
[i
][j
]);
137 /* now the down clues */
138 fprintf (outfile
, "DOWN CLUES\n");
139 for (int i
= 0; i
< p
->grid_size
; i
++)
141 for (int j
= 0; j
< p
->grid_size
; j
++)
143 if (p
->start_down_word
[i
][j
] != -1)
144 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
151 /* Output the grid to image - if answerkey is true export filled grid */
152 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
154 int img_size
= p
->grid_size
* GRID_PIXELS
;
155 FILE * outfile
= fopen (filename
, "wb");
158 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
162 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
163 gdImageColorAllocate (img
, 255,255,255);
164 int black
= gdImageColorAllocate (img
, 0, 0, 0);
165 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
166 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
167 gdFontPtr lg_fnt
= gdFontGetGiant ();
169 for (int i
= 0; i
< p
->grid_size
; i
++)
171 for (int j
= 0; j
< p
->grid_size
; j
++)
173 /* if it is a block, draw the black square */
174 if (p
->chars
[i
][j
] == '#')
175 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
176 j
*GRID_PIXELS
+GRID_PIXELS
,
177 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
180 /* draw a regular square */
181 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
182 j
*GRID_PIXELS
+GRID_PIXELS
,
183 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
185 /* print the numers, if it is either start across word or
187 if (p
->start_across_word
[i
][j
] != -1 ||
188 p
->start_down_word
[i
][j
] != -1)
190 if (p
->start_across_word
[i
][j
] != -1)
193 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
194 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
196 (unsigned char *)str
, blue
);
201 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
202 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
204 (unsigned char *)str
, blue
);
207 /* if answerkey is true, draw the character in the cell */
210 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
211 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
217 gdImagePng (img
, outfile
);
218 gdImageDestroy (img
);
222 /* Set the terminal colour */
223 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
224 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
227 /* Reset the terminal colour */
228 void reset_color () {
232 /* check if previous row is blank or not */
233 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
235 if (r
== 0) return true;
236 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
239 /* check if next row is blank or not */
240 bool next_row_blank (Puzzle
*p
, int r
, int c
)
242 if (r
== p
->grid_size
- 1) return true;
243 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
246 /* check if previous col is blank or not */
247 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
249 if (c
== 0) return true;
250 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
253 /* check if the next col is blank or not */
254 bool next_col_blank (Puzzle
*p
, int r
, int c
)
256 if (c
== p
->grid_size
-1) return true;
257 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
261 /* unfreeze the grid - make editing possible to change words */
262 void unfreeze_puzzle (Puzzle
*p
)
264 for (int i
= 0; i
< p
->grid_size
; i
++)
266 for (int j
= 0; j
< p
->grid_size
; j
++)
268 if (p
->chars
[i
][j
] == '#')
269 p
->chars
[i
][j
] = ' ';
271 p
->start_across_word
[i
][j
] = -1;
272 p
->start_down_word
[i
][j
] = -1;
275 p
->grid_frozen
= false;
278 /* freeze the grid - make editing impossible because it finalizes the
279 across and down words in the grid */
280 void freeze_puzzle (Puzzle
*p
)
283 bool across_word_start
, down_word_start
;
284 for (int i
= 0; i
< p
->grid_size
; i
++)
286 for (int j
= 0; j
< p
->grid_size
; j
++)
288 across_word_start
= false;
289 down_word_start
= false;
290 /* if it is a blank cell - cover it with a block */
291 if (p
->chars
[i
][j
] == ' ' || p
->chars
[i
][j
] == '#')
292 p
->chars
[i
][j
] = '#';
293 /* it is not a blank cell - check all possibilities */
296 bool prev_row
= prev_row_blank (p
, i
, j
);
297 bool next_row
= next_row_blank (p
, i
, j
);
298 bool prev_col
= prev_col_blank (p
, i
, j
);
299 bool next_col
= next_col_blank (p
, i
, j
);
300 if (prev_row
&& ! next_row
)
301 down_word_start
= true;
302 if (prev_col
&& ! next_col
)
303 across_word_start
= true;
306 if (across_word_start
== true)
307 p
->start_across_word
[i
][j
] = word_num
;
309 p
->start_across_word
[i
][j
] = -1;
310 if (down_word_start
== true)
311 p
->start_down_word
[i
][j
] = word_num
;
313 p
->start_down_word
[i
][j
] = -1;
314 if (across_word_start
== true || down_word_start
== true)
318 p
->grid_frozen
= true;
321 /* reset the entire grid */
322 void init_puzzle (Puzzle
*p
, int grid_size
)
324 p
->grid_size
= grid_size
;
325 p
->grid_frozen
= false;
326 for (int i
= 0; i
< p
->grid_size
; i
++)
328 for (int j
= 0; j
< p
->grid_size
; j
++)
330 p
->chars
[i
][j
] = ' ';
331 p
->start_across_word
[i
][j
] = -1;
332 p
->start_down_word
[i
][j
] = -1;
333 strcpy (p
->clue_across
[i
][j
], "");
334 strcpy (p
->clue_down
[i
][j
], "");
337 strcpy (p
->hashed_password
, "\0");
338 strcpy (p
->salt
, "\0");
342 /* save the puzzle to a file */
343 void save_puzzle (Puzzle
*puzzle
, const char* file
) {
345 /* First output the uncompressed contents to a temp file */
346 outfile
= tmpfile ();
349 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
352 /* grid size is the first field */
353 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
354 /* whether grid is frozen or not */
355 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
356 /* the hashed password */
357 fprintf (outfile
, "%s\n", puzzle
->hashed_password
);
359 fprintf (outfile
, "%s\n", puzzle
->salt
);
361 /* First output the grid characters columns/rows */
362 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
364 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
365 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
366 fprintf (outfile
, "\n");
369 /* Next output the start across/down numbers */
370 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
372 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
374 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
375 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
377 fprintf (outfile
, "\n");
380 /* Output the across clues */
381 fprintf (outfile
, "ACROSS\n");
382 /* Search the grid for across words */
383 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
385 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
387 /* if it is an across word, then put the word index followed by
388 tab character (as separator) and the clue */
389 if (puzzle
->start_across_word
[i
][j
] != -1)
390 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
391 puzzle
->clue_across
[i
][j
]);
395 /* Output the down clues */
396 fprintf (outfile
, "DOWN\n");
397 /* Search the grid for down words */
398 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
400 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
402 /* same as across word, put the word index followed by the tab
403 character and then the clue */
404 if (puzzle
->start_down_word
[i
][j
] != -1)
405 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
406 puzzle
->clue_down
[i
][j
]);
410 /* Flush the buffer and rewind to beginning - to read and save into
411 gzip compressed file */
413 fseek (outfile
, 0, 0);
415 /* now compress the file and save it to destination file */
416 gzFile outdestfile
= gzopen (file
, "wb");
417 if (outdestfile
== NULL
)
419 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
424 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
427 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
430 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
434 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
436 gzclose (outdestfile
);
441 /* read the puzzle from a file */
442 Puzzle
load_puzzle (const char* file
) {
443 /* First open the GZip file */
444 gzFile insourcefile
= gzopen (file
, "rb");
445 if (insourcefile
== NULL
)
447 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
450 /* Open a temporary file to uncompress the contents */
451 FILE *infile
= tmpfile ();
454 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
457 /* Put the uncompressed content to the temp file */
460 num
= gzread (insourcefile
, buf
, 128);
463 int res
= fwrite (buf
, 1, num
, infile
);
466 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
468 gzclose (insourcefile
);
471 num
= gzread (insourcefile
, buf
, 128);
473 /* Close the gzip file */
474 gzclose (insourcefile
);
475 /* Flush the temp file buffer and rewind to beginning */
477 fseek (infile
, 0, 0);
479 /* Read the temporary file contents to the structure Puzzle */
481 char line
[MAX_CLUE_LENGTH
+10];
482 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
483 p
.grid_size
= atoi (line
);
484 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
485 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
486 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
487 if (strlen (line
) != 1)
488 strcpy (p
.hashed_password
, strtok (line
, "\n"));
490 strcpy (p
.hashed_password
, "\0");
491 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
492 if (strlen (line
) != 1)
493 strcpy (p
.salt
, strtok (line
, "\n"));
495 strcpy (p
.salt
, "\0");
497 /* read each character of the grid */
498 for (int i
= 0; i
< p
.grid_size
; i
++ )
500 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
501 for (int j
= 0; j
< p
.grid_size
; j
++)
502 p
.chars
[i
][j
] = line
[j
];
504 /* read the word numbers */
505 for (int i
= 0; i
< p
.grid_size
; i
++)
507 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
508 char *token
= strtok (line
, " ");
509 for (int j
= 0; j
< p
.grid_size
; j
++)
512 p
.start_across_word
[i
][j
] = atoi (token
);
513 token
= strtok (NULL
, " ");
515 p
.start_down_word
[i
][j
] = atoi (token
);
516 token
= strtok (NULL
, " ");
520 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
523 char clues
[100][MAX_CLUE_LENGTH
];
526 /* first read the across clues from file */
529 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
530 /* if reached the end of across clues */
531 if (strcmp (line
, "DOWN\n") == 0)
533 word_num
[c
] = atoi (strtok (line
, "\t"));
534 char *cl
= strtok (NULL
, "\n");
536 strcpy (clues
[c
], cl
);
538 strcpy (clues
[c
], "\0");
541 /* set the clue to the correct cell in grid */
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_across_word
[i
][j
] == word_num
[r
])
548 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
554 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
556 word_num
[c
] = atoi (strtok (line
, "\t"));
557 char* cl
= strtok (NULL
, "\n");
559 strcpy (clues
[c
], cl
);
561 strcpy (clues
[c
], "\0");
564 for (int i
= 0; i
< p
.grid_size
; i
++)
566 for (int j
= 0; j
< p
.grid_size
; j
++)
568 for (int r
= 0; r
< c
; r
++)
569 if (p
.start_down_word
[i
][j
] == word_num
[r
])
570 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
578 /* display the puzzle */
579 void print_puzzle (Puzzle
*p
)
582 set_color (WHITE
, CYAN
, NORMAL
);
584 for (int i
= 0; i
< p
->grid_size
; i
++)
588 for (int i
= 0; i
< p
->grid_size
; i
++)
590 set_color (WHITE
, CYAN
, NORMAL
);
592 for (int j
= 0; j
< p
->grid_size
; j
++)
594 if (p
->chars
[i
][j
] == '#') {
595 set_color (WHITE
, BLACK
, NORMAL
);
600 if (p
->start_across_word
[i
][j
] != -1 ||
601 p
->start_down_word
[i
][j
] != -1)
603 set_color (BLUE
, WHITE
, NORMAL
);
604 if (p
->start_across_word
[i
][j
] != -1)
605 printf ("%-2d", p
->start_across_word
[i
][j
]);
607 printf ("%-2d", p
->start_down_word
[i
][j
]);
611 set_color (BLACK
, WHITE
,NORMAL
);
615 set_color (BLACK
, WHITE
, BOLD
);
616 printf ("%c", p
->chars
[i
][j
]);
622 /* print the clues if set */
623 if (p
->grid_frozen
== true)
625 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
626 for (int i
= 0; i
< p
->grid_size
; i
++)
628 for (int j
= 0; j
< p
->grid_size
; j
++)
630 if (p
->start_across_word
[i
][j
] != -1)
632 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
633 p
->clue_across
[i
][j
]);
637 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
638 for (int i
= 0; i
< p
->grid_size
; i
++)
640 for (int j
= 0; j
< p
->grid_size
; j
++)
642 if (p
->start_down_word
[i
][j
] != -1)
644 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
653 /* function to check if a word is valid or not */
654 char* is_valid_word (char *word
)
656 if (word
== NULL
|| strlen(word
) == 0)
658 for (int i
= 0; i
< strlen (word
) - 1; i
++)
659 if (! isalpha (word
[i
]))
662 return strtok (word
, "\n");
666 /* function to set a clue for an across word */
667 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
669 for (int i
= 0; i
< p
->grid_size
; i
++)
671 for (int j
= 0; j
< p
->grid_size
; j
++)
675 if (p
->start_across_word
[i
][j
] == index
)
677 strcpy (p
->clue_across
[i
][j
], clue
);
681 else if (order
== DOWN
)
683 if (p
->start_down_word
[i
][j
] == index
)
685 strcpy (p
->clue_down
[i
][j
], clue
);
694 /* function to print a menu */
695 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
696 char **items
, int num_items
, int padding
)
699 printf ("\e[1;1H\e[2J");
700 set_color (fg
, bg
, NORMAL
);
702 for (int i
= 0; i
< padding
; i
++)
705 reset_color (); printf ("\n");
706 set_color (fg
, bg
, NORMAL
);
708 set_color (fg
, bg
, BOLD
);
709 printf ("%-*s", padding
, title
);
711 set_color (fg
, bg
, NORMAL
);
713 reset_color (); printf ("\n");
714 set_color (fg
, bg
, NORMAL
);
716 for (int i
= 0; i
< padding
; i
++)
719 reset_color (); printf ("\n");
720 for (int i
= 0; i
< num_items
; i
++)
722 set_color (fg
, bg
, NORMAL
);
723 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
724 reset_color (); printf ("\n");
726 set_color (fg
, bg
, NORMAL
);
728 for (int i
= 0; i
< padding
; i
++)
731 reset_color (); printf ("\n");
734 /* reset the player data, from the new file */
735 void reset_player_data (MainPlayerData
*app_data
, const char *filename
)
737 app_data
->puzzle
= load_puzzle (filename
);
739 app_data
->is_loaded
= app_data
->puzzle
.grid_frozen
;
740 app_data
->cur_col
= -1;
741 app_data
->cur_row
= -1;
742 strcpy (app_data
->filename
, filename
);
743 /* reset the answer keys */
744 for (int i
= 0; i
< app_data
->puzzle
.grid_size
; i
++)
745 for (int j
= 0; j
< app_data
->puzzle
.grid_size
; j
++)
746 app_data
->char_ans
[i
][j
] = ' ';
750 /* in the player app, move the current selection index left or right */
751 void move_current_col (MainPlayerData
*app_data
, enum DIRECTION dir
)
753 int r
= app_data
->cur_row
;
754 int c
= app_data
->cur_col
;
755 if (dir
== DIR_FORWARD
)
758 while (c
< app_data
->puzzle
.grid_size
)
760 if (app_data
->puzzle
.chars
[r
][c
] == '#')
765 if (c
< app_data
->puzzle
.grid_size
)
766 app_data
->cur_col
= c
;
773 if (app_data
->puzzle
.chars
[r
][c
] == '#')
779 app_data
->cur_col
= c
;
783 /* in the player app move the current selection index up or down */
784 void move_current_row (MainPlayerData
*app_data
, enum DIRECTION dir
)
786 int r
= app_data
->cur_row
;
787 int c
= app_data
->cur_col
;
788 if (dir
== DIR_FORWARD
)
791 while (r
< app_data
->puzzle
.grid_size
)
793 if (app_data
->puzzle
.chars
[r
][c
] == '#')
798 if (r
< app_data
->puzzle
.grid_size
)
799 app_data
->cur_row
= r
;
806 if (app_data
->puzzle
.chars
[r
][c
] == '#')
812 app_data
->cur_row
= r
;