/* codeword - Newspaper codeword solver Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include typedef enum { eLetter, eNumber, eMissing } CharacterClass; typedef struct { CharacterClass type; int number; char letter; } Character; static void PrepLine(char *p) { size_t l = strlen(p); size_t f; while (l && p[l-1] == '\n') { p[--l] = 0; } for(f = 0; f < l; f++) { p[f] = tolower((unsigned char)p[f]); } } static void Error(const char *p) { perror(p); exit(EXIT_FAILURE); } static int ContainsLetter(char c, const Character *chars, int no_chars) { int f; for(f=0; f < no_chars; f++) { if (chars[f].type == eLetter && chars[f].letter == c) { return 1; } } return 0; } static int CheckLettersAndLength(const char *buff, const Character *chars, int no_chars) { size_t l = strlen(buff); int f; if (l != no_chars) { return 0; } for(f=0; f < no_chars; f++) { if (chars[f].type == eLetter && (chars[f].letter != buff[f])) { return 0; } } return 1; } static int CheckNumbers(const char *buff, const Character *chars, int no_chars) { int f; int n; char code[27] = {0}; for(f = 0; f < no_chars; f++) { if (chars[f].type == eNumber) { int i = chars[f].number - 1; if (ContainsLetter(buff[f], chars, no_chars)) { return 0; } if (code[i] && code[i] != buff[f]) { return 0; } for(n = 0; n < 27; n++) { if (n != i && code[n] == buff[f]) { return 0; } } code[i] = buff[f]; } } return 1; } int CheckAnagram(const char *buff, const Character *chars, int no_chars) { size_t l = strlen(buff); int *used; int f; int match; if (l != no_chars) { return 0; } used = malloc(sizeof *used * no_chars); if (!used) { Error("malloc"); } for(f = 0; f < no_chars; f++) { used[f] = 0; } for(f = 0; f < no_chars; f++) { int i; int found = 0; for(i = 0; i < no_chars && !found; i++) { if (!used[i] && chars[i].type == eLetter && chars[i].letter == buff[f]) { used[i] = 1; found = 1; } } if (!found) { for(i = 0; i < no_chars && !found; i++) { if (!used[i] && chars[i].type == eMissing) { used[i] = 1; found = 1; } } } if (!found) { free(used); return 0; } } match = 1; for(f = 0; f < no_chars; f++) { if (!used[f]) { match = 0; } } free(used); return match; } int main(int argc, char *argv[]) { int no_chars; Character *chars; int f; FILE *fp; char buff[1024]; int anagram_mode; int argc_base = 1; if (argc > 1 && strcmp(argv[1], "-a") == 0) { anagram_mode = 1; argc_base = 2; } if (argc < 1 + argc_base) { fprintf(stderr, "usage: %s [-a] wordlist ...\n", argv[0]); exit(EXIT_FAILURE); } no_chars = argc - argc_base - 1; chars = malloc(sizeof *chars * no_chars); if (!chars) { Error("malloc"); } for(f = 0; f < no_chars; f++) { int i; i = atoi(argv[f + 1 + argc_base]); if (i < 1 || i > 27) { chars[f].type = eLetter; chars[f].letter = tolower((unsigned char)argv[f + 1 + argc_base][0]); chars[f].number = 0; if (chars[f].letter == '.') { chars[f].type = eMissing; } } else { if (anagram_mode) { fprintf(stderr, "Can't use numbers in anagram mode\n"); exit(EXIT_FAILURE); } chars[f].type = eNumber; chars[f].number = i; chars[f].letter = 0; } } fp = fopen(argv[argc_base], "r"); if (!fp) { Error(argv[argc_base]); } while(fgets(buff, sizeof buff, fp)) { PrepLine(buff); if (anagram_mode) { if (CheckAnagram(buff, chars, no_chars)) { printf("%s\n", buff); } } else { if (CheckLettersAndLength(buff, chars, no_chars) && CheckNumbers(buff, chars, no_chars)) { printf("%s\n", buff); } } } return EXIT_SUCCESS; }