美文网首页
设计模式之指令模式——巫师打架

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

作者: 秋元_92a3 | 来源:发表于2021-12-04 17:28 被阅读0次

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

首先介绍下剧情
有两个巫师,巫师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")));
  }
}

项目源码

魔改前原始地址

相关文章

网友评论

      本文标题:设计模式之指令模式——巫师打架

      本文链接:https://www.haomeiwen.com/subject/luxbxrtx.html