commit 5ed51b7e9cbb76b4c50bce09eea87fa38f7de221 Author: Shanti Chellaram Date: Mon Jan 31 20:50:07 2022 -0500 makin me a wordle clone diff --git a/README.md b/README.md new file mode 100644 index 0000000..62bb9ea --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Proof that wordle is solvable in 6 moves. diff --git a/wordle.c b/wordle.c new file mode 100644 index 0000000..bf4c3a0 --- /dev/null +++ b/wordle.c @@ -0,0 +1,207 @@ +#include +#include +#include +#include +#include + +#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; +}