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

猜数字游戏是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();
        }
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355