Type: docs and refactoring Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
This commit is contained in:
parent
7ac468db20
commit
c413e0902e
@ -9,18 +9,234 @@ tags:
|
|||||||
---
|
---
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
Allows to encode behaviour as instructions for virtual machine.
|
|
||||||
|
Allows encoding behavior as instructions for a virtual machine.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Real world example
|
||||||
|
|
||||||
|
> A team is working on a new game where wizards battle against each other. The wizard behavior
|
||||||
|
> needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not
|
||||||
|
> optimal to ask the programmer to make changes each time the game designer wants to vary the
|
||||||
|
> behavior, so the wizard behavior is implemented as a data-driven virtual machine.
|
||||||
|
|
||||||
|
In plain words
|
||||||
|
|
||||||
|
> Bytecode pattern enables behavior driven by data instead of code.
|
||||||
|
|
||||||
|
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation
|
||||||
|
states:
|
||||||
|
|
||||||
|
> An instruction set defines the low-level operations that can be performed. A series of
|
||||||
|
> instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one
|
||||||
|
> at a time, using a stack for intermediate values. By combining instructions, complex high-level
|
||||||
|
> behavior can be defined.
|
||||||
|
|
||||||
|
**Programmatic Example**
|
||||||
|
|
||||||
|
One of the most important game objects is the `Wizard` class.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@Slf4j
|
||||||
|
public class Wizard {
|
||||||
|
|
||||||
|
private int health;
|
||||||
|
private int agility;
|
||||||
|
private int wisdom;
|
||||||
|
private int numberOfPlayedSounds;
|
||||||
|
private int numberOfSpawnedParticles;
|
||||||
|
|
||||||
|
public void playSound() {
|
||||||
|
LOGGER.info("Playing sound");
|
||||||
|
numberOfPlayedSounds++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spawnParticles() {
|
||||||
|
LOGGER.info("Spawning particles");
|
||||||
|
numberOfSpawnedParticles++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, we show the available instructions for our virtual machine. Each of the instructions has its
|
||||||
|
own semantics on how it operates with the stack data. For example, the ADD instruction takes the top
|
||||||
|
two items from the stack, adds them together and pushes the result to the stack.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public enum Instruction {
|
||||||
|
|
||||||
|
LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
|
||||||
|
SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
|
||||||
|
SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
|
||||||
|
SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
|
||||||
|
PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
|
||||||
|
SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
|
||||||
|
GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
|
||||||
|
GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
|
||||||
|
GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
|
||||||
|
ADD(10), // e.g. "ADD", pop 2 values, push their sum
|
||||||
|
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
At the heart of our example is the `VirtualMachine` class. It takes instructions as input and
|
||||||
|
executes them to provide the game object behavior.
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Getter
|
||||||
|
@Slf4j
|
||||||
|
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[] bytecode) {
|
||||||
|
for (var i = 0; i < bytecode.length; i++) {
|
||||||
|
Instruction instruction = Instruction.getInstruction(bytecode[i]);
|
||||||
|
switch (instruction) {
|
||||||
|
case LITERAL:
|
||||||
|
// Read the next byte from the bytecode.
|
||||||
|
int value = bytecode[++i];
|
||||||
|
// Push the next value to stack
|
||||||
|
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 GET_WISDOM:
|
||||||
|
wizard = stack.pop();
|
||||||
|
stack.push(getWisdom(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;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid instruction value");
|
||||||
|
}
|
||||||
|
LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHealth(int wizard, int amount) {
|
||||||
|
wizards[wizard].setHealth(amount);
|
||||||
|
}
|
||||||
|
// other setters ->
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can show the full example utilizing the virtual machine.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public static void main(String[] args) {
|
||||||
|
|
||||||
|
var vm = new VirtualMachine(
|
||||||
|
new Wizard(45, 7, 11, 0, 0),
|
||||||
|
new Wizard(36, 18, 8, 0, 0));
|
||||||
|
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here is the console output.
|
||||||
|
|
||||||
|
```
|
||||||
|
16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0]
|
||||||
|
16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18]
|
||||||
|
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2]
|
||||||
|
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9]
|
||||||
|
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54]
|
||||||
|
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []
|
||||||
|
```
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
|
|
||||||
Use the Bytecode pattern when you have a lot of behavior you need to define and your
|
Use the Bytecode pattern when you have a lot of behavior you need to define and your
|
||||||
game’s implementation language isn’t a good fit because:
|
game’s implementation language isn’t a good fit because:
|
||||||
|
|
||||||
* it’s too low-level, making it tedious or error-prone to program in.
|
* It’s too low-level, making it tedious or error-prone to program in.
|
||||||
* iterating on it takes too long due to slow compile times or other tooling issues.
|
* Iterating on it takes too long due to slow compile times or other tooling issues.
|
||||||
* it has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase.
|
* It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase.
|
||||||
|
|
||||||
|
## Related patterns
|
||||||
|
|
||||||
|
* [Interpreter](https://java-design-patterns.com/patterns/interpreter/)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB |
@ -1,49 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<class-diagram version="1.2.3" icons="true" always-add-relationships="false" generalizations="true" realizations="true"
|
|
||||||
associations="true" dependencies="false" nesting-relationships="true" router="FAN">
|
|
||||||
<class id="1" language="java" name="com.iluwatar.bytecode.VirtualMachine" project="bytecode"
|
|
||||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java" binary="false" corner="BOTTOM_RIGHT">
|
|
||||||
<position height="-1" width="-1" x="455" y="173"/>
|
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
|
||||||
sort-features="false" accessors="true" visibility="true">
|
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
</display>
|
|
||||||
</class>
|
|
||||||
<class id="2" language="java" name="com.iluwatar.bytecode.App" project="bytecode"
|
|
||||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/App.java" binary="false" corner="BOTTOM_RIGHT">
|
|
||||||
<position height="-1" width="-1" x="148" y="110"/>
|
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
|
||||||
sort-features="false" accessors="true" visibility="true">
|
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
</display>
|
|
||||||
</class>
|
|
||||||
<class id="3" language="java" name="com.iluwatar.bytecode.Wizard" project="bytecode"
|
|
||||||
file="/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java" binary="false" corner="BOTTOM_RIGHT">
|
|
||||||
<position height="-1" width="-1" x="148" y="416"/>
|
|
||||||
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
|
||||||
sort-features="false" accessors="true" visibility="true">
|
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
</display>
|
|
||||||
</class>
|
|
||||||
<association id="4">
|
|
||||||
<end type="SOURCE" refId="1" navigable="false" variant="ASSOCIATION">
|
|
||||||
<attribute id="5" name="wizards">
|
|
||||||
<position height="18" width="48" x="296" y="291"/>
|
|
||||||
</attribute>
|
|
||||||
<multiplicity id="6" minimum="0" maximum="2147483647">
|
|
||||||
<position height="0" width="0" x="-327" y="-27"/>
|
|
||||||
</multiplicity>
|
|
||||||
</end>
|
|
||||||
<end type="TARGET" refId="3" navigable="true" variant="ASSOCIATION"/>
|
|
||||||
<display labels="true" multiplicity="true"/>
|
|
||||||
</association>
|
|
||||||
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
|
|
||||||
sort-features="false" accessors="true" visibility="true">
|
|
||||||
<attributes public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
<operations public="true" package="true" protected="true" private="true" static="true"/>
|
|
||||||
</classifier-display>
|
|
||||||
<association-display labels="true" multiplicity="true"/>
|
|
||||||
</class-diagram>
|
|
Binary file not shown.
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 84 KiB |
@ -3,7 +3,6 @@ package com.iluwatar.bytecode {
|
|||||||
class App {
|
class App {
|
||||||
- LOGGER : Logger {static}
|
- LOGGER : Logger {static}
|
||||||
+ App()
|
+ App()
|
||||||
- interpretInstruction(instruction : String, vm : VirtualMachine) {static}
|
|
||||||
+ main(args : String[]) {static}
|
+ main(args : String[]) {static}
|
||||||
}
|
}
|
||||||
enum Instruction {
|
enum Instruction {
|
||||||
@ -18,22 +17,25 @@ package com.iluwatar.bytecode {
|
|||||||
+ SET_HEALTH {static}
|
+ SET_HEALTH {static}
|
||||||
+ SET_WISDOM {static}
|
+ SET_WISDOM {static}
|
||||||
+ SPAWN_PARTICLES {static}
|
+ SPAWN_PARTICLES {static}
|
||||||
- value : int
|
- intValue : int
|
||||||
+ getInstruction(value : int) : Instruction {static}
|
+ getInstruction(value : int) : Instruction {static}
|
||||||
+ getIntValue() : int
|
+ getIntValue() : int
|
||||||
+ valueOf(name : String) : Instruction {static}
|
+ valueOf(name : String) : Instruction {static}
|
||||||
+ values() : Instruction[] {static}
|
+ values() : Instruction[] {static}
|
||||||
}
|
}
|
||||||
class VirtualMachine {
|
class VirtualMachine {
|
||||||
|
- LOGGER : Logger {static}
|
||||||
- stack : Stack<Integer>
|
- stack : Stack<Integer>
|
||||||
- wizards : Wizard[]
|
- wizards : Wizard[]
|
||||||
+ VirtualMachine()
|
+ VirtualMachine()
|
||||||
|
+ VirtualMachine(wizard1 : Wizard, wizard2 : Wizard)
|
||||||
+ execute(bytecode : int[])
|
+ execute(bytecode : int[])
|
||||||
+ getAgility(wizard : int) : int
|
+ getAgility(wizard : int) : int
|
||||||
+ getHealth(wizard : int) : int
|
+ getHealth(wizard : int) : int
|
||||||
+ getStack() : Stack<Integer>
|
+ getStack() : Stack<Integer>
|
||||||
+ getWisdom(wizard : int) : int
|
+ getWisdom(wizard : int) : int
|
||||||
+ getWizards() : Wizard[]
|
+ getWizards() : Wizard[]
|
||||||
|
- randomInt(min : int, max : int) : int
|
||||||
+ setAgility(wizard : int, amount : int)
|
+ setAgility(wizard : int, amount : int)
|
||||||
+ setHealth(wizard : int, amount : int)
|
+ setHealth(wizard : int, amount : int)
|
||||||
+ setWisdom(wizard : int, amount : int)
|
+ setWisdom(wizard : int, amount : int)
|
||||||
@ -45,7 +47,7 @@ package com.iluwatar.bytecode {
|
|||||||
- numberOfPlayedSounds : int
|
- numberOfPlayedSounds : int
|
||||||
- numberOfSpawnedParticles : int
|
- numberOfSpawnedParticles : int
|
||||||
- wisdom : int
|
- wisdom : int
|
||||||
+ Wizard()
|
+ Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int)
|
||||||
+ getAgility() : int
|
+ getAgility() : int
|
||||||
+ getHealth() : int
|
+ getHealth() : int
|
||||||
+ getNumberOfPlayedSounds() : int
|
+ getNumberOfPlayedSounds() : int
|
||||||
@ -54,6 +56,8 @@ package com.iluwatar.bytecode {
|
|||||||
+ playSound()
|
+ playSound()
|
||||||
+ setAgility(agility : int)
|
+ setAgility(agility : int)
|
||||||
+ setHealth(health : int)
|
+ setHealth(health : int)
|
||||||
|
+ setNumberOfPlayedSounds(numberOfPlayedSounds : int)
|
||||||
|
+ setNumberOfSpawnedParticles(numberOfSpawnedParticles : int)
|
||||||
+ setWisdom(wisdom : int)
|
+ setWisdom(wisdom : int)
|
||||||
+ spawnParticles()
|
+ spawnParticles()
|
||||||
}
|
}
|
||||||
|
@ -49,33 +49,21 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
var wizard = new Wizard();
|
var vm = new VirtualMachine(
|
||||||
wizard.setHealth(45);
|
new Wizard(45, 7, 11, 0, 0),
|
||||||
wizard.setAgility(7);
|
new Wizard(36, 18, 8, 0, 0));
|
||||||
wizard.setWisdom(11);
|
|
||||||
|
|
||||||
var vm = new VirtualMachine();
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
vm.getWizards()[0] = wizard;
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
||||||
String literal = "LITERAL 0";
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
||||||
interpretInstruction(literal, vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
||||||
interpretInstruction(literal, vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
||||||
interpretInstruction("GET_HEALTH", vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
interpretInstruction(literal, vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
||||||
interpretInstruction("GET_AGILITY", vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
||||||
interpretInstruction(literal, vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
||||||
interpretInstruction("GET_WISDOM ", vm);
|
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
||||||
interpretInstruction("ADD", vm);
|
|
||||||
interpretInstruction("LITERAL 2", vm);
|
|
||||||
interpretInstruction("DIVIDE", vm);
|
|
||||||
interpretInstruction("ADD", vm);
|
|
||||||
interpretInstruction("SET_HEALTH", vm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void interpretInstruction(String instruction, VirtualMachine vm) {
|
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode(instruction));
|
|
||||||
var stack = vm.getStack();
|
|
||||||
LOGGER.info(instruction + String.format("%" + (12 - instruction.length()) + "s", "") + stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,17 +33,17 @@ import lombok.Getter;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum Instruction {
|
public enum Instruction {
|
||||||
|
|
||||||
LITERAL(1),
|
LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
|
||||||
SET_HEALTH(2),
|
SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
|
||||||
SET_WISDOM(3),
|
SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
|
||||||
SET_AGILITY(4),
|
SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
|
||||||
PLAY_SOUND(5),
|
PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
|
||||||
SPAWN_PARTICLES(6),
|
SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
|
||||||
GET_HEALTH(7),
|
GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
|
||||||
GET_AGILITY(8),
|
GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
|
||||||
GET_WISDOM(9),
|
GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
|
||||||
ADD(10),
|
ADD(10), // e.g. "ADD", pop 2 values, push their sum
|
||||||
DIVIDE(11);
|
DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
|
||||||
|
|
||||||
private final int intValue;
|
private final int intValue;
|
||||||
|
|
||||||
|
@ -24,12 +24,15 @@
|
|||||||
package com.iluwatar.bytecode;
|
package com.iluwatar.bytecode;
|
||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of virtual machine.
|
* Implementation of virtual machine.
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
|
@Slf4j
|
||||||
public class VirtualMachine {
|
public class VirtualMachine {
|
||||||
|
|
||||||
private final Stack<Integer> stack = new Stack<>();
|
private final Stack<Integer> stack = new Stack<>();
|
||||||
@ -37,12 +40,21 @@ public class VirtualMachine {
|
|||||||
private final Wizard[] wizards = new Wizard[2];
|
private final Wizard[] wizards = new Wizard[2];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* No-args constructor.
|
||||||
*/
|
*/
|
||||||
public VirtualMachine() {
|
public VirtualMachine() {
|
||||||
for (var i = 0; i < wizards.length; i++) {
|
wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||||
wizards[i] = new Wizard();
|
0, 0);
|
||||||
}
|
wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor taking the wizards as arguments.
|
||||||
|
*/
|
||||||
|
public VirtualMachine(Wizard wizard1, Wizard wizard2) {
|
||||||
|
wizards[0] = wizard1;
|
||||||
|
wizards[1] = wizard2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,6 +69,7 @@ public class VirtualMachine {
|
|||||||
case LITERAL:
|
case LITERAL:
|
||||||
// Read the next byte from the bytecode.
|
// Read the next byte from the bytecode.
|
||||||
int value = bytecode[++i];
|
int value = bytecode[++i];
|
||||||
|
// Push the next value to stack
|
||||||
stack.push(value);
|
stack.push(value);
|
||||||
break;
|
break;
|
||||||
case SET_AGILITY:
|
case SET_AGILITY:
|
||||||
@ -107,6 +120,7 @@ public class VirtualMachine {
|
|||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Invalid instruction value");
|
throw new IllegalArgumentException("Invalid instruction value");
|
||||||
}
|
}
|
||||||
|
LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,4 +147,8 @@ public class VirtualMachine {
|
|||||||
public int getAgility(int wizard) {
|
public int getAgility(int wizard) {
|
||||||
return wizards[wizard].getAgility();
|
return wizards[wizard].getAgility();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int randomInt(int min, int max) {
|
||||||
|
return ThreadLocalRandom.current().nextInt(min, max + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
package com.iluwatar.bytecode;
|
package com.iluwatar.bytecode;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@ -31,16 +32,15 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* This class represent game objects which properties can be changed by instructions interpreted by
|
* This class represent game objects which properties can be changed by instructions interpreted by
|
||||||
* virtual machine.
|
* virtual machine.
|
||||||
*/
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
@Setter
|
@Setter
|
||||||
@Getter
|
@Getter
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Wizard {
|
public class Wizard {
|
||||||
|
|
||||||
private int health;
|
private int health;
|
||||||
|
|
||||||
private int agility;
|
private int agility;
|
||||||
private int wisdom;
|
private int wisdom;
|
||||||
|
|
||||||
private int numberOfPlayedSounds;
|
private int numberOfPlayedSounds;
|
||||||
private int numberOfSpawnedParticles;
|
private int numberOfSpawnedParticles;
|
||||||
|
|
||||||
@ -53,5 +53,4 @@ public class Wizard {
|
|||||||
LOGGER.info("Spawning particles");
|
LOGGER.info("Spawning particles");
|
||||||
numberOfSpawnedParticles++;
|
numberOfSpawnedParticles++;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,4 @@ public class InstructionConverterUtil {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user