-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.c
162 lines (138 loc) · 3.71 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#define _POSIX_C_SOURCE 200809L
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define NUM_LETTERS 26
#define PUZZLE_CHAR_COUNT 7
#define MIN_WORD_LEN 4
// '0b00000011111111111111111111111111' (26 '1's)
#define PUZZLE_LETTERS_MASK (1 << NUM_LETTERS) - 1
#define MAX_LEN 128
#define CHAR_OFFSET(letter) ((toupper(letter)) - 'A')
enum check_result { NOT_MATCH, MATCH, PANGRAM };
/*
* A `int32_t` is chosen to represent the signature of a word.
*
* A signature being: the existence of letters from 'A' to 'Z'.
*
* Starting from the LSB as wheter letter 'A' exists
* All the way to the 26th bit from right representing whether letter 'Z' exists
*/
int32_t word_signature(const char *word) {
int32_t result = 0;
for (size_t i = 0; i < strnlen(word, MAX_LEN); i++) {
char letter = word[i];
if (!isalpha(letter)) {
continue;
}
result |= (1 << CHAR_OFFSET(letter));
}
return result;
}
/*
* Checks wheter a word is a solution.
*
* It must:
* Contain at least 4 letters (letters can be reused)
* Include the center letter
*/
enum check_result check_word(const char *word, const int32_t puzzle,
const int32_t center_letter) {
size_t char_counts = 0;
for (size_t i = 0; i < strnlen(word, MAX_LEN); i++) {
// Only count letters
if (isalpha(word[i])) {
char_counts++;
}
}
if (char_counts < MIN_WORD_LEN) {
return NOT_MATCH; // Word too short
}
int32_t signature_for_word = word_signature(word);
if ((signature_for_word & center_letter) != center_letter) {
return NOT_MATCH; // Does not contain center letter
} else if (signature_for_word == puzzle) {
return PANGRAM;
} else if ((signature_for_word | puzzle) == puzzle) {
return MATCH;
} else {
return NOT_MATCH;
}
}
/*
* Checks whether the command line arg contains 7 unique letters.
*/
bool check_arg(const char *arg) {
if (strnlen(arg, MAX_LEN) != PUZZLE_CHAR_COUNT) {
return false;
}
int32_t signature = 0;
for (size_t i = 0; i < PUZZLE_CHAR_COUNT; i++) {
char letter = arg[i];
if (!isalpha(letter)) {
return false;
}
int32_t target = 1 << CHAR_OFFSET(letter);
// Bit already set to 1, duplicate!
if ((signature & target) == target) {
return false;
}
signature |= target;
}
return true;
}
int main(int argc, char *const *argv) {
int option;
char *words_file_path = "/usr/share/dict/words";
while ((option = getopt(argc, argv, "f:")) != -1) {
switch (option) {
case 'f':
words_file_path = optarg;
break;
default:
break;
}
}
if (optind != argc - 1) {
printf("Usage: %s [-f <words_list_path>] "
"<puzzle_letters_starting_with_center_letter>\n",
argv[0]);
exit(EXIT_FAILURE);
}
if (!check_arg(argv[optind])) {
printf("The puzzle letters should be of size 7 and no duplicates\n");
exit(EXIT_FAILURE);
}
char *puzzle_letters = argv[optind];
int32_t puzzle = word_signature(puzzle_letters);
int32_t center_letter = 1 << CHAR_OFFSET(puzzle_letters[0]);
char line[MAX_LEN];
FILE *file = fopen(words_file_path, "r");
if (file == NULL) {
printf("Cannot open file at path: %s\n", words_file_path);
exit(EXIT_FAILURE);
}
while (fgets(line, sizeof(line), file)) {
switch (check_word(line, puzzle, center_letter)) {
case PANGRAM:
printf("* ");
case MATCH:
for (size_t i = 0; i < strnlen(line, MAX_LEN); i++) {
if (!isalpha(line[i])) {
continue;
}
printf("%c", toupper(line[i]));
}
printf("\n");
break;
default:
continue;
}
}
fclose(file);
exit(EXIT_SUCCESS);
}