运用指令模式编写个小游戏,巫师打架
首先介绍下剧情
有两个巫师,巫师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")));
}
}