24点游戏---java编写
Posted 易小顺
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了24点游戏---java编写相关的知识,希望对你有一定的参考价值。
24点游戏
前言
24点游戏是经典的纸牌益智游戏。
常见游戏规则:
从扑克中每次取出4张牌。使用加减乘除,第一个能得出24者为赢。
其中,J代表11,Q代表12,K代表13,A代表1
基本要求:
随机生成4个代表扑克牌牌面的数字字母,程序自动列出所有可能算出24的表达式,用Java实现程序解决问题。
1、 算法分析
1 .1 接收玩家结果输入与结果判定。
将玩家的输入保存到一个string变量中,与计算机自动产生的数据进行匹配,如果结果包含则认为用户答对,将用户积分递增1,如果输入为空或null或者不匹配则认为玩家答案错误,将玩家血量递减1。
1.2 工具类TimeUtils、CardUtils。
TimeUtils负责整个游戏的定时计算,是一个后台线程,可以调用主线程中的数据进行分析,看是否达到退出游戏的条件,如果满足则直接进行调用主线程的退出方法,进行线程的关闭并结束进程。否则查看当前的时间计数单位是否超时,如果超时,则调用主线程的outTime()方法进行玩家血量的递减,否则继续进行游戏数据的监听更新。
CardUtils有两个字典映射num2card、card2num,负责转换牌面 1-K 对应的数字,方便游戏的计算
1.3 数据生成与结果计算。
由generateNumbers()方法对数据进行生成,先生成 1-13 的4个数字再将数字转换为对应的牌面对用户进行展示,如果当前的4个数不能满足24点的规律则会从新生成,直到至少有一个答案为止。
calculateResult()方法对生成的4个随机数进行穷举所有可能,并计算相应的结果,如果满足24点规律则将当前的排列进行保存,否则舍弃掉。运算规则是先从4个数中抽取两个数进行四则运算,再将结果和剩下的数据进行合并再次计算,直到只有一个数据为止,防止出现数据重合的现象,采取不回头的方式进行,从左到右开始数据的选取,如果已经有了数据的组合,则后面的运算就不在进行。结果生成之后进行清洗,去除多余的括号,并检查相似的结构将其提出。
2、 概要设计
2.1 结构设计
App类,为算法的入口类
包含10个成员变量(random、faceContent、trueResult、blood、score、playingInfo、flag、timer、timeUtils)分别负责随机数的生成,牌面结果保存,玩家输入结果保存,计算结果保存,玩家的血量和积分,玩家游戏中产生的数据信息,及3个定时器相关变量;
19个成员方法(startGame()、savePlayingInfo()、generateNumbers()、outTime()、calculateResult()、clearn()、calcute()、saveResult)()。。。)分别用于程序的入口,游戏的管理,数据生成,结果计算,游戏数据的本地保存,玩家交互,及各个变量所需的getter、setter方法。
package com.beordie;
import com.beordie.utils.CardUtils;
import com.beordie.utils.TimeUtils;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.*;
/**
* @Classname App
* @Description 游戏实现主类
* @Date 2021/5/8 22:30
* @Created 30500
*/
public class App {
// 随机生成器
private static Random random = new Random();
// 牌面保存
private static List<String> faceContent = new ArrayList<>();
// 存储用户输入的运算表达式
private static String buffer;
// 存储正确结果
private static List<String> trueResult = new ArrayList<>();
// 玩家血量 积分
private static int blood = 3;
private static int score = 0;
// 保存玩家的数据信息
private static List<String> playingInfo = new ArrayList<>();
// 定时器
private static Integer flag = 0;
private static Timer timer = new Timer();
private static TimeUtils timeUtils = new TimeUtils();
/**
* @description 入口函数
* @author 30500
* @date 2021/5/9 9:47
* @type [java.lang.String[]]
* @return void
*/
public static void main(String[] args) {
// 开始游戏
startGame();
}
/**
* @description 游戏控制器
* @author 30500
* @date 2021/5/9 12:37
* @type []
* @return void
*/
public static void startGame() {
while (blood > 0) {
// 生成随机数
generateNumbers();
// 定时器初始化
if (flag == 0) {
timer.schedule(timeUtils, 0 , 1000);
flag = 1;
}
// 玩家输入
input();
// 检查玩家输入是否正确
if (checkInput()) {
System.out.println("答对了,加一分");
playingInfo.add(buffer + " 正确;");
score++;
} else {
System.out.println("答错了,血量减一");
playingInfo.add(buffer + " 错误;");
blood--;
}
timeUtils.setTime(15);
}
// 取消定时器任务
timer.cancel();
exit();
}
/**
* @description 追加添加玩家的游戏信息
* 一行表示一个玩家记录
* @author 30500
* @date 2021/5/9 13:03
* @type []
* @return void
*/
public static void savePlayingInfo() {
try{
BufferedWriter br = new BufferedWriter(new FileWriter("src/TopList.txt", true)); //数据保存在本地
for(int i = 0;i< playingInfo.size();i++){
br.write(playingInfo.get(i)+"\\t");
}
br.write("\\n");
br.close();
}catch(Exception e){
e.printStackTrace();
}finally {
// 游戏结束直接释放所有任务
System.exit(0);
}
}
/**
* @description 生成四个代表牌面的随机数
* @author 30500
* @date 2021/5/8 22:51
* @type []
* @return void
*/
public static void generateNumbers() {
faceContent.clear();
// 保存随机数临时变量
int[] num = new int[4];
// 生成四个随机数
for (int i = 0; i < 4; i++) {
// 生成 1-13 的随机数
num[i] = random.nextInt(13) + 1;
// 将随机数对应的牌面保存
faceContent.add((String) CardUtils.num2card.get(num[i]));
}
// 输出结果给用户
calculateResult(num[0], num[1], num[2], num[3]);
// 判断当前牌面是否可解
if (trueResult.size() > 0) {
print();
saveResult();
return;
}
// 不可解重新递归生成
generateNumbers();
}
/**
* @description 输出牌面结果给用户
* @author 30500
* @date 2021/5/8 23:06
* @type []
* @return void
*/
public static void print() {
System.out.println("扑克牌牌面为");
for (String key : faceContent) {
System.out.printf("%-8s", key);
}
System.out.println();
}
/**
* @description 接收用户输入
* @author 30500
* @date 2021/5/8 23:06
* @type []
* @return void
*/
public static void input() {
// 输入设备句柄
Scanner input = new Scanner(System.in);
// 接收用户输入
System.out.println("请输入:");
buffer = input.nextLine();
}
/**
* @description 游戏超时更新数据
* @author 30500
* @date 2021/5/9 12:40
* @type []
* @return void
*/
public static void outTime() {
blood--;
playingInfo.add("******" + " 超时;");
System.out.println("游戏超时,血量减一");
}
/**
* @description 计算可能性结果
* 采取不回头的方法进行,每个数据从头到尾,一次展开,不回头计算
* @author 30500
* @date 2021/5/9 12:41
* @type [int, int, int, int]
* @return void
*/
public static void calculateResult(int num1, int num2, int num3, int num4) {
// 表示四个牌面
StringBuffer face1 = new StringBuffer(CardUtils.num2card.get(num1));
StringBuffer face2 = new StringBuffer(CardUtils.num2card.get(num2));
StringBuffer face3 = new StringBuffer(CardUtils.num2card.get(num3));
StringBuffer face4 = new StringBuffer(CardUtils.num2card.get(num4));
int resultNum = 0;
for (int i = 0; i < 4; i++) {
// 取出运算符
char operator1 = CardUtils.arithmetic[i];
// 第1次计算,先从4个数中任意选择2个进行计算,将计算结果再次参加运算
// 先选第一,和第二个数进行计算
int firstResult = calcute(num1, num2, operator1);
// 先选第二和第三两个数进行计算
int secondResult = calcute(num2, num3, operator1);
// 先选第三和第四俩个数进行计算
int thirdResult = calcute(num3, num4, operator1);
for (int j = 0; j < 4; j++) {
// 取出运算符
char operator2 = CardUtils.arithmetic[j];
// 第2次计算,从3个数中选择2个进行计算
int firstMidResult = calcute(firstResult, num3, operator2);
int firstTailResult = calcute(num3, num4, operator2);
int midFirstResult = calcute(num1, secondResult, operator2);
int midTailResult = calcute(secondResult, num4, operator2);
int tailMidResult = calcute(num2, thirdResult, operator2);
for (int k = 0; k < 4; k++) {
// 取出运算符
char operator3 = CardUtils.arithmetic[k];
//第3次计算,计算两个数的结果
if(calcute(firstMidResult, num4, operator3) == 24)
trueResult.add("((" + face1 + operator1 + face2 + ")" + operator2 + face3 + ")" + operator3 + face4);
if(calcute(firstResult, firstTailResult, operator3) == 24)
trueResult.add("(" + face1 + operator1 + face2 + ")" + operator3 + "(" + face3 + operator2 + face4 + ")");
if(calcute(midFirstResult, num4, operator3) == 24)
trueResult.add("(" + face1 + operator2 + "(" + face2 + operator1 + face3 + "))" + operator3 + face4);
if(calcute(num1,midTailResult, operator3) == 24)
trueResult.add("" + face1 + operator3 + "((" + face2 + operator1 + face3 + ")" + operator2 + face4 + ")");
if(calcute(num1,tailMidResult,operator3) == 24)
trueResult.add("" + face1 + operator3 + "(" + face2 + operator2 + "(" + face3 + operator1 + face4 + "))");
}
}
}
// 进行数据清洗
clearn();
}
/**
* @description 清洗多余的数据结果
* @author 30500
* @date 2021/5/9 12:43
* @type []
* @return void
*/
private static void clearn() {
List<String> tempResult = new ArrayList<>();
String temp = null;
// 清除数据结构中多余的()符号
for (int i = 0; i < trueResult.size(); i++) {
temp = trueResult.get(i);
if ((temp.indexOf('-') == -1 && temp.indexOf('+') == -1) || (temp.indexOf('*') == -1 && temp.indexOf('/') == -1)) {
temp = temp.replaceAll("[()]", "");
}
if (tempResult.indexOf(temp) == -1) {
tempResult.add(temp);
}
}
trueResult.clear();
trueResult = tempResult;
}
/**
* @description 判断用户输入是否正确
* @author 30500
* @date 2021/5/9 9:03
* @type []
* @return boolean
*/
public static boolean checkInput() {
// 检查数据是否合理
if (buffer == null) {
return false;
}
// 判断是否是正确答案
if (trueResult.indexOf(buffer.trim()) == -1) {
return false;
}
return true;
}
/**
* @description 两个数之间的四则运算
* @author 30500
* @date 2021/5/9 9:46
* @type [int, int, char]
* @return int
*/
public static int calcute(int count1, int count2, char operator) {
// 加
if (operator == '+') {
return count1 + count2;
}
// 减
else if (operator == '-') {
return count1 - count2;
}
// 乘
else if (operator == '*') {
return count1 * count2;
}
// 除
else if (operator == '/' && count2 != 0) {
return count1 / count2;
}
return -1;
}
/**
* @description 保存当前牌面的正确结果
* @author 30500
* @date 2021/5/9 12:43
* @type []
* @return void
*/
public static void saveResult() {
try{
BufferedWriter br = new BufferedWriter(new FileWriter("src/result.txt")); //数据保存在本地
for(int i = 0;i< trueResult.size();i++){
br.write(trueResult.get(i)+"\\n");
}
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
public static void exit() {
System.out.println("游戏结束,玩家得分:" + score);
savePlayingInfo();
}
public static int getBlood() {
return blood;
}
public static void setBlood(int blood) {
App.blood = blood;
}
public static int getScore() {
return score;
}
public static void setScore(int score) {
App.score = score;
}
public static Integer getFlag() {
return flag;
}
public static void setFlag(Integer flag) {
App.flag = flag;
}
}
CardUtils类是游戏的工具类,负责数字与牌面点数的转换,主要有三个静态成员变量num2card、card2num保存数据间的映射关系,arithmetic负责存储游戏中需要使用的(+、-、*、/)四则运算,通过下标即可访问相应的运算符,提供一个私有构造方法不允许外部进行对象的实例化,所有变量通过类名直接进行数据的引用。
24点游戏(dfs)