Java 数字炸弹小游戏(控制台版)+ IO 数据存储
- 数字炸弹小游戏概述
- 功能实现
- 实体类
- User.java 玩家信息实体类
- GameRecode.java 游戏记录实体类
- 自定义异常
- AccountLockedException.java 账号锁定异常
- PasswordErrorException.java 密码错误异常
- UnknowAccountException 账号不存在异常
- 游戏主类
- Game.java 游戏主类
数字炸弹小游戏概述
数字炸弹控制台版小游戏是Java 集合、流程控制、IO、异常、常用类等技术的综合练习。核心需求如下:
实现数字炸弹游戏,要求如下:
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 玩家信息实体类
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 游戏记录实体类
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 账号锁定异常
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 密码错误异常
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 账号不存在异常
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 游戏主类
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("该玩家不存在!");
}
}
}