c++编写的算24程序

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

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

4.21更新

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

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

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

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

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

有关源代码如下:

//Token.h :用于Parse,以及操作符,优先级的定义 
#ifndef TOKEN_H
#define TOKEN_H
enum Token_type {Numeric = 0,Op};
enum Operator{ADD_OPR = 0,MINUS_OPR,
MUL_OPR,DIV_OPR,
LEFT_BRA,RIGHT_BRA,
TER};
enum PRI{HIGHER = 0,LOWER,EQUAL,NO_POSSIBLE};
class Token
{
public:
	Token(){}
	Token(Token_type _type,double _x,Operator _op):type(_type),
		x(_x),op(_op){}
	Token_type type;
	double x;
	Operator op;
};
void Parse(string expression,vector<Token>& tokens);
bool isOp(char c);
#endif


//Token.cpp 
#include <string>
#include <vector>
#include <sstream>
using namespace std;
#include "Token.h"
extern int OpTypeNum;
char operators[7] = {'+','-','*','/','(',')','#'};

bool isOp(char c,int &index)
{
	for (int i = 0;i < OpTypeNum;i++)
	{
		if (c == operators[i])
		{
			index = i;
			return true;
		}
	}
	return false;
}
void Parse(string expression,vector<Token>& tokens)
{
	stringstream ss (stringstream::in | stringstream::out);
	ss << expression;
	char c;
	int val,index;
	while (ss >> c)
	{
		if (isdigit(c))
		{
			ss.putback(c);
			ss >> val;
			tokens.push_back(Token(Numeric,val,Operator(0)));
		}else if (isOp(c,index))
			tokens.push_back(Token(Op,-1,Operator(index)));
	}
}


//ExpCalc.h 用堆栈实现的中缀和后缀表达式的计算 
#ifndef EXPCALC_H
#define EXPCALC_H
class tree_Node
{
public:
	tree_Node(){}
	~tree_Node();
	void Print();
	tree_Node(tree_Node * _left,tree_Node * _right,Token _token):
	  left(_left),right(_right),token(_token){}
	tree_Node * left;
	tree_Node * right;
	Token token;
};
class ExpCalc
{
public:
	void ShowInfixExp(vector<Token>& tokens);

	bool PostfixCalc(vector<Token> & tokens,double & res);
	bool infixCalc(vector<Token>& tokens,double& res);
	bool Calc(double x1,double x2,Operator op,double & res);
	void Clear();
private:
	bool doWhenHigher(Operator op);
	bool doWhenLower(Operator op,Operator nxt_op);
	bool doWhenEqual();

	stack<double> operands_stack;
	stack<Operator> operators_stack;
};
#endif
//ExpCalc.cpp 
#include <stack>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
#include "Token.h"
#include "ExpCalc.h"
#include "24Game.h"
int OpTypeNum = 7;
PRI operatorPRIs[7][7] ={{HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},
						{HIGHER,HIGHER,LOWER,LOWER,LOWER,HIGHER,HIGHER},
						{HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},
						{HIGHER,HIGHER,HIGHER,HIGHER,LOWER,HIGHER,HIGHER},
						{LOWER,LOWER,LOWER,LOWER,LOWER,EQUAL,NO_POSSIBLE},
						{NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE,\
						 NO_POSSIBLE,NO_POSSIBLE,NO_POSSIBLE},
						{LOWER,LOWER,LOWER,LOWER,LOWER,NO_POSSIBLE,EQUAL}};
extern char operators[7];

bool ExpCalc::Calc(double x1,double x2,Operator op,double& res)
{
	bool flag = true;
	switch(op)
	{
		case ADD_OPR:
			res = x1 + x2;
			break;
		case MINUS_OPR:
			res = x1 - x2;
			break;
		case MUL_OPR:
			res = x1 * x2;
			break;
		case DIV_OPR:
			if (fabs(x2) < 1e-6)
				flag = false;
			else
				res = x1 / x2;
			break;
		default:
			flag = false;
	}
	return flag;
}

bool ExpCalc::PostfixCalc(vector<Token> &tokens,double& res)
{
	Clear();
	for (int i = 0;i<tokens.size();i++)
	{
		if (tokens[i].type == Numeric)
			operands_stack.push(tokens[i].x);
		else
		{
			if (operands_stack.size()>=2)
			{
				double x2 = operands_stack.top();
				operands_stack.pop();
				double x1 = operands_stack.top();
				operands_stack.pop();
				double r;
				bool isOk = Calc(x1,x2,tokens[i].op,r);
				if (!isOk)	return false;
				operands_stack.push(r);
			}else{
				return false;
			}
		}
	}
	if (operands_stack.size()!=1)
		return false;
	res = operands_stack.top();
	return true;
}
void ExpCalc::Clear()
{
	while (!operands_stack.empty())
		operands_stack.pop();
	while (!operators_stack.empty())
		operators_stack.pop();
}
bool ExpCalc::doWhenHigher(Operator op)
{
	if (operands_stack.size()<2)
		return false;
	double x2 = operands_stack.top();
	operands_stack.pop();
	double x1 = operands_stack.top();
	operands_stack.pop();
	double res;
	bool isOk = Calc(x1,x2,op,res);
	if (!isOk) return false;
	operands_stack.push(res);
	operators_stack.pop();
	return true;
}
bool ExpCalc::doWhenLower(Operator op,Operator nxt_op)
{
	operators_stack.push(nxt_op);
	return true;
}
bool ExpCalc::doWhenEqual()
{
	if (operators_stack.empty())
		return false;
	operators_stack.pop();
	return true;
}

