写在开头:
有一说一,郭老师上课还真挺彳亍的,一方面讲得通俗易懂,另一方面还会拓展一些例子出来(比如:如何用编程方法求出圆周率的近似值)。而且备课也很充分,上课手码代码。试卷出的也很简单。别看这实验那么唬人,其实重要的组成部分他上课都讲过了(比如怎么让标签下落)。真神!!!
声明:本人java入门水平,佬们没必要浪费时间看这坨代码。
然后也希望有精力,有自信的后辈,可以把这段代码(虽然我写的一坨)精进一下。
如有任何违法的内容或地方,请立刻与我联系,我会在第一时间删除此文。
一、目的与要求
实验题目:题:用 Java编程开发”六级单词强化记忆”游戏
(0)在网上下载英语六级词汇表,中英文对应。保存在服务器端,服务器可以让1个客
广端连入。客户端初始分数为 10分。
(1)根据中文输入英文词汇。客户端界面打开,开始游戏。服务器随机选出一个英文单
词的中文单词描达,发给客广端显示。该中文单词描达,从界面顶端落下。果面底端出现四个备选单词,其中有一个正确答案。要求,其他三个备选单词 ,和正确答案的单词至少有30%的字母相同。客户端在文本框中准确输入自己认为正确的相应单词,提交。规则:在中文单词描述掉到底端之前,提交正确,该客户端增加 1分 ,进入下一局;提交错误的,该客户端扣2分,进入下一局;在掉到底端前不回答,扣1分 ,进入下一局。进入下一局之前,若回答正确,容户端显示"恭喜回答正确”;回答错误,客户端显示'回答错误 ,答案是xxx”;没回答,客户端显示”您没有回答,答案是xxx〞。用户分数扣到0分,则游戏输掉退出。注:此项考察多线程、界面开发、网络编程、字符串处理。
(2)错词保存。如果一个单词,在以上功能中,被用户答对,将其保存在“已掌握单词.txt'中;如果一个单词,在以上功能中 ,答错或没有答,则保存在“末掌握单词.txt'中(标注是答错还是没答),用户可以打开复习。对于客户端,“已掌握单词.txt”和“未掌握单词.txt”,保存在本地。注:此顶考察多线程、界面开发、IO处理。
实验要求:学习Java界面开发基础知识。利用所学到的Java编程知识和编程技巧,设计一个实际的应用软件,初步认识软件设计的基本方法,提高进行工程设计的基本技能及分析、解决实际问题的能力,为后续课程和以后的工程实践打下良好的基础。
二、实验环境
硬件: maxbook pro M1
软件:Eclipse
三、实验内容
整个文件包含两个类:
1.Server类
在这个类中包含的函数有:(1)构造函数,将文件中的英文-中文对读入到字符串数组中,然后监听本地的9999号窗口,并返回套接字。(2)compare函数,比较两个字符串是否相似度是否超过30%,相似度用的是重复字母个数除以短的字符串的串长,不区分大小写,并用两个hashmap记录字符出现次数。(3)countLetters函数,计数并返回一个hashmap,它包含着字符--字符出现次数key-value pair。(4)GetAWord函数,产生一个随机数,并返回随机数对应的字符串。(5)GetSimilar函数,从单词数组里面找出三个相似的英文。(6)main函数:实现了与客户端交流的功能,对于客户端发送过来的请求协议,若为“请求单词”,则发送四个单词和一个中文过去,若为“结束”,则跳出循环,断开与客户端的链接。
2.Client类
在这个类中包含的类有:(1)Socket_input类,用来连接本地的9999号端口。(2)myJPanel类,用来产生带背景图的JPanel。
在这个类中包含的函数有:(1)构造函数,初始化控件,并显示客户端。(2)getlife函数,获得生命值(分数)。(3)success函数,给出回答正确的提示框,并分数+1.(4)fail函数,给出回答错误的提示框,并分数-2.(5)overtime函数,给出回答超时的提示框,并分数-1.(6)run函数,修改中文所在的JLabel的Y坐标,呈现出下降的样子。(7)setjlb1函数,设置JLabel1的内容。(8)setjlb2函数,设置JLabel2的内容。(9)setjlb3函数,设置JLabel3的内容。(10)setjlb4函数,设置JLabel4的内容。(11)setjlb5函数,设置JLabel5的内容。(12)actionPerformed函数,提交按钮被点击时,记录下输入答案(13)main函数,当生命值大于0时,与服务器端进行对话,并开始每一局游戏,对于每一局游戏的结果进行提示,并记录正确答案到相应的文件中,当生命值小于等于0时,提示“您输掉了游戏”,关闭两个文件读写类,并断开链接,发送结束协议,释放所有Swing控件的资源。
四、实验结果
先运行服务器端,此时客户端未打开,输出“未连接”:
客户端运行界面,中文正在下落:
此时服务器端输出“已连接”:
回答正确的弹窗:
回答错误的弹窗:
超时的弹窗:
游戏结束的弹窗:
五、有待提升的地方
1.预处理:在服务器被打开的时候,就把每个单词的相似单词给筛选出来。类似于先排序,再折半查找,在查找次数较多的情况下,效益比纯乱序查找要高。
2.相似度概念的优化:或许可以做成“最长公共子串”这种形式,而不是统计相同字母出现的次数。
3.单词保存的优化:对于要保存到文件内的单词,可以加上执行保存操作时的时间;或者单词的错误/正确的次数等。
4.做一个主界面:主界面包括以下一些按钮:开始游戏;打开错题本;打开对题本;退出等等。
5.UI设计永远有待提升。
六、实验源代码
服务器端代码:
package class6;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
public class Server
{
public ServerSocket connect;
public Socket socket;
int lines = 2089;
int temp;//保存当前选出的字符的位置
String[] danci = new String[lines+1];
Server()//读入文件中的六级单词,然后监听9999号端口,
{
try
{
BufferedReader b1 = new BufferedReader(new FileReader("/Users/zpt/eclipse-workspace/六级单词.txt"));
for(int i=0;i<lines;i++)
{
danci[i]= b1.readLine();
}
b1.close();
connect = new ServerSocket(9999);
System.out.println("未连接");
socket = connect.accept();
System.out.println("已连接");
}catch(Exception e) {e.printStackTrace();}
}
public boolean compare(String str1,String str2)
{
str1 = str1.toLowerCase();
str2 = str2.toLowerCase();//不区分大小写
Map<Character, Integer> countMap1 = countLetters(str1);
Map<Character, Integer> countMap2 = countLetters(str2); // 统计每个字符串中字母的出现次数
int count = 0;//记录重复字符的个数
for (Map.Entry<Character, Integer> entry : countMap1.entrySet()) {
char ch = entry.getKey();
if (countMap2.containsKey(ch)) {
count += Math.min(entry.getValue(), countMap2.get(ch));
}
}
int ml = Math.min(str1.length(), str2.length());
double similarity = (double) count / ml;// 计算相似度百分比
return similarity>=0.3;
}
private Map<Character, Integer> countLetters(String str) {
Map<Character, Integer> countMap = new HashMap<>();
for (char ch : str.toCharArray()) {
if (Character.isLetter(ch)) {
countMap.put(ch, countMap.getOrDefault(ch, 0) + 1);
}
}
return countMap;
}
public String GetAWord()
{
temp = (int)(Math.random()*lines);
return danci[temp];
}
public String GetSimilar(String a) throws Exception
{
String[] goal = new String[4];
int num = 1;
//int cnt=0;
String temp1;
while(true)
{
int j = (int) ((Math.random()-0.5)*100);
//cnt++;
while(j==0||(temp+j)>(lines-1)||(temp+j)<0)
{
j = (int) ((Math.random()-0.5)*100);
//cnt++;
}
temp1 = danci[temp+j];
String[] all = temp1.split("\\s+");//按一个或多个空格来分割
goal[num] = all[0];
if(compare(a,goal[num]))
{
if(num==1)
{
num++;
}
else if(num==2)
{
if(!goal[1].equals(goal[num]))
{
num++;
}
}
else if(num==3)
{
if((!goal[1].equals(goal[num]))&&(!goal[2].equals(goal[num])))
{
break;
}
}
}
}
//System.out.println(cnt);
return goal[1]+"\t"+goal[2]+"\t"+goal[3];
}
public static void main(String[] args) throws Exception
{
Server s = new Server();
InputStream is;
BufferedReader br;
String str;
OutputStream os;
PrintStream ps;
while(true)
{
if(s.socket==null)
continue;
is = s.socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
str = br.readLine();
try{
if(str.equals("请求单词"))
{
os = s.socket.getOutputStream();
ps = new PrintStream(os);
String[] goal = s.GetAWord().split("\\s+");
ps.println(goal[1]);
ps.println(goal[0]);
String t1 = goal[0];
String[] Similar = s.GetSimilar(t1).split("\\s+");
ps.println(Similar[0]);
ps.println(Similar[1]);
ps.println(Similar[2]);
}
else if(str.equals("结束"))
{
System.out.println("连接已断开");
break;
}
}catch(NullPointerException e)
{
System.out.println("异常断开");
break;
}
}
System.out.println("服务器端退出");
s.socket.close();
s.connect.close();
}
}
客户端代码:
package class6;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import java.util.*;
class Socket_input
{
public Socket socket;
public Socket_input()
{
try
{
socket = new Socket("127.0.0.1",9999);//申请连接本机的9999号端口
}catch(Exception e) {System.out.println("客户端出错啦");}
}
}
class myJPanel extends JPanel//带背景图的JPanel
{
ImageIcon icon = new ImageIcon("/Users/zpt/eclipse-workspace/photo2.png");
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(icon.getImage(), 0, 0, this.getWidth(), this.getHeight(),null);
}
}
public class Client extends JFrame implements Runnable,ActionListener
{
private JPanel jpn = new myJPanel();
private JLabel jlb1 = new JLabel("这里是下坠的文本");
private JLabel jlb2 = new JLabel("这里是选项1");
private JLabel jlb3 = new JLabel("这里是选项2");
private JLabel jlb4 = new JLabel("这里是选项3");
private JLabel jlb5 = new JLabel("这里是选项4");
private JLabel jlb6 = new JLabel("这里是生命值:");
private JTextField jtf = new JTextField("在这里输入答案");
private JButton jbt = new JButton("提交答案");
public int Y=0;//单词的中文的纵坐标
private int life=10;//得分
public String ans;//玩家输入的答案
public String str1;//存中文
public String str2;//存英文1
public String str3;//存英文2
public String str4;//存英文3
public String str5;//存英文4
public String correct;//正确答案
public boolean running;
public Client()
{
this.add(jpn);
this.setSize(700,1000);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
jpn.setLayout(null);
jlb6.setText("得分:"+life);
jlb1.setForeground(Color.ORANGE);
jlb2.setForeground(Color.ORANGE);
jlb3.setForeground(Color.ORANGE);
jlb4.setForeground(Color.ORANGE);
jlb5.setForeground(Color.ORANGE);
jlb6.setForeground(Color.ORANGE);
jtf.setBackground(Color.CYAN);
jlb1.setBounds(400, 0, 400, 100); jlb2.setBounds(0, 300, 200, 50); jlb3.setBounds(0, 400, 200, 50); jlb4.setBounds(0, 500, 200, 50); jlb5.setBounds(0, 600, 200, 50); jlb6.setBounds(0, 0, 200, 100); jtf.setBounds(0, 200, 200, 50);
jbt.setBounds(200, 200, 100, 50);
jpn.add(jlb1); jpn.add(jlb2); jpn.add(jlb3); jpn.add(jlb4); jpn.add(jlb5); jpn.add(jlb6); jpn.add(jtf); jpn.add(jbt);
jbt.addActionListener(this);
}
public int getlife()
{
return life;
}
public void success()
{
life++;
jlb6.setText("得分:"+life);
JOptionPane.showMessageDialog(this, "恭喜回答正确");
}
public void fail()
{
life-=2;
jlb6.setText("得分:"+life);
JOptionPane.showMessageDialog(this, "回答错误,答案是"+correct+"\t"+str1);
}
public void overtime()
{
life--;
jlb6.setText("得分:"+life);
JOptionPane.showMessageDialog(this, "您没有回答,答案是"+correct+"\t"+str1);
}
public void run()
{
Y=1;
while(running)
{
jlb1.setLocation(500, Y);
try{
Thread.sleep(10);
}catch(Exception e) {e.printStackTrace();}
if(Y>this.getHeight())
break;
Y++;
}
}
public void setjlb1()
{
jlb1.setText(str1);
}
public void setjlb2(String a)
{
jlb2.setText(" "+a);
}
public void setjlb3(String a)
{
jlb3.setText(" "+a);
}
public void setjlb4(String a)
{
jlb4.setText(" "+a);
}
public void setjlb5(String a)
{
jlb5.setText(" "+a);
}
public void actionPerformed(ActionEvent e)
{
ans = jtf.getText();
}
public static void main(String[] args) throws Exception
{
Client c = new Client();
Socket_input s = new Socket_input();
OutputStream os;
PrintStream ps;
InputStream is;
BufferedReader br;
Thread th1;
BufferedWriter w1 = new BufferedWriter(new FileWriter("/Users/zpt/eclipse-workspace/已掌握单词.txt",true));
BufferedWriter w2 = new BufferedWriter(new FileWriter("/Users/zpt/eclipse-workspace/未掌握单词.txt",true));
while(c.getlife()>0)
{
os = s.socket.getOutputStream();
ps = new PrintStream(os);
ps.println("请求单词");
is = s.socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
c.str1 = br.readLine();
c.setjlb1();
c.str2 = br.readLine();
c.correct = c.str2;
c.str3 = br.readLine();
c.str4 = br.readLine();
c.str5 = br.readLine();
String[] temp = new String[4];
temp[0] = c.str2;
temp[1] = c.str3;
temp[2] = c.str4;
temp[3] = c.str5;
for(int i=1;i<=10;i++)//打乱选项
{
int a = (int)(Math.random()*4);
int b = (int)(Math.random()*4);
if(a!=b)
{
String temp1 = temp[a];
temp[a] = temp[b];
temp[b] = temp1;
}
}
c.setjlb2(temp[0]);
c.setjlb3(temp[1]);
c.setjlb4(temp[2]);
c.setjlb5(temp[3]);
//发送请求,得到一个正确答案和3个错误答案
//蹦出开始游戏按钮,点击后开始,并显示四个选项
c.jtf.setText(null);
c.jtf.requestFocusInWindow();
c.ans = null;
c.running = true;
th1 = new Thread(c);
th1.start();
//一局结束以后,弹出弹窗(正确,错误,超时)
while(true)
{
Thread.sleep(100);
if(c.ans != null||c.Y >= c.getHeight())
break;
}
c.running = false;//让run能够停下来
if(c.ans != null)
{
if(c.ans.equals(c.correct))
{
w1.write(c.correct+"\t"+c.str1);
w1.newLine();
c.success();
}
else
{
w2.write(c.correct+"\t"+c.str1+"\t答错");
w2.newLine();
c.fail();
}
}
else
{
w2.write(c.correct+"\t"+c.str1+"\t未答");
w2.newLine();
c.overtime();
}
}
JOptionPane.showMessageDialog(c, "您输掉了游戏");
w1.close();
w2.close();
os = s.socket.getOutputStream();
ps = new PrintStream(os);
ps.println("结束");
s.socket.close();
c.dispose();
}
}