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

猜拳游戏

结果呈现

在这里插入图片描述
请添加图片描述

游戏规则类

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.将角色对象作为类属性,并通过构造方法新创建对象,调用各自的方法。(详见面向对象知识总结之组合关系),因为游戏主类包含了人类,机器,裁判三者,并且游戏主类由多个其他类的对象组成,组成了更复杂的对象结构,所以此处选择该方式。
(请记住一个原则:多用组合 少用继承)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Byyyi耀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值