带权重的点名系统
案例要求
文件中有学生的信息,每个学生的信息独占一行。包括学生的姓名,性别,权重
要求每次被抽中的学生,再次被抽中的概率在原先的基础上降低一半。
本题的核心就是带权重的随机
分析
权重,权重和,权重占比
如何将概率降低为原先的一半
假设总共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();//添加事件监听,鼠标监听事件MouseListenerlogin.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())) {//相同返回truereturn true;}//不同返回falsereturn false;}//定义方法查询用户名是否在集合中存在,存在返回索引,不存在返回-1private 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,100jDialog.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//鼠标单击调用该方法@Overridepublic 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);}}//鼠标按下调用该方法@Overridepublic 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"));}}//鼠标释放调用该方法@Overridepublic 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"));}}//鼠标划入调用该方法@Overridepublic void mouseEntered(MouseEvent e) {}//鼠标划出调用该方法@Overridepublic 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();//添加事件监听,鼠标监听事件MouseListenersubmit.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,100jDialog.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);}//鼠标单击事件@Overridepublic 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;}//鼠标按下事件@Overridepublic 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"));}}//鼠标释放事件@Overridepublic 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"));}}@Overridepublic void mouseEntered(MouseEvent e) {}@Overridepublic 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();//给界面添加事件监听,键盘监听KeyListenerthis.addKeyListener(this);//调用方法添加菜单initMenu();//给菜单里的条目添加事件监听,动作监听ActionListenerrestartItem.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里面是可以再次添加其他的JMenuJMenu 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.dataint 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,鼠标单击或按空格调用该方法@Overridepublic void actionPerformed(ActionEvent e) {Object source = e.getSource();if (source == restartItem) {//重新开始//重新初始化数据initData();//计数器归0stepCount = 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();//计数器归0stepCount = 0;//重新根据初始化后的数据加载图片loadImages();} else if (source == girllItem) {//随机更换美女图片int rGirlNum = r.nextInt(13) + 1;path = "PuzzleGame\\image\\girl\\girl" + rGirlNum + "\\";//重新初始化数据initData();//计数器归0stepCount = 0;//重新根据初始化后的数据加载图片loadImages();} else if (source == sportItem) {//随机更换运动图片int rSportlNum = r.nextInt(10) + 1;path = "PuzzleGame\\image\\sport\\sport" + rSportlNum + "\\";//重新初始化数据initData();//计数器归0stepCount = 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,步数stepCountGameInfo 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//该方法基本不使用@Overridepublic void keyTyped(KeyEvent e) {}//长按键盘时调用该方法@Overridepublic 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();}}//按下并松开键盘时调用该方法@Overridepublic 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--;//步数加1stepCount++;//按照此数据重新加载图片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--;//步数加1stepCount++;//按照此数据重新加载图片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++;//步数加1stepCount++;//按照此数据重新加载图片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++;//步数加1stepCount++;//按照此数据重新加载图片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;}@Overridepublic String toString() {return "uesrname="+userName+"&password="+passWord;}
}
封装游戏信息
import java.io.Serial;
import java.io.Serializable;public class GameInfo implements Serializable {@Serialprivate 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~Zchar[] 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);}}