c++编写的算24程序

本文介绍了一个C++实现的24点游戏算法,通过解析输入的后缀表达式,穷举所有可能的组合,并利用中缀表达式进行计算。作者更新了程序,修复了对后缀表达式的理解错误,现在可以接受用户输入并给出计算答案,功能已较为完善。源代码包括Token解析、中缀与后缀表达式计算和24点游戏逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天在大本营看到了一个人家写的24程序,感觉人家的思路非常清晰,于是自己也着手写了一个,顺便温习了一下标准c++。在我的程序中,使用了stringstream类做parse,后缀表达式穷举所有可行解,而用中缀表达式计算用户输入的表达式。因为比较懒,当用户算不出来时,按出来的答案是个后缀表达式。

 

===============================================

4.21更新

1.发现对后缀表达式的理解错了。修改了穷举算24的函数,穷举空间改为4!*4^3 * 5,其中3个操作符在长度为7的后缀表达式的可能位置为5种。

2.使用二叉树和堆栈实现了后缀表达式向中缀的转化,可以给出算24的答案

3.加入了接受用户输入的4个数,也可以让系统产生四个数。

功能上已经比较完整了,用户界面上还不是很好,不过这个已经不是很重要的问题的。这部分代码,如果要做

图形界面,应该是很好移过去的。

有关源代码如下:

Token.h :用于Parse,以及操作符,优先级的定义

Code:
  1. #ifndef TOKEN_H  
  2. #define TOKEN_H  
  3. enum Token_type {Numeric = 0,Op};  
  4. enum Operator{ADD_OPR = 0,MINUS_OPR,  
  5. MUL_OPR,DIV_OPR,  
  6. LEFT_BRA,RIGHT_BRA,  
  7. TER};  
  8. enum PRI{HIGHER = 0,LOWER,EQUAL,NO_POSSIBLE};  
  9. class Token  
  10. {  
  11. public:  
  12.     Token(){}  
  13.     Token(Token_type _type,double _x,Operator _op):type(_type),  
  14.         x(_x),op(_op){}  
  15.     Token_type type;  
  16.     double x;  
  17.     Operator op;  
  18. };  
  19. void Parse(string expression,vector<Token>& tokens);  
  20. bool isOp(char c);  
  21. #endif  

Token.cpp

Code:
  1. #include <string>  
  2. #include <vector>  
  3. #include <sstream>  
  4. using namespace std;  
  5. #include "Token.h"  
  6. extern int OpTypeNum;  
  7. char operators[7] = {'+','-','*','/','(',')','#'};  
  8.   
  9. bool isOp(char c,int &index)  
  10. {  
  11.     for (int i = 0;i < OpTypeNum;i++)  
  12.     {  
  13.         if (c == operators[i])  
  14.         {  
  15.             index = i;  
  16.             return true;  
  17.         }  
  18.     }  
  19.     return false;  
  20. }  
  21. void Parse(string expression,vector<Token>& tokens)  
  22. {  
  23.     stringstream ss (stringstream::in | stringstream::out);  
  24.     ss << expression;  
  25.     char c;  
  26.     int val,index;  
  27.     while (ss >> c)  
  28.     {  
  29.         if (isdigit(c))  
  30.         {  
  31.             ss.putback(c);  
  32.             ss >> val;  
  33.             tokens.push_back(Token(Numeric,val,Operator(0)));  
  34.         }else if (isOp(c,index))  
  35.             tokens.push_back(Token(Op,-1,Operator(index)));  
  36.     }  
  37. }  

ExpCalc.h  用堆栈实现的中缀和后缀表达式的计算,以及后缀转化为中缀表达式的功能

Code:
  1. #ifndef EXPCALC_H  
  2. #define EXPCALC_H  
  3. class tree_Node  
  4. {  
  5. public:  
  6.     tree_Node(){}  
  7.     ~tree_Node();  
  8.     void Print();  
  9.     tree_Node(tree_Node * _left,tree_Node * _right,Token _token):  
  10.       left(_left),right(_right),token(_token){}  
  11.     tree_Node * left;  
  12.     tree_Node * right;  
  13.     Token token;  
  14. };  
  15. class ExpCalc  
  16. {  
  17. public:  
  18.     void ShowInfixExp(vector<Token>& tokens);  
  19.   
  20.     bool PostfixCalc(vector<Token> & tokens,double & res);  
  21.     bool infixCalc(vector<Token>& tokens,double& res);  
  22.     bool Calc(double x1,double x2,Operator op,double & res);  
  23.     void Clear();  
  24. private:  
  25.     bool doWhenHigher(Operator op);  
  26.     bool doWhenLower(Operator op,Operator nxt_op);  
  27.     bool doWhenEqual();  
  28.   
  29.     stack<double> operands_stack;  
  30.     stack<Operator> operators_stack;  
  31. };  
  32. #endif  

 

