手把手教你用java OOP实现猜拳游戏!好玩有用!

本文涉及的产品
实时计算 Flink 版,5000CU*H 3个月
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
大数据开发治理平台 DataWorks,不限时长
简介: 手把手教你用java OOP实现猜拳游戏!好玩有用!

猜拳游戏

结果呈现image.png

image.png

游戏规则类

public class Rule {
   
   
    /**输入工具:为什么要把Scanner作为属性存在?
     * 因为 输入姓名 和 输入有效的数值 都要运用到输入工具 写在属性就不用在各个方法中重复写 节省内存
     */
    private final Scanner INPUT = new Scanner(System.in);
    //随机工具
    private final Random RAND = new Random();
    //正则:两个以上的汉字
    private final String NAME_REGEX = "^[\u4e00-\u9fa5]{2,}$";
    //正则:数值
    private final String DIGIT_REGEX = "^[0-9]{1,}$";
    //拳谱
    private final String FIST = "布、剪、锤";
    //拳值数组
    private final String[] FISTS;
    //无参构造 给拳值数组赋值
    public Rule() {
   
   
        FISTS = FIST.split("、");
    }

    /**
     * 获取控制台输入一个有效昵称
     * @param title 输出提示主体
     * @return String
     */
    public String getInputName(String title){
   
   
        String name = null;
        System.out.printf("请输入"+title+"昵称:");
        do{
   
   
            name = INPUT.nextLine();
            if(name.matches(NAME_REGEX))
                break;
            System.out.println("昵称必须为两个汉字以上的形式!请重新输入:");
        }while(true);
        return name;
    }

    /**
     * 从控制台获取一个输入的整数
     * @param min 取值下限
     * @param max 取值上限
     * @return
     */
    public int getInputInt(String title,int min,int max){
   
   
        int digit = 0;
        do{
   
   
            System.out.print("请输入"+title+":");
            String num = INPUT.next();
            if(num.matches(DIGIT_REGEX)){
   
   
                digit = Integer.parseInt(num);
                if(digit >= min && digit <= max){
   
   
                    break;
                }
                System.out.println("数值必须位于"+min+"~"+max+"之间,请重新输入:");
            }
        }while(true);
        return digit;
    }

    /**
     * 获取控制台输入的有效拳值对应拳值数组的下标(索引)
     * @param nickname 出拳玩家的昵称
     * @return int 拳值对应的下标
     */

    public int getInputFistIx(String nickname){
   
   
        int fist = -1;
        System.out.print(nickname+"出拳:");
        do{
   
   
            String strFist = INPUT.nextLine();
            for (int i = 0; i < FISTS.length; i++) {
   
   
                if(strFist.equals(FISTS[i])){
   
   
                    fist = i;
                    break;
                }
            }
            if(fist >= 0)
                break;
            System.out.println("拳值必须为布、剪、锤,请"+nickname+"重新出拳");
        }while(true);
        return fist;
    }

    /**
     * 单局根据拳值下标之差判断输赢
     * @param humanFist 人类玩家的拳值
     * @param machineFist 机器玩家的拳值
     * @return int(0:平局,1/2:人类玩家胜利,-1/-2:机器玩家胜利)
     */

    public int compare(int humanFist,int machineFist){
   
   
        if(humanFist*machineFist == 0&&humanFist+machineFist == 2){
   
   
            if(humanFist == 0){
   
   
                humanFist = 3;
            }
            else{
   
   
                machineFist = 3;
            }
        }
        return humanFist - machineFist;
    }

    /**
     * 获取随机生成的拳值(下标) 并且输出机器的拳
     * @param nickname 机器玩家的昵称 作为输出提示
     * @return int 0~2
     */
    public int getRandFistIX(String nickname){
   
   
        int rst = RAND.nextInt(FISTS.length);
        System.out.println(nickname+"出拳:"+FISTS[rst]);
        return rst;
    }

}

1.简单模板的提取
观察我们可以得到,我们能够从游戏规则类中提取出一些方法模板:

public xxx get(String title){
   
   
    String line = null;
    do{
   
   
        System.out.println(...)//一个与title拼接的字符串,说明输入信息
        line = INPUT.nextLine();
        if(line.matches(...)){
   
   //或者是说处于某个范围 等于某个值(条件判断)
            break;
        }
        System.out.println(...)//另一个与title拼接的字符串,说明错误原因
    }while(true);
}

我们能够看出在OOP的学习过程中存在典型的方法模板,需要积累。

