【玩儿】Java 数字炸弹小游戏(控制台版)+ IO 数据存储

Java 数字炸弹小游戏(控制台版)+ IO 数据存储

  • 数字炸弹小游戏概述
  • 功能实现
    • 实体类
      • [User.java 玩家信息实体类](#User.java 玩家信息实体类)
      • [GameRecode.java 游戏记录实体类](#GameRecode.java 游戏记录实体类)
    • 自定义异常
      • [AccountLockedException.java 账号锁定异常](#AccountLockedException.java 账号锁定异常)
      • [PasswordErrorException.java 密码错误异常](#PasswordErrorException.java 密码错误异常)
      • [UnknowAccountException 账号不存在异常](#UnknowAccountException 账号不存在异常)
    • 游戏主类
      • [Game.java 游戏主类](#Game.java 游戏主类)

数字炸弹小游戏概述

数字炸弹控制台版小游戏是Java 集合、流程控制、IO、异常、常用类等技术的综合练习。核心需求如下:

text 复制代码
实现数字炸弹游戏,要求如下:
	1、创建游戏菜单:
		1)注册
		2)登录
		3)开始游戏
		4)游戏记录
		5)游戏排行
		6)退出游戏
	2、菜单含义:
		1)注册:注册游戏玩家,要求玩家名字不能重复
		2)登录:使用玩家名字和密码进行登陆
		3)开始游戏:进入游戏,只有登录的玩家才可以开始游戏
		4)游戏记录:展示当前玩家的游戏记录,序号、炸弹、猜测次数、游戏开始时间、游戏结束时间、积分
		5)游戏排行:展示所有用户的昵称、游戏次数、总积分(倒序)
		6)退出游戏:结束游戏
	3、游戏规则
		a、生成100以内的随机值作为炸弹
		b、从控制台输入猜测的数值
		c、每次输入猜测值之后缩小猜测范围直到猜中为止
		d、本轮游戏结束之后反馈菜单(继续游戏、返回菜单)
		e、本轮游戏结束之后根据猜测次数和游戏时间换算积分
	4、游戏积分
		a、1-3次内猜中(含3次),时间在20秒以内,积分+10
		b、4-6次内猜中(含6次),时间在21-60秒,积分+5
		c、7-10次内猜中(含10次),时间在60秒以上,积分+2
		d、10次以上猜中不得分
	5、拓展功能:
		a、给游戏排行榜新加菜单选项,支持升序、降序的展示菜单。
		b、新增玩家管理功能,锁定玩家和解锁玩家。
		c、给登录功能增加异常处理,自定义:账号不存在异常、认证失败异常、锁定异常
		d、基于 IO 存储玩家信息和记录信息到文件。

功能实现

实体类

User.java 玩家信息实体类

java 复制代码
package com.riu.collect.game2.entity;

import java.io.Serializable;
import java.util.Objects;

/**
 * 玩家类,玩家的相关信息
 */
public class User implements Comparable<User>, Serializable {
    // 账号
    private String name;
    // 密码
    private String password;
    // 总的猜测数量、游戏的次数
    private Integer totalCount;
    // 总的积分
    private Integer totalPoints;
    // 玩家锁定、解锁的标记:false(解锁状态)true(锁定状态)
    private boolean isLock = false;

    public User() {
    }

    public User(String name, String password, Integer totalCount, Integer totalPoints) {
        this.name = name;
        this.password = password;
        this.totalCount = totalCount;
        this.totalPoints = totalPoints;
    }

