/*
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;
union
{
int number;
char letter;
} data;
} 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].data.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].data.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].data.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].data.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 = 0;
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].data.letter =
tolower((unsigned char)argv[f + 1 + argc_base][0]);
if (chars[f].data.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].data.number = i;
}
}
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;
}