ExpCalc.cpp

Code:
  1. #include <stack>  
  2. #include <vector>  
  3. #include <cmath>  
  4. #include <iostream>  
  5. using namespace std;  
  6. #include "Token.h"  
  7. #include "ExpCalc.h"  
  8. #include "24Game.h"  
  9. int OpTypeNum = 7;  
  10. PRI operatorPRIs[7][7] ={{HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},  
  11.                         {HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},  
  12.                         {HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},  
  13.                         {HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},  
  14.                         {LOWER,LOWER,LOWER,LOWER,LOWER,EQUAL,NO_POSSIBLE},  
  15.                         {NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,/  
  16.                          NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE},  
  17.                         {LOWER,LOWER,LOWER,LOWER,LOWER,NO_POSSIBLE,EQUAL}};  
  18. extern char operators[7];  
  19.   
  20. bool ExpCalc::Calc(double x1,double x2,Operator op,double& res)  
  21. {  
  22.     bool flag = true;  
  23.     switch(op)  
  24.     {  
  25.         case ADD_OPR:  
  26.             res = x1 + x2;  
  27.             break;  
  28.         case MINUS_OPR:  
  29.             res = x1 - x2;  
  30.             break;  
  31.         case MUL_OPR:  
  32.             res = x1 * x2;  
  33.             break;  
  34.         case DIV_OPR:  
  35.             if (fabs(x2) < 1e-6)  
  36.                 flag = false;  
  37.             else  
  38.                 res = x1 / x2;  
  39.             break;  
  40.         default:  
  41.             flag = false;  
  42.     }  
  43.     return flag;  
  44. }  
  45.   
  46. bool ExpCalc::PostfixCalc(vector<Token> &tokens,double& res)  
  47. {  
  48.     Clear();  
  49.     for (int i = 0;i<tokens.size();i++)  
  50.     {  
  51.         if (tokens[i].type == Numeric)  
  52.             operands_stack.push(tokens[i].x);  
  53.         else  
  54.         {  
  55.             if (operands_stack.size()>=2)  
  56.             {  
  57.                 double x2 = operands_stack.top();  
  58.                 operands_stack.pop();  
  59.                 double x1 = operands_stack.top();  
  60.                 operands_stack.pop();  
  61.                 double r;  
  62.                 bool isOk = Calc(x1,x2,tokens[i].op,r);  
  63.                 if (!isOk)  return false;  
  64.                 operands_stack.push(r);  
  65.             }else{  
  66.                 return false;  
  67.             }  
  68.         }  
  69.     }  
  70.     if (operands_stack.size()!=1)  
  71.         return false;  
  72.     res = operands_stack.top();  
  73.     return true;  
  74. }  
  75. void ExpCalc::Clear()  
  76. {  
  77.     while (!operands_stack.empty())  
  78.         operands_stack.pop();  
  79.     while (!operators_stack.empty())  
  80.         operators_stack.pop();  
  81. }  
  82. bool ExpCalc::doWhenHigher(Operator op)  
  83. {  
  84.     if (operands_stack.size()<2)  
  85.         return false;  
  86.     double x2 = operands_stack.top();  
  87.     operands_stack.pop();  
  88.     double x1 = operands_stack.top();  
  89.     operands_stack.pop();  
  90.     double res;  
  91.     bool isOk = Calc(x1,x2,op,res);  
  92.     if (!isOk) return false;  
  93.     operands_stack.push(res);  
  94.     operators_stack.pop();  
  95.     return true;  
  96. }  
  97. bool ExpCalc::doWhenLower(Operator op,Operator nxt_op)  
  98. {  
  99.     operators_stack.push(nxt_op);  
  100.     return true;  
  101. }  
  102. bool ExpCalc::doWhenEqual()  
  103. {  
  104.     if (operators_stack.empty())  
  105.         return false;  
  106.     operators_stack.pop();  
  107.     return true;  
  108. }  
  109.   
  110. bool ExpCalc::infixCalc(vector<Token>& tokens,double& res)  
  111. {  
  112.     Clear();  
  113.     operators_stack.push(TER);  
  114.     double x1,x2;  
  115.     bool isOk;  
  116.     for (int i = 0;i<tokens.size();i++)  
  117.     {  
  118.         if (tokens[i].type == Numeric)  
  119.         {  
  120.             operands_stack.push(tokens[i].x);  
  121.         }else{  
  122.             if (operators_stack.empty())  
  123.             {  
  124.                 return false;  
  125.             }  
  126.             Operator nxt_op = tokens[i].op;  
  127.             bool over = false;  
  128.             while (!operators_stack.empty())  
  129.             {  
  130.                 Operator op = operators_stack.top();  
  131.                 PRI pri = operatorPRIs[int(op)][int(nxt_op)];  
  132.                 switch(pri)  
  133.                 {  
  134.                 case HIGHER:  
  135.                      isOk = doWhenHigher(op);  
  136.                      break;  
  137.                 case LOWER:  
  138.                      isOk = doWhenLower(op,nxt_op);  
  139.                      over = true;  
  140.                     break;  
  141.                 case EQUAL:  
  142.                      isOk = doWhenEqual();  
  143.                      over = true;  
  144.                     break;  
  145.                 case NO_POSSIBLE:  
  146.                     return false;  
  147.                 default:  
  148.                     return false;  
  149.                 }  
  150.                 if (!isOk)  
  151.                     return false;  
  152.                 if(over)  
  153.                     break;  
  154.             }  
  155.         }  
  156.     }  
  157.     if (!operators_stack.empty() || operands_stack.size()!=1)  
  158.         return false;  
  159.     res = operands_stack.top();  
  160.     return true;  
  161. }  
  162.   
  163. void ExpCalc::ShowInfixExp(vector<Token>& tokens)  
  164. {  
  165.     stack<tree_Node *> tn_stack;  
  166.     tree_Node * root = NULL;  
  167.     for (int i = 0;i<tokens.size();i++)  
  168.     {  
  169.         if (tokens[i].type == Numeric)  
  170.         {  
  171.             tn_stack.push(new tree_Node(NULL,NULL,tokens[i]));  
  172.         }else{  
  173.             if (tn_stack.size()<2)  
  174.             {  
  175.                 cout << "后缀表达式有错!"<<endl;  
  176.                 return;  
  177.             }  
  178.             tree_Node * x2 = tn_stack.top();  
  179.             tn_stack.pop();  
  180.             tree_Node * x1 = tn_stack.top();  
  181.             tn_stack.pop();  
  182.             double res;  
  183.             bool isOk = Calc(x1->token.x,x2->token.x,tokens[i].op,res);  
  184.             if (!isOk)   
  185.             {  
  186.                 cout << "计算过程有误"<<endl;  
  187.                 return;  
  188.             }  
  189.             root = new tree_Node(x1,x2,Token(Numeric,res,tokens[i].op));  
  190.             tn_stack.push(root);  
  191.         }  
  192.     }  
  193.     if (root == NULL)  
  194.     {  
  195.         cout << "后缀表达式有误"<<endl;  
  196.         return;  
  197.     }  
  198.     root->Print();  
  199.     cout << endl;  
  200.     delete root;  
  201. }  
  202.   
  203. void tree_Node::Print()  
  204. {  
  205.     if (left == NULL && right == NULL)  
  206.     {     
  207.         cout << token.x << " ";  
  208.         return;  
  209.     }  
  210.     cout << "(";  
  211.     if (left!=NULL)  
  212.         left->Print();  
  213.     cout << operators[token.op] << " ";  
  214.     if (right!=NULL)  
  215.         right->Print();  
  216.     cout << ")";  
  217. }  
  218.   
  219. tree_Node::~tree_Node()  
  220. {  
  221.     if (left!=NULL)  
  222.     {  
  223.         delete left;  
  224.         left = NULL;  
  225.     }  
  226.     if (right!=NULL)  
  227.     {  
  228.         delete right;  
  229.         right = NULL;  
  230.     }  
  231. }  

 

