设计模式之指令模式——巫师打架

运用指令模式编写个小游戏,巫师打架

首先介绍下剧情
有两个巫师,巫师0和巫师1,打架,打架前他们指定了如下的规则:
游戏规则
怒吼一次 对方生命值 -5
打枪一次 对方生命值 -10
双方的初始生命值是100。

实现之后的运行结果
16:52:04.665 [main] INFO App - 巫师 A 冲 B 大吼一声
16:52:04.709 [main] INFO App - 巫师 B 冲 A 大吼一声
16:52:04.709 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.710 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.710 [main] INFO App - 巫师 B 冲 A 大吼一声
16:52:04.710 [main] INFO App - 巫师 A 向 B 施法 寄生
16:52:04.710 [main] INFO App - 巫师 B 冲 A 大吼一声
16:52:04.711 [main] INFO App - 巫师 A 冲 B 大吼一声
16:52:04.711 [main] INFO App - 巫师 A 冲 B 大吼一声
16:52:04.711 [main] INFO App - 巫师 A 冲 B 大吼一声
16:52:04.711 [main] INFO App - 巫师 A 向 B 施法 寄生
16:52:04.711 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.712 [main] INFO App - 巫师 A 冲 B 大吼一声
16:52:04.712 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.712 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.712 [main] INFO App - 巫师 B 冲 A 大吼一声
16:52:04.713 [main] INFO App - 巫师 A 向 B 施法 寄生
16:52:04.713 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.713 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.713 [main] INFO App - 巫师 B 向 A 施法 缠绕
16:52:04.713 [main] INFO App - Game over~! 1 win
16:52:04.714 [main] INFO App - 0 wizard's health is : 0
16:52:04.715 [main] INFO App - 1 wizard's health is : 45

设计实现

该实现模拟计虚拟机及虚拟机应用的关系;讲述app发送动作到虚拟机,虚拟机解释执行操作的过程,在虚拟机中,引入了一个经典变量stack,指令模拟计算机指令。
指令是由虚拟机定义并解释执行,触发方是其身上部署的虚拟机应用。

指令集定义了可以执行的低级操作。 一系列指令被编码为字节序列。 虚拟机一次一条地执行这些指令,将堆栈用于中间值。 通过组合指令,可以定义复杂的高级行为。

项目讲解

@AllArgsConstructor
@Getter
public enum Instruction {
  LITERAL(1),         // e.g. "LITERAL 0" , push 0 to stack ,将命令后面的参数推入到栈中
  SET_HEALTH(2),      // e.g. "SET_HEALTH" , 依次在stack弹出 health 值,和 wizard 值,然后往对应的对象中进行属性设置操作
  SET_WISDOM(3),      // e.g. "SET_WISDOM" , 依次在stack弹出 wisdom 值,和 wizard 值,然后往对应的对象中进行属性设置操作
  SET_AGILITY(4),     // e.g. "SET_AGILITY" , 依次在stack弹出 agility 值,和 wizard 值,然后往对应的对象中进行属性设置操作
  PLAY_SOUND(5),      // e.g. "PLAY_SOUND" ,
  SPAWN_PARTICLES(6),
  GET_HEALTH(7),
  GET_AGILITY(8),
  GET_WISDOM(9),
  ADD(10),
  DIVIDE(11),
  REDUCE(12);

  private final int intValue;

  public static Instruction getInstruction (int value) {
    return Arrays.stream(values()).filter(i -> i.getIntValue() == value).findAny().orElse(null);
  }
}

虚拟机的实际实现如下:

import java.util.Stack;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import lombok.var;

import java.util.concurrent.ThreadLocalRandom;


@Slf4j
@Getter
public class VirtualMachine {

  private final Stack<Integer> stack = new Stack<>();
  /**
   * 一个虚拟机里两个 向导
   */
  private final Wizard[] wizards = new Wizard[2];

  public VirtualMachine () {
    wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
    wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
  }

  public VirtualMachine (Wizard wizard1, Wizard wizard2) {
    wizards[0] = wizard1;
    wizards[1] = wizard2;
  }

  public void execute (int[] bytecodes) {
    for (int i = 0; i < bytecodes.length; i++) {
      Instruction instruction = Instruction.getInstruction(bytecodes[i]);
      if (null == instruction) {
        continue;
      }
      switch (instruction) {
        case LITERAL:
          var value = bytecodes[++i];
          stack.push(value);
          break;
        case SET_AGILITY:
          var amount = stack.pop();
          var wizard = stack.pop();
          setAgility(wizard, amount);
          break;
        case SET_WISDOM:
          amount = stack.pop();
          wizard = stack.pop();
          setWisdom(wizard, amount);
          break;
        case SET_HEALTH:
          amount = stack.pop();
          wizard = stack.pop();
          setHealth(wizard, amount);
          break;
        case GET_HEALTH:
          wizard = stack.pop();
          stack.push(getHealth(wizard));
          break;
        case GET_AGILITY:
          wizard = stack.pop();
          stack.push(getAgility(wizard));
          break;
        case ADD:
          var a = stack.pop();
          var b = stack.pop();
          stack.push(a + b);
          break;
        case DIVIDE:
          a = stack.pop();
          b = stack.pop();
          stack.push(b / a);
          break;
        case PLAY_SOUND:
          wizard = stack.pop();
          getWizards()[wizard].playSound();
          break;
        case SPAWN_PARTICLES:
          wizard = stack.pop();
          getWizards()[wizard].spawnParticles();
          break;
        case REDUCE:
          a = stack.pop();
          b = stack.pop();
          stack.push(b - a);
          break;
        default:
          throw new IllegalArgumentException("Invalid instruction value");
      }
      log.trace("Executed {} ,Stack contains {}", instruction.name(), getStack());
    }
  }

