猜數(shù)字游戲是80后很多人在小時(shí)候經(jīng)常玩的一個(gè)邏輯游戲。這個(gè)游戲一般需要兩個(gè)參與者,每個(gè)人秘密聲明一個(gè)各個(gè)位值上數(shù)字不相同的四位數(shù)?;ハ嗖聹y(cè)對(duì)方的數(shù)字,如果猜測(cè)的數(shù)字中位置正確且數(shù)字正確,對(duì)方會(huì)給出一個(gè)A,如果猜測(cè)的數(shù)字中位置不正確但數(shù)字正確,對(duì)方會(huì)給出B。兩個(gè)參與者互相猜測(cè)多輪直至一方完全猜測(cè)出另一方的數(shù)字。
理論上8次就可以猜出對(duì)方的數(shù)字。
比如兩個(gè)玩家"趙四"和"劉能"
| 動(dòng)作 | 趙四 | 劉能 |
|---|---|---|
| 秘密聲明 | 1024 | 9102 |
| 猜測(cè)對(duì)方 | 1234(得到劉能答復(fù)0A2B) | 1234(得到趙四答復(fù)2A1B) |
| 猜測(cè)對(duì)方 | 5678(得到劉能答復(fù)0A0B) | 5678(得到趙四答復(fù)0A0B) |
在這個(gè)步驟:雙方都能排除對(duì)方不包含5678。趙四可以確定劉能的數(shù)字一定含有9和0
根據(jù)分析再次繼續(xù)猜測(cè)
| 動(dòng)作 | 趙四 | 劉能 |
|---|---|---|
| 猜測(cè)對(duì)方 | 9012(得到劉能答復(fù)2A2B) | 1239(得到趙四答復(fù)1A1B) |
在這個(gè)步驟:趙四可以確定劉能的四個(gè)數(shù)字,劉能可以確定9不是且4是A
根據(jù)分析再次繼續(xù)猜測(cè)
| 動(dòng)作 | 趙四 | 劉能 |
|---|---|---|
| 猜測(cè)對(duì)方 | 9102(得到劉能答復(fù)4A0B) | 1204(得到趙四答復(fù)2A2B) |
此時(shí)趙四猜中劉能的數(shù)字,獲勝。劉能也幾乎快要猜到趙四的數(shù)字。
現(xiàn)在通過所學(xué)的Java知識(shí)完成這樣的游戲設(shè)計(jì)
1. 客戶端首先需要使用賬號(hào)密碼登錄,服務(wù)器連接數(shù)據(jù)庫驗(yàn)證登錄
2. 客戶端若沒有賬號(hào)需要訪問服務(wù)器進(jìn)行注冊(cè),服務(wù)器在數(shù)據(jù)庫中保存賬號(hào)信息
3. 客戶端開始游戲,服務(wù)器生成數(shù)字
4. 客戶端猜測(cè)數(shù)字,服務(wù)器給出nAnB的回應(yīng)
5. 客戶端投降,服務(wù)器給出生成的數(shù)字
6. 客戶端猜中數(shù)字,服務(wù)器記錄用時(shí)并保存最好記錄
7. 客戶端可以查看當(dāng)前速度最快的前10名的排名
1. 數(shù)據(jù)庫
players表

