【课设】24点游戏设计 //递归计算24点//中缀转后缀//计时输入

题目

设计一个带有记分功能的24点游戏(简易版本,只使用1-10之间的数)
要求:游戏开始,程序随机自动生成四张牌(以1-10之间的数值代替),游戏者要求尽快给出表达式,只能使用 + - × % 运算。可以使用(),不涉及小数运算。比如机器给出四个数为3、3、6、2,游戏者输入表达式:(3+3-2)×6,程序检查运算成功,根据要求3计算时间。如果给出的四个数不能得出24,如:1、2、1、3,那么游戏者必须输入NO,要求计算机进入下一题,但此次如果是正确判断一样记分。如此玩3次,计算总分。任何一次在要求时间内不能给出表达式或NO,那么游戏此轮结束。

分析题目

本题有三个难点:

  1. 是本题的核心问题,如何判断随机给的四个数能否算出24;
  2. 题目要求如果可以算出24,则要输入表达式,也就是要求中缀表达式转为后缀表达式然后再求值;
  3. 限时输入,如果到一定时间还没输入成功则该回合得0分;

思路

首先利用结构体,注意初始化:

struct data
{
	int n; //存数
	char c; //存符号
	data()
	{
		n = -1;
		c = '#';
	}
};

接着逐一突破难点。首先是第一个问题,这里打算用递归解决:

bool count24(int n)
{
	int i, j;
    double p, q;
    if (n == 1)
    {
        if (fabs(a[0] - 24) < 1e-3)
            return true;
        else
            return false;
    }
    for (i = 0; i < n - 1; i++)
	{
        for (j = i + 1; j < n; j++)
        {
            p = a[i]; //p记录第一位
            q = a[j]; //q记录第二位
            a[j] = a[n - 1]; //将最后一位前移
			//开始执行递归
            a[i] = p + q; //首位将p,q相加,再不停递归相加
            if (count24(n - 1)) 
                return true;
            //注意相减时出现负数的情况
            a[i] = p - q;
            if (count24(n - 1)) 
                return true;
            a[i] = q - p;
            if (count24(n - 1)) 
                return true;
            a[i] = p * q;
            if (count24(n - 1)) 
                return true;
            //注意分母为0的情况
            if (q != 0)
            {
                a[i] = p / q;
                if (count24(n - 1)) 
                    return true;
            }
            if (p != 0)
            {
                a[i] = q / p;
                if (count24(n - 1)) 
                    return true;
            }
            a[i] = p; 
            a[j] = q;
        }
	}
    return false;
}
举例说明递归:
  • 第一次[a,b,c,d]传入,变成[c,d,X],X是a和b的和,差,积,商(4种情况)
  • 第二次[c,d,X]传入,变成[X,Y],Y是c和d的和,差,积,商(4种情况)
  • 第三次[X,Y]传入,变成[Z],Z是X和Y的和,差,积,商(4种情况)
  • 最后判断Z是不是24

接下来解决手动输入中缀表达式转为后缀,然后求值。这需要使用栈来辅助解决。首先要明白中缀转后缀的规则:
从左到右扫描字符串

  1. 如果遇到操作数,我们就直接将其输出。
  2. 如果遇到操作符,当栈为空直接进栈;不为空,判断栈顶元素操作符优先级是否比当前操作符小,如果比当前操作符小则直接把当前操作符进栈;比当前操作符大则栈顶元素循环弹栈,直到栈顶元素操作符优先级比当前操作符小
  3. 遇到左括号时我们也将其放入栈中。
  4. 如果遇到一个右括号,则将栈元素弹出,将弹出的操作符输出直到遇到左括号为止。注意,左括号只弹出并不输出。
  5. 如果我们读到了输入的末尾,则将栈中所有元素依次弹出。

按照这个规则配合STL即可将元素提取出,代码如下:

