diff --git a/bytecode/README.md b/bytecode/README.md index b93bc8e26..a31b6be40 100644 --- a/bytecode/README.md +++ b/bytecode/README.md @@ -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 game’s implementation language isn’t 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)