3 ms·
Here is a program that solves the New York Times puzzle "Letter Boxed". Its only uses of pointers are in processing main()'s arguments, which are provided via p
by angiosperm 3y ago
Here is a program that solves the New York Times puzzle "Letter Boxed". Its only uses of pointers are in processing main()'s arguments, which are provided via pointer, and mapping an input file, which Posix provides via a pointer. It does use std::string_view, which has pointers in it; the point is that you don't operate directly on the pointers, so cannot have bugs caused by misusing the pointers.
Try: "g++ -std=c++20 lb.cc && ./a.out iodhuamplrnc words".
#include <array>
#include <bit> // popcount
#include <iostream>
#include <utility>
#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 {
std::string_view str;
unsigned bits{};
static unsigned index(unsigned c) { return c - 'a'; }; // 'a'..'z' -> 0..25; others -> >25
Word(std::string_view s, unsigned accept, long long sides) : str(s) {
long long prev_side{-1}, side; // Values are 0..3. First letter gets a free pass.
for (char c: s) {
const auto ix = index(c);
if (ix < 26 && (accept & (1u << ix)) && (side = ((sides >> 2*ix) & 0x3)) != prev_side) {
bits |= (1u << ix), prev_side = side;
} else { str = {}; 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; }
const std::array prefixes = { "", "/usr/share/dict/", "/usr/share/dict/american-english-" };
const auto name = (ac == 3) ? av[2] : prefixes.back() + std::string{"large"};
std::string_view file;
for (auto pfx = 0u; pfx != prefixes.size(); ++pfx) {
int fd = ::open((prefixes[pfx] + name).c_str(), O_RDONLY);
std::size_t file_size = ::lseek(fd, 0, SEEK_END);
void const* addr = ::mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr != MAP_FAILED) { file = {static_cast<char const*>(addr), file_size}; break; }
}
if (file.empty()) { return std::cerr << av[0] << ": cannot read " << name << '\n', 3; }
auto target_sum = 0u; auto sides_sum = 0ll;
for (unsigned i{}, ix{}; i < 12 && (ix = Word::index(av[1][i])) < 26; ++i)
{ target_sum |= (1u << ix), sides_sum |= (i/3ll << 2*ix); }
if (std::popcount(target_sum) != 12 || av[1][12] != '\0') { return usage(), 3; }
const auto target = target_sum; const auto sides = sides_sum;
const auto candidates = [target,sides,file] { std::array<std::vector<Word>, 26> candidates;
for (std::string_view in = file, next; !(next = in.substr(0, in.find('\n'))).empty();
in.remove_prefix(next.size() + (next.size() < in.size()))) { // Skip past '\n' if present
if (const Word word{next, target, sides}; word.str.size() >= 3)
{ candidates.at(Word::index(word.str.front())).push_back(word); }
}
return candidates;
}();
std::array<std::vector<std::array<std::string_view, 2>>, 100> answers; // radix sort by length
for (auto const& firsts: candidates) for (const auto first: firsts) {
for (const auto second: candidates.at(Word::index(first.str.back()))) {
if ((first.bits | second.bits) == target) {
answers.at(first.str.size() + second.str.size()).push_back({first.str, second.str}); }}
}
for (auto const& of_len_N: answers) for (const auto answer: of_len_N)
{ std::cout << answer[0] << ' ' << answer[1] << '\n'; }
}
- erik_seaberg 3y agoI don’t have a modern implementation handy, but according to https://en.cppreference.com/w/cpp/string/basic_string_view/data https://en.cppreference.com/w/cpp/string/basic_string_view/d... a string_view depends on its data() pointer, and the lifetime of that buffer doesn’t seem to have guarantees.
- angiosperm 3y agoThus, the word "view". It refers to storage being managed by the creator of the string_view object, in this case a file mapped using mmap and not unmapped until after program termination.
- protomolecule 3y ago"so cannot have bugs caused by misusing the pointers" std::string_view lets you have some of the bugs that are caused by misusing pointers.