8886fabaa9b388f0543834698dabfb1d4d3270bb
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 /* get a number from the user */
71 bool verify_password (Puzzle
*p
, const char* password
)
74 if (strcmp (p
->hashed_password
, "\0") == 0)
77 /* hash the user input password and compare it with the stored password */
78 char* hashed_password
= crypt (password
, (const char *)p
->salt
);
80 if (strcmp (p
->hashed_password
, hashed_password
) == 0)
86 /* Set or reset password for puzzle */
87 void set_puzzle_password (Puzzle
*p
, const char *password
)
89 /* if it is a null string, reset the password */
90 if (strcmp (password
, "\0") == 0)
92 strcpy (p
->hashed_password
, "\0");
93 strcpy (p
->salt
, "\0");
99 sprintf (salt
, "puzzle%d", rand()%1000);
100 char* hashedpwd
= crypt (password
, (const char*)salt
);
101 strcpy (p
->hashed_password
, hashedpwd
);
102 strcpy (p
->salt
, salt
);
106 /* Output the clues to text file */
107 void export_clues (Puzzle
*p
, const char *filename
)
109 FILE *outfile
= fopen (filename
, "w");
112 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
115 /* first the across clues */
116 fprintf (outfile
, "ACROSS CLUES\n");
117 for (int i
= 0; i
< p
->grid_size
; i
++)
119 for (int j
= 0; j
< p
->grid_size
; j
++)
121 if (p
->start_across_word
[i
][j
] != -1)
122 fprintf (outfile
, "%d - %s\n", p
->start_across_word
[i
][j
],
123 p
->clue_across
[i
][j
]);
126 /* now the down clues */
127 fprintf (outfile
, "DOWN 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_down_word
[i
][j
] != -1)
133 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
140 /* Output the grid to image - if answerkey is true export filled grid */
141 void export_grid_image (Puzzle
*p
, const char *filename
, bool answerkey
)
143 int img_size
= p
->grid_size
* GRID_PIXELS
;
144 FILE * outfile
= fopen (filename
, "wb");
147 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
151 gdImagePtr img
= gdImageCreate (img_size
, img_size
);
152 gdImageColorAllocate (img
, 255,255,255);
153 int black
= gdImageColorAllocate (img
, 0, 0, 0);
154 int blue
= gdImageColorAllocate (img
, 0, 0, 216);
155 gdFontPtr sm_fnt
= gdFontGetMediumBold ();
156 gdFontPtr lg_fnt
= gdFontGetGiant ();
158 for (int i
= 0; i
< p
->grid_size
; i
++)
160 for (int j
= 0; j
< p
->grid_size
; j
++)
162 /* if it is a block, draw the black square */
163 if (p
->chars
[i
][j
] == '#')
164 gdImageFilledRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
165 j
*GRID_PIXELS
+GRID_PIXELS
,
166 i
*GRID_PIXELS
+GRID_PIXELS
,black
);
169 /* draw a regular square */
170 gdImageRectangle (img
, j
*GRID_PIXELS
, i
*GRID_PIXELS
,
171 j
*GRID_PIXELS
+GRID_PIXELS
,
172 i
*GRID_PIXELS
+GRID_PIXELS
, black
);
174 /* print the numers, if it is either start across word or
176 if (p
->start_across_word
[i
][j
] != -1 ||
177 p
->start_down_word
[i
][j
] != -1)
179 if (p
->start_across_word
[i
][j
] != -1)
182 sprintf (str
, "%d", p
->start_across_word
[i
][j
]);
183 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
185 (unsigned char *)str
, blue
);
190 sprintf (str
, "%d", p
->start_down_word
[i
][j
]);
191 gdImageString (img
, sm_fnt
, j
*GRID_PIXELS
+2,
193 (unsigned char *)str
, blue
);
196 /* if answerkey is true, draw the character in the cell */
199 gdImageChar (img
, lg_fnt
, j
*GRID_PIXELS
+15,
200 i
*GRID_PIXELS
+10, p
->chars
[i
][j
], black
);
206 gdImagePng (img
, outfile
);
207 gdImageDestroy (img
);
211 /* Set the terminal colour */
212 void set_color (enum COLOR fg
, enum COLOR bg
, enum ATTR at
) {
213 printf ("\x1B[%d;%d;%dm", fg
+30, bg
+40, at
);
216 /* Reset the terminal colour */
217 void reset_color () {
221 /* check if previous row is blank or not */
222 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
224 if (r
== 0) return true;
225 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
228 /* check if next row is blank or not */
229 bool next_row_blank (Puzzle
*p
, int r
, int c
)
231 if (r
== p
->grid_size
- 1) return true;
232 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
235 /* check if previous col is blank or not */
236 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
238 if (c
== 0) return true;
239 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
242 /* check if the next col is blank or not */
243 bool next_col_blank (Puzzle
*p
, int r
, int c
)
245 if (c
== p
->grid_size
-1) return true;
246 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
250 /* unfreeze the grid - make editing possible to change words */
251 void unfreeze_puzzle (Puzzle
*p
)
253 for (int i
= 0; i
< p
->grid_size
; i
++)
255 for (int j
= 0; j
< p
->grid_size
; j
++)
257 if (p
->chars
[i
][j
] == '#')
258 p
->chars
[i
][j
] = ' ';
260 p
->start_across_word
[i
][j
] = -1;
261 p
->start_down_word
[i
][j
] = -1;
264 p
->grid_frozen
= false;
267 /* freeze the grid - make editing impossible because it finalizes the
268 across and down words in the grid */
269 void freeze_puzzle (Puzzle
*p
)
272 bool across_word_start
, down_word_start
;
273 for (int i
= 0; i
< p
->grid_size
; i
++)
275 for (int j
= 0; j
< p
->grid_size
; j
++)
277 across_word_start
= false;
278 down_word_start
= false;
279 /* if it is a blank cell - cover it with a block */
280 if (p
->chars
[i
][j
] == ' ')
281 p
->chars
[i
][j
] = '#';
282 /* it is not a blank cell - check all possibilities */
285 bool prev_row
= prev_row_blank (p
, i
, j
);
286 bool next_row
= next_row_blank (p
, i
, j
);
287 bool prev_col
= prev_col_blank (p
, i
, j
);
288 bool next_col
= next_col_blank (p
, i
, j
);
289 if (prev_row
&& ! next_row
)
290 down_word_start
= true;
291 if (prev_col
&& ! next_col
)
292 across_word_start
= true;
295 if (across_word_start
== true)
296 p
->start_across_word
[i
][j
] = word_num
;
298 p
->start_across_word
[i
][j
] = -1;
299 if (down_word_start
== true)
300 p
->start_down_word
[i
][j
] = word_num
;
302 p
->start_down_word
[i
][j
] = -1;
303 if (across_word_start
== true || down_word_start
== true)
307 p
->grid_frozen
= true;
310 /* reset the entire grid */
311 void init_puzzle (Puzzle
*p
, int grid_size
)
313 p
->grid_size
= grid_size
;
314 p
->grid_frozen
= false;
315 for (int i
= 0; i
< p
->grid_size
; i
++)
317 for (int j
= 0; j
< p
->grid_size
; j
++)
319 p
->chars
[i
][j
] = ' ';
320 p
->start_across_word
[i
][j
] = -1;
321 p
->start_down_word
[i
][j
] = -1;
322 strcpy (p
->clue_across
[i
][j
], "");
323 strcpy (p
->clue_down
[i
][j
], "");
326 strcpy (p
->hashed_password
, "\0");
327 strcpy (p
->salt
, "\0");
331 /* save the puzzle to a file */
332 void save_puzzle (Puzzle
*puzzle
, const char* file
) {
334 /* First output the uncompressed contents to a temp file */
335 outfile
= tmpfile ();
338 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
341 /* grid size is the first field */
342 fprintf (outfile
, "%d\n", puzzle
->grid_size
);
343 /* whether grid is frozen or not */
344 fprintf (outfile
, "%d\n", puzzle
->grid_frozen
);
345 /* the hashed password */
346 fprintf (outfile
, "%s\n", puzzle
->hashed_password
);
348 fprintf (outfile
, "%s\n", puzzle
->salt
);
350 /* First output the grid characters columns/rows */
351 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
353 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
354 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
355 fprintf (outfile
, "\n");
358 /* Next output the start across/down numbers */
359 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
361 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
363 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
364 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
366 fprintf (outfile
, "\n");
369 /* Output the across clues */
370 fprintf (outfile
, "ACROSS\n");
371 /* Search the grid for across words */
372 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
374 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
376 /* if it is an across word, then put the word index followed by
377 tab character (as separator) and the clue */
378 if (puzzle
->start_across_word
[i
][j
] != -1)
379 fprintf (outfile
, "%d\t%s\n", puzzle
->start_across_word
[i
][j
],
380 puzzle
->clue_across
[i
][j
]);
384 /* Output the down clues */
385 fprintf (outfile
, "DOWN\n");
386 /* Search the grid for down words */
387 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
389 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
391 /* same as across word, put the word index followed by the tab
392 character and then the clue */
393 if (puzzle
->start_down_word
[i
][j
] != -1)
394 fprintf (outfile
, "%d\t%s\n", puzzle
->start_down_word
[i
][j
],
395 puzzle
->clue_down
[i
][j
]);
399 /* Flush the buffer and rewind to beginning - to read and save into
400 gzip compressed file */
402 fseek (outfile
, 0, 0);
404 /* now compress the file and save it to destination file */
405 gzFile outdestfile
= gzopen (file
, "wb");
406 if (outdestfile
== NULL
)
408 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
413 int num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
416 int res
= gzwrite (outdestfile
, buf
, num
*sizeof(char) );
419 fprintf (stderr
, "%s %s\n", ERROR_WRITING_FILE
, COMPRESSED
);
423 num
= fread (buf
, sizeof(char), sizeof(char)*128, outfile
);
425 gzclose (outdestfile
);
430 /* read the puzzle from a file */
431 Puzzle
load_puzzle (const char* file
) {
432 /* First open the GZip file */
433 gzFile insourcefile
= gzopen (file
, "rb");
434 if (insourcefile
== NULL
)
436 fprintf (stderr
, "%s %s\n", ERROR_READING_FILE
, COMPRESSED
);
439 /* Open a temporary file to uncompress the contents */
440 FILE *infile
= tmpfile ();
443 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
446 /* Put the uncompressed content to the temp file */
449 num
= gzread (insourcefile
, buf
, 128);
452 int res
= fwrite (buf
, 1, num
, infile
);
455 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
457 gzclose (insourcefile
);
460 num
= gzread (insourcefile
, buf
, 128);
462 /* Close the gzip file */
463 gzclose (insourcefile
);
464 /* Flush the temp file buffer and rewind to beginning */
466 fseek (infile
, 0, 0);
468 /* Read the temporary file contents to the structure Puzzle */
470 char line
[MAX_CLUE_LENGTH
+10];
471 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
472 p
.grid_size
= atoi (line
);
473 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
474 p
.grid_frozen
= atoi (line
) == 0 ? false : true ;
475 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
476 if (strlen (line
) != 1)
477 strcpy (p
.hashed_password
, strtok (line
, "\n"));
479 strcpy (p
.hashed_password
, "\0");
480 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
481 if (strlen (line
) != 1)
482 strcpy (p
.salt
, strtok (line
, "\n"));
484 strcpy (p
.salt
, "\0");
486 /* read each character of the grid */
487 for (int i
= 0; i
< p
.grid_size
; i
++ )
489 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
490 for (int j
= 0; j
< p
.grid_size
; j
++)
491 p
.chars
[i
][j
] = line
[j
];
493 /* read the word numbers */
494 for (int i
= 0; i
< p
.grid_size
; i
++)
496 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
497 char *token
= strtok (line
, " ");
498 for (int j
= 0; j
< p
.grid_size
; j
++)
501 p
.start_across_word
[i
][j
] = atoi (token
);
502 token
= strtok (NULL
, " ");
504 p
.start_down_word
[i
][j
] = atoi (token
);
505 token
= strtok (NULL
, " ");
509 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
512 char clues
[100][MAX_CLUE_LENGTH
];
515 /* first read the across clues from file */
518 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
519 /* if reached the end of across clues */
520 if (strcmp (line
, "DOWN\n") == 0)
522 word_num
[c
] = atoi (strtok (line
, "\t"));
523 char *cl
= strtok (NULL
, "\n");
525 strcpy (clues
[c
], cl
);
527 strcpy (clues
[c
], "\0");
530 /* set the clue to the correct cell in grid */
531 for (int i
= 0; i
< p
.grid_size
; i
++)
533 for (int j
= 0; j
< p
.grid_size
; j
++)
535 for (int r
= 0; r
< c
; r
++)
536 if (p
.start_across_word
[i
][j
] == word_num
[r
])
537 strcpy (p
.clue_across
[i
][j
], clues
[r
]);
543 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
545 word_num
[c
] = atoi (strtok (line
, "\t"));
546 char* cl
= strtok (NULL
, "\n");
548 strcpy (clues
[c
], cl
);
550 strcpy (clues
[c
], "\0");
553 for (int i
= 0; i
< p
.grid_size
; i
++)
555 for (int j
= 0; j
< p
.grid_size
; j
++)
557 for (int r
= 0; r
< c
; r
++)
558 if (p
.start_down_word
[i
][j
] == word_num
[r
])
559 strcpy (p
.clue_down
[i
][j
], clues
[r
]);
567 /* display the puzzle */
568 void print_puzzle (Puzzle
*p
)
571 set_color (WHITE
, CYAN
, NORMAL
);
573 for (int i
= 0; i
< p
->grid_size
; i
++)
577 for (int i
= 0; i
< p
->grid_size
; i
++)
579 set_color (WHITE
, CYAN
, NORMAL
);
581 for (int j
= 0; j
< p
->grid_size
; j
++)
583 if (p
->chars
[i
][j
] == '#') {
584 set_color (WHITE
, BLACK
, NORMAL
);
589 if (p
->start_across_word
[i
][j
] != -1 ||
590 p
->start_down_word
[i
][j
] != -1)
592 set_color (BLUE
, WHITE
, NORMAL
);
593 if (p
->start_across_word
[i
][j
] != -1)
594 printf ("%-2d", p
->start_across_word
[i
][j
]);
596 printf ("%-2d", p
->start_down_word
[i
][j
]);
600 set_color (BLACK
, WHITE
,NORMAL
);
604 set_color (BLACK
, WHITE
, BOLD
);
605 printf ("%c", p
->chars
[i
][j
]);
611 /* print the clues if set */
612 if (p
->grid_frozen
== true)
614 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
615 for (int i
= 0; i
< p
->grid_size
; i
++)
617 for (int j
= 0; j
< p
->grid_size
; j
++)
619 if (p
->start_across_word
[i
][j
] != -1)
621 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
622 p
->clue_across
[i
][j
]);
626 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
627 for (int i
= 0; i
< p
->grid_size
; i
++)
629 for (int j
= 0; j
< p
->grid_size
; j
++)
631 if (p
->start_down_word
[i
][j
] != -1)
633 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
642 /* function to check if a word is valid or not */
643 char* is_valid_word (char *word
)
645 if (word
== NULL
|| strlen(word
) == 0)
647 for (int i
= 0; i
< strlen (word
) - 1; i
++)
648 if (! isalpha (word
[i
]))
651 return strtok (word
, "\n");
655 /* function to set a clue for an across word */
656 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
658 for (int i
= 0; i
< p
->grid_size
; i
++)
660 for (int j
= 0; j
< p
->grid_size
; j
++)
664 if (p
->start_across_word
[i
][j
] == index
)
666 strcpy (p
->clue_across
[i
][j
], clue
);
670 else if (order
== DOWN
)
672 if (p
->start_down_word
[i
][j
] == index
)
674 strcpy (p
->clue_down
[i
][j
], clue
);
683 /* function to print a menu */
684 void print_menu (enum COLOR fg
, enum COLOR bg
, const char* title
,
685 char **items
, int num_items
, int padding
)
688 printf ("\e[1;1H\e[2J");
689 set_color (fg
, bg
, NORMAL
);
691 for (int i
= 0; i
< padding
; i
++)
694 reset_color (); printf ("\n");
696 set_color (fg
, bg
, BOLD
);
697 printf ("%-*s", padding
, title
);
699 set_color (fg
, bg
, NORMAL
);
701 reset_color (); printf ("\n");
702 set_color (fg
, bg
, NORMAL
);
704 for (int i
= 0; i
< padding
; i
++)
707 reset_color (); printf ("\n");
708 for (int i
= 0; i
< num_items
; i
++)
710 set_color (fg
, bg
, NORMAL
);
711 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
712 reset_color (); printf ("\n");
714 set_color (fg
, bg
, NORMAL
);
716 for (int i
= 0; i
< padding
; i
++)
719 reset_color (); printf ("\n");