  private Integer getAgility (Integer wizard) {
    return wizards[wizard].getAgility();
  }

  private Integer getHealth (Integer wizard) {
    return wizards[wizard].getHealth();
  }

  private void setHealth (Integer wizard, Integer amount) {
    wizards[wizard].setHealth(amount);
  }

  private void setWisdom (Integer wizard, Integer amount) {
    wizards[wizard].setWisdom(amount);
  }


  public void setAgility (int wizard, int amount) {
    wizards[wizard].setAgility(amount);
  }

  private int randomInt (int min, int max) {
    return ThreadLocalRandom.current().nextInt(min, max + 1);
  }
}

app的设计实现如下:

import lombok.extern.slf4j.Slf4j;
import lombok.var;

@Slf4j
public class App {

  /**
   * 系统指令,是由字母组合 + 数组构成的
   */
  private static final String LITERAL_0 = "LITERAL 0";
  private static final String HEALTH_PATTERN = "%s_HEALTH";
  private static final String GET_AGILITY = "GET_AGILITY";
  private static final String GET_WISDOM = "GET_WISDOM";
  private static final String ADD = "ADD";
  private static final String REDUCE = "REDUCE";
  private static final String LITERAL_1 = "LITERAL 1";
  private static final String LITERAL_5 = "LITERAL 5";
  private static final String LITERAL_10 = "LITERAL 10";
  private static final String DIVIDE = "DIVIDE";

  public static void main (String[] args) {
    var vm = new VirtualMachine(new Wizard(100, 100, 100, 0, 0), new Wizard(100, 100, 100, 0, 0));
    Script script = new Script();
    do {
      switch (script.action()) {
        case Script.A:
          aPlaySound(vm);
          break;
        case Script.B:
          bPlaySound(vm);
          break;
        case Script.C:
          aPawnedParticles(vm);
          break;
        case Script.D:
          bPawnedParticles(vm);
          break;
      }
    } while (vm.getWizards()[0].getHealth() > 0 && vm.getWizards()[1].getHealth() > 0);
    log.info("Game over~! {} win ", vm.getWizards()[0].getHealth() > vm.getWizards()[1].getHealth() ? 0 : 1);
    log.info("0 wizard's health is : {}", vm.getWizards()[0].getHealth());
    log.info("1 wizard's health is : {}", vm.getWizards()[1].getHealth());
  }


  private static void aPlaySound (VirtualMachine vm) {
    log.info("巫师 A 冲 B 大吼一声");
    // 给1号巫师减5分
    doHealth(vm, LITERAL_1, LITERAL_5);
  }

  private static void bPlaySound (VirtualMachine vm) {
    log.info("巫师 B 冲 A 大吼一声");
    // 给0号巫师减5分
    doHealth(vm, LITERAL_0, LITERAL_5);
  }

  private static void bPawnedParticles (VirtualMachine vm) {
    // 给0号巫师减5分
    log.info("巫师 B 向 A 施法 缠绕");
    doHealth(vm, LITERAL_0, LITERAL_10);
  }

  private static void aPawnedParticles (VirtualMachine vm) {
    // 给1号巫师减5分
    log.info("巫师 A 向 B 施法 寄生");
    doHealth(vm, LITERAL_1, LITERAL_10);
  }

  private static void doHealth (VirtualMachine vm, String wizard, String reduceHealth) {
    vm.execute(InstructionConverterUtil.convertToByteCode(wizard));
    vm.execute(InstructionConverterUtil.convertToByteCode(wizard));
    vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
    vm.execute(InstructionConverterUtil.convertToByteCode(reduceHealth));
    vm.execute(InstructionConverterUtil.convertToByteCode(REDUCE));
    vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
  }
}

项目源码

魔改前原始地址

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

推荐阅读更多精彩内容

  • Docker 简介与概述1.Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 ...
    滔滔逐浪阅读 321评论 0 0
  • 仿真器是一种很酷的技术,允许用户在另一个系统之上运行完全不同的系统。 模拟器有广泛的应用程序,例如在ARM设备上运...
    大前端奕辰阅读 1,274评论 0 1
  • 1:什么是云计算? 云计算是一种按量付费的模式!云计算的底层是通过虚拟化技术来实现的! KVM + opensta...
    陪伴是最长情的告白阅读 119评论 0 0
  • 1.什么是容器 我们在生活中容器有很多,比如盘子,碗,一个桶都可以理解为一个容器 而在Linux中容器就是在隔离的...
    张桐随写阅读 202评论 0 0
  • 冯诺依曼结构 从20世纪初,物理学和电子学科学家们就在争论制造可以进行数值计算的机器应该采用什么样的结构。人们被十...
    流年_5b31阅读 141评论 0 0