bool ExpCalc::infixCalc(vector<Token>& tokens,double& res)
{
	Clear();
	operators_stack.push(TER);
	double x1,x2;
	bool isOk;
	for (int i = 0;i<tokens.size();i++)
	{
		if (tokens[i].type == Numeric)
		{
			operands_stack.push(tokens[i].x);
		}else{
			if (operators_stack.empty())
			{
				return false;
			}
			Operator nxt_op = tokens[i].op;
			bool over = false;
			while (!operators_stack.empty())
			{
				Operator op = operators_stack.top();
				PRI pri = operatorPRIs[int(op)][int(nxt_op)];
				switch(pri)
				{
				case HIGHER:
					 isOk = doWhenHigher(op);
					 break;
				case LOWER:
					 isOk = doWhenLower(op,nxt_op);
					 over = true;
					break;
				case EQUAL:
					 isOk = doWhenEqual();
					 over = true;
					break;
				case NO_POSSIBLE:
					return false;
				default:
					return false;
				}
				if (!isOk)
					return false;
				if(over)
					break;
			}
		}
	}
	if (!operators_stack.empty() || operands_stack.size()!=1)
		return false;
	res = operands_stack.top();
	return true;
}

void ExpCalc::ShowInfixExp(vector<Token>& tokens)
{
	stack<tree_Node *> tn_stack;
	tree_Node * root = NULL;
	for (int i = 0;i<tokens.size();i++)
	{
		if (tokens[i].type == Numeric)
		{
			tn_stack.push(new tree_Node(NULL,NULL,tokens[i]));
		}else{
			if (tn_stack.size()<2)
			{
				cout << "后缀表达式有错!"<<endl;
				return;
			}
			tree_Node * x2 = tn_stack.top();
			tn_stack.pop();
			tree_Node * x1 = tn_stack.top();
			tn_stack.pop();
			double res;
			bool isOk = Calc(x1->token.x,x2->token.x,tokens[i].op,res);
			if (!isOk) 
			{
				cout << "计算过程有误"<<endl;
				return;
			}
			root = new tree_Node(x1,x2,Token(Numeric,res,tokens[i].op));
			tn_stack.push(root);
		}
	}
	if (root == NULL)
	{
		cout << "后缀表达式有误"<<endl;
		return;
	}
	root->Print();
	cout << endl;
	delete root;
}

void tree_Node::Print()
{
	if (left == NULL && right == NULL)
	{	
		cout << token.x << " ";
		return;
	}
	cout << "(";
	if (left!=NULL)
		left->Print();
	cout << operators[token.op] << " ";
	if (right!=NULL)
		right->Print();
	cout << ")";
}

tree_Node::~tree_Node()
{
	if (left!=NULL)
	{
		delete left;
		left = NULL;
	}
	if (right!=NULL)
	{
		delete right;
		right = NULL;
	}
}
//24Game.h 算24游戏的有关逻辑 
#ifndef GAME_H
#define GAME_H
class Game
{
public:
	Game();
	void GenNewNumbers();
	bool hasSolutions();
	void ShowNums();
	void ShowSolution();
	bool CheckInput(vector<int>& inputs);
	bool Calc(vector<Token>& tokens,double & res);
	bool Check(vector<Token>& tokens);
	static int minOperand;
	static int maxOperand;
private:
	vector<Token> solu_tokens;
	bool hasSolu;
	string expression;
	
	ExpCalc calc;//表达式计算器
	int generated_operands[4];
	int input_operands[4];
	char input_operators[3];
};
#endif
//24Game.cpp 
#include <stack>
#include <time.h>
#include <stdlib.h>
#include <string>
#include <algorithm>
#include <vector>
#include <cmath>
#include <iostream>
using namespace std;
#include "Token.h"
#include "ExpCalc.h"
#include "24Game.h"
int Game::minOperand = 1;
int Game::maxOperand = 13;
extern char operators[7];
Game::Game()
{
	srand(time(NULL));
}
bool Game::CheckInput(vector<int>& inputs)
{
	for (int i = 0;i<4;i++)
	{
		generated_operands[i] = inputs[i];
		if (generated_operands[i] > maxOperand || 
			generated_operands[i] < minOperand)
		{
			return false;
		}
	}
	return true;
}
void Game::GenNewNumbers()
{
	while (1)
	{
		for (int i = 0;i<4;i++)
			generated_operands[i] = rand()%(maxOperand-minOperand+1) + minOperand;
		if (hasSolutions())	break;
	}
	//generated_operands[0] = 4;
	//generated_operands[1] = 8;
	//generated_operands[2] = 4;
	//generated_operands[3] = 9;

}