24Game.h 算24游戏的有关逻辑

Code:
  1. #ifndef GAME_H  
  2. #define GAME_H  
  3. class Game  
  4. {  
  5. public:  
  6.     Game();  
  7.     void GenNewNumbers();  
  8.     bool hasSolutions();  
  9.     void ShowNums();  
  10.     void ShowSolution();  
  11.     bool CheckInput(vector<int>& inputs);  
  12.     bool Calc(vector<Token>& tokens,double & res);  
  13.     bool Check(vector<Token>& tokens);  
  14.     static int minOperand;  
  15.     static int maxOperand;  
  16. private:  
  17.     vector<Token> solu_tokens;  
  18.     bool hasSolu;  
  19.     string expression;  
  20.       
  21.     ExpCalc calc;//表达式计算器  
  22.     int generated_operands[4];  
  23.     int input_operands[4];  
  24.     char input_operators[3];  
  25. };  
  26. #endif  

 

24Game.cpp

Code:
  1. #include <stack>  
  2. #include <time.h>  
  3. #include <stdlib.h>  
  4. #include <string>  
  5. #include <algorithm>  
  6. #include <vector>  
  7. #include <cmath>  
  8. #include <iostream>  
  9. using namespace std;  
  10. #include "Token.h"  
  11. #include "ExpCalc.h"  
  12. #include "24Game.h"  
  13. int Game::minOperand = 1;  
  14. int Game::maxOperand = 13;  
  15. extern char operators[7];  
  16. Game::Game()  
  17. {  
  18.     srand(time(NULL));  
  19. }  
  20. bool Game::CheckInput(vector<int>& inputs)  
  21. {  
  22.     for (int i = 0;i<4;i++)  
  23.     {  
  24.         generated_operands[i] = inputs[i];  
  25.         if (generated_operands[i] > maxOperand ||   
  26.             generated_operands[i] < minOperand)  
  27.         {  
  28.             return false;  
  29.         }  
  30.     }  
  31.     return true;  
  32. }  
  33. void Game::GenNewNumbers()  
  34. {  
  35.     while (1)  
  36.     {  
  37.         for (int i = 0;i<4;i++)  
  38.             generated_operands[i] = rand()%(maxOperand-minOperand+1) + minOperand;  
  39.         if (hasSolutions()) break;  
  40.     }  
  41.     //generated_operands[0] = 4;  
  42.     //generated_operands[1] = 8;  
  43.     //generated_operands[2] = 4;  
  44.     //generated_operands[3] = 9;  
  45.   
  46. }  
  47.   
  48. //若有解,只保存一组先  
  49. bool Game::hasSolutions()  
  50. {  
  51.     vector<double> a(4);//操作数  
  52.     double res;  
  53.     copy(generated_operands,generated_operands+4,a.begin());      
  54.     sort(a.begin(),a.end());  
  55.     int operandPos[5][4] = {{0,1,2,3},{0,1,2,4},{0,1,2,5},{0,1,3,4},{0,1,3,5}};  
  56.     int opPos[5][3] = {{4,5,6},{3,5,6},{3,4,6},{2,5,6},{2,4,6}};  
  57.     vector<Token> tokens(7);  
  58.     while (next_permutation(a.begin(),a.end()))  
  59.     {  
  60.         for (int p = 0;p<5;p++)  
  61.         {  
  62.             for (int idx = 0;idx<4;idx++)  
  63.             {  
  64.                 tokens[operandPos[p][idx]].type = Numeric;  
  65.                 tokens[operandPos[p][idx]].x = a[idx];  
  66.             }  
  67.             for (int idx = 0;idx<3;idx++)  
  68.                 tokens[opPos[p][idx]].type = Op;  
  69.   
  70.             for (int i = ADD_OPR;i<=DIV_OPR;i++)  
  71.             {  
  72.                 for (int j = ADD_OPR;j<=DIV_OPR;j++)  
  73.                 {  
  74.                     for (int k = ADD_OPR;k<=DIV_OPR;k++)  
  75.                     {  
  76.                         tokens[opPos[p][0]].op = Operator(i);  
  77.                         tokens[opPos[p][1]].op = Operator(j);  
  78.                         tokens[opPos[p][2]].op = Operator(k);  
  79.   
  80.                         bool isOk = calc.PostfixCalc(tokens,res);  
  81.                         if (isOk && fabs(res - 24) < 1e-6)  
  82.                         {  
  83.                             solu_tokens = tokens;  
  84.                             hasSolu = true;  
  85.                             return true;  
  86.                         }  
  87.                     }  
  88.                 }  
  89.             }  
  90.   
  91.         }  
  92.     }  
  93.     hasSolu = false;  
  94.     return false;  
  95. }  
  96.   
  97. void Game::ShowNums()  
  98. {  
  99.     for (int i = 0;i<4;i++)  
  100.         cout << generated_operands[i] << " ";  
  101.     cout << endl;  
  102. }  
  103.   
  104. void Game::ShowSolution()  
  105. {  
  106.     calc.ShowInfixExp(solu_tokens);  
  107.     cout << endl;  
  108. }  
  109.   
  110. bool Game::Calc(vector<Token>& tokens,double & res)  
  111. {  
  112.     return calc.infixCalc(tokens,res);  
  113. }  
  114.   
  115. bool Game::Check(vector<Token>& tokens)  
  116. {  
  117.     vector<double> a;  
  118.     for (int i = 0;i<tokens.size();i++)  
  119.     {  
  120.         if (tokens[i].type == Numeric)  
  121.             a.push_back(tokens[i].x);  
  122.     }  
  123.     sort(a.begin(),a.end());  
  124.   
  125.     vector<double> opr_copy(4);  
  126.     copy(generated_operands,generated_operands+4,opr_copy.begin());   
  127.     sort(opr_copy.begin(),opr_copy.end());  
  128.   
  129.     if (opr_copy.size()!=a.size())  
  130.     {  
  131.         return false;  
  132.     }  
  133.     for (int i = 0;i<a.size();i++)  
  134.     {  
  135.         if (a[i]!=opr_copy[i])  
  136.         {  
  137.             return false;  
  138.         }  
  139.     }  
  140.     return true;  
  141. }  

 

