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