//若有解,只保存一组先
bool Game::hasSolutions()
{
	vector<double> a(4);//操作数
	double res;
	copy(generated_operands,generated_operands+4,a.begin());	
	sort(a.begin(),a.end());
	int operandPos[5][4] = {{0,1,2,3},{0,1,2,4},{0,1,2,5},{0,1,3,4},{0,1,3,5}};
	int opPos[5][3] = {{4,5,6},{3,5,6},{3,4,6},{2,5,6},{2,4,6}};
	vector<Token> tokens(7);
	while (next_permutation(a.begin(),a.end()))
	{
		for (int p = 0;p<5;p++)
		{
			for (int idx = 0;idx<4;idx++)
			{
				tokens[operandPos[p][idx]].type = Numeric;
				tokens[operandPos[p][idx]].x = a[idx];
			}
			for (int idx = 0;idx<3;idx++)
				tokens[opPos[p][idx]].type = Op;

			for (int i = ADD_OPR;i<=DIV_OPR;i++)
			{
				for (int j = ADD_OPR;j<=DIV_OPR;j++)
				{
					for (int k = ADD_OPR;k<=DIV_OPR;k++)
					{
						tokens[opPos[p][0]].op = Operator(i);
						tokens[opPos[p][1]].op = Operator(j);
						tokens[opPos[p][2]].op = Operator(k);

						bool isOk = calc.PostfixCalc(tokens,res);
						if (isOk && fabs(res - 24) < 1e-6)
						{
							solu_tokens = tokens;
							hasSolu = true;
							return true;
						}
					}
				}
			}

		}
	}
	hasSolu = false;
	return false;
}

void Game::ShowNums()
{
	for (int i = 0;i<4;i++)
		cout << generated_operands[i] << " ";
	cout << endl;
}

void Game::ShowSolution()
{
	calc.ShowInfixExp(solu_tokens);
	cout << endl;
}

bool Game::Calc(vector<Token>& tokens,double & res)
{
	return calc.infixCalc(tokens,res);
}

bool Game::Check(vector<Token>& tokens)
{
	vector<double> a;
	for (int i = 0;i<tokens.size();i++)
	{
		if (tokens[i].type == Numeric)
			a.push_back(tokens[i].x);
	}
	sort(a.begin(),a.end());

	vector<double> opr_copy(4);
	copy(generated_operands,generated_operands+4,opr_copy.begin());	
	sort(opr_copy.begin(),opr_copy.end());

	if (opr_copy.size()!=a.size())
	{
		return false;
	}
	for (int i = 0;i<a.size();i++)
	{
		if (a[i]!=opr_copy[i])
		{
			return false;
		}
	}
	return true;
}
//24Points:主文件
#include <string>
#include <iostream>
#include <vector>
#include <stack>
#include <cmath>
using namespace std;
#include "Token.h"
#include "ExpCalc.h"
#include "24Game.h"
int main()
{
	Game game;
	double res;
	while (1)
	{
		cout << "开始本轮算24游戏(输入g:系统随机产生测试数,输入s:用户自行输入4个要测试的数)"<<endl;
		
		char c;
		cin >> c;
		if (c == 'g')
		{
			game.GenNewNumbers();
			game.ShowNums();
		}else if(c == 's')
		{
			vector<int> inputs(4);
			cout << "输入四个1到13的整数,以空格隔开:";
			for (int i = 0;i<4;i++)
				cin >> inputs[i];
			bool isOk = game.CheckInput(inputs);
			if (!isOk){
				cout << "输入的数应在1到13之间"<<endl;
				continue;
			}
			if(!game.hasSolutions())
			{
				cout << "您的输入无解"<<endl;
				continue;
			}
		}else
		{
			cout << "无效命令"<<endl;
			continue;
		}
		cin.ignore();//吃掉回车键
		while(1)
		{
			cout << "输入你的表达式:以#结尾,只输入#将显示答案,案例:(4*9 -4-8)"<<endl;
			string expression;
			getline(cin,expression);	
			if (expression == "#")
			{
				game.ShowSolution();
				break;
			}
			vector<Token> tokens;
			Parse(expression,tokens);

			if (!game.Check(tokens))
			{			
				cout << "输入的数字不对"<<endl;
				continue;
			}
			bool isOk = game.Calc(tokens,res);
			if (!isOk)
				cout << "输入格式有误" <<endl;
			else if (fabs(res - 24) < 1e-6 )
				cout <<"您算对了"<<endl;
			else
				cout << "您算错了"<<endl;
		}
	}
	return 0;
}


截图

2

posted on 2010-04-20 22:16  speedmancs  阅读(1099)  评论(0编辑  收藏  举报

导航