24Points:主文件

Code:
  1. #include <string>  
  2. #include <iostream>  
  3. #include <vector>  
  4. #include <stack>  
  5. #include <cmath>  
  6. using namespace std;  
  7. #include "Token.h"  
  8. #include "ExpCalc.h"  
  9. #include "24Game.h"  
  10. int main()  
  11. {  
  12.     Game game;  
  13.     double res;  
  14.     while (1)  
  15.     {  
  16.         cout << "开始本轮算24游戏(输入g:系统随机产生测试数,输入s:用户自行输入4个要测试的数)"<<endl;  
  17.           
  18.         char c;  
  19.         cin >> c;  
  20.         if (c == 'g')  
  21.         {  
  22.             game.GenNewNumbers();  
  23.             game.ShowNums();  
  24.         }else if(c == 's')  
  25.         {  
  26.             vector<int> inputs(4);  
  27.             cout << "输入四个1到13的整数,以空格隔开:";  
  28.             for (int i = 0;i<4;i++)  
  29.                 cin >> inputs[i];  
  30.             bool isOk = game.CheckInput(inputs);  
  31.             if (!isOk){  
  32.                 cout << "输入的数应在1到13之间"<<endl;  
  33.                 continue;  
  34.             }  
  35.             if(!game.hasSolutions())  
  36.             {  
  37.                 cout << "您的输入无解"<<endl;  
  38.                 continue;  
  39.             }  
  40.         }else  
  41.         {  
  42.             cout << "无效命令"<<endl;  
  43.             continue;  
  44.         }  
  45.         cin.ignore();//吃掉回车键  
  46.         while(1)  
  47.         {  
  48.             cout << "输入你的表达式:以#结尾,只输入#将显示答案,案例:(4*9 -4-8)"<<endl;  
  49.             string expression;  
  50.             getline(cin,expression);      
  51.             if (expression == "#")  
  52.             {  
  53.                 game.ShowSolution();  
  54.                 break;  
  55.             }  
  56.             vector<Token> tokens;  
  57.             Parse(expression,tokens);  
  58.   
  59.             if (!game.Check(tokens))  
  60.             {             
  61.                 cout << "输入的数字不对"<<endl;  
  62.                 continue;  
  63.             }  
  64.             bool isOk = game.Calc(tokens,res);  
  65.             if (!isOk)  
  66.                 cout << "输入格式有误" <<endl;  
  67.             else if (fabs(res - 24) < 1e-6 )  
  68.                 cout <<"您算对了"<<endl;  
  69.             else  
  70.                 cout << "您算错了"<<endl;  
  71.         }  
  72.     }  
  73.     return 0;  
  74. }  

 

截图

 

 

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值