Type: docs and refactoring Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
This commit is contained in:
		@@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user