    public User(String name, String password, Integer totalCount, Integer totalPoints, boolean isLock) {
        this.name = name;
        this.password = password;
        this.totalCount = totalCount;
        this.totalPoints = totalPoints;
        this.isLock = isLock;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public Integer getTotalCount() {
        return totalCount;
    }

    public void setTotalCount(Integer totalCount) {
        this.totalCount = totalCount;
    }

    public Integer getTotalPoints() {
        return totalPoints;
    }

    public void setTotalPoints(Integer totalPoints) {
        this.totalPoints = totalPoints;
    }

    public boolean isLock() {
        return isLock;
    }

    public void setLock(boolean lock) {
        isLock = lock;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", password='" + password + '\'' +
                ", totalCount=" + totalCount +
                ", totalPoints=" + totalPoints +
                ", isLock=" + isLock +
                '}';
    }

    /**
     * equals 和 hashCode 可以帮助我们判断对象的唯一性
     * 当前类的唯一性的条件是:用户名字,玩家在注册时候可以保证唯一性
     * @param o
     * @return
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(name, user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name);
    }

    @Override
    public int compareTo(User o) {
        return this.getName().compareTo(o.name);
    }
}

GameRecode.java 游戏记录实体类

java 复制代码
package com.riu.collect.game2.entity;

import java.io.Serializable;
import java.util.Date;

/**
 * 每次游戏的记录信息
 */
public class GameRecode implements Serializable {
    // 炸弹
    private Integer boom;
    // 猜测次数
    private Integer count;
    // 游戏开始时间
    private Date startTime;
    // 游戏结束时间
    private Date endTime;
    // 积分
    private Integer points;

    public Integer getBoom() {
        return boom;
    }

    public void setBoom(Integer boom) {
        this.boom = boom;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public Date getStartTime() {
        return startTime;
    }

    public void setStartTime(Date startTime) {
        this.startTime = startTime;
    }

    public Date getEndTime() {
        return endTime;
    }

    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }

    public Integer getPoints() {
        return points;
    }

    public void setPoints(Integer points) {
        this.points = points;
    }

    @Override
    public String toString() {
        return "GameRecode{" +
                "boom=" + boom +
                ", count=" + count +
                ", startTime=" + startTime +
                ", endTime=" + endTime +
                ", points=" + points +
                '}';
    }
}

自定义异常

AccountLockedException.java 账号锁定异常

java 复制代码
package com.riu.collect.game2.exception;

/**
 * 玩家账号锁定异常
 */
public class AccountLockedException extends Exception {
    public AccountLockedException() {
        super();
    }

    public AccountLockedException(String message) {
        super(message);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

PasswordErrorException.java 密码错误异常

java 复制代码
package com.riu.collect.game2.exception;

/**
 * 玩家密码错误,认证失败
 */
public class PasswordErrorException extends Exception {
    public PasswordErrorException() {
        super();
    }

    public PasswordErrorException(String message) {
        super(message);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

UnknowAccountException 账号不存在异常

java 复制代码
package com.riu.collect.game2.exception;

/**
 * 玩家账号不存在异常
 */
public class UnknowAccountException extends Exception {
    public UnknowAccountException() {
        super();
    }

    public UnknowAccountException(String message) {
        super(message);
    }

    @Override
    public String getMessage() {
        return super.getMessage();
    }
}

游戏主类

Game.java 游戏主类

java 复制代码
package com.riu.collect.game2;

import com.riu.collect.game2.entity.GameRecode;
import com.riu.collect.game2.entity.User;
import com.riu.collect.game2.exception.AccountLockedException;
import com.riu.collect.game2.exception.PasswordErrorException;
import com.riu.collect.game2.exception.UnknowAccountException;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Stream;

public class Game {
    // 创建一个用户的集合
    private List<User> userList = new ArrayList<>();
    // 玩家和记录的对应关系
    private Map<String, List<GameRecode>> recodeMap = new HashMap<>();
    // 当前登录的玩家名字
    private String loginName;
    // 判断玩家是登录的标记,默认 false 表示没有登录。
    // 等成功之后可以进行一些菜单操作
    boolean isLogin = false;

    // 两个文件路径
    private String userInfoPath = "src/com/riu/collect/game2/userInfo.txt";
    private String recodeInfoPath = "src/com/riu/collect/game2/recodeInfo.txt";
    // 定义公共的输入对象
    private Scanner scanner = new Scanner(System.in);


    public static void main(String[] args) {
        Game game = new Game();
        game.init();
        while (true){
            game.menu();
        }
    }

    /**
     * 初始化系统,从文件中将数据读取到 List
     */
    private void init(){
        try {

            /* 用户玩家信息读取 */
            File userInfoFile = new File(userInfoPath);
            if(userInfoFile.exists()){
                ObjectInputStream oisUserInfoStream = new ObjectInputStream(new FileInputStream(userInfoPath));
                Object userInfoObject = oisUserInfoStream.readObject();
                if(userInfoObject != null){
                    userList = (List<User>) userInfoObject;
                }
                oisUserInfoStream.close();
            }

            /* 用户玩家游戏记录读取 */
            File recodeInfoFile = new File(recodeInfoPath);
            if(recodeInfoFile.exists()) {
                ObjectInputStream oisRecodeInfoStream = new ObjectInputStream(new FileInputStream(recodeInfoPath));
                Object recodeObject = oisRecodeInfoStream.readObject();
                if (recodeObject != null) {
                    recodeMap = (Map<String, List<GameRecode>>) recodeObject;
                }
                oisRecodeInfoStream.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void menu(){
        System.out.println("===================================");
        System.out.println("\t\t\t1)注册");
        System.out.println("\t\t\t2)登录");
        if(isLogin){
            System.out.println("\t\t\t3)开始游戏");
            System.out.println("\t\t\t4)游戏记录");
        }
        System.out.println("\t\t\t5)游戏排行");
        System.out.println("\t\t\t6)退出游戏");
        System.out.println("\t\t\t7)玩家管理");
        System.out.println("===================================");
        System.out.print("请输入菜单编号:");
        int menuNum = scanner.nextInt();
        switch (menuNum) {
            case 1:
                // 注册
                reg();
                break;
            case 2:
                // 登录
                try {
                    login();
                } catch (UnknowAccountException | PasswordErrorException | AccountLockedException e) {
                    System.out.println(e.getMessage());
                }
                break;
            case 3:
                // 开始游戏
                startGame();
                break;
            case 4:
                // 展示游戏记录
                gameRecode();
                break;
            case 5:
                // 展示游戏排行榜
                gameTop();
                break;
            case 6:
                // 退出游戏
                exit();
                break;
            case 7:
                // 退出游戏
                userControl();
                break;
        }
    }


    /**
     * 注册
     */
    private void reg() {
        System.out.print("请输入玩家昵称:");
        String name = scanner.next();
        System.out.print("请输入密码:");
        String password = scanner.next();

        // 把输入的信息封装到对象中
        User user = new User();
        user.setName(name);
        user.setPassword(password);

        // 判断集合中是否已经存在玩家信息
        // 这里使用 contains 对比,要求 user 类必须重写 equals 和 hashcode 方法
        if(userList.contains(user)){
            System.out.println("玩家已经存在,快去开始游戏吧...");
        } else {
            // 把对象添加到集合
            userList.add(user);
            System.out.println("注册成功,快去开始游戏吧...");
        }
    }

    /**
     * 登录
     * 1、账号是否存在
     * 2、账号密码是否匹配
     * 3、账号是否被锁定
     *
     * 账号是用户输入的,根据用户输入的信息,递进验证账号是否可以使用。
     * 1、账号是否存在:拿着用户输入的信息,从集合中获取用户的信息对象。可以把用户对象临时存储一下。
     * 2、账号密码是否匹配:拿着用户输入的密码,和获取到的用户对象的密码对比。
     * 3、账号是否被锁定:根据获取到的用户的对象信息中的锁定状态判断。
     */
    private void login() throws UnknowAccountException, PasswordErrorException, AccountLockedException {
        System.out.print("请输入玩家昵称:");
        String name = scanner.next();
        System.out.print("请输入密码:");
        String password = scanner.next();

        // 用于临时存储用户信息的对象
        User tempUser = null;

        // 循环遍历,找账号信息
        for (User item : userList) {
            String tempName = item.getName();

            // 用户输入的账号,和集合中获取的用户的账号对比
            // 找到用户了。
            if (name.equals(tempName)) {
                tempUser = item;
                break;
            }
        }

        // 基于账号信息(tempUser)做3种别判断
        // 1、账号是否存在
        if(tempUser == null){
            // System.out.println("账号不存在!");
            throw new UnknowAccountException("该玩家不存在!");
        } else {
            // System.out.println("账号存在!");
            // 2、账号密码是否匹配
            // 玩家输入的密码和 tempUser 的密码匹配
            if(password.equals(tempUser.getPassword())){
                // System.out.println("密码正确!");
                // 3、账号是否被锁定
                if(tempUser.isLock()){
                    // System.out.println("账号被锁定");
                    throw new AccountLockedException("账号被锁定!");
                } else {
                    System.out.println("账号可用,登录成功!");
                    // 玩家存在的标记修改为 true
                    isLogin = true;

                    // 登录成功之后把当前玩家的名字赋值给全局的变量
                    loginName = name;
                    // 登录成功了,给当前用户创建一个用于存储游戏记录的list结果
                    recodeMap.put(name, new ArrayList<>());
                }
            } else {
                // System.out.println("密码不正确,认证失败!");
                throw new PasswordErrorException("密码错误!");
            }
        }
    }

    /**
     * 开始游戏
     */
    private void startGame() {
        // 可以给循环做一个标记:lab 就是这个循环的标记名字,名字可以任意。
        lab:
        while (true){
            /* 游戏本身需要的相关变量 */
            // 区间开始和结束
            int start = 0;
            int end = 100;
            /* 游戏本身需要的相关变量 */

            /* 游戏记录数据需要的相关变量 */
            // 每一轮游戏的过程
            // 随机炸弹
            int boom = new Random().nextInt(100);

            // 开始和结束时间
            Date startTime = new Date();
            Date endTime = null;

            // 每一轮游戏猜的次数
            int count = 0;
            // 每一轮游戏的积分变量
            int points = 0;
            /* 游戏记录数据需要的相关变量 */

            while (true){
                // 猜测次数的累加
                count++;
                System.out.print("请猜:");
                int num = scanner.nextInt();
                if(num > boom){
                    end = num;
                } else if(num < boom){
                    start = num;
                } else {
                    System.out.println("💣游戏结束💣");
                    /* 游戏结束才开始收集游戏信息:开始 */
                    // 结束时间
                    endTime = new Date();

                    // 计算时间间隔
                    long l = (endTime.getTime() - startTime.getTime()) / 1000;
                    // 计算次数
                    if (count >= 1 && count <= 3) {
                        points = 10;
                    } else if (count >= 4 && count <= 6) {
                        points = 5;
                    } else if (count >= 7 && count <= 10) {
                        points = 2;
                    } else {
                        points = 0;
                    }

                    // 创建记录对象,封装游戏过程中的记录信息
                    GameRecode gameRecode = new GameRecode();
                    gameRecode.setBoom(boom);
                    gameRecode.setCount(count);
                    gameRecode.setStartTime(startTime);
                    gameRecode.setEndTime(endTime);
                    gameRecode.setPoints(points);

                    // 这些记录是哪个玩家的。把记录和玩家挂钩。
                    List<GameRecode> gameRecodeList = recodeMap.get(loginName);
                    gameRecodeList.add(gameRecode);
                    /* 游戏结束才开始收集游戏信息:结束 */

                    // 跳出猜测的循环
                    break;
                }
                System.out.println("游戏继续,区间是:[" + start + ", " + end + "]");
            }

            System.out.println("****************************************");
            System.out.println("\t\t\t1)继续游戏");
            System.out.println("\t\t\t2)返回菜单");
            System.out.println("****************************************");
            System.out.print("请输入菜单编号:");
            String menuNum = scanner.next();
            switch (menuNum) {
                case "1":
                    // 结束的是 switch
                    break;
                case "2":
                    // 结束标记是 lab 的循环
                    break lab;
            }
        }
    }

    /**
     * 展示当前用户的游戏记录
     */
    private void gameRecode() {
        // 通过玩家和记录的Map集合,获取登录玩家的记录集合
        List<GameRecode> gameRecodeList = recodeMap.get(loginName);
        // 遍历当前玩家的记录集合
        System.out.println("序号\t炸弹\t次数\t开始时间\t\t\t\t结束时间\t\t\t\t积分");
        int i = 1;
        for (GameRecode gameRecode : gameRecodeList) {
            System.out.println(
                i++ + "\t" +
                gameRecode.getBoom()+ "\t" +
                gameRecode.getCount()+ "\t" +
                formatTime(gameRecode.getStartTime())+ "\t" +
                formatTime(gameRecode.getEndTime())+ "\t" +
                gameRecode.getPoints()+ "\t"
            );
        }
    }

    /**
     * 格式化时间的方法
     * @param date
     * @return
     */
    private String formatTime(Date date) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return sdf.format(date);
    }

    /**
     * 按照积分的降序展示每个用户的信息
     * 用户的昵称、游戏次数、总积分(倒序)
     */
    private void gameTop() {
        // 遍历用户结果集,汇总玩家的记录数据,循环走完之后,userList 中的每个玩家的游戏次数和总积分都会有值了
        for (User user : userList) {
            // 获取当前用户游戏记录的结果集
            List<GameRecode> gameRecodeList = recodeMap.get(user.getName());

            // 遍历游戏记录结果集,统计所有积分的和
            int sumPoints = 0;
            for (GameRecode gameRecode : gameRecodeList) {
                Integer points = gameRecode.getPoints();
                sumPoints += points;
            }

            // 将统计好的结果在赋值给用户对象
            user.setTotalCount(gameRecodeList.size());
            user.setTotalPoints(sumPoints);
        }

        System.out.println("-----------------------------------");
        System.out.println("\t\t\t1)升序展示");
        System.out.println("\t\t\t2)降序展示");
        System.out.println("-----------------------------------");
        System.out.print("请输入菜单编号:");
        String menuNum = scanner.next();

        System.out.println("昵称\t游戏次数\t总积分");
        // 默认升序排序,调用 sorted 之后返回一个可以继续操作的流
        Stream<User> newStream = userList.stream();

        switch (menuNum) {
            case "1":
                newStream = newStream.sorted(Comparator.comparing(User::getTotalPoints));
                break;
            case "2":
                newStream = newStream.sorted(Comparator.comparing(User::getTotalPoints).reversed());
                break;
        }

        // 输出放到最后
        newStream.forEach(user -> {
            System.out.println(
                    user.getName()+ "\t" +
                            user.getTotalCount() + "\t\t" +
                            user.getTotalPoints()
            );
        });


        // 根据积分倒叙排序
        // 1、List 转化为 Stream 流对象
        // 2、调用 Stream 的 sorted 方法进行排序
        // 3、sorted 需要传递一个排序的规则,这个规则是 Comparator 类型。这里思考:如何获取 Comparator 类型
        // 4、Comparator.comparing 方法可以返回一个 Comparator 类型,也就是排序的规则对象。其中还要知道排序的数据是哪个?是积分
        // 解析:Comparator.comparing 构建一个规则对象。方法传递要排序的关键字(数据属性)。reversed() 就是降序
        /*userList.stream()
                .sorted(Comparator.comparing(User::getTotalPoints).reversed())
                .forEach(user -> {
                    System.out.println(
                            user.getName()+ "\t" +
                                    user.getTotalCount() + "\t\t" +
                                    user.getTotalPoints()
                    );
                });*/
    }

    /**
     * 结束游戏
     */
    private void exit() {

        // 添加 IO 的操作,把玩家的信息写入到文件。
        try {
            /* 用户信息的存储 */
            // 也就是把 userList 的数据写入到文件
            File userInfoFile = new File(userInfoPath);
            if(!userInfoFile.exists()){
                userInfoFile.createNewFile();
            }

            // 创建对象的输出流
            ObjectOutputStream oosUserInfoSteam = new ObjectOutputStream(new FileOutputStream(userInfoFile));
            oosUserInfoSteam.writeObject(userList);
            oosUserInfoSteam.close();

            /* 用户游戏记录的存储 */
            // 也就是把 userList 的数据写入到文件
            File recodeFile = new File(recodeInfoPath);
            if(!recodeFile.exists()){
                recodeFile.createNewFile();
            }

            // 创建对象的输出流
            ObjectOutputStream oosRecodeInfoSteam = new ObjectOutputStream(new FileOutputStream(recodeInfoPath));
            oosRecodeInfoSteam.writeObject(recodeMap);
            oosRecodeInfoSteam.close();
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("文件操作异常!");
        }

        System.exit(0);
    }

    /**
     * 玩家管理:对玩家进行锁定和解锁
     */
    private void userControl(){
        System.out.print("请输入要管理的玩家昵称:");
        // 要匹配的玩家名字
        String userName = scanner.next();
        User lockUser = null;
        boolean isLock = false;

        for (User user : userList) {
            String name = user.getName();

            if(userName.equals(name)){
                lockUser = user;
                break;
            }
        }

        if(lockUser != null){
            // 找到玩家之后,再进行业务处理
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
            System.out.println("\t\t\t1)锁定玩家");
            System.out.println("\t\t\t2)解锁玩家");
            System.out.println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
            System.out.print("请输入菜单编号:");
            String menuNum = scanner.next();

            switch (menuNum){
                case "1":
                    isLock = true;
                    break;
                case "2":
                    isLock = false;
                    break;
            }

            lockUser.setLock(isLock);
            System.out.println("^^^^^操作成功^^^^^");
        } else {
            System.out.println("该玩家不存在!");
        }
    }
}
相关推荐
武子康24 分钟前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘1 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意1 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上2 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进2 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人3 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言
皓木.3 小时前
Mybatis-Plus
java·开发语言
不良人天码星3 小时前
lombok插件不生效
java·开发语言·intellij-idea
守护者1703 小时前
JAVA学习-练习试用Java实现“使用Arrays.toString方法将数组转换为字符串并打印出来”
java·学习
源码哥_博纳软云3 小时前
JAVA同城服务场馆门店预约系统支持H5小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台