#include #include #include #include #define KBLIB_DEF_MACROS 1 #include #include #include #include "two_candles_problem.h" #include "vote_count.h" void pascal(int depth, int print, int verbose); auto match_name(std::string_view a, std::string_view name, std::string_view def) { if (kblib::starts_with(a, kblib::concat(name, '=')) or kblib::starts_with(a, kblib::concat(name, ':'))) { return std::pair(true, a.substr(name.length() + 1)); } else if (kblib::starts_with(a, name)) { return std::pair(true, def); } else { return std::pair(false, std::string_view{}); } } auto match_name(std::string_view a, std::string_view name) { if (kblib::starts_with(a, kblib::concat(name, '=')) or kblib::starts_with(a, kblib::concat(name, ':'))) { return std::pair(true, a.substr(name.length() + 1)); } else { return std::pair(false, std::string_view{}); } } int main(int argc, char** argv) { using namespace std::literals; try { TCLAP::CmdLine cmd("Solver for Matt Parker's Maths Puzzles", ' ', "0.0"); std::vector puzzle_names{ "none", "pascal", "candles", "votecount", }; TCLAP::ValuesConstraint puzzles(puzzle_names); TCLAP::UnlabeledValueArg puzzle("p", "The puzzle to solve", true, "", &puzzles, cmd); TCLAP::UnlabeledMultiArg remainder("", "Puzzle parameters", false, "params...", cmd); cmd.parse(argc, argv); if (puzzle.getValue() == "pascal") { int depth = 128; int verbose{}; int print{}; for (std::string_view a : remainder.getValue()) { a = a.substr(0 + kblib::starts_with(a, '-')); if (auto [success, value] = match_name(a, "d"); success) { depth = pFromStr(int, value); } else if (auto [success, value] = match_name(a, "v", "1"); success) { if (not value.empty()) { verbose = pFromStr(int, value); } else { verbose = true; } } else if (auto [success, value] = match_name(a, "p", "1"); success) { if (not value.empty()) { print = pFromStr(int, value); } else { print = true; } } } pascal(depth, print, verbose); } else if (puzzle.getValue() == "candles") { auto iterations_lb2 = kblib::lexical_cast(remainder.getValue()[0]); auto r = get_avg_positions_ab(iterations_lb2); std::cout << kblib::concat("avg(a) = ", r.min, "\navg(b) = ", r.max, "\nb - a = ", r.max - r.min, '\n'); } else if (puzzle.getValue() == "votecount") { auto min = kblib::lexical_cast(remainder.getValue()[0]); auto total = kblib::lexical_cast(remainder.getValue()[1]); if (min > total) { std::cout << "invalid arguments to votecount; min must be <= total.\n"; std::exit(1); } auto est = vote_calc(min, total); std::cout << "expected: " << est << '\n'; std::cout << "counted: " << vote_count(min, total) << '\n'; } } catch (TCLAP::ArgException& e) { std::cerr << "error: " << e.error() << " for arg " << e.argId() << '\n'; return 1; } return 0; } void pascal(int depth, int print, int verbose) { using bit = unsigned char; std::vector parent{0, 1, 0}; std::vector current{0, 0, 0, 0}; unsigned long long odd_count = 1; unsigned long long even_count = 0; parent.reserve(depth + 2); current.reserve(depth + 3); auto print_row = [](const auto& row) { for (auto i : kblib::range(std::vector::size_type{1}, row.size() - 1)) { std::cout << (row[i] ? "▉" : "░"); } std::cout << '\n'; }; if (print) { print_row(parent); } int field_width = kblib::count_digits(depth); auto stats = [](auto odds, auto evens) { return (std::ostringstream{} << '\t' << odds << ":" << evens << " odd:even, " << odds << '/' << (odds + evens) << " = " << (100. * odds / (odds + evens)) << "% odd.\n") .str(); }; if (verbose) { std::cout << "Row " << std::setw(field_width) << std::setfill('0') << 0 << ": " << stats(1, 0); std::cout << "Cumulative: " << stats(odd_count, even_count); } unsigned long long row_odd_count{}; for (auto row : kblib::range(1, std::max(depth, 0))) { row_odd_count = 0; for (auto i : kblib::range(std::size_t{1}, current.size() - 1)) { if ((current[i] = parent[i - 1] ^ parent[i])) { // assignment intended ++row_odd_count; } else { ++even_count; } } if (print and row % print == 0) { print_row(current); } odd_count += row_odd_count; if (verbose and row % verbose == 0) { unsigned long long row_width = parent.size() - 1; unsigned long long row_even_count = row_width - row_odd_count; std::cout << "Row " << std::setw(field_width) << std::setfill('0') << row << ": " << stats(row_odd_count, row_even_count); std::cout << "Cumulative: " << stats(odd_count, even_count); } parent = std::exchange(current, std::vector(current.size() + 1)); } if (verbose and (depth - 1) % verbose != 0) { unsigned long long row_width = parent.size() - 1; unsigned long long row_even_count = row_width - row_odd_count; std::cout << '\n'; if (print and (depth - 1) % print != 0) { print_row(parent); } std::cout << "Row " << std::setw(field_width) << std::setfill('0') << depth - 1 << ": " << stats(row_odd_count, row_even_count); std::cout << "Cumulative: " << stats(odd_count, even_count); std::cout << '\n'; } std::cout << "Total of " << std::setw(field_width) << std::setfill('0') << depth << " rows: " << stats(odd_count, even_count); }