You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
4.8 KiB
207 lines
4.8 KiB
#include <inttypes.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <strings.h>
|
|
#include <stdlib.h>
|
|
|
|
#define WORD_LENGTH 5
|
|
|
|
/* represents a 5-bit letter */
|
|
typedef uint8_t let_t;
|
|
|
|
/* represents a 5-bit letter + 3-bit position */
|
|
typedef uint8_t lpos_t;
|
|
|
|
/* represents a set of possible letters (technically, 26-bits) */
|
|
typedef uint32_t let_set_t;
|
|
|
|
/* represents 5 5-bit letters, a word */
|
|
typedef uint32_t word_t;
|
|
|
|
#define let_from_char(c) (((c) - 'a') & 0x1F)
|
|
|
|
#define let_to_char(c) ((c) + 'a')
|
|
|
|
lpos_t cptolp(char c, uint8_t pos) {
|
|
return (let_from_char(c) << 3) | pos;
|
|
}
|
|
|
|
let_set_t ls_add(let_set_t set, let_t letter) {
|
|
return set | (1 << letter);
|
|
}
|
|
|
|
let_set_t ls_test(let_set_t set, let_t letter) {
|
|
return (set & (1 << letter)) != 0;
|
|
}
|
|
|
|
uint8_t lp_pos(lpos_t lp) {
|
|
return 0x07 & lp;
|
|
}
|
|
|
|
char lp_char(lpos_t lp) {
|
|
return let_to_char(lp >> 3);
|
|
}
|
|
|
|
/* result is a 2-bit value: */
|
|
typedef enum result {
|
|
RES_BLACK = 0x01,
|
|
RES_YELLOW = 0x02,
|
|
RES_GREEN = 0x03
|
|
} result_t;
|
|
|
|
/* represents a wordle result: 5 2-bit result codes */
|
|
typedef uint16_t result_set_t;
|
|
|
|
result_set_t rs_set(result_t res, size_t index, result_t value) {
|
|
return res | ((value & 0x3) << (2 * index));
|
|
}
|
|
|
|
result_t rs_get(result_t res, size_t index) {
|
|
return (res >> (2 * index)) & 0x3;
|
|
}
|
|
|
|
char word_charat(word_t word, size_t index) {
|
|
return (char) ((word >> (index * 5)) & 0x1F) + 'a';
|
|
}
|
|
|
|
/* converts a 5-letter word to its compacted representation */
|
|
word_t strtoword(char *str) {
|
|
word_t word = 0;
|
|
for (size_t i = 0; i < WORD_LENGTH; ++i) {
|
|
word |= (str[i] - 'a') << (5 * i);
|
|
}
|
|
return word;
|
|
}
|
|
|
|
void wordtostr(char *out, word_t word) {
|
|
for (size_t i = 0; i < WORD_LENGTH; ++i) {
|
|
out[i] = word_charat(word, i);
|
|
}
|
|
}
|
|
|
|
result_set_t guess_match_werd(char *answer, char *guess) {
|
|
bool guess_consumed[WORD_LENGTH] = {false};
|
|
bool answer_consumed[WORD_LENGTH] = {false};
|
|
result_set_t result_set = 0;
|
|
for (size_t i = 0; i < WORD_LENGTH; ++i) {
|
|
if (answer[i] == guess[i]) {
|
|
answer_consumed[i] = true;
|
|
guess_consumed[i] = true;
|
|
result_set = rs_set(result_set, i, RES_GREEN);
|
|
}
|
|
}
|
|
for (size_t i = 0; i < WORD_LENGTH; ++i) {
|
|
if (guess_consumed[i]) { continue; }
|
|
bool found = false;
|
|
for (size_t j = 0; j < WORD_LENGTH; ++j) {
|
|
if (answer_consumed[j]) { continue; }
|
|
if (answer[j] == guess[i]) {
|
|
answer_consumed[j] = true;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
result_set = rs_set(result_set, i, RES_YELLOW);
|
|
} else {
|
|
result_set = rs_set(result_set, i, RES_BLACK);
|
|
}
|
|
}
|
|
return result_set;
|
|
}
|
|
|
|
result_set_t guess_match(word_t answer_word, word_t guess_word) {
|
|
char answer[6] = {0},
|
|
guess[6] = {0};
|
|
wordtostr(answer, answer_word);
|
|
wordtostr(guess, guess_word);
|
|
return guess_match_werd(answer, guess);
|
|
}
|
|
|
|
/* returns whether or not the game was won */
|
|
typedef struct wordle_game {
|
|
word_t target;
|
|
uint32_t turn_limit;
|
|
int (*guess_fn)(word_t *, void *);
|
|
void *guess_fn_data;
|
|
void (*display_fn)(result_set_t, void *);
|
|
void *display_fn_data;
|
|
} wordle_game_t;
|
|
|
|
bool play_game(wordle_game_t *game) {
|
|
for (uint32_t turn = 1; turn <= game->turn_limit; ++turn) {
|
|
word_t guess = 0;
|
|
if (game->guess_fn(&guess, game->guess_fn_data) != 0) {
|
|
printf("guesser failure\n");
|
|
}
|
|
result_set_t result_set = guess_match(game->target, guess);
|
|
game->display_fn(result_set, game->display_fn_data);
|
|
if (guess == game->target) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
struct linebuf {
|
|
char *buffer;
|
|
size_t capacity;
|
|
};
|
|
|
|
int term_guess(word_t *word, void *data) {
|
|
struct linebuf *linebuf = data;
|
|
while (true) {
|
|
ssize_t bytes_read = getline(&(linebuf->buffer), &(linebuf->capacity), stdin);
|
|
if (bytes_read == -1) {
|
|
return -1;
|
|
}
|
|
char *buf_end = linebuf->buffer;
|
|
strsep(&buf_end, "\n");
|
|
if (strchr(linebuf->buffer, '\0') - linebuf->buffer == WORD_LENGTH) {
|
|
*word = strtoword(linebuf->buffer);
|
|
return 0;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void term_display(result_set_t result_set, void *ignored) {
|
|
(void) ignored;
|
|
for (size_t i = 0; i < WORD_LENGTH; ++i) {
|
|
switch (rs_get(result_set, i)) {
|
|
case RES_BLACK:
|
|
fputc(' ', stdout);
|
|
break;
|
|
case RES_YELLOW:
|
|
fputc('Y', stdout);
|
|
break;
|
|
case RES_GREEN:
|
|
fputc('G', stdout);
|
|
break;
|
|
default:
|
|
fputc('X', stdout);
|
|
break;
|
|
}
|
|
}
|
|
fputs(" result", stdout);
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
struct linebuf lb = {
|
|
.capacity = 6,
|
|
.buffer = (char *) malloc(lb.capacity),
|
|
};
|
|
wordle_game_t game = {
|
|
.target = strtoword("total"),
|
|
.turn_limit = 6,
|
|
.guess_fn = term_guess,
|
|
.guess_fn_data = &lb,
|
|
.display_fn = term_display,
|
|
.display_fn_data = NULL,
|
|
};
|
|
int x = play_game(&game);
|
|
printf("endgame %d\n", x);
|
|
return 0;
|
|
}
|