Similarly I was fascinated by LetterBoxed, another NYT game and took a crack at a solver. https://hlfshell.ai/posts/letter-puzzles/
Similarly I was fascinated by LetterBoxed, another NYT game and took a crack at a solver. https://hlfshell.ai/posts/letter-puzzles/
The NYT’s solutions are always two words, which I rarely get on my own. But once, a couple of years ago, I discovered a one-word solution to one day’s puzzle: LEXICOGRAPHY. Very elegant, I thought to myself smugly.
I happened to remember that solution a couple of months ago, and I decided to see if I could find others. I am not a programmer, but by asking ChatGPT 4 for help I was able to create a Python program and run it in Google Colab using a large list of English words that I had compiled from various online word lists.
Here is the beginning of the resulting list of one-word solutions to LetterBoxed:
acetylcholinesterase [a c e] [h i l] [n o r] [s t y]
acetylcholinesterases [a c e] [h i l] [n o r] [s t y]
achondroplastic [a c d] [h i l] [n o p] [r s t]
acknowledgement [a c d] [e g k] [l m n] [o t w]
acknowledgment [a c d] [e g k] [l m n] [o t w]
The code that ChatGPT 4 wrote for me and the full list of solutions are here:
https://gally.net/temp/20240318onewordsolutionstoletterboxed...
https://gally.net/temp/20240318onewordsolutionstoletterboxed...
But all I did was report the errors to the LLMs and paste their revised code back into Colab. The core logic of the search algorithm was created entirely by the LLMs based on my natural-language description of the LetterBoxed rules and the solutions I was looking for. I could not have written that code myself.
#include <array>
#include <algorithm>
#include <bitset>
#include <iostream>
#include <string_view>
#include <vector>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
struct Word {
using Bits = std::bitset<32>; // A bit for each a..z, plus an "error" bit at index 26.
using Rules = std::array<Bits, 27>; // Map from letter indices to forbidden letters (plus a spare).
std::string_view str;
Bits bits;
static unsigned ix(char c) { return c - 'a'; }; // 'a'..'z' -> 0..25; others -> >25
bool ok() const { return !bits.test(26) && str.size() > 1; }
explicit Word(std::string_view s, Bits accept = {0x3ffffff}, Rules const& rules = {}) : str(s) {
for (unsigned i = 0, prev_ix = 26, bit_ix = 0; i != str.size(); prev_ix = bit_ix, ++i) {
bit_ix = std::min(ix(str[i]), 26u);
if (accept.test(bit_ix) && !rules.at(prev_ix).test(bit_ix)) { bits.set(bit_ix); }
else { bits.set(26); return; }}}
};
int main(int ac, char** av) {
auto usage = [av](){ std::cerr << "usage: " << av[0] << " abcdefghijkl [<wordlist>]\n"; };
if (ac != 2 && ac != 3) { return usage(), 1; }
char const* const name = (ac == 3) ? av[2] : "/usr/share/dict/american-english-large";
const int fd = ::open(name , O_RDONLY);
if (fd < 0) { return std::cerr << av[0] << ": failed to open " << name << '\n', 3; }
const std::size_t file_size = ::lseek(fd, 0, SEEK_END);
void const* const addr = ::mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) { return std::cerr << av[0] << ": failed to open " << name << '\n', 3; }
const auto target = Word{av[1]};
if (target.bits.test(26) || target.str.size() != 12 || target.bits.count() != 12)
{ return usage(), 3; }
const auto rules = [w=target.str, ix=Word::ix](Word::Rules rules = {}) {
for (int i = 0; i < 12; i += 3)
rules.at(ix(w[i])) = rules.at(ix(w[i+1])) = rules.at(ix(w[i+2])) = Word(w.substr(i, 3)).bits;
return rules;
}();
const auto candidates = [&](std::array<std::vector<Word>, 26> candidates = {}) {
for (std::string_view in = {static_cast<char const*>(addr), file_size}; !in.empty();) {
const Word word{in.substr(0, in.find('\n')), target.bits, rules};
if (word.ok()) { candidates.at(Word::ix(word.str.front())).push_back(word); }
in = in.substr(word.str.size() + 1); // Skip past '\n' if present (might not be, at EOF).
}
return candidates;
}();
for (auto const& firsts : candidates) {
for (const auto first : firsts) {
for (const auto second : candidates.at(Word::ix(first.str.back()))) {
if ((first.bits | second.bits) == target.bits) {
std::cout << first.str << ' ' << second.str << '\n'; }}}}
}