]> git.cworth.org Git - wordgame/blob - dict.c
Add printing to dict interface. Breakup grid prints by letter count.
[wordgame] / dict.c
1 /*
2  * Copyright © 2006 Carl Worth
3  *
4  * This program is free software; you can redistribute it and\/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA."
17  */
18
19 /* Portably, schmortability. I want ease of programming. */
20 #define _GNU_SOURCE
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <ctype.h>
26
27 #include "dict.h"
28
29 void *
30 xmalloc (size_t size)
31 {
32     void *res;
33
34     res = malloc (size);
35     if (res == NULL) {
36         fprintf (stderr, "Error: out of memory\n");
37         exit (1);
38     }
39
40     return res;
41 }
42
43 void *
44 xrealloc (void *ptr, size_t size)
45 {
46     void *res;
47
48     res = realloc (ptr, size);
49     if (res == NULL) {
50         fprintf (stderr, "Error: out of memory\n");
51         exit (1);
52     }
53
54     return res;
55 }
56
57 void
58 chomp (char *s)
59 {
60     int len = strlen (s);
61     if (s[len - 1] == '\n')
62         s[len - 1] = '\0';
63 }
64
65 void
66 string_init (string_t *string)
67 {
68     string->size = 0;
69     string->s = NULL;
70     string->len = 0;
71 }
72
73 void
74 string_fini (string_t *string)
75 {
76     string->size = 0;
77     if (string->s)
78         free (string->s);
79     string->len = 0;
80 }
81
82 void
83 string_append_char (string_t *string, char c)
84 {
85     if (string->size == 0) {
86         string->size = 8;
87         string->s = xmalloc (string->size);
88         string->s[0] = '\0';
89         string->len = 0;
90     }
91
92     if (string->len + 1 == string->size) {
93         string->size *= 2;
94         string->s = xrealloc (string->s, string->size);
95     }
96
97     string->s[string->len++] = c;
98     string->s[string->len] = '\0';
99 }
100
101 void
102 string_chop (string_t *string)
103 {
104     if (string->len == 0)
105         return;
106
107     string->s[--string->len] = '\0';
108 }
109
110 static void
111 trie_init (trie_t *trie)
112 {
113     int i;
114     trie->flags = 0;
115     for (i = 0; i < 26; i++)
116         trie->next[i] = NULL;
117 }
118
119 void
120 trie_destroy (trie_t *trie);
121
122 static void
123 trie_fini (trie_t *trie)
124 {
125     int i;
126
127     for (i = 0; i < 26; i++)
128         if (trie->next[i])
129             trie_destroy (trie->next[i]);
130 }
131
132 trie_t *
133 trie_create (void)
134 {
135     trie_t *trie;
136
137     trie = xmalloc (sizeof (trie_t));
138
139     trie_init (trie);
140
141     return trie;
142 }
143
144 void
145 trie_destroy (trie_t *trie)
146 {
147     trie_fini (trie);
148
149     free (trie);
150 }
151
152 void
153 trie_add (trie_t        *trie,
154           const char    *word_chunk)
155 {
156     char c = word_chunk[0];
157     int i;
158
159     if (c == '\0') {
160         trie->flags |= TRIE_FLAGS_IS_WORD;
161         return;
162     }
163
164     i = TRIE_CHAR_TO_INDEX (c);
165     if (trie->next[i] == NULL)
166         trie->next[i] = trie_create ();
167
168     trie_add (trie->next[i], word_chunk + 1);
169 }
170
171 trie_t *
172 trie_find (trie_t       *trie,
173            const char   *word_chunk)
174 {
175     const char *s = word_chunk;
176
177     while (trie && *s)
178         trie = trie->next[TRIE_CHAR_TO_INDEX (*s++)];
179
180     return trie;
181 }
182
183 int
184 trie_count (trie_t              *trie,
185             trie_predicate_t     predicate)
186 {
187     int i;
188     int count = 0;
189
190     if ((predicate) (trie))
191         count = 1;
192
193     for (i = 0; i < 26; i++) {
194         if (trie->next[i] == NULL)
195             continue;
196
197         count += trie_count (trie->next[i], predicate);
198     }
199
200     return count;
201 }
202
203 int
204 trie_print (trie_t              *trie,
205             string_t            *string,
206             trie_predicate_t     predicate,
207             int                  length,
208             int                  min_length,
209             int                  max_length)
210 {
211     char c;
212     int i;
213     int count = 0;
214
215     if (length >= min_length && (predicate) (trie))
216     {
217         count = 1;
218         printf ("%s ", string->s);
219     }
220
221     if (length == max_length)
222         return count;
223
224     /* Loop over each element, appending the character and recursing. */
225     for (i = 0; i < 26; i++) {
226         if (trie->next[i] == NULL)
227             continue;
228
229         c = TRIE_INDEX_TO_CHAR (i);
230
231         string_append_char (string, c);
232         count += trie_print (trie->next[i], string, predicate,
233                              length + 1, min_length, max_length);
234         string_chop (string);
235     }
236
237     return count;
238 }
239
240 void
241 dict_init (dict_t *dict)
242 {
243     trie_init (dict);
244 }
245
246 void
247 dict_fini (dict_t *dict)
248 {
249     trie_fini (dict);
250 }
251
252 void
253 dict_add_word (dict_t           *dict,
254                const char       *word)
255 {
256     trie_add (dict, word);
257 }
258
259 void
260 dict_add_words_from_file (dict_t        *dict,
261                           const char    *filename)
262 {
263     FILE *file;
264     char *line = NULL;
265     size_t line_size = 0;
266     ssize_t bytes_read;
267
268     file = fopen (filename, "r");
269     if (file == NULL) {
270         fprintf (stderr, "Error: failed to open %s: %s\n",
271                  filename, strerror (errno));
272         exit (1);
273     }
274
275     while (1) {
276         bytes_read = getline (&line, &line_size, file);
277         if (bytes_read == -1)
278             break;
279         chomp (line);
280         dict_add_word (dict, line);
281     }
282     if (line)
283         free (line);
284
285     fclose (file);
286 }
287
288 dict_entry_t *
289 dict_lookup (dict_t     *dict,
290              const char *word)
291 {
292     trie_t *trie;
293
294     trie = trie_find (dict, word);
295     if (trie == NULL)
296         return NULL;
297     else
298         return &trie->flags;
299 }
300
301 /* Yes, this function is rather silly. I have it here in the hope that
302  * it could be used for some better type-checking, (if only C had such
303  * a thing). */
304 dict_cursor_t
305 dict_root (dict_t *dict)
306 {
307     return dict;
308 }
309
310 dict_cursor_t
311 dict_cursor_next (dict_cursor_t cursor,
312                   char          next)
313 {
314     trie_t *trie = cursor;
315
316     if (trie == NULL)
317         return DICT_CURSOR_NIL;
318
319     return trie->next[TRIE_CHAR_TO_INDEX (next)];
320 }
321
322 dict_entry_t *
323 dict_cursor_resolve (dict_cursor_t cursor)
324 {
325     trie_t *trie;
326
327     if (cursor == DICT_CURSOR_NIL)
328         return NULL;
329
330     trie = cursor;
331     return &trie->flags;
332 }
333
334 /* XXX: This static function pointer is really nasty and definitely
335  * un-thread safe. What I really want it a lambda so I can construct a
336  * composite predicate on the fly. Oh well... */
337 static dict_entry_predicate_t dict_entry_predicate = NULL;
338
339 static bool_t
340 dict_predicate (trie_t *trie)
341 {
342     dict_entry_t *entry;
343
344     entry = dict_cursor_resolve (trie);
345     if (! DICT_ENTRY_IS_WORD (entry))
346         return FALSE;
347
348     if (dict_entry_predicate)
349         return (dict_entry_predicate) (*entry);
350
351     return TRUE;
352 }
353
354 int
355 dict_count (dict_t                      *dict,
356             dict_entry_predicate_t       predicate)
357 {
358     dict_entry_predicate = predicate;
359     return  trie_count (dict, dict_predicate);
360 }
361
362 int
363 dict_print (dict_t *dict)
364 {
365     int count;
366     string_t string;
367
368     string_init (&string);
369
370     dict_entry_predicate = NULL;
371     count = trie_print (dict, &string, dict_predicate,
372                         0, 0, -1);
373
374     string_fini (&string);
375
376     return count;
377 }
378
379 int
380 dict_print_if (dict_t                   *dict,
381                dict_entry_predicate_t    predicate)
382 {
383     int count;
384     string_t string;
385
386     string_init (&string);
387
388     dict_entry_predicate = predicate;
389     count = trie_print (dict, &string, dict_predicate,
390                         0, 0, -1);
391
392     string_fini (&string);
393
394     return count;
395 }
396
397 int
398 dict_print_by_length_if (dict_t                 *dict,
399                          dict_entry_predicate_t  predicate)
400 {
401     int length, total, words, count = 0;
402     string_t string;
403
404     string_init (&string);
405
406     dict_entry_predicate = predicate;
407
408     total = trie_count (dict, dict_predicate);
409
410     length = 1;
411     do {
412         words = trie_print (dict, &string, dict_predicate,
413                             0, length, length);
414         if (words) {
415             printf ("\n");
416             count += words;
417         }
418         length++;
419     } while (count < total);
420
421     string_fini (&string);
422     
423     return count;
424 }