2.学会用一些数学思想去优化条件判断

if(humanFist*machineFist == 0&&humanFist+machineFist == 2){
   
   

因为拳值只可能为0,1,2,而分别代表着布剪锤,即0<1,1<2,2<0,所以如果非一布一锤的情况,值大即胜利,但是当同时出现2和0的时候,谁是0谁就胜利,通过这个优化的条件判断来确定了这种情况。

3.对于这种比较类,我们一般通过程序逻辑自定义比较规则,并将其详细地写在注释中,便于看代码。

4.关键点:将不可比较的拳类,构造出拳值数组,通过输入的字符串与拳值数组匹配得到下标转化为可比较的拳值。

游戏角色类

public class Role extends Rule{
   
   
    private String nickname;

    public Role(String title) {
   
   
        this.nickname = getInputName(title);
    }/* 先传到同包子类的方法中 获取一个昵称 再对属性赋值
     而到ROLE这一层还无法确定是要输入的是人类还是机器人的昵称 所以要给构造方法同样的title参数
     以便于让human和machine的类调用
     */

    /**
     * 获取角色昵称
     * @return
     */

    public String getNickname() {
   
   
        return nickname;
    }//作为提取角色昵称的出口 打破private获取数据的限制

    /**
     * 角色的标准方法:出拳
     * 由于人类角色和机器角色出拳的方式不同 在此处做方法的定义
     * * @return int(拳值对应的下标 是判断输赢的依据)
     */

    public int showFist(){
   
   
        return -1;
    }
}

1.showFist()抽象方法的差异化实现

机器角色类

public class Machine extends Role {
   
   

    public Machine(){
   
   
        super("机器角色");
    }

    /**
     * 重写实现机器角色出拳:随机
     */
    @Override
    public int showFist() {
   
   
        return getRandFistIX(getNickname());
    }
}

人类角色类

public class Human extends Role{
   
   

    //可以或缺 不写也能在子类的构造中自动调用父类的构造
    public Human(){
   
   
        super("人类角色");
    }

    /**
     * 重写实现人类角色出拳
     * @return
     */
    @Override
    public int showFist() {
   
   
        return getInputFistIx(getNickname());//此处可以不用super 原因在于在human里面并没有getNickname() 会自动追踪到父类
    }
}

裁判类

public class Judge extends Rule{
   
   
    /**
     * 裁判掌握的信息 就是 属性
     */
    private String name;

    /**
     * 获取裁判名字
     * @return String
     */
    public String getName() {
   
   
        return name;
    }
    /**
     * 游戏总局数
     */
    private int totalSets;

    /**
     * 获取游戏的总局数
     * @return int
     */
    public int getTotalSets() {
   
   
        return totalSets;
    }

    /**
     * 游戏数据:为了简洁,设计成整形的数组,也可以设计成三个变量,但不够简洁
     */

    private int[] data;
    public Judge(){
   
   
        name = getInputName("裁判");
        totalSets = getInputInt("总局数",3,10);
        data = new int[3]; // data [人类玩家胜局数,机器玩家胜局数,平局数]
    }

    /**
     *
     * 裁判执裁判断单局输赢 完成所有游戏数据的赋值
     * @param humanName
     * @param humanFistInt
     * @param machineName
     * @param machineFistInt
     * @return
     */
    public void judge(String humanName,int humanFistInt,String machineName,int machineFistInt){
   
   
        String whoWin = "胜";
        switch(compare(humanFistInt,machineFistInt)){
   
   
            case 1:
                data[0]++;
                whoWin = humanName + whoWin;
                break;
            case 0:
                data[2]++;
                whoWin = "平局";
                break;
            case -1:
                data[1]++;
                whoWin = machineName + whoWin;
                break;
        }
        System.out.println("本局["+whoWin+"]");
    }
    public void finalJudge(String humanName,String machineName){
   
   
        String whoWin = "胜";
        final int rst = data[0] - data[1];
        if(rst > 0){
   
   
            whoWin += humanName;
        }else if(rst == 0){
   
   
            whoWin = "平局";
        }else{
   
   
            whoWin += machineName;
        }
        System.out.println("-------最终结果-------");
        System.out.println("本次比赛共:"+totalSets+"局");
        System.out.println("人类玩家胜:"+data[0]+"局");
        System.out.println("机器玩家胜:"+data[1]+"局");
        System.out.println("平:"+data[2]+"局");
        System.out.println("最终"+whoWin);
    }
}

1.学习这种字符串拼接方式

String whoWin = "胜";
whoWin = humanName + whoWin;

游戏主类

public class FistGame {
   
   
    //建立游戏角色
    /**
     * 人类角色对象(引用)
     */
    private Human human;
    /**
     * 机器角色对象(引用)
     */
    private Machine machine;
    /**
     * 裁判角色对象(引用)
     */
    private Judge judge;

    /**
     * 游戏业务
     */
    public FistGame(){
   
   
        human = new Human();
        machine = new Machine();
        judge = new Judge();
    }
    public void start(){
   
   
        //提取角色昵称
        final String humanName = human.getNickname();
        final String machineName = machine.getNickname();
        final String judgeName = judge.getName();
        //输出控制
        System.out.println("------- 双人猜拳游戏 -------");
        System.out.println("------- "+humanName+" VS "+machineName+" -------");
        System.out.println("------- 本场裁判 [ "+judgeName+" ]");
        //游戏进程
        final int totalSets = judge.getTotalSets();
        for (int i = 1; i <= totalSets; i++) {
   
   
            System.out.println("第"+i+"局:");
            //双方分别出拳
            final int humanFistInt = human.showFist();
            final int machineFistInt = machine.showFist();
            //裁判针对当局执行裁决
            judge.judge(humanName,humanFistInt,machineName,machineFistInt);
            //输出空行,控制输出格式
            System.out.println();
        }
        //最终执裁
        judge.finalJudge(humanName,machineName);
    }


}

1.执裁过程是先单局再多局,只有先通过单局给到data数组数据,多局执裁才能利用data数组进行结果输出。

笔者认为,这道题是一道非常好的练习OOP的题目(当然也可以适用于其他语言),首先需要有游戏主类、规则类,再考虑游戏角色,既有Role类与具体角色的继承关系,差异化行为实现,又有考虑到人类和机器角色之间一些共性行为和差异化行为(比如说人类是通过输入的拳类来获取拳值,而机器是通过随机生成拳值。)
2.将角色对象作为类属性,并通过构造方法新创建对象,调用各自的方法。(详见面向对象知识总结之组合关系),因为游戏主类包含了人类,机器,裁判三者,并且游戏主类由多个其他类的对象组成,组成了更复杂的对象结构,所以此处选择该方式。
(请记住一个原则:多用组合 少用继承)

目录
相关文章
|
8天前
|
算法 JavaScript 前端开发
【经典算法】LCR187:破冰游戏(约瑟夫问题,Java/C/Python3/JavaScript实现含注释说明,Easy)
【经典算法】LCR187:破冰游戏(约瑟夫问题,Java/C/Python3/JavaScript实现含注释说明,Easy)
12 1
|
10天前
|
Java
电脑玩诺基亚手机jar游戏java-涂鸦地带举例
电脑玩诺基亚手机jar游戏java-涂鸦地带举例
|
30天前
|
Java
04|零基础玩转面向对象编程:Java OOP
04|零基础玩转面向对象编程:Java OOP
13 0
|
30天前
|
网络协议 Ubuntu Java
如何使用MCSM搭建我的世界Java版服务器并实现远程联机游戏
如何使用MCSM搭建我的世界Java版服务器并实现远程联机游戏
125 0
|
30天前
|
JavaScript Java 测试技术
基于Java的游戏虚拟道具交易网站的设计与实现(源码+lw+部署文档+讲解等)
基于Java的游戏虚拟道具交易网站的设计与实现(源码+lw+部署文档+讲解等)
35 0
|
30天前
|
网络协议 Java Windows
打造个人的Minecraft服务器:Java+cpolar实现我的世界联机游戏
打造个人的Minecraft服务器:Java+cpolar实现我的世界联机游戏
167 0
|
30天前
|
JavaScript Java 测试技术
基于Java的游戏商城的设计与实现(源码+lw+部署文档+讲解等)
基于Java的游戏商城的设计与实现(源码+lw+部署文档+讲解等)
35 2
|
30天前
|
JavaScript Java 测试技术
基于Java的游戏资源管理系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的游戏资源管理系统的设计与实现(源码+lw+部署文档+讲解等)
27 0
|
30天前
|
Java
java猜拳游戏
java猜拳游戏
|
10月前
|
IDE 小程序 前端开发
详细解读java的俄罗斯方块游戏的源代码--【课程设计】
详细解读java的俄罗斯方块游戏的源代码--【课程设计】