commit
5ed51b7e9c
2 changed files with 208 additions and 0 deletions
@ -0,0 +1 @@ |
|||
Proof that wordle is solvable in 6 moves. |
|||
@ -0,0 +1,207 @@ |
|||
#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; |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue