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