8 #include "constantstrings.h"
10 #define MAX_PUZZLE_SIZE 20
11 #define MAX_CLUE_LENGTH 150
13 /* Enum to define terminal colours */
35 typedef char String
[MAX_CLUE_LENGTH
];
37 /* The main puzzle struct type */
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
];
46 char hashed_password
[256];
50 /* get a number from the user */
60 bool verify_password (Puzzle
*p
, const char* password
)
63 if (strcmp (p
->hashed_password
, "\0") == 0)
66 /* hash the user input password and compare it with the stored password */
67 char* hashed_password
= crypt (password
, (const char *)p
->salt
);
69 if (strcmp (p
->hashed_password
, hashed_password
) == 0)
75 /* Set or reset password for puzzle */
76 void set_puzzle_password (Puzzle
*p
, const char *password
)
78 /* if it is a null string, reset the password */
79 if (strcmp (password
, "\0") == 0)
81 strcpy (p
->hashed_password
, "\0");
82 strcpy (p
->salt
, "\0");
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
);
95 /* Output the clues to text file */
96 void export_clues (Puzzle
*p
, const char *filename
)
98 FILE *outfile
= fopen (filename
, "w");
101 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
104 /* first the across clues */
105 fprintf (outfile
, "ACROSS CLUES\n");
106 for (int i
= 0; i
< p
->grid_size
; i
++)
108 for (int j
= 0; j
< p
->grid_size
; j
++)
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
]);
115 /* now the down clues */
116 fprintf (outfile
, "DOWN 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_down_word
[i
][j
] != -1)
122 fprintf (outfile
, "%d - %s\n", p
->start_down_word
[i
][j
],
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
)
132 int img_size
= p
->grid_size
* 40;
133 FILE * outfile
= fopen (filename
, "wb");
136 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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 ();
147 for (int i
= 0; i
< p
->grid_size
; i
++)
149 for (int j
= 0; j
< p
->grid_size
; j
++)
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,
157 /* draw a regular square */
158 gdImageRectangle (img
, j
*40, i
*40, j
*40+40,
161 /* print the numers, if it is either start across word or
163 if (p
->start_across_word
[i
][j
] != -1 ||
164 p
->start_down_word
[i
][j
] != -1)
166 if (p
->start_across_word
[i
][j
] != -1)
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
);
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
);
181 /* if answerkey is true, draw the character in the cell */
184 gdImageChar (img
, lg_fnt
, j
*40+15, i
*40+15,
185 p
->chars
[i
][j
], black
);
191 gdImagePng (img
, outfile
);
192 gdImageDestroy (img
);
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
);
201 /* Reset the terminal colour */
202 void reset_color () {
206 /* check if previous row is blank or not */
207 bool prev_row_blank (Puzzle
*p
, int r
, int c
)
209 if (r
== 0) return true;
210 if (p
->chars
[r
-1][c
] == ' ' || p
->chars
[r
-1][c
] == '#') return true;
213 /* check if next row is blank or not */
214 bool next_row_blank (Puzzle
*p
, int r
, int c
)
216 if (r
== p
->grid_size
- 1) return true;
217 if (p
->chars
[r
+1][c
] == ' ' || p
->chars
[r
+1][c
] == '#') return true;
220 /* check if previous col is blank or not */
221 bool prev_col_blank (Puzzle
*p
, int r
, int c
)
223 if (c
== 0) return true;
224 if (p
->chars
[r
][c
-1] == ' ' || p
->chars
[r
][c
-1] == '#') return true;
227 /* check if the next col is blank or not */
228 bool next_col_blank (Puzzle
*p
, int r
, int c
)
230 if (c
== p
->grid_size
-1) return true;
231 if (p
->chars
[r
][c
+1] == ' ' || p
->chars
[r
][c
+1] == '#') return true;
235 /* unfreeze the grid - mak editing possible to change words */
236 void unfreeze_puzzle (Puzzle
*p
)
238 for (int i
= 0; i
< p
->grid_size
; i
++)
240 for (int j
= 0; j
< p
->grid_size
; j
++)
242 if (p
->chars
[i
][j
] == '#')
243 p
->chars
[i
][j
] = ' ';
245 p
->start_across_word
[i
][j
] = -1;
246 p
->start_down_word
[i
][j
] = -1;
249 p
->grid_frozen
= false;
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
)
257 bool across_word_start
, down_word_start
;
258 for (int i
= 0; i
< p
->grid_size
; i
++)
260 for (int j
= 0; j
< p
->grid_size
; j
++)
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 */
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;
280 if (across_word_start
== true)
281 p
->start_across_word
[i
][j
] = word_num
;
283 p
->start_across_word
[i
][j
] = -1;
284 if (down_word_start
== true)
285 p
->start_down_word
[i
][j
] = word_num
;
287 p
->start_down_word
[i
][j
] = -1;
288 if (across_word_start
== true || down_word_start
== true)
292 p
->grid_frozen
= true;
295 /* reset the entire grid */
296 void init_puzzle (Puzzle
*p
, int grid_size
)
298 p
->grid_size
= grid_size
;
299 p
->grid_frozen
= false;
300 for (int i
= 0; i
< p
->grid_size
; i
++)
302 for (int j
= 0; j
< p
->grid_size
; j
++)
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
], "");
311 strcpy (p
->hashed_password
, "\0");
312 strcpy (p
->salt
, "\0");
316 /* save the puzzle to a file */
317 void save_puzzle (Puzzle
*puzzle
, const char* file
) {
319 outfile
= fopen (file
, "w");
322 fprintf (stderr
, "%s\n", ERROR_WRITING_FILE
);
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
++)
331 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
332 fprintf (outfile
, "%c", puzzle
->chars
[i
][j
]);
333 fprintf (outfile
, "\n");
335 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
337 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
339 fprintf (outfile
, "%d ", puzzle
->start_across_word
[i
][j
]);
340 fprintf (outfile
, "%d ", puzzle
->start_down_word
[i
][j
]);
342 fprintf (outfile
, "\n");
344 fprintf (outfile
, "ACROSS\n");
345 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
347 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
354 fprintf (outfile
, "DOWN\n");
355 for (int i
= 0; i
< puzzle
->grid_size
; i
++)
357 for (int j
= 0; j
< puzzle
->grid_size
; j
++)
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
]);
368 /* read the puzzle from a file */
369 Puzzle
load_puzzle (const char* file
) {
372 infile
= fopen (file
, "rb");
375 fprintf (stderr
, "%s\n", ERROR_READING_FILE
);
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"));
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"));
392 strcpy (p
.salt
, "\0");
394 /* read each character of the grid */
395 for (int i
= 0; i
< p
.grid_size
; i
++ )
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
];
401 /* read the word numbers */
402 for (int i
= 0; i
< p
.grid_size
; i
++)
404 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
405 char *token
= strtok (line
, " ");
406 for (int j
= 0; j
< p
.grid_size
; j
++)
409 p
.start_across_word
[i
][j
] = atoi (token
);
410 token
= strtok (NULL
, " ");
412 p
.start_down_word
[i
][j
] = atoi (token
);
413 token
= strtok (NULL
, " ");
417 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
420 char clues
[100][MAX_CLUE_LENGTH
];
423 /* first read the across clues from file */
426 fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
);
427 /* if reached the end of across clues */
428 if (strcmp (line
, "DOWN\n") == 0)
430 word_num
[c
] = atoi (strtok (line
, "\t"));
431 char *cl
= strtok (NULL
, "\n");
433 strcpy (clues
[c
], cl
);
435 strcpy (clues
[c
], "\0");
438 /* set the clue to the correct cell in grid */
439 for (int i
= 0; i
< p
.grid_size
; i
++)
441 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
451 while (fgets (line
, MAX_CLUE_LENGTH
+ 10, infile
))
453 word_num
[c
] = atoi (strtok (line
, "\t"));
454 char* cl
= strtok (NULL
, "\n");
456 strcpy (clues
[c
], cl
);
458 strcpy (clues
[c
], "\0");
461 for (int i
= 0; i
< p
.grid_size
; i
++)
463 for (int j
= 0; j
< p
.grid_size
; j
++)
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
]);
475 /* display the puzzle */
476 void print_puzzle (Puzzle
*p
)
479 set_color (WHITE
, CYAN
, NORMAL
);
481 for (int i
= 0; i
< p
->grid_size
; i
++)
485 for (int i
= 0; i
< p
->grid_size
; i
++)
487 set_color (WHITE
, CYAN
, NORMAL
);
489 for (int j
= 0; j
< p
->grid_size
; j
++)
491 if (p
->chars
[i
][j
] == '#') {
492 set_color (WHITE
, BLACK
, NORMAL
);
497 if (p
->start_across_word
[i
][j
] != -1 ||
498 p
->start_down_word
[i
][j
] != -1)
500 set_color (BLUE
, WHITE
, NORMAL
);
501 if (p
->start_across_word
[i
][j
] != -1)
502 printf ("%-2d", p
->start_across_word
[i
][j
]);
504 printf ("%-2d", p
->start_down_word
[i
][j
]);
508 set_color (BLACK
, WHITE
,NORMAL
);
512 set_color (BLACK
, WHITE
, BOLD
);
513 printf ("%c", p
->chars
[i
][j
]);
519 /* print the clues if set */
520 if (p
->grid_frozen
== true)
522 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
523 for (int i
= 0; i
< p
->grid_size
; i
++)
525 for (int j
= 0; j
< p
->grid_size
; j
++)
527 if (p
->start_across_word
[i
][j
] != -1)
529 printf ("%d - %s; ", p
->start_across_word
[i
][j
],
530 p
->clue_across
[i
][j
]);
534 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
535 for (int i
= 0; i
< p
->grid_size
; i
++)
537 for (int j
= 0; j
< p
->grid_size
; j
++)
539 if (p
->start_down_word
[i
][j
] != -1)
541 printf ("%d - %s; ", p
->start_down_word
[i
][j
],
550 /* function to check if a word is valid or not */
551 char* is_valid_word (char *word
)
553 if (word
== NULL
|| strlen(word
) == 0)
555 for (int i
= 0; i
< strlen (word
) - 1; i
++)
556 if (! isalpha (word
[i
]))
559 return strtok (word
, "\n");
563 /* function to set a clue for an across word */
564 bool set_clue (Puzzle
*p
, String clue
, int index
, enum ORIENTATION order
)
566 for (int i
= 0; i
< p
->grid_size
; i
++)
568 for (int j
= 0; j
< p
->grid_size
; j
++)
572 if (p
->start_across_word
[i
][j
] == index
)
574 strcpy (p
->clue_across
[i
][j
], clue
);
578 else if (order
== DOWN
)
580 if (p
->start_down_word
[i
][j
] == index
)
582 strcpy (p
->clue_down
[i
][j
], clue
);
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
)
596 printf ("\e[1;1H\e[2J");
597 set_color (fg
, bg
, NORMAL
);
599 for (int i
= 0; i
< padding
; i
++)
602 reset_color (); printf ("\n");
604 set_color (fg
, bg
, BOLD
);
605 printf ("%-*s", padding
, title
);
607 set_color (fg
, bg
, NORMAL
);
609 reset_color (); printf ("\n");
610 set_color (fg
, bg
, NORMAL
);
612 for (int i
= 0; i
< padding
; i
++)
615 reset_color (); printf ("\n");
616 for (int i
= 0; i
< num_items
; i
++)
618 set_color (fg
, bg
, NORMAL
);
619 printf ("\u2551%-*s\u2551", padding
, items
[i
]);
620 reset_color (); printf ("\n");
622 set_color (fg
, bg
, NORMAL
);
624 for (int i
= 0; i
< padding
; i
++)
627 reset_color (); printf ("\n");