docs: update bytecode

This commit is contained in:
Ilkka Seppälä
2024-05-24 16:52:35 +03:00
parent d224b8dfa0
commit 4082742ae2
+32 -27
View File
@@ -3,7 +3,12 @@ title: Bytecode
category: Behavioral
language: en
tag:
- Abstraction
- Code simplification
- Encapsulation
- Game programming
- Performance
- Runtime
---
## Intent
@@ -14,18 +19,20 @@ Allows encoding behavior as instructions for a virtual machine.
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.
> An analogous real-world example of the Bytecode design pattern can be seen in the process of translating a book into multiple languages. Instead of directly translating the book from the original language into every other language, the book is first translated into a common intermediate language, like Esperanto. This intermediate version is easier to translate because it is simpler and more structured. Translators for each target language then translate from Esperanto into their specific languages. This approach ensures consistency, reduces errors, and simplifies the translation process, similar to how bytecode serves as an intermediate representation to optimize and facilitate the execution of high-level programming languages across different platforms.
In plain words
> Bytecode pattern enables behavior driven by data instead of code.
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation states:
[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**
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.
One of the most important game objects is the `Wizard` class.
```java
@@ -73,7 +80,8 @@ public enum Instruction {
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
// ...
// Other properties and methods...
}
```
@@ -166,33 +174,33 @@ public class VirtualMachine {
public void setHealth(int wizard, int amount) {
wizards[wizard].setHealth(amount);
}
// other setters ->
// ...
// Other properties and methods...
}
```
Now we can show the full example utilizing the virtual machine.
```java
public static void main(String[]args){
public static void main(String[] args) {
var vm=new VirtualMachine(
new Wizard(45,7,11,0,0),
new Wizard(36,18,8,0,0));
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"));
}
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
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(String.format(HEALTH_PATTERN, "SET")));
}
```
Here is the console output.
@@ -212,10 +220,6 @@ Here is the console output.
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []
```
## Class diagram
![alt text](./etc/bytecode.urm.png "Bytecode class diagram")
## Applicability
Use the Bytecode pattern when you have a lot of behavior you need to define and your games implementation language isnt a good fit because:
@@ -251,5 +255,6 @@ Trade-offs:
## Credits
* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html)
* [Game Programming Patterns](https://amzn.to/3K96fOn)
* [Programming Language Pragmatics](https://amzn.to/49Tusnn)
* [Bytecode (Game Programming Patterns)](http://gameprogrammingpatterns.com/bytecode.html)