2. 服務(wù)器(GameServer)
DBConfig.properties
數(shù)據(jù)庫配置文件,請(qǐng)根據(jù)實(shí)際情況修改。
IPAdress=localhost
port=3306
database=mydb
username=root
password=root
timezone=GMT%2B8
DBUtil.java
數(shù)據(jù)庫連接工具類
public class DBUtil {
private static String url;
private static String username;
private static String password;
private DBUtil() {}
static {
// 1.加載驅(qū)動(dòng)
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//讀取配置文件,并完成連接字符串
Properties p = new Properties();
try {
p.load(DBUtil.class.getResourceAsStream("DBConfig.properties"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String ip = p.getProperty("IPAdress");
String port = p.getProperty("port");
String db = p.getProperty("database");
String tz = p.getProperty("timezone");
url = "jdbc:mysql://"+ip+":"+port+"/"+db+"?useUnicode=true&characterEncoding=utf-8&serverTimezone="+tz;
username = p.getProperty("username");
password = p.getProperty("password");
}
public static Connection getConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, username, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
public static void close(Connection conn, Statement pst, ResultSet rs) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(pst != null) {
try {
pst.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void close(Connection conn, Statement pst) {
close(conn, pst, null);
}
public static void close(Connection conn) {
close(conn, null, null);
}
}
Player.java
玩家pojo類,映射Players表
public class Player {
private String account;
private String apass;
private long besttime;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getApass() {
return apass;
}
public void setApass(String apass) {
this.apass = apass;
}
public long getBesttime() {
return besttime;
}
public void setBesttime(long besttime) {
this.besttime = besttime;
}
}
PlayerDAO.java
Player持久化類(含部分業(yè)務(wù)邏輯)
public class PlayerDAO {
/**
* 登錄
* @param p
* @return
*/
public boolean login(Player p) {
Connection conn = DBUtil.getConnection();
String sql = "select * from players where account = ? and apass = ?";
PreparedStatement pst = null;
ResultSet rs = null;
boolean r = false;
try {
pst = conn.prepareStatement(sql);
pst.setString(1, p.getAccount());
pst.setString(2, p.getApass());
rs = pst.executeQuery();
if(rs.next()) {
r = true;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DBUtil.close(conn, pst, rs);
}
return r;
}
/**
* 注冊(cè)
*/
public boolean regist(Player p) {
Connection conn = DBUtil.getConnection();
String sql = "insert into players values(?,?,?)";
PreparedStatement pst = null;
try {
pst = conn.prepareStatement(sql);
pst.setString(1, p.getAccount());
pst.setString(2, p.getApass());
pst.setLong(3, p.getBesttime());
pst.executeUpdate();
} catch (SQLException e) {
return false;
} finally {
DBUtil.close(conn, pst);
}
return true;
}
/**
* 取得當(dāng)前記錄
*/
public long getBestTime(String account) {
Connection conn = DBUtil.getConnection();
String sql = "select besttime from players where account = ?";
PreparedStatement pst = null;
ResultSet rs = null;
long r = 0;
try {
pst = conn.prepareStatement(sql);
pst.setString(1, account);
rs = pst.executeQuery();
while(rs.next()) {
r = rs.getLong("besttime");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DBUtil.close(conn, pst, rs);
}
return r;
}
/**
* 更新記錄
*/
public boolean updateTime(Player p) {
Connection conn = DBUtil.getConnection();
String sql = "update players set besttime = ? where account = ?";
PreparedStatement pst = null;
try {
pst = conn.prepareStatement(sql);
pst.setLong(1, p.getBesttime());
pst.setString(2, p.getAccount());
pst.executeUpdate();
} catch (SQLException e) {
return false;
} finally {
DBUtil.close(conn, pst);
}
return true;
}
/**
* 查詢記錄
*/
public String getList() {
Connection conn = DBUtil.getConnection();
String sql = "select account, besttime from players "
+ "where besttime <> 0 order by besttime asc limit 0,10";
PreparedStatement pst = null;
ResultSet rs = null;
StringBuffer sb = new StringBuffer();
try {
pst = conn.prepareStatement(sql);
rs = pst.executeQuery();
while(rs.next()) {
sb.append(rs.getString("account"));
sb.append(",");
sb.append(rs.getLong("besttime"));
sb.append(";");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
DBUtil.close(conn, pst, rs);
}
return sb.toString();
}
}
UserThread.java
核心業(yè)務(wù)處理類,每個(gè)訪問服務(wù)器的對(duì)象都利用該類的對(duì)象創(chuàng)建線程對(duì)應(yīng)處理
public class UserThread extends Thread{
private Socket s;
public UserThread(Socket s) {
this.s = s;
}
@Override
public void run() {
PlayerDAO dao = new PlayerDAO();
int number = 0;
long l1 = 0;
long l2 = 0;
String ac = null; //記錄線程屬于哪個(gè)用戶
while(true) {
//通過輸入流獲取客戶端發(fā)來信息
try {
DataInputStream dis = new DataInputStream(s.getInputStream());//處理輸入流
DataOutputStream dos = new DataOutputStream(s.getOutputStream());//處理輸出流
//操作dis就可獲得客戶端的消息,這也是一個(gè)阻塞事件
String str = dis.readUTF();
String rec = null; //回應(yīng)的消息
String[] ss = str.split(",");
//解析暗號(hào)
int signal = Integer.parseInt(ss[0]); //將字符串轉(zhuǎn)換成數(shù)字
if(signal == 100) {
//登錄
String account = ss[1];
String apass = ss[2];
Player p = new Player();
p.setAccount(account);
p.setApass(apass);
boolean b = dao.login(p);
if(b) {
rec = "OK";
ac = account; //登錄成功的時(shí)候設(shè)置線程擁有者
}else {
rec = "NG";
}
}else if(signal == 101) {
//注冊(cè)
String account = ss[1];
String apass = ss[2];
Player p = new Player();
p.setAccount(account);
p.setApass(apass);
p.setBesttime(0);
boolean b = dao.regist(p);
if(b) {
rec = "OK";
}else {
rec = "NG";
}
}else if(signal == 102) {
//登錄頁面退出
break;
}else if(signal == 103) {
//開始游戲(生成數(shù)字)
number = StartServer.getRandomNumber(); //得到隨機(jī)數(shù)字
System.out.println(number);
l1 = System.currentTimeMillis();
rec = "OK";
}else if(signal == 104) {
//猜數(shù)字
int guess = Integer.parseInt(ss[1]); //用戶猜測(cè)的數(shù)字
rec = StartServer.compareNumber(number, guess);
//結(jié)果是4A0B,結(jié)束計(jì)時(shí)
if(rec.equals("4A0B")) {
l2 = System.currentTimeMillis();
long t = l2 - l1; //耗時(shí)
long n = dao.getBestTime(ac);//數(shù)據(jù)庫中的當(dāng)前記錄
//如果當(dāng)前記錄是0 ,t保存
//如果t小于當(dāng)前的最好記錄,t保存
if(n == 0 || t < n) {
Player temp = new Player();
temp.setAccount(ac);
temp.setBesttime(t);
dao.updateTime(temp);
}
l1 = 0;
l2 = 0;
number = 0;
}
}else if(signal == 105) {
//投降
rec = number+"";
l1 = 0;
l2 = 0;
number = 0;
}else if(signal == 106) {
//查看排名
rec = dao.getList();
}else {
}
dos.writeUTF(rec); //回應(yīng)客戶端
dos.flush();
} catch (IOException e) {
break;
}
}
}
}
StartServer.java
服務(wù)器啟動(dòng)類,開啟通信監(jiān)聽和創(chuàng)建用戶處理線程,提供部分工具性質(zhì)方法。
public class StartServer {
public static void main(String[] args) {
//創(chuàng)建了ServerSocket服務(wù)器
ServerSocket ss = null;
try {
ss = new ServerSocket(40000);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
while(true) {
try {
//監(jiān)聽來自客戶端的請(qǐng)求,這是一個(gè)阻塞事件
Socket s = ss.accept();
UserThread ut = new UserThread(s);
ut.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* 隨機(jī)生成一個(gè)各個(gè)為上數(shù)字不同的四位數(shù)
* @return
*/
public static int getRandomNumber() {
Random r = new Random();
int q = r.nextInt(9)+1; //[1,9]
int b,s,g;
while(true) {
int temp = r.nextInt(10); //[0,9]
if(temp != q) {
b = temp;
break;
}
}
while(true) {
int temp = r.nextInt(10); //[0,9]
if(temp != q && temp != b) {
s = temp;
break;
}
}
while(true) {
int temp = r.nextInt(10); //[0,9]
if(temp != q && temp != b && temp != s) {
g = temp;
break;
}
}
return q*1000+b*100+s*10+g;
}
/**
* 比較n1和n2的結(jié)果
* @param n1
* @param n2
* @return
*/
public static String compareNumber(int n1, int n2) {
int[] a = new int[4];
int[] b = new int[4];
a[0] = n1/1000;
a[1] = n1/100%10;
a[2] = n1/10%10;
a[3] = n1%10;
b[0] = n2/1000;
b[1] = n2/100%10;
b[2] = n2/10%10;
b[3] = n2%10;
int x = 0; //A
int y = 0; //B
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
if(a[i] == b[j] && i == j) {
x++;
}else if(a[i] == b[j] && i != j) {
y++;
}
}
}
return x+"A"+y+"B";
}
}
3. 客戶端(GameClient)
StartClient.java
通信,數(shù)字輸入和各項(xiàng)操作。相當(dāng)于客戶端界面
public class StartClient {
public static void main(String[] args) {
//建議IP和端口通過配置文件獲取
try {
Socket s = new Socket("localhost", 40000);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());//處理輸出流
DataInputStream dis = new DataInputStream(s.getInputStream());//處理輸入流
while(true) {
/*
* 數(shù)據(jù)庫表players表, 賬號(hào),密碼,最好時(shí)間
*
* 1. 用戶輸入賬號(hào)和密碼 登錄
* 2. 注冊(cè)
*
* 3. 開始游戲 - 猜數(shù)字
* 4. 查看排名
*
* 5. 退出
*/
System.out.println("1 - 登錄");
System.out.println("2 - 注冊(cè)");
System.out.println("9 - 退出");
System.out.println("請(qǐng)選擇:");
Scanner sc1 = new Scanner(System.in);
int s1 = sc1.nextInt();
if(s1 == 1) {
//登錄
//1.提示用戶輸入賬號(hào)
Scanner sc2 = new Scanner(System.in);
System.out.println("請(qǐng)輸入賬號(hào):");
String account = sc2.nextLine();
//2.提示用戶輸入密碼
Scanner sc3 = new Scanner(System.in);
System.out.println("請(qǐng)輸入密碼:");
String apass = sc3.nextLine();
//3.將賬號(hào)和密碼發(fā)送給服務(wù)器 (服務(wù)器驗(yàn)證)
dos.writeUTF(100+","+account+","+apass);
dos.flush();
//4.接收服務(wù)器的驗(yàn)證結(jié)果 NG-重新執(zhí)行該界面
String rec = dis.readUTF();
if(rec.equals("OK")) {
//OK- 進(jìn)入下一個(gè)界面
while(true) {
System.out.println("1 - 開始游戲");
System.out.println("2 - 查看排名");
System.out.println("9 - 退出");
System.out.println("請(qǐng)選擇:");
Scanner sc4 = new Scanner(System.in);
int s2 = sc4.nextInt();
if(s2 == 1) {
//開始游戲
//1.發(fā)送指令,服務(wù)器生成隨機(jī)數(shù)字
dos.writeUTF(103+",");
dos.flush();
String rec1 = dis.readUTF();
if(rec1.equals("OK")) {
//2.開始猜
while(true) {
Scanner sc5 = new Scanner(System.in);
System.out.println("請(qǐng)輸入猜測(cè)的四位數(shù)(投降請(qǐng)輸入-1):");
int guess = sc5.nextInt();
if(guess == -1) {
//(2)投降 -1
dos.writeUTF(105+",");
dos.flush();
String rec2 = dis.readUTF();
System.out.println("服務(wù)器生成的數(shù)字是:"+rec2);
break;
}else {
//(1)輸入數(shù)字 - 猜
dos.writeUTF(104+","+guess);
dos.flush();
String rec2 = dis.readUTF();
System.out.println("比較結(jié)果:"+rec2);
if(rec2.equals("4A0B")) {
System.out.println("恭喜您猜對(duì)了");
break;
}
}
}
}else {
System.out.println("服務(wù)器出現(xiàn)問題");
}
}else if(s2 == 2) {
//查看排名
dos.writeUTF(106+",");
dos.flush();
//接收消息
String rec2 = dis.readUTF();
String[] ps = rec2.split(";");
for(String p : ps) {
String[] ts = p.split(",");
for(String t : ts) {
System.out.print(t);
System.out.print("\t");
}
System.out.println();
}
}else if(s2 == 9) {
//退出
dos.writeUTF(102+",");
dos.flush();
System.exit(0);
}
}
}else {
System.out.println("賬號(hào)密碼錯(cuò)誤");
}
}else if(s1 == 2) {
//注冊(cè)
//1.提示用戶輸入賬號(hào)
Scanner sc2 = new Scanner(System.in);
System.out.println("請(qǐng)輸入賬號(hào):");
String account = sc2.nextLine();
//2.提示用戶輸入密碼
Scanner sc3 = new Scanner(System.in);
System.out.println("請(qǐng)輸入密碼:");
String apass = sc3.nextLine();
//3.將賬號(hào)和密碼發(fā)送給服務(wù)器 (服務(wù)器保存)
dos.writeUTF(101+","+account+","+apass);
dos.flush();
//4.接收服務(wù)器的驗(yàn)證結(jié)果
String rec = dis.readUTF();
if(rec.equals("OK")) {
//注冊(cè)成功
System.out.println("注冊(cè)成功");
}else {
System.out.println("注冊(cè)失?。嘿~號(hào)被使用");
}
}else if(s1 == 9) {
//退出
dos.writeUTF(102+",");
dos.flush();
break;
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}