mp['+'] = mp['-'] = 1; //利用散列表模拟运算符号的优先级
mp['*'] = mp['/'] = 2;
vector<char> rpn; //后缀数组
void transform(string s)
{
	rpn.clear();
	stack<data> stk;
	for (int i = 0; i < s.size(); i++)
	{
		//如果是数字则直接入栈
		if (i < s.size() && s[i] >= '0' && s[i] <= '9')
		{
			data node;
			node.n = s[i] - '0';
			//因为可能存在10这种两位数,所以要往前判断是否为数字
			int rec = i + 1, flag = 0;
			while (rec < s.size() && s[rec] >= '0' && s[rec] <= '9')
			{
				flag = 1;
				node.n = node.n * 10 + s[rec] - '0';
				rec++;
			}
			if (flag) //如果前一位也是数字,则i要往前自增一位
				++i;
			rpn.push_back(node);
		}
		else if (s[i] == '(') //左括号直接入栈
		{
			data node;
			node.c = s[i];
			stk.push(node);
		}
		else if (s[i] == ')') //右括号则循环弹栈,直到遇到左括号
		{
			while (stk.top().c != '(')
			{
				rpn.push_back(stk.top());
				stk.pop();
			}
			stk.pop(); //注意把左括号也弹掉
		}
		else //否则是符号
		{	
			//如果栈顶符号优先级大于等于当前符号优先级,则弹栈
			while (!stk.empty() && mp[stk.top().c] >= mp[s[i]])
			{
				rpn.push_back(stk.top());
				stk.pop();
			}
			data node;
			node.c = s[i];
			stk.push(node);
		}
	}
	while (!stk.empty()) //最后如果栈不为空,则全部输出
	{
		rpn.push_back(stk.top());
		stk.pop();
	}
}

由中缀转为的后缀表达式已经全部放入vector容器中,接着只要将后缀表达式求值就行了。这里同样使用了一个栈,从头遍历vector,当是数字时入栈,当是符号时将栈顶两个元素弹出,进行该符号的运算再入栈。代码如下:

int count(char c, int a, int b)
{
	switch (c)
	{
	case '+':
		return a + b;
		break;
	case '-':
		return a - b;
		break;
	case '*':
		return a * b;
		break;
	case '/':
		return a / b;
		break;
	default:
		cout << "Error\n";
		break;
	}
}
bool calculate(vector<data> vec)
{
	stack<int> cal;
	for (int i = 0; i < vec.size(); i++)
	{
		if (vec[i].n != -1)  //不为-1说明是数字,入栈
			cal.push(vec[i].n);
		else if (vec[i].c != '#') //不是#说明是符号
		{
			//注意符号运算的先后关系
			int b = cal.top();
			cal.pop();
			int a = cal.top();
			cal.pop();
			cal.push(count(vec[i].c, a, b));
		}
	}
	return cal.top() == 24;
}

最后是计时功能,要求超过计时时间则该回合结束,这里使用了clock()函数:

int flag_in = 1;
float sec1 = 60.0;
clock_t delay1 = sec1 * CLOCKS_PER_SEC; //delay1表示现实生活中的60秒
clock_t start = clock(); //start表示此时的时间
while (1)
{
	//clock()会不停更新当前的时间,所以当和start相差60秒时则退出循环
	if(clock() - start >= delay1)
		break;
	if (kbhit()) //kbhit()函数当键盘有输入时候返回true,否则返回false
	{
		flag_in = 0;
		break;
	}
}
if (flag_in) //如果while循环结束后flag_in仍是1,则说明超时
{
	cout << "\n已超时,该回合0分!\n";
	continue;
}

完整代码如下:

