24点游戏的一种实现
24点游戏是一种扑克牌游戏,抽四张扑克牌,寻找表达式,以这四张扑克牌的数字表示为操作数,如果表达式的结果为24,则代表成功找到。实现使用了穷举法,对于任意大于零小于13的四个数,遍历这四个数的所有四则运算表达式,如果表达式结果为24则输出。使用了boost spirit x3来做表达式值的解析,原本使用boost spirit2实现,可有除零错;为了处理除零,改用spirit x3,使用lambda来表示语法解析时的动作。
用法例子:
./game 5 5 5 5
((5*5)-(5/5))
//game.cpp
#include <algorithm>
#include <complex>
#include <exception>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include "exprs.h"
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace parser
{
using x3::eps;
using x3::int_;
using x3::lexeme;
x3::rule<class expr, int> const expr = "expr";
x3::rule<class term, int> const term = "term";
x3::rule<class factor, int> const factor = "factor";
x3::rule<class group, int> const group = "group";
auto indentity = [&](auto& ctx) { _val(ctx) = _attr(ctx); };
auto multi = [&](auto& ctx) { _val(ctx) *= _attr(ctx); };
auto divide = [&](auto& ctx) {
if (_attr(ctx) && (!(_val(ctx) % _attr(ctx)))) {
_val(ctx) /= _attr(ctx);
} else {
_pass(ctx) = false;
}
};
auto add = [&](auto& ctx) { _val(ctx) += _attr(ctx); };
auto sub = [&](auto& ctx) { _val(ctx) -= _attr(ctx); };
auto const expr_def = term[indentity] >> *(('+' >> term[add]) | ('-' >> term[sub])); //左结合
auto const term_def = factor[indentity] >> *(('*' >> (factor)[multi]) | ('/' >> (factor)[divide])); //左结合
auto const factor_def = (int_ | group);
auto const group_def = '(' >> expr >> ')';
BOOST_SPIRIT_DEFINE(expr, term, factor, group);
} // namespace parser
bool verify(std::string const& sv) noexcept
{
size_t l = 0;
try {
int n = std::stoi(sv, &l);
return (l == sv.size() && (n >= 0) && (n < 14));
} catch (...) {
std::cerr << "function verify: string:" << sv << " stoi failed" << std::endl;
return false;
}
}
int main(int argc, char** argv)
{
if (argc < 5) {
std::cerr << "usage sample: ./game [1-13] [1-13] [1-13] [1-13] \n";
return 0;
}
bool good = std::all_of(&argv[1], &argv[5], verify);
if (!good) {
std::cerr << "usage sample: ./game [1-13] [1-13] [1-13] [1-13] \n";
return 0;
}
std::vector<std::string> v{std::string(argv[1]), std::string(argv[2]), std::string(argv[3]), std::string(argv[4])};
using parser::expr;
auto allexprs = getAllexprs(v);
for (auto&& oneexpr : allexprs) {
int result{0};
std::string::const_iterator iter = oneexpr.cbegin();
std::string::const_iterator end = oneexpr.cend();
try {
bool r = x3::parse(iter, end, expr, result);
if (r && iter == end && result == 24) {
std::cout << oneexpr << std::endl;
} else {
}
} catch (...) {
std::cout << oneexpr << ": exception caught\n";
}
}
}
// exprs.cpp
#include <algorithm>
#include <complex>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <exception>
#include "exprs.h"
std::vector<std::string> getAllexprs(std::vector<std::string>& v)
{
std::vector<std::string> r;
auto all = getAllPerms(v);
for (auto&& i : all) {
auto pairs = generateExprs(i.begin(), i.end());
for (auto&& t : pairs) {
}
r.insert(r.end(), pairs.begin(), pairs.end());
}
for (auto&& expr : r) {
}
return r;
}
std::vector<std::vector<std::string>> getAllPerms(std::vector<std::string> v)
{
std::vector<std::vector<std::string>> r;
r.push_back(v);
while (std::next_permutation(v.begin(), v.end())) {
r.push_back(v);
}
return r;
}
//exprs.h
#pragma once
#include <vector>
#include <string>
template <class I>
std::vector<std::string> generateExprs(I b, I e)
{
int n = e - b;
std::vector<std::string> v;
v.reserve(24);
if (n == 0) {
v.push_back(std::string{});
return v;
} else if (n == 1) {
v.push_back(std::string("") + *b + std::string(""));
return v;
} else {
for (int i = 1; i < n; ++i) {
auto l = generateExprs(b, b + i);
auto r = generateExprs(b + i, e);
for (auto li : l) {
for (auto ri : r) {
std::string s = std::string("(") + li + std::string("+") + ri + ")";
v.push_back(s);
s = std::string("(") + li + std::string("-") + ri + ")";
v.push_back(s);
s = std::string("(") + li + std::string("*") + ri + ")";
v.push_back(s);
s = std::string("(") + li + std::string("/") + ri + ")";
v.push_back(s);
}
}
}
return v;
}
}
std::vector<std::string> getAllexprs(std::vector<std::string>& v);
std::vector<std::vector<std::string>> getAllPerms(std::vector<std::string> v);
Posted 2020-10-22