带权重的点名系统
案例要求
文件中有学生的信息,每个学生的信息独占一行。包括学生的姓名,性别,权重
要求每次被抽中的学生,再次被抽中的概率在原先的基础上降低一半。
本题的核心就是带权重的随机
分析
权重,权重和,权重占比
如何将概率降低为原先的一半
假设总共10个人,一开始设定每个人的权重都是1,权重和是10,权重占比=权重/权重和,那么每个人的权重占比是0.1,说明每个人一开始被抽中的概率都是0.1,抽中后再次被抽中的概率在原先的基础上降低一半,需要将其权重降低为原来的一半。修改完权重要将其写回文件,下次再抽人时权重就是0.5了。
细节:
权重降低为原来的一半,权重和也会改变,但是改变很小(由10变为9.5),人数越多改变越小(假如一共100人时,权重和为100,抽到一人,这人权重降低为原来的一半,权重和由100变为99.5),可以视作不变,而这个人权重降低为原来的一半,权重和又视作不变,则权重占比就是原来的一半。被抽中的概率就是原先的一半
权重占比的区间范围
如何确定抽到哪个人
一开始每个人的权重占比是0.1,说明可以将长度为1的数轴分为10等份,每个人占其中一份
这就是权重占比的区间范围,例如:
于沧浪-男-1 (0.0~0.1]
双文楠-男-1 (0.1~0.2]
鞠弘杰-男-1 (0.2~0.3]
乐弘雪-男-1 (0.3~0.4]
贝泽瀚-男-1 (0.4~0.5]
孙秀逸-女-1 (0.5~0.6]
蔺文思-女-1 (0.6~0.7]
燕芷灵-女-1 (0.7~0.8]
酆若环-女-1 (0.8~0.9]
广飞瑶-女-1 (0.9~1.0]随机一个0~1之间的数,其在哪个范围就表示抽取到了哪个同学
代码实现
Student类用于封装文件中的数据
public class Student {
private String name;
private String sex;
private double weight;
public Student() {
}
public Student(String name, String sex, double weight) {
this.name = name;
this.sex = sex;
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public double getWeight() {
return weight;
}
public void setWeight(double weight) {
this.weight = weight;
}
//toString方法数据格式与文件中相同
//这样直接传递学生对象的toString方法就能写出相同格式的数据,
public String toString() {
return name + "-" + sex + "-" + weight;
}
}
测试类
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
public class Test2 {
public static void main(String[] args) throws IOException {
//定义集合存储学生对象
ArrayList<Student> list = new ArrayList<>();
//将文件中的数据读出,交给Student对象进行封装
BufferedReader br = new BufferedReader(new FileReader("day05\\names.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] arr = line.split("-");
Student stu = new Student(arr[0], arr[1], Double.parseDouble(arr[2]));
//将该Student添加到集合中
list.add(stu);
}
br.close();
//获取总权重
double weightSum = 0;
for (Student stu : list) {
weightSum = weightSum + stu.getWeight();
}
//计算每个人的权重占比,保存到一个数组中
double[] arr = new double[list.size()];
int index = 0;
for (Student stu : list) {
arr[index] = stu.getWeight() / weightSum;
index++;
}
System.out.println(Arrays.toString(arr));
//计算每个人权重占比的区间范围
//例如第一个人权重占比是0.1,那么就规定0~0.1是他的权重占比的区间范围,记为0.1
//0~0.1是第一个人的,那么第二个人的权重占比的区间范围就从0.1开始
//所以第二个人的权重占比的区间范围是 第一个人权重占比~第一个人权重占比+自己的权重占比
//依次往后
//随机一个0~1之间的数,若该数处于0~0.1,就表示随机到了第一个人
for (int i = 1; i < arr.length; i++) {
//利用BigDecimal精确运算
BigDecimal bd1 = BigDecimal.valueOf(arr[i - 1]);
BigDecimal bd2 = BigDecimal.valueOf(arr[i]);
arr[i] = bd1.add(bd2).doubleValue();
}
System.out.println(Arrays.toString(arr));
//随机0~1之间的数
double random = Math.random();
System.out.println(random);
//判断random在数组中的位置
//数组中元素是有序的利用二分查找法
//如果要查找的元素在数组中,返回该元素的索引
//如果要查找的元素不在数组中,返回值 = -索引 -1
//通过加法交换率得 索引= -(返回值 +1)
int i = Arrays.binarySearch(arr, random);
if (i >= 0) {
System.out.println(i);
} else {
i = -i - 1;
System.out.println(i);
}
//索引i表示抽到的人
Student stu = list.get(i);
System.out.println(stu);
//修改这个人的权重为原来的一半
stu.setWeight(stu.getWeight() / 2);
//将修改后的数据写回文件
BufferedWriter bw = new BufferedWriter(new FileWriter("day05\\names.txt"));
for (Student s : list) {
bw.write(s.toString());
bw.newLine();
}
bw.close();
}
}
一开始文件中的数据 第一次抽取 第二次抽取 第三次抽取
游戏注册功能以及存档功能
可以将用户信息和游戏进度写到一个文件中永久保存,这样结束程序重新启动时,数据不会丢失,借助IO将前面介绍的拼图游戏完善
程序启动类
import java.io.IOException;
public class App {
public static void main(String[] args) throws IOException {
//创建登录界面,在登录界面中成功登录后创建游戏界面
new LoginJFrame();
}
}
登录界面
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
public class LoginJFrame extends JFrame implements MouseListener {
//不止一个方法使用的变量定义在成员变量位置
JButton login;
JButton register;
JTextField username;
JTextField password;
JTextField code;
String codeStr;
JLabel showCode;
JDialog jDialog;
//记录所有用户
ArrayList<User> list = new ArrayList();
//空参构造初始化登录
public LoginJFrame() throws IOException {
//调用方法获取文件中记录的用户信息,存储在集合中
UserInfo();
//调用方法初始化登录界面
initFrame();
//调用方法加载登录界面图式
loadImages();
//添加事件监听,鼠标监听事件MouseListener
login.addMouseListener(this);
register.addMouseListener(this);
showCode.addMouseListener(this);
//设置界面可视,建议放在最后
this.setVisible(true);
}
//定义方法获取文件中记录的用户信息,存储在集合中
private void UserInfo() throws IOException {
BufferedReader br = new BufferedReader(new FileReader("PuzzleGame\\UserInfo.txt"));
String line;
while ((line = br.readLine()) != null) {
String[] arr = line.split("&");
String username = arr[0].split("=")[1];
String password = arr[1].split("=")[1];
User u = new User(username, password);
list.add(u);
}
br.close();
}
//定义方法加载登录界面图式
private void loadImages() {
//1. 添加用户名文字
JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\用户名.png"));
usernameText.setBounds(116, 135, 47, 17);
this.getContentPane().add(usernameText);
//2.添加用户名输入框
username = new JTextField();
username.setBounds(195, 134, 200, 30);
this.getContentPane().add(username);
//3.添加密码文字
JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\密码.png"));
passwordText.setBounds(130, 195, 32, 16);
this.getContentPane().add(passwordText);
//4.密码输入框
password = new JTextField();
password.setBounds(195, 195, 200, 30);
this.getContentPane().add(password);
//验证码提示
JLabel codeText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\验证码.png"));
codeText.setBounds(133, 256, 50, 30);
this.getContentPane().add(codeText);
//验证码的输入框
code = new JTextField();
code.setBounds(195, 256, 100, 30);
this.getContentPane().add(code);
//调用工具类生成验证码
codeStr = CodeUtil.getCode();
//显示生成的验证码
showCode = new JLabel();
//设置内容
showCode.setText(codeStr);
//位置和宽高
showCode.setBounds(300, 256, 50, 30);
//添加到界面
this.getContentPane().add(showCode);
//5.添加登录按钮
login = new JButton();
login.setBounds(123, 310, 128, 47);
//设置按钮背景图
login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按钮.png"));
//去除按钮的默认边框
login.setBorderPainted(false);
//去除按钮的默认背景
login.setContentAreaFilled(false);
this.getContentPane().add(login);
//6.添加注册按钮
register = new JButton();
register.setBounds(256, 310, 128, 47);
//设置按钮背景图
register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按钮.png"));
//去除按钮的默认边框
register.setBorderPainted(false);
//去除按钮的默认背景
register.setContentAreaFilled(false);
this.getContentPane().add(register);
//7.添加界面背景图片
JLabel background = new JLabel(new ImageIcon("puzzlegame\\image\\login\\background.png"));
background.setBounds(0, 0, 470, 390);
this.getContentPane().add(background);
}
//定义方法初始化登录界面
private void initFrame() {
this.setSize(488, 430);
this.setTitle("拼图游戏登录");
this.setAlwaysOnTop(true);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(3);
this.setLayout(null);
}
//定义方法判断用户名密码是否正确
private boolean checkUser(ArrayList<User> list, User u) {
//查询用户名是否在集合中存在
int index = checkUserName(list, u);
if (index == -1) {
//System.out.println("用户名和密码错误");
return false;
}
//到这步,证明用户名存在,判断对应的密码是否相同
if (list.get(index).getPassWord().equals(u.getPassWord())) {
//相同返回true
return true;
}
//不同返回false
return false;
}
//定义方法查询用户名是否在集合中存在,存在返回索引,不存在返回-1
private int checkUserName(ArrayList<User> list, User u) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getUserName().equals(u.getUserName())) {
return i;
}
}
return -1;
}
//定义方法展示弹窗
private void showDialog(String s) {
jDialog = new JDialog();
//设置弹框的宽和高:100,100
jDialog.setSize(100, 100);
//设置弹框居中
jDialog.setLocationRelativeTo(null);
//设置弹框置顶
jDialog.setAlwaysOnTop(true);
//设置关闭后进行其他操作
jDialog.setModal(true);
//创建一个JLabel去编写文本内容
JLabel textJlabel = new JLabel(s);
textJlabel.setBounds(20, 50, 120, 60);
//把文本JLabel添加到弹框当中
jDialog.getContentPane().add(textJlabel);
//把弹框展示出来
jDialog.setVisible(true);
}
//鼠标监听事件MouseListener
//鼠标单击调用该方法
@Override
public void mouseClicked(MouseEvent e) {
Object source = e.getSource();
if (source == login) {
//如果是登录按钮,获取输入框中的用户名,密码,验证码
String myUserName = username.getText();
String myPassWord = password.getText();
String myCode = code.getText();
//判断是否为空,如果为空,提示:用户名或密码为空
if (myUserName.equals("") || myPassWord.equals("")) {
//展示弹框:用户名或密码为空
//调用方法展示弹框,参数为需要展示的文字
showDialog("用户名或密码为空");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//判断验证码是否正确
if (!(myCode.equalsIgnoreCase(codeStr))) {
//展示弹框:验证码错误
showDialog("验证码错误");
System.out.println("验证码错误");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//判断用户名和密码是否为正确,如果正确隐藏登录界面,进入游戏界面。
//调用方法判断
User myUser = new User(myUserName, myPassWord);
boolean flag = checkUser(list, myUser);
if (!flag) {
//展示弹框:用户名或密码错误
showDialog("用户名或密码错误");
System.out.println("用户名或密码错误");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//到这步,证明用户名和密码正确
//隐藏登录界面,进入游戏界面
this.setVisible(false);
new GameJFrame();
} else if (source == showCode) {
//重新生成验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
} else if (source == register) {
//进行注册
//关闭登录界面
this.setVisible(false);
//创建注册界面
//把存储用户信息的集合传递过去,注册时用户名不能与集合中的重复
new RegisterJFrame(list);
}
}
//鼠标按下调用该方法
@Override
public void mousePressed(MouseEvent e) {
Object source = e.getSource();
if (source == login) {
//登录按钮
//按下不松的时候利用setIcon方法,修改登录按钮的背景色
login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按下.png"));
} else if (source == register) {
//注册按钮
//按下不松的时候利用setIcon方法,修改注册按钮的背景色
register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按下.png"));
}
}
//鼠标释放调用该方法
@Override
public void mouseReleased(MouseEvent e) {
Object source = e.getSource();
//登录按钮
//释放的时候利用setIcon方法,恢复登录按钮的背景色
if (source == login) {
login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按钮.png"));
} else if (source == register) {
//注册按钮
//释放的时候利用setIcon方法,恢复注册按钮的背景色
register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按钮.png"));
}
}
//鼠标划入调用该方法
@Override
public void mouseEntered(MouseEvent e) {
}
//鼠标划出调用该方法
@Override
public void mouseExited(MouseEvent e) {
}
}
注册界面
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
public class RegisterJFrame extends JFrame implements MouseListener {
//存储用户集合
ArrayList<User> list;
//提升三个输入框的变量的作用范围,让这三个变量可以在本类中所有方法里面可以使用。
JTextField username = new JTextField();
JTextField password = new JTextField();
JTextField rePassword = new JTextField();
//提升两个按钮变量的作用范围,让这两个变量可以在本类中所有方法里面可以使用。
JButton submit = new JButton();
JButton reset = new JButton();
//弹窗
JDialog jDialog;
public RegisterJFrame(ArrayList<User> list) {
//接收传递过来的用户集合
this.list = list;
//初始化界面
initFrame();
//调用方法加载登录界面图式
initView();
//添加事件监听,鼠标监听事件MouseListener
submit.addMouseListener(this);
reset.addMouseListener(this);
//设置界面可视,建议放在最后
this.setVisible(true);
}
//初始化界面
private void initFrame() {
//对自己的界面做一些设置。
//设置宽高
this.setSize(488, 430);
//设置标题
this.setTitle("拼图游戏 V1.0注册");
//取消内部默认布局
this.setLayout(null);
//设置关闭模式
this.setDefaultCloseOperation(3);
//设置居中
this.setLocationRelativeTo(null);
//设置置顶
this.setAlwaysOnTop(true);
}
//加载登录界面图式
private void initView() {
//添加注册用户名的文本
JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\注册用户名.png"));
usernameText.setBounds(85, 135, 80, 20);
//添加注册用户名的输入框
username.setBounds(195, 134, 200, 30);
//添加注册密码的文本
JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\注册密码.png"));
passwordText.setBounds(97, 193, 70, 20);
//添加密码输入框
password.setBounds(195, 195, 200, 30);
//添加再次输入密码的文本
JLabel rePasswordText = new JLabel(new ImageIcon("puzzlegame\\image\\register\\再次输入密码.png"));
rePasswordText.setBounds(64, 255, 95, 20);
//添加再次输入密码的输入框
rePassword.setBounds(195, 255, 200, 30);
//注册的按钮
submit.setIcon(new ImageIcon("puzzlegame\\image\\register\\注册按钮.png"));
submit.setBounds(123, 310, 128, 47);
submit.setBorderPainted(false);
submit.setContentAreaFilled(false);
//重置的按钮
reset.setIcon(new ImageIcon("puzzlegame\\image\\register\\重置按钮.png"));
reset.setBounds(256, 310, 128, 47);
reset.setBorderPainted(false);
reset.setContentAreaFilled(false);
//背景图片
JLabel background = new JLabel(new ImageIcon("puzzlegame\\image\\register\\background.png"));
background.setBounds(0, 0, 470, 390);
this.getContentPane().add(usernameText);
this.getContentPane().add(passwordText);
this.getContentPane().add(rePasswordText);
this.getContentPane().add(username);
this.getContentPane().add(password);
this.getContentPane().add(rePassword);
this.getContentPane().add(submit);
this.getContentPane().add(reset);
this.getContentPane().add(background);
}
//定义方法展示弹窗
private void showDialog(String s) {
jDialog = new JDialog();
//设置弹框的宽和高:100,100
jDialog.setSize(100, 100);
//设置弹框居中
jDialog.setLocationRelativeTo(null);
//设置弹框置顶
jDialog.setAlwaysOnTop(true);
//设置关闭后进行其他操作
jDialog.setModal(true);
//创建一个JLabel去编写文本内容
JLabel textJlabel = new JLabel(s);
textJlabel.setBounds(20, 50, 120, 60);
//把文本JLabel添加到弹框当中
jDialog.getContentPane().add(textJlabel);
//把弹框展示出来
jDialog.setVisible(true);
}
//鼠标单击事件
@Override
public void mouseClicked(MouseEvent e) {
Object source = e.getSource();
if (source == submit) {
//注册按钮
//得到三个输入框中的数据
String myUsername = username.getText();
String myPassword = password.getText();
String myRePassword = rePassword.getText();
//三个输入框中的数据不能为空
if (myUsername.length() == 0 || myPassword.length() == 0 || myRePassword.length() == 0) {
//弹窗提醒
showDialog("输入框不能为空");
//结束方法
return;
}
//密码和再次输入的密码要相同
if (!myPassword.equals(myRePassword)) {
//弹窗提醒
showDialog("两次输入的密码不同");
//结束方法
return;
}
//用户名要符合规定,4-16位大小写字母和数字
if (!myUsername.matches("[a-zA-Z0-9]{4,16}")) {
//弹窗提醒
showDialog("用户名要4-16位大小写字母和数字");
//结束方法
return;
}
//密码要符合规定,至少6位大小写字母和数字
if (!myPassword.matches("[a-zA-Z0-9]{6,}")) {
//弹窗提醒
showDialog("密码要至少6位大小写字母和数字");
//结束方法
return;
}
//用户名不能与集合中的重复
//调用方法判断
if (containsUsername(myUsername)) {
//弹窗提醒
showDialog("用户名已存在");
//结束方法
return;
}
//到这一步,说明用户名和密码符合条件
//将其添加到用户集合中,并写到文件中永久存储
User u = new User(myUsername, myPassword);
list.add(u);
try {
BufferedWriter bw = new BufferedWriter(new FileWriter("PuzzleGame\\UserInfo.txt"));
for (User user : list) {
bw.write(user.toString());
bw.newLine();
}
bw.close();
//开启登录界面
this.setVisible(false);
new LoginJFrame();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else if (source == reset) {
//重置按钮
//清空三个输入框中的内容
username.setText("");
password.setText("");
rePassword.setText("");
}
}
//判断自定义用户名是否与集合中的用户名重复
private boolean containsUsername(String myUsername) {
for (User u : list) {
if (u.getUserName().equals(myUsername)) {
return true;
}
}
return false;
}
//鼠标按下事件
@Override
public void mousePressed(MouseEvent e) {
Object source = e.getSource();
//切换按钮深色背景
if (source == submit) {
submit.setIcon(new ImageIcon("PuzzleGame\\image\\register\\注册按下.png"));
} else if (source == reset) {
reset.setIcon(new ImageIcon("PuzzleGame\\image\\register\\重置按下.png"));
}
}
//鼠标释放事件
@Override
public void mouseReleased(MouseEvent e) {
Object source = e.getSource();
//切换按钮正常背景
if (source == submit) {
submit.setIcon(new ImageIcon("PuzzleGame\\image\\register\\注册按钮.png"));
} else if (source == reset) {
reset.setIcon(new ImageIcon("PuzzleGame\\image\\register\\重置按钮.png"));
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
游戏界面
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.util.Random;
//游戏界面类继承界面类,实现键盘监听和动作监听
public class GameJFrame extends JFrame implements KeyListener, ActionListener {
//多个方法需要用到的变量,记录在成员变量的位置
//定义二维数组记录打乱后的0~15,每一个数字对应一张拼图
int[][] data = new int[4][4];
//定义胜利时的数据数组
int[][] winArr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 0}
};
//定义坐标记录0的位置
int x = 0;
int y = 0;
//定义计数器记录移动步数
int stepCount = 0;
//创建菜单选项里下面的条目
JMenuItem restartItem = new JMenuItem("重新开始");
JMenuItem reLoginItem = new JMenuItem("重新登录");
JMenuItem exitItem = new JMenuItem("退出游戏");
JMenuItem animalItem = new JMenuItem("动物");
JMenuItem girllItem = new JMenuItem("美女");
JMenuItem sportItem = new JMenuItem("运动");
JMenuItem AccountItem = new JMenuItem("公众号");
JMenu saveJmenu = new JMenu("存档");
JMenuItem saveItem0 = new JMenuItem("存档0(空)");
JMenuItem saveItem1 = new JMenuItem("存档1(空)");
JMenuItem saveItem2 = new JMenuItem("存档2(空)");
JMenu loadJmenu = new JMenu("读档");
JMenuItem loadItem0 = new JMenuItem("读档0(空)");
JMenuItem loadItem1 = new JMenuItem("读档1(空)");
JMenuItem loadItem2 = new JMenuItem("读档2(空)");
//定义图片路径,方便后面修改
String path = "PuzzleGame\\image\\animal\\animal1\\";
Random r = new Random();
//空参构造初始化游戏
public GameJFrame() {
//调用方法初始化界面
initFrame();
//给界面添加事件监听,键盘监听KeyListener
this.addKeyListener(this);
//调用方法添加菜单
initMenu();
//给菜单里的条目添加事件监听,动作监听ActionListener
restartItem.addActionListener(this);
reLoginItem.addActionListener(this);
exitItem.addActionListener(this);
AccountItem.addActionListener(this);
animalItem.addActionListener(this);
girllItem.addActionListener(this);
sportItem.addActionListener(this);
saveItem0.addActionListener(this);
saveItem1.addActionListener(this);
saveItem2.addActionListener(this);
loadItem0.addActionListener(this);
loadItem1.addActionListener(this);
loadItem2.addActionListener(this);
//调用方法初始化数据
initData();
//调用方法根据初始化后的数据加载图片
loadImages();
//设置界面可视,建议放在最后
this.setVisible(true);
}
//定义方法判断游戏是否胜利
private boolean victoty() {
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
if (data[i][j] != winArr[i][j]) {
//data中的数据有一个与胜利数组中的数据不同,就说明没有胜利
return false;
}
}
}
return true;
}
//定义方法根据初始化后的数据加载图片
private void loadImages() {
//清空所有图片
this.getContentPane().removeAll();
//如果胜利,加载胜利图标
if (victoty()) {
JLabel vicJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\win.png"));
vicJLabel.setBounds(203, 283, 197, 73);
this.getContentPane().add(vicJLabel);
}
//加载计数器
JLabel countJLabel = new JLabel("步数:" + stepCount);
countJLabel.setBounds(50, 30, 100, 20);
this.getContentPane().add(countJLabel);
//利用循环加载拼图图片
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
//获得data数组里的数据
int number = data[i][j];
//根据数据创建图片对象
ImageIcon icon = new ImageIcon(path + number + ".jpg");
//创建管理容器,将图片交给管理容器
JLabel jLabel = new JLabel(icon);
//设置管理容器位置,大小
jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
//设置边框
jLabel.setBorder(new BevelBorder(0));
//将管理容器添加到界面中
this.getContentPane().add(jLabel);
}
}
//添加背景图片
JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
bgJLabel.setBounds(40, 40, 508, 560);
this.getContentPane().add(bgJLabel);
//刷新一下
this.getContentPane().repaint();
}
//定义方法初始化数据
private void initData() {
int[] tempArr = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
//打乱数据,根据打乱后的数据就能实现打乱拼图
for (int i = 0; i < tempArr.length; i++) {
int index = r.nextInt(tempArr.length);
int temp = tempArr[i];
tempArr[i] = tempArr[index];
tempArr[index] = temp;
}
//将打乱后的数据记录在二维数组data中
for (int i = 0; i < tempArr.length; i++) {
//如果是0,记录0的位置
if (tempArr[i] == 0) {
x = i / 4;
y = i % 4;
}
data[i / 4][i % 4] = tempArr[i];
}
}
//定义方法添加菜单
private void initMenu() {
//创建菜单
JMenuBar jMenuBar = new JMenuBar();
//创建菜单的选项
JMenu functionJmenu = new JMenu("功能");
JMenu aboutJmenu = new JMenu("关于我们");
//嵌套二级菜单,JMenu里面是可以再次添加其他的JMenu
JMenu updateJmenu = new JMenu("更换图片");
updateJmenu.add(animalItem);
updateJmenu.add(girllItem);
updateJmenu.add(sportItem);
//存档
saveJmenu.add(saveItem0);
saveJmenu.add(saveItem1);
saveJmenu.add(saveItem2);
//读档
loadJmenu.add(loadItem0);
loadJmenu.add(loadItem1);
loadJmenu.add(loadItem2);
//将条目添加到选项中
functionJmenu.add(updateJmenu);
functionJmenu.add(restartItem);
functionJmenu.add(reLoginItem);
functionJmenu.add(exitItem);
functionJmenu.add(saveJmenu);
functionJmenu.add(loadJmenu);
aboutJmenu.add(AccountItem);
//将选项添加到菜单中
jMenuBar.add(functionJmenu);
jMenuBar.add(aboutJmenu);
//将菜单添加到界面中
this.setJMenuBar(jMenuBar);
//游戏开始时获取游戏数据信息修改菜单提示
getGameInfo();
}
//游戏开始时获取游戏数据信息修改菜单提示
private void getGameInfo() {
//遍历存档文件夹
File f = new File("PuzzleGame\\save");
File[] files = f.listFiles();
if (files == null) {
return;
}
for (File file : files) {
//读取每一个文件信息
GameInfo gi = null;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
gi = (GameInfo) ois.readObject();
ois.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
//获取要体现在菜单上的步数信息
int step = gi.getStepCount();
//获取要修改的条目索引
String name = file.getName();//save0/1/2.data
int index = name.charAt(4) - '0';
//进行修改
JMenuItem item1 = saveJmenu.getItem(index);
item1.setText("存档" + index + "(" + step + "步)");
JMenuItem item2 = loadJmenu.getItem(index);
item2.setText("读档" + index + "(" + step + "步)");
}
}
//定义方法初始化界面
private void initFrame() {
//设置大小
this.setSize(603, 680);
//设置标题
this.setTitle("拼图游戏");
//设置居中
this.setLocationRelativeTo(null);
//设置置顶
this.setAlwaysOnTop(true);
//设置关闭模式
this.setDefaultCloseOperation(3);
//设置解除默认居中放置,只有解除了,才能根据xy轴的方式添加主件
this.setLayout(null);
}
//动作监听ActionListener,鼠标单击或按空格调用该方法
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == restartItem) {
//重新开始
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == reLoginItem) {
//重新登录
//关闭本类(游戏类)
this.setVisible(false);
//创建登录界面
try {
new LoginJFrame();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else if (source == exitItem) {
//退出游戏
System.exit(0);
} else if (source == AccountItem) {
//显示公众号
//创建弹窗
JDialog accJDialog = new JDialog();
//加载弹窗中的图片
JLabel aboutJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\about.png"));
aboutJLabel.setBounds(0, 0, 258, 258);
//将图片添加到弹窗中
accJDialog.getContentPane().add(aboutJLabel);
//设置弹窗的大小
accJDialog.setSize(344, 344);
//设置弹窗置顶
accJDialog.setAlwaysOnTop(true);
//设置弹窗居中放置
accJDialog.setLocationRelativeTo(null);
//设置关闭后进行其他操作
accJDialog.setModal(true);
//设置弹窗可视
accJDialog.setVisible(true);
} else if (source == animalItem) {
//随机更换动物图片
int rAnimalNum = r.nextInt(8) + 1;
path = "PuzzleGame\\image\\animal\\animal" + rAnimalNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == girllItem) {
//随机更换美女图片
int rGirlNum = r.nextInt(13) + 1;
path = "PuzzleGame\\image\\girl\\girl" + rGirlNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == sportItem) {
//随机更换运动图片
int rSportlNum = r.nextInt(10) + 1;
path = "PuzzleGame\\image\\sport\\sport" + rSportlNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == saveItem0 || source == saveItem1 || source == saveItem2) {
//进行存档
//得到点击的存档序号
JMenuItem item = (JMenuItem) source;
String text = item.getText();//存档0/1/2(空)
int index = text.charAt(2) - '0';
//封装要保存的游戏数据
//包括:对应拼图的data数组,记录0位置的坐标x,y,图片路径path,步数stepCount
GameInfo gi = new GameInfo(data, x, y, stepCount, path);
//将游戏信息对象以序列化流写入对应文件中,防止篡改数据
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("PuzzleGame\\save\\save" + index + ".data"));
oos.writeObject(gi);
oos.close();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
//修改菜单上的提示信息
item.setText("存档" + index + "(" + stepCount + "步)");
//获取与存档对应的读档菜单
JMenuItem item1 = loadJmenu.getItem(index);
item1.setText("读档" + index + "(" + stepCount + "步)");
} else if (source == loadItem0 || source == loadItem1 || source == loadItem2) {
//进行读档
//得到点击的读档序号
JMenuItem item = (JMenuItem) source;
String text = item.getText();//读档0/1/2(空)
int index = text.charAt(2) - '0';
//利用反序列化流读取文件中的游戏数据
GameInfo gi = null;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("PuzzleGame\\save\\save" + index + ".data"));
gi = (GameInfo) ois.readObject();
ois.close();
} catch (IOException ex) {
throw new RuntimeException(ex);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
//将游戏数据更新为存档的游戏数据
data = gi.getData();
x = gi.getX();
y = gi.getY();
path = gi.getPath();
stepCount = gi.getStepCount();
//重新加载游戏
loadImages();
}
}
//键盘监听KeyListener
//该方法基本不使用
@Override
public void keyTyped(KeyEvent e) {
}
//长按键盘时调用该方法
@Override
public void keyPressed(KeyEvent e) {
//判断是否胜利
if (victoty()) {
//如果胜利,禁止进行显示全图操作,直接结束方法
return;
}
//长按w显示全图
int keyCode = e.getKeyCode();
if (keyCode == 87) {
//清空所有图片
this.getContentPane().removeAll();
//加载计数器
JLabel countJLabel = new JLabel("步数:" + stepCount);
countJLabel.setBounds(50, 30, 100, 20);
this.getContentPane().add(countJLabel);
//加载全图
JLabel picJLabel = new JLabel(new ImageIcon(path + "all.jpg"));
picJLabel.setBounds(83, 134, 420, 420);
this.getContentPane().add(picJLabel);
//添加背景图片
JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
bgJLabel.setBounds(40, 40, 508, 560);
this.getContentPane().add(bgJLabel);
//刷新一下
this.getContentPane().repaint();
}
}
//按下并松开键盘时调用该方法
@Override
public void keyReleased(KeyEvent e) {
//判断是否胜利
if (victoty()) {
//如果胜利,禁止进行移动操作,直接结束方法
return;
}
int keyCode = e.getKeyCode();
if (keyCode == 37) {
//判断是否到了最左边
if (y == 0) {
//如果到了最左边,不能进行向左,直接结束方法
return;
}
//没到最左边,进行下面的操作
System.out.println("向左");
//更改data数组里的数据
data[x][y] = data[x][y - 1];
data[x][y - 1] = 0;
//更改0的坐标
y--;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 38) {
//判断是否到了最上边
if (x == 0) {
//如果到了最上边,不能进行向上,直接结束方法
return;
}
//没到最上边,进行下面的操作
System.out.println("向上");
//更改data数组里的数据
data[x][y] = data[x - 1][y];
data[x - 1][y] = 0;
//更改0的坐标
x--;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 39) {
//判断是否到了最右边
if (y == 3) {
//如果到了最右边,不能进行向右,直接结束方法
return;
}
//没到最右边,进行下面的操作
System.out.println("向右");
//更改data数组里的数据
data[x][y] = data[x][y + 1];
data[x][y + 1] = 0;
//更改0的坐标
y++;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 40) {
//判断是否到了最下边
if (x == 3) {
//如果到了最下边,不能进行向下,直接结束方法
return;
}
//没到最下边,进行下面的操作
System.out.println("向下");
//更改data数组里的数据
data[x][y] = data[x + 1][y];
data[x + 1][y] = 0;
//更改0的坐标
x++;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 87) {
//松开w恢复原状
loadImages();
} else if (keyCode == 65) {
//一键通过
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = winArr[i][j];
}
}
loadImages();
}
}
}
封装用户信息
public class User {
private String userName;
private String passWord;
public User() {
}
public User(String userName, String passWord) {
this.userName = userName;
this.passWord = passWord;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
@Override
public String toString() {
return "uesrname="+userName+"&password="+passWord;
}
}
封装游戏信息
import java.io.Serial;
import java.io.Serializable;
public class GameInfo implements Serializable {
@Serial
private static final long serialVersionUID = 3545018877843048289L;
private int[][] data;
private int x;
private int y;
private int stepCount;
private String path;
public GameInfo() {
}
public GameInfo(int[][] data, int x, int y, int stepCount, String path) {
this.data = data;
this.x = x;
this.y = y;
this.stepCount = stepCount;
this.path = path;
}
public int[][] getData() {
return data;
}
public void setData(int[][] data) {
this.data = data;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getStepCount() {
return stepCount;
}
public void setStepCount(int stepCount) {
this.stepCount = stepCount;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String toString() {
return "GameInfo{data = " + data + ", x = " + x + ", y = " + y + ", stepCount = " + stepCount + ", path = " + path + "}";
}
}
生成验证码工具类
import java.util.Random;
public class CodeUtil {
private CodeUtil(){}
public static String getCode(){
//定义StringBuilder方便拼接字符串
StringBuilder sb = new StringBuilder();
//记录a~z,A~Z
char[] ch = new char[52];
for (int i = 0; i < ch.length; i++) {
if (i < 26) {
ch[i] = (char) ('a' + i);
} else {
ch[i] = (char) ('A' + i - 26);
}
}
Random r = new Random();
//随机抽取4个字母
for (int i = 0; i < 4; i++) {
int index = r.nextInt(ch.length);
char randomCh = ch[index];
//拼接
sb.append(randomCh);
}
//随机抽取1个字母
int randomNum = r.nextInt(10);
//拼接
sb.append(randomNum);
//数字可以位于随机位置,因此最后一位需要与随机位置交换
char[] chars = sb.toString().toCharArray();
int index = r.nextInt(chars.length);
char temp = chars[chars.length-1];
chars[chars.length-1] = chars[index];
chars[index] = temp;
return new String(chars);
}
}