bd286e3b2ba5207c20f030db7b1a18f78df18adc
[wordblah.git] / wordblox.h
1 #ifndef __WORDBLOX_H
2 #define __WORDBLOX_H
3
4 #include <gd.h>
5 #include <gdfontmb.h>
6 #include <gdfontg.h>
7 #include "constantstrings.h"
8
9 #define MAX_PUZZLE_SIZE 20
10 #define MAX_CLUE_LENGTH 150
11
12 /* Enum to define terminal colours */
13 enum COLOR {
14 BLACK = 0,
15 RED= 1,
16 GREEN=2,
17 YELLOW=3,
18 BLUE=4,
19 MAGENTA=5,
20 CYAN=6,
21 WHITE=7
22 };
23
24 enum ATTR {
25 NORMAL = 23,
26 BOLD=1
27 };
28
29 enum ORIENTATION {
30 ACROSS=1,
31 DOWN=2
32 };
33
34 typedef char String[MAX_CLUE_LENGTH];
35
36 /* The main puzzle struct type */
37 typedef struct {
38 char chars[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
39 int start_across_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
40 int start_down_word[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
41 String clue_across[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
42 String clue_down[MAX_PUZZLE_SIZE][MAX_PUZZLE_SIZE];
43 int grid_size;
44 bool grid_frozen;
45 } Puzzle;
46
47 /* get a number from the user */
48 int get_num ()
49 {
50 char s[5];
51 fgets (s, 5, stdin);
52 int n = atoi (s);
53 return n;
54 }
55
56 /* Output the clues to text file */
57 void export_clues (Puzzle *p, const char *filename)
58 {
59 FILE *outfile = fopen (filename, "w");
60 if (outfile == NULL)
61 {
62 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
63 exit (1);
64 }
65 /* first the across clues */
66 fprintf (outfile, "ACROSS CLUES\n");
67 for (int i = 0; i < p->grid_size; i ++)
68 {
69 for (int j = 0; j < p->grid_size; j ++)
70 {
71 if (p->start_across_word[i][j] != -1)
72 fprintf (outfile, "%d - %s\n", p->start_across_word[i][j],
73 p->clue_across[i][j]);
74 }
75 }
76 /* now the down clues */
77 fprintf (outfile, "DOWN CLUES\n");
78 for (int i = 0; i < p->grid_size; i ++)
79 {
80 for (int j = 0; j < p->grid_size; j ++)
81 {
82 if (p->start_down_word[i][j] != -1)
83 fprintf (outfile, "%d - %s\n", p->start_down_word[i][j],
84 p->clue_down[i][j]);
85 }
86 }
87 fclose (outfile);
88 }
89
90 /* Output the grid to image - if answerkey is true export filled grid */
91 void export_grid_image (Puzzle *p, const char *filename, bool answerkey)
92 {
93 int img_size = p->grid_size * 40;
94 FILE * outfile = fopen (filename, "wb");
95 if (outfile == NULL)
96 {
97 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
98 exit (1);
99 }
100
101 gdImagePtr img = gdImageCreate (img_size, img_size);
102 int white = gdImageColorAllocate (img, 255,255,255);
103 int black = gdImageColorAllocate (img, 0, 0, 0);
104 int blue = gdImageColorAllocate (img, 0, 0, 216);
105 gdFontPtr sm_fnt = gdFontGetMediumBold ();
106 gdFontPtr lg_fnt = gdFontGetGiant ();
107
108 for (int i = 0; i < p->grid_size; i ++)
109 {
110 for (int j = 0; j < p->grid_size; j++)
111 {
112 /* if it is a block, draw the black square */
113 if (p->chars[i][j] == '#')
114 gdImageFilledRectangle (img, j*40, i*40, j*40+40,
115 i*40+40,black);
116 else
117 {
118 /* draw a regular square */
119 gdImageRectangle (img, j*40, i*40, j*40+40,
120 i*40+40, black);
121
122 /* print the numers, if it is either start across word or
123 a down word */
124 if (p->start_across_word[i][j] != -1 ||
125 p->start_down_word[i][j] != -1)
126 {
127 if (p->start_across_word[i][j] != -1)
128 {
129 char str[5];
130 sprintf (str, "%d", p->start_across_word[i][j]);
131 gdImageString (img, sm_fnt, j*40+2, i*40+2,
132 (unsigned char *)str, blue);
133 }
134 else
135 {
136 char str[5];
137 sprintf (str, "%d", p->start_down_word[i][j]);
138 gdImageString (img, sm_fnt, j*40+2, i*40+2,
139 (unsigned char *)str, blue);
140 }
141 }
142 /* if answerkey is true, draw the character in the cell */
143 if (answerkey)
144 {
145 gdImageChar (img, lg_fnt, j*40+15, i*40+15,
146 p->chars[i][j], black);
147 }
148 }
149 }
150 }
151
152 gdImagePng (img, outfile);
153 gdImageDestroy (img);
154 fclose (outfile);
155 }
156
157 /* Set the terminal colour */
158 void set_color (enum COLOR fg, enum COLOR bg, enum ATTR at) {
159 printf ("\x1B[%d;%d;%dm", fg+30, bg+40, at);
160 }
161
162 /* Reset the terminal colour */
163 void reset_color () {
164 printf ("\x1B[0m");
165 }
166
167 /* check if previous row is blank or not */
168 bool prev_row_blank (Puzzle *p, int r, int c)
169 {
170 if (r == 0) return true;
171 if (p->chars[r-1][c] == ' ' || p->chars[r-1][c] == '#') return true;
172 return false;
173 }
174 /* check if next row is blank or not */
175 bool next_row_blank (Puzzle *p, int r, int c)
176 {
177 if (r == p->grid_size - 1) return true;
178 if (p->chars[r+1][c] == ' ' || p->chars[r+1][c] == '#') return true;
179 return false;
180 }
181 /* check if previous col is blank or not */
182 bool prev_col_blank (Puzzle *p, int r, int c)
183 {
184 if (c == 0) return true;
185 if (p->chars[r][c-1] == ' ' || p->chars[r][c-1] == '#') return true;
186 return false;
187 }
188 /* check if the next col is blank or not */
189 bool next_col_blank (Puzzle *p, int r, int c)
190 {
191 if (c == p->grid_size -1) return true;
192 if (p->chars[r][c+1] == ' ' || p->chars[r][c+1] == '#') return true;
193 return false;
194 }
195
196 /* unfreeze the grid - mak editing possible to change words */
197 void unfreeze_puzzle (Puzzle *p)
198 {
199 for (int i = 0; i < p->grid_size; i ++)
200 {
201 for (int j = 0; j < p->grid_size; j ++)
202 {
203 if (p->chars[i][j] == '#')
204 p->chars[i][j] = ' ';
205
206 p->start_across_word[i][j] = -1;
207 p->start_down_word[i][j] = -1;
208 }
209 }
210 p->grid_frozen = false;
211 }
212
213 /* freeze the grid - make editing impossible because it finalizes the
214 across and down words in the grid */
215 void freeze_puzzle (Puzzle *p)
216 {
217 int word_num = 1;
218 bool across_word_start, down_word_start;
219 for (int i = 0; i < p->grid_size; i ++)
220 {
221 for (int j = 0; j < p->grid_size; j++)
222 {
223 across_word_start = false;
224 down_word_start = false;
225 /* if it is a blank cell - cover it with a block */
226 if (p->chars[i][j] == ' ')
227 p->chars[i][j] = '#';
228 /* it is not a blank cell - check all possibilities */
229 else
230 {
231 bool prev_row = prev_row_blank (p, i, j);
232 bool next_row = next_row_blank (p, i, j);
233 bool prev_col = prev_col_blank (p, i, j);
234 bool next_col = next_col_blank (p, i, j);
235 if (prev_row && ! next_row)
236 down_word_start = true;
237 if (prev_col && ! next_col)
238 across_word_start = true;
239 }
240
241 if (across_word_start == true)
242 p->start_across_word[i][j] = word_num;
243 else
244 p->start_across_word[i][j] = -1;
245 if (down_word_start == true)
246 p->start_down_word[i][j] = word_num;
247 else
248 p->start_down_word[i][j] = -1;
249 if (across_word_start == true || down_word_start == true)
250 word_num ++;
251 }
252 }
253 p->grid_frozen = true;
254 }
255
256 /* reset the entire grid */
257 void init_puzzle (Puzzle *p, int grid_size)
258 {
259 p->grid_size = grid_size;
260 p->grid_frozen = false;
261 for (int i = 0; i < p->grid_size; i ++)
262 {
263 for (int j = 0; j < p->grid_size; j ++)
264 {
265 p->chars[i][j] = ' ';
266 p->start_across_word[i][j] = -1;
267 p->start_down_word[i][j] = -1;
268 strcpy (p->clue_across[i][j], "");
269 strcpy (p->clue_down[i][j], "");
270 }
271 }
272 }
273
274 /* save the puzzle */
275 void save_puzzle (Puzzle *puzzle, const char* file) {
276 FILE *outfile;
277 outfile = fopen (file, "wb");
278 if (outfile == NULL)
279 {
280 fprintf (stderr, "%s\n", ERROR_WRITING_FILE);
281 exit (1);
282 }
283 fwrite (puzzle, sizeof (*puzzle), 1, outfile);
284 fclose (outfile);
285 }
286
287 /* read the puzzle */
288 Puzzle load_puzzle (const char* file) {
289 FILE *infile;
290 Puzzle p;
291 infile = fopen (file, "rb");
292 if (infile == NULL)
293 {
294 fprintf (stderr, "%s\n", ERROR_READING_FILE);
295 exit (1);
296 }
297 fread (&p, sizeof(p), 1, infile);
298 fclose (infile);
299 return p;
300 }
301
302 /* display the puzzle */
303 void print_puzzle (Puzzle *p)
304 {
305 printf ("\n");
306 set_color (WHITE, CYAN, NORMAL);
307 printf (" ");
308 for (int i = 0; i < p->grid_size; i ++)
309 printf ("%3d", i);
310 reset_color ();
311 printf("\n");
312 for (int i = 0; i < p->grid_size; i ++)
313 {
314 set_color (WHITE, CYAN, NORMAL);
315 printf ("%3d ", i);
316 for (int j = 0; j < p->grid_size; j ++)
317 {
318 if (p->chars[i][j] == '#') {
319 set_color (WHITE, BLACK, NORMAL);
320 printf (" ");
321 }
322 else
323 {
324 if (p->start_across_word[i][j] != -1 ||
325 p->start_down_word[i][j] != -1)
326 {
327 set_color (BLUE, WHITE, NORMAL);
328 if (p->start_across_word[i][j] != -1)
329 printf ("%-2d", p->start_across_word[i][j]);
330 else
331 printf ("%-2d", p->start_down_word[i][j]);
332 }
333 else
334 {
335 set_color (BLACK, WHITE,NORMAL);
336 printf (" ");
337 }
338
339 set_color (BLACK, WHITE, BOLD);
340 printf ("%c", p->chars[i][j]);
341 }
342 reset_color ();
343 }
344 printf ("\n");
345 }
346 /* print the clues if set */
347 if (p->grid_frozen == true)
348 {
349 printf ("\x1B[1mACROSS - CLUES\x1B[0m\n");
350 for (int i = 0; i < p->grid_size; i ++)
351 {
352 for (int j = 0; j < p->grid_size; j ++)
353 {
354 if (p->start_across_word[i][j] != -1)
355 {
356 printf ("%d - %s; ", p->start_across_word[i][j],
357 p->clue_across[i][j]);
358 }
359 }
360 }
361 printf ("\n\x1B[1mDOWN - CLUES\x1B[0m\n");
362 for (int i = 0; i < p->grid_size; i ++)
363 {
364 for (int j = 0; j < p->grid_size; j ++)
365 {
366 if (p->start_down_word[i][j] != -1)
367 {
368 printf ("%d - %s; ", p->start_down_word[i][j],
369 p->clue_down[i][j]);
370 }
371 }
372 }
373 printf ("\n");
374 }
375 }
376
377 /* function to check if a word is valid or not */
378 char* is_valid_word (char *word)
379 {
380 if (word == NULL || strlen(word) == 0)
381 return NULL;
382 for (int i = 0; i < strlen (word) - 1; i ++)
383 if (! isalpha (word[i]))
384 return NULL;
385
386 return strtok (word, "\n");
387 }
388
389
390 /* function to set a clue for an across word */
391 bool set_clue (Puzzle *p, String clue, int index, enum ORIENTATION order)
392 {
393 for (int i = 0; i < p->grid_size; i ++)
394 {
395 for (int j = 0; j < p->grid_size; j ++)
396 {
397 if (order == ACROSS)
398 {
399 if (p->start_across_word[i][j] == index)
400 {
401 strcpy (p->clue_across[i][j], clue);
402 return true;
403 }
404 }
405 else if (order == DOWN)
406 {
407 if (p->start_down_word[i][j] == index)
408 {
409 strcpy (p->clue_down[i][j], clue);
410 return true;
411 }
412 }
413 }
414 }
415 return false;
416 }
417
418 /* function to print a menu */
419 void print_menu (enum COLOR fg, enum COLOR bg, const char* title,
420 char **items, int num_items, int padding)
421 {
422 /* clear screen */
423 printf ("\e[1;1H\e[2J");
424 set_color (fg, bg, NORMAL);
425 printf ("\u2554");
426 for (int i = 0; i < padding; i ++)
427 printf ("\u2550");
428 printf ("\u2557");
429 reset_color (); printf ("\n");
430 printf ("\u2551");
431 set_color (fg, bg, BOLD);
432 printf ("%-*s", padding, title);
433 reset_color ();
434 set_color (fg, bg, NORMAL);
435 printf ("\u2551");
436 reset_color (); printf ("\n");
437 set_color (fg, bg, NORMAL);
438 printf ("\u2560");
439 for (int i = 0; i < padding; i ++)
440 printf ("\u2550");
441 printf ("\u2563");
442 reset_color (); printf ("\n");
443 for (int i = 0; i < num_items; i ++)
444 {
445 set_color (fg, bg, NORMAL);
446 printf ("\u2551%-*s\u2551", padding, items[i]);
447 reset_color (); printf ("\n");
448 }
449 set_color (fg, bg, NORMAL);
450 printf ("\u255A");
451 for (int i = 0; i < padding; i ++)
452 printf ("\u2550");
453 printf ("\u255D");
454 reset_color (); printf ("\n");
455 }
456
457 #endif