#include <iostream>
#include <cmath>
#include <cstdlib>
#include <string>
#include <map>
#include <vector>
#include <ctime>
#include <time.h>
#include <Windows.h>
#include <stack>
#include <conio.h>
using namespace std;
struct data
{
	int n; //存数
	char c; //存符号
	data()
	{
		n = -1;
		c = '#';
	}
};
double a[4];
vector<data> rpn;
map<char, int> mp;
string change(string);
void transform(string);
bool calculate(vector<data>);
int count(char c, int a, int b);
bool count24(int n);
int main()
{
	int cnt = 3, score = 0, rounds = 1;
	mp['+'] = mp['-'] = 1;
	mp['*'] = mp['/'] = 2;
	srand((unsigned)time(NULL)); //生成随机数的种子
	while (cnt--)
	{
		cout << "******************回合" << rounds++ << "******************" << endl;
		for (int i = 0; i < 4; i++)
			a[i] = (rand() % 10) + 1;
		cout << "随机生成4个数字:";
		for (int i = 0; i < 4; i++)
		{
			Sleep(500); //Sleep()函数模拟停顿
			cout << a[i];
			if (i != 3)
				cout << ' ';
		}
		Sleep(500);
		cout << endl;
		cout << "是否能算出24(限时60秒)?.........[Yes]\\[No]:";
		string choose;
		int flag_in = 1;
		float sec1 = 60.0;
		clock_t delay1 = sec1 * CLOCKS_PER_SEC;
		clock_t start = clock();
		while (1)
		{
			if(clock() - start >= delay1)
				break;
			if (kbhit())
			{
				flag_in = 0;
				break;
			}
		}
		if (flag_in)
		{
			cout << "\n已超时,该回合0分!\n";
			continue;
		}
		cin >> choose;
		choose = change(choose);
		if (choose == "yes" && count24(4))
		{
			cout << "请输入您的表达式(限时30秒):";
			int chance = 3, timeout = 1;
			string ans;
			clock_t start_ = clock();
			float sec2 = 30.0;
			clock_t delay2 = sec2 * CLOCKS_PER_SEC;
			while (1)
			{
				if(clock() - start_ >= delay2)
					break;
				if (kbhit())
				{
					timeout = 0;
					break;
				}
			}
			if (timeout)
			{
				cout << "\n已超时,本轮的0分!\n";
				continue;
			}
			cin >> ans;
			transform(ans);
			while (!calculate(rpn))
			{
				if (chance == 0)
					break;
				else
				{
					cout << "表达式错误!剩余尝试次数:"<< --chance << endl;
					cout << "请输入您的表达式:";
					cin >> ans;
					transform(ans);
				}
			}
			if (chance == 0)
			{
				cout << "尝试次数已用尽,该回合得5分\n" << endl;
				score += 5;
			}
			else
			{
				cout << "表达式正确,得10分!\n" << endl;
				score += 10;
			}

		}
		else if (choose == "yes" && !count24(4))
			cout << "错误!该回合得0分\n";
		else if (choose == "no" && !count24(4))
		{
			cout << "正确!得10分!\n";
			score += 10;
		}
		else if (choose == "no" && count24(4))
			cout << "错误!该回合得0分\n";
		cout << endl;
		if (cnt == 4)
			break;
	}
	cout << "************************\n";
	cout << "您的最终得分为:" << score << "分" << endl;
	cout << "************************\n";
	system("pause");
	return 0;
}
string change(string s)
{
	string ans;
	for (int i = 0; i < s.size(); i++)
		ans += tolower(s[i]);
	return ans;
}
int count(char c, int a, int b)
{
	switch (c)
	{
	case '+':
		return a + b;
		break;
	case '-':
		return a - b;
		break;
	case '*':
		return a * b;
		break;
	case '/':
		return a / b;
		break;
	default:
		cout << "Error\n";
		break;
	}
}
bool count24(int n)
{
	int i, j;
    double p, q;
    if (n == 1)
    {
        if (fabs(a[0] - 24) < 1e-3)
            return true;
        else
            return false;
    }
    for (i = 0; i < n - 1; i++)
	{
        for (j = i + 1; j < n; j++)
        {
            p = a[i]; 
            q = a[j];
            a[j] = a[n - 1];

            a[i] = p + q;
            if (count24(n - 1)) 
                return true;
            a[i] = p - q;
            if (count24(n - 1)) 
                return true;
            a[i] = q - p;
            if (count24(n - 1)) 
                return true;
            a[i] = p * q;
            if (count24(n - 1)) 
                return true;
            if (q != 0)
            {
                a[i] = p / q;
                if (count24(n - 1)) 
                    return true;
            }
            if (p != 0)
            {
                a[i] = q / p;
                if (count24(n - 1)) 
                    return true;
            }
            a[i] = p; 
            a[j] = q;
        }
	}
    return false;
}
void transform(string s)
{
	rpn.clear();
	stack<data> stk;
	for (int i = 0; i < s.size(); i++)
	{
		if (i < s.size() && s[i] >= '0' && s[i] <= '9')
		{
			data node;
			node.n = s[i] - '0';
			int rec = i + 1, flag = 0;
			while (rec < s.size() && s[rec] >= '0' && s[rec] <= '9')
			{
				flag = 1;
				node.n = node.n * 10 + s[rec] - '0';
				rec++;
			}
			if (flag)
				++i;
			rpn.push_back(node);
		}
		else if (s[i] == '(')
		{
			data node;
			node.c = s[i];
			stk.push(node);
		}
		else if (s[i] == ')')
		{
			while (stk.top().c != '(')
			{
				rpn.push_back(stk.top());
				stk.pop();
			}
			stk.pop();
		}
		else
		{
			while (!stk.empty() && mp[stk.top().c] >= mp[s[i]])
			{
				rpn.push_back(stk.top());
				stk.pop();
			}
			data node;
			node.c = s[i];
			stk.push(node);
		}
	}
	while (!stk.empty())
	{
		rpn.push_back(stk.top());
		stk.pop();
	}
}
bool calculate(vector<data> vec)
{
	stack<int> cal;
	for (int i = 0; i < vec.size(); i++)
	{
		if (vec[i].n != -1)
			cal.push(vec[i].n);
		else if (vec[i].c != '#')
		{
			int b = cal.top();
			cal.pop();
			int a = cal.top();
			cal.pop();
			cal.push(count(vec[i].c, a, b));
		}
	}
	return cal.top() == 24;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值