Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitc413e09

Browse files
iluwatarohbus
andauthored
docs:iluwatar#590 add explanation for bytecode pattern (iluwatar#1687)
Type: docs and refactoringCo-authored-by: Subhrodip Mohanta <hello@subho.xyz>
1 parent7ac468d commitc413e09

File tree

10 files changed

+277
-103
lines changed

10 files changed

+277
-103
lines changed

‎bytecode/README.md

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,234 @@ tags:
99
---
1010

1111
##Intent
12-
Allows to encode behaviour as instructions for virtual machine.
12+
13+
Allows encoding behavior as instructions for a virtual machine.
14+
15+
##Explanation
16+
17+
Real world example
18+
19+
>A team is working on a new game where wizards battle against each other. The wizard behavior
20+
>needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not
21+
>optimal to ask the programmer to make changes each time the game designer wants to vary the
22+
>behavior, so the wizard behavior is implemented as a data-driven virtual machine.
23+
24+
In plain words
25+
26+
>Bytecode pattern enables behavior driven by data instead of code.
27+
28+
[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation
29+
states:
30+
31+
>An instruction set defines the low-level operations that can be performed. A series of
32+
>instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one
33+
>at a time, using a stack for intermediate values. By combining instructions, complex high-level
34+
>behavior can be defined.
35+
36+
**Programmatic Example**
37+
38+
One of the most important game objects is the`Wizard` class.
39+
40+
```java
41+
@AllArgsConstructor
42+
@Setter
43+
@Getter
44+
@Slf4j
45+
publicclassWizard {
46+
47+
privateint health;
48+
privateint agility;
49+
privateint wisdom;
50+
privateint numberOfPlayedSounds;
51+
privateint numberOfSpawnedParticles;
52+
53+
publicvoidplaySound() {
54+
LOGGER.info("Playing sound");
55+
numberOfPlayedSounds++;
56+
}
57+
58+
publicvoidspawnParticles() {
59+
LOGGER.info("Spawning particles");
60+
numberOfSpawnedParticles++;
61+
}
62+
}
63+
```
64+
65+
Next, we show the available instructions for our virtual machine. Each of the instructions has its
66+
own semantics on how it operates with the stack data. For example, the ADD instruction takes the top
67+
two items from the stack, adds them together and pushes the result to the stack.
68+
69+
```java
70+
@AllArgsConstructor
71+
@Getter
72+
publicenumInstruction {
73+
74+
LITERAL(1),// e.g. "LITERAL 0", push 0 to stack
75+
SET_HEALTH(2),// e.g. "SET_HEALTH", pop health and wizard number, call set health
76+
SET_WISDOM(3),// e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
77+
SET_AGILITY(4),// e.g. "SET_AGILITY", pop agility and wizard number, call set agility
78+
PLAY_SOUND(5),// e.g. "PLAY_SOUND", pop value as wizard number, call play sound
79+
SPAWN_PARTICLES(6),// e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
80+
GET_HEALTH(7),// e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
81+
GET_AGILITY(8),// e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
82+
GET_WISDOM(9),// e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
83+
ADD(10),// e.g. "ADD", pop 2 values, push their sum
84+
DIVIDE(11);// e.g. "DIVIDE", pop 2 values, push their division
85+
// ...
86+
}
87+
```
88+
89+
At the heart of our example is the`VirtualMachine` class. It takes instructions as input and
90+
executes them to provide the game object behavior.
91+
92+
```java
93+
@Getter
94+
@Slf4j
95+
publicclassVirtualMachine {
96+
97+
privatefinalStack<Integer> stack=newStack<>();
98+
99+
privatefinalWizard[] wizards=newWizard[2];
100+
101+
publicVirtualMachine() {
102+
wizards[0]=newWizard(randomInt(3,32), randomInt(3,32), randomInt(3,32),
103+
0,0);
104+
wizards[1]=newWizard(randomInt(3,32), randomInt(3,32), randomInt(3,32),
105+
0,0);
106+
}
107+
108+
publicVirtualMachine(Wizardwizard1,Wizardwizard2) {
109+
wizards[0]= wizard1;
110+
wizards[1]= wizard2;
111+
}
112+
113+
publicvoidexecute(int[]bytecode) {
114+
for (var i=0; i< bytecode.length; i++) {
115+
Instruction instruction=Instruction.getInstruction(bytecode[i]);
116+
switch (instruction) {
117+
caseLITERAL:
118+
// Read the next byte from the bytecode.
119+
int value= bytecode[++i];
120+
// Push the next value to stack
121+
stack.push(value);
122+
break;
123+
caseSET_AGILITY:
124+
var amount= stack.pop();
125+
var wizard= stack.pop();
126+
setAgility(wizard, amount);
127+
break;
128+
caseSET_WISDOM:
129+
amount= stack.pop();
130+
wizard= stack.pop();
131+
setWisdom(wizard, amount);
132+
break;
133+
caseSET_HEALTH:
134+
amount= stack.pop();
135+
wizard= stack.pop();
136+
setHealth(wizard, amount);
137+
break;
138+
caseGET_HEALTH:
139+
wizard= stack.pop();
140+
stack.push(getHealth(wizard));
141+
break;
142+
caseGET_AGILITY:
143+
wizard= stack.pop();
144+
stack.push(getAgility(wizard));
145+
break;
146+
caseGET_WISDOM:
147+
wizard= stack.pop();
148+
stack.push(getWisdom(wizard));
149+
break;
150+
caseADD:
151+
var a= stack.pop();
152+
var b= stack.pop();
153+
stack.push(a+ b);
154+
break;
155+
caseDIVIDE:
156+
a= stack.pop();
157+
b= stack.pop();
158+
stack.push(b/ a);
159+
break;
160+
casePLAY_SOUND:
161+
wizard= stack.pop();
162+
getWizards()[wizard].playSound();
163+
break;
164+
caseSPAWN_PARTICLES:
165+
wizard= stack.pop();
166+
getWizards()[wizard].spawnParticles();
167+
break;
168+
default:
169+
thrownewIllegalArgumentException("Invalid instruction value");
170+
}
171+
LOGGER.info("Executed"+ instruction.name()+", Stack contains"+ getStack());
172+
}
173+
}
174+
175+
publicvoidsetHealth(intwizard,intamount) {
176+
wizards[wizard].setHealth(amount);
177+
}
178+
// other setters ->
179+
// ...
180+
}
181+
```
182+
183+
Now we can show the full example utilizing the virtual machine.
184+
185+
```java
186+
publicstaticvoid main(String[] args) {
187+
188+
var vm=newVirtualMachine(
189+
newWizard(45,7,11,0,0),
190+
newWizard(36,18,8,0,0));
191+
192+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
193+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
194+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
195+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
196+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
197+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
198+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
199+
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
200+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
201+
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
202+
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
203+
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
204+
}
205+
```
206+
207+
Here is the console output.
208+
209+
```
210+
16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0]
211+
16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0]
212+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45]
213+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0]
214+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7]
215+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0]
216+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11]
217+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18]
218+
16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2]
219+
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9]
220+
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54]
221+
16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains []
222+
```
13223

14224
##Class diagram
225+
15226
![alt text](./etc/bytecode.urm.png"Bytecode class diagram")
16227

17228
##Applicability
229+
18230
Use the Bytecode pattern when you have a lot of behavior you need to define and your
19231
game’s implementation language isn’t a good fit because:
20232

21-
* it’s too low-level, making it tedious or error-prone to program in.
22-
* iterating on it takes too long due to slow compile times or other tooling issues.
23-
* 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.
233+
* It’s too low-level, making it tedious or error-prone to program in.
234+
* Iterating on it takes too long due to slow compile times or other tooling issues.
235+
* 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.
236+
237+
##Related patterns
238+
239+
*[Interpreter](https://java-design-patterns.com/patterns/interpreter/)
24240

25241
##Credits
26242

‎bytecode/etc/bytecode.png

-19.4 KB
Binary file not shown.

‎bytecode/etc/bytecode.ucls

Lines changed: 0 additions & 49 deletions
This file was deleted.

‎bytecode/etc/bytecode.urm.png

17.4 KB
Loading

‎bytecode/etc/bytecode.urm.puml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.iluwatar.bytecode {
33
classApp {
44
-LOGGER : Logger {static}
55
+App()
6-
-interpretInstruction(instruction : String, vm : VirtualMachine) {static}
76
+main(args : String[]) {static}
87
}
98
enumInstruction {
@@ -18,22 +17,25 @@ package com.iluwatar.bytecode {
1817
+SET_HEALTH {static}
1918
+SET_WISDOM {static}
2019
+SPAWN_PARTICLES {static}
21-
-value :int
20+
-intValue :int
2221
+getInstruction(value : int) : Instruction {static}
2322
+getIntValue() :int
2423
+valueOf(name : String) : Instruction {static}
2524
+values() : Instruction[] {static}
2625
}
2726
classVirtualMachine {
27+
-LOGGER : Logger {static}
2828
-stack : Stack<Integer>
2929
-wizards : Wizard[]
3030
+VirtualMachine()
31+
+VirtualMachine(wizard1 : Wizard, wizard2 : Wizard)
3132
+execute(bytecode : int[])
3233
+getAgility(wizard : int) : int
3334
+getHealth(wizard : int) : int
3435
+getStack() : Stack<Integer>
3536
+getWisdom(wizard : int) : int
3637
+getWizards() : Wizard[]
38+
-randomInt(min : int, max : int) : int
3739
+setAgility(wizard : int, amount : int)
3840
+setHealth(wizard : int, amount : int)
3941
+setWisdom(wizard : int, amount : int)
@@ -45,7 +47,7 @@ package com.iluwatar.bytecode {
4547
-numberOfPlayedSounds :int
4648
-numberOfSpawnedParticles :int
4749
-wisdom :int
48-
+Wizard()
50+
+Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int)
4951
+getAgility() :int
5052
+getHealth() :int
5153
+getNumberOfPlayedSounds() :int
@@ -54,6 +56,8 @@ package com.iluwatar.bytecode {
5456
+playSound()
5557
+setAgility(agility : int)
5658
+setHealth(health : int)
59+
+setNumberOfPlayedSounds(numberOfPlayedSounds : int)
60+
+setNumberOfSpawnedParticles(numberOfSpawnedParticles : int)
5761
+setWisdom(wisdom : int)
5862
+spawnParticles()
5963
}

‎bytecode/src/main/java/com/iluwatar/bytecode/App.java

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,33 +49,21 @@ public class App {
4949
*/
5050
publicstaticvoidmain(String[]args) {
5151

52-
varwizard =newWizard();
53-
wizard.setHealth(45);
54-
wizard.setAgility(7);
55-
wizard.setWisdom(11);
52+
varvm =newVirtualMachine(
53+
newWizard(45,7,11,0,0),
54+
newWizard(36,18,8,0,0));
5655

57-
varvm =newVirtualMachine();
58-
vm.getWizards()[0] =wizard;
59-
60-
Stringliteral ="LITERAL 0";
61-
62-
interpretInstruction(literal,vm);
63-
interpretInstruction(literal,vm);
64-
interpretInstruction("GET_HEALTH",vm);
65-
interpretInstruction(literal,vm);
66-
interpretInstruction("GET_AGILITY",vm);
67-
interpretInstruction(literal,vm);
68-
interpretInstruction("GET_WISDOM ",vm);
69-
interpretInstruction("ADD",vm);
70-
interpretInstruction("LITERAL 2",vm);
71-
interpretInstruction("DIVIDE",vm);
72-
interpretInstruction("ADD",vm);
73-
interpretInstruction("SET_HEALTH",vm);
74-
}
75-
76-
privatestaticvoidinterpretInstruction(Stringinstruction,VirtualMachinevm) {
77-
vm.execute(InstructionConverterUtil.convertToByteCode(instruction));
78-
varstack =vm.getStack();
79-
LOGGER.info(instruction +String.format("%" + (12 -instruction.length()) +"s","") +stack);
56+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
57+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
58+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
59+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
60+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
61+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
62+
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
63+
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
64+
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
65+
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
66+
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
67+
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
8068
}
8169
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp