给Java初学者的学习笔记

第23章 控制台局域网小游戏-猜数字

2019-04-24  本文已影响42人  yangsg

猜数字游戏是80后很多人在小时候经常玩的一个逻辑游戏。这个游戏一般需要两个参与者,每个人秘密声明一个各个位值上数字不相同的四位数。互相猜测对方的数字,如果猜测的数字中位置正确且数字正确,对方会给出一个A,如果猜测的数字中位置不正确但数字正确,对方会给出B。两个参与者互相猜测多轮直至一方完全猜测出另一方的数字。
理论上8次就可以猜出对方的数字。
比如两个玩家"赵四"和"刘能"

动作 赵四 刘能
秘密声明 1024 9102
猜测对方 1234(得到刘能答复0A2B) 1234(得到赵四答复2A1B)
猜测对方 5678(得到刘能答复0A0B) 5678(得到赵四答复0A0B)

在这个步骤:双方都能排除对方不包含5678。赵四可以确定刘能的数字一定含有9和0
根据分析再次继续猜测

动作 赵四 刘能
猜测对方 9012(得到刘能答复2A2B) 1239(得到赵四答复1A1B)

在这个步骤:赵四可以确定刘能的四个数字,刘能可以确定9不是且4是A
根据分析再次继续猜测

动作 赵四 刘能
猜测对方 9102(得到刘能答复4A0B) 1204(得到赵四答复2A2B)

此时赵四猜中刘能的数字,获胜。刘能也几乎快要猜到赵四的数字。

现在通过所学的Java知识完成这样的游戏设计
1. 客户端首先需要使用账号密码登录,服务器连接数据库验证登录
2. 客户端若没有账号需要访问服务器进行注册,服务器在数据库中保存账号信息
3. 客户端开始游戏,服务器生成数字
4. 客户端猜测数字,服务器给出nAnB的回应
5. 客户端投降,服务器给出生成的数字
6. 客户端猜中数字,服务器记录用时并保存最好记录
7. 客户端可以查看当前速度最快的前10名的排名

1. 数据库

players表

players表

2. 服务器(GameServer)

DBConfig.properties
数据库配置文件,请根据实际情况修改。

IPAdress=localhost
port=3306
database=mydb
username=root
password=root
timezone=GMT%2B8

DBUtil.java
数据库连接工具类

public class DBUtil {
    private static String url;
    private static String username;
    private static String password;
    
    private DBUtil() {}
    
    static {
        // 1.加载驱动
        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持久化类(含部分业务逻辑)

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;
    }   
    /**
     * 注册
     */
    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;
    }
    /**
     * 取得当前记录
     */
    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
核心业务处理类,每个访问服务器的对象都利用该类的对象创建线程对应处理

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; //记录线程属于哪个用户
        
        while(true) {
            //通过输入流获取客户端发来信息
            try {
                DataInputStream dis = new DataInputStream(s.getInputStream());//处理输入流
                DataOutputStream dos = new DataOutputStream(s.getOutputStream());//处理输出流
                
                //操作dis就可获得客户端的消息,这也是一个阻塞事件
                String str = dis.readUTF(); 
                String rec = null; //回应的消息
                String[] ss = str.split(",");
                //解析暗号
                int signal = Integer.parseInt(ss[0]);  //将字符串转换成数字
                
                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; //登录成功的时候设置线程拥有者
                    }else {
                        rec = "NG";
                    }
                    
                }else if(signal == 101) {
                    //注册
                    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) {
                    //开始游戏(生成数字)
                    number = StartServer.getRandomNumber(); //得到随机数字
                    System.out.println(number);
                    l1 = System.currentTimeMillis();
                    rec = "OK";
                }else if(signal == 104) {
                    //猜数字
                    int guess = Integer.parseInt(ss[1]); //用户猜测的数字
                    rec = StartServer.compareNumber(number, guess);
                    //结果是4A0B,结束计时
                    if(rec.equals("4A0B")) {
                        l2 = System.currentTimeMillis();
                        long t = l2 - l1; //耗时
                        long n = dao.getBestTime(ac);//数据库中的当前记录
                        //如果当前记录是0  ,t保存
                        //如果t小于当前的最好记录,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); //回应客户端
                dos.flush(); 
                
            } catch (IOException e) {
                break;
            }
        }
    }
}

StartServer.java
服务器启动类,开启通信监听和创建用户处理线程,提供部分工具性质方法。

public class StartServer {

    public static void main(String[] args) {
        //创建了ServerSocket服务器
        ServerSocket ss = null; 
        try {
            ss = new ServerSocket(40000);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
        while(true) {
            try {
                //监听来自客户端的请求,这是一个阻塞事件
                Socket s = ss.accept();
                UserThread ut = new UserThread(s);
                ut.start();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } 
        }
    }
    
    
    /**
     * 随机生成一个各个为上数字不同的四位数
     * @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的结果
     * @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
通信,数字输入和各项操作。相当于客户端界面

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) {
                /*
                 * 数据库表players表, 账号,密码,最好时间
                 * 
                 * 1. 用户输入账号和密码  登录       
                 * 2. 注册
                 * 
                 * 3. 开始游戏 - 猜数字
                 * 4. 查看排名
                 * 
                 * 5. 退出
                 */
                
                System.out.println("1 - 登录");
                System.out.println("2 - 注册");
                System.out.println("9 - 退出");
                System.out.println("请选择:");
                Scanner sc1 = new Scanner(System.in);
                int s1 = sc1.nextInt();
                if(s1 == 1) {
                    //登录
                    //1.提示用户输入账号
                    Scanner sc2 = new Scanner(System.in);
                    System.out.println("请输入账号:");
                    String account = sc2.nextLine();
                    //2.提示用户输入密码
                    Scanner sc3 = new Scanner(System.in);
                    System.out.println("请输入密码:");
                    String apass = sc3.nextLine();
                    //3.将账号和密码发送给服务器   (服务器验证)
                    dos.writeUTF(100+","+account+","+apass);
                    dos.flush();
                    //4.接收服务器的验证结果   NG-重新执行该界面
                    String rec = dis.readUTF();
                    if(rec.equals("OK")) {
                        //OK- 进入下一个界面
                        while(true) {
                            System.out.println("1 - 开始游戏");
                            System.out.println("2 - 查看排名");
                            System.out.println("9 - 退出");
                            System.out.println("请选择:");
                            Scanner sc4 = new Scanner(System.in);
                            int s2 = sc4.nextInt();
                            if(s2 == 1) {
                                //开始游戏
                                //1.发送指令,服务器生成随机数字
                                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("请输入猜测的四位数(投降请输入-1):");
                                        int guess = sc5.nextInt();
                                        if(guess == -1) {
                                            //(2)投降 -1
                                            dos.writeUTF(105+",");
                                            dos.flush();
                                            
                                            String rec2 = dis.readUTF();
                                            System.out.println("服务器生成的数字是:"+rec2);
                                            break;
                                        }else {
                                            //(1)输入数字 - 猜
                                            dos.writeUTF(104+","+guess);
                                            dos.flush();
                                            
                                            String rec2 = dis.readUTF();
                                            System.out.println("比较结果:"+rec2);
                                            if(rec2.equals("4A0B")) {
                                                System.out.println("恭喜您猜对了");
                                                break;
                                            }
                                        }
                                    }
                                    
                                }else {
                                    System.out.println("服务器出现问题");
                                }
                                
                            }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("账号密码错误");
                    }
                    
                }else if(s1 == 2) {
                    //注册
                    //1.提示用户输入账号
                    Scanner sc2 = new Scanner(System.in);
                    System.out.println("请输入账号:");
                    String account = sc2.nextLine();
                    //2.提示用户输入密码
                    Scanner sc3 = new Scanner(System.in);
                    System.out.println("请输入密码:");
                    String apass = sc3.nextLine();
                    //3.将账号和密码发送给服务器   (服务器保存)
                    dos.writeUTF(101+","+account+","+apass);
                    dos.flush();
                    
                    //4.接收服务器的验证结果   
                    String rec = dis.readUTF();
                    if(rec.equals("OK")) {
                        //注册成功
                        System.out.println("注册成功");
                    }else {
                        System.out.println("注册失败:账号被使用");
                    }
                }else if(s1 == 9) {
                    //退出
                    dos.writeUTF(102+",");
                    dos.flush();
                    break;
                }
            }       
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
上一篇 下一篇

猜你喜欢

热点阅读