Bytecode pattern #553 (#896)

* Added bytecode pattern

* Diagram changed and added licence information

* Added bytecode module to main pom.

* Fixed missing dependency error
This commit is contained in:
Pawel Zawitowski 2019-09-03 20:50:48 +02:00 committed by Ilkka Seppälä
parent 7f6067f19f
commit 318f811fea
13 changed files with 819 additions and 0 deletions

25
bytecode/README.md Normal file
View File

@ -0,0 +1,25 @@
---
layout: pattern
title: Bytecode
folder: bytecode
permalink: /patterns/bytecode/
categories: Behavioral
tags:
- Java
- Difficulty-Beginner
---
## Intent
Allows to encode behaviour as instructions for virtual machine.
## 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:
* its 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.
* it has too much trust. If you want to ensure the behavior being defined cant break the game, you need to sandbox it from the rest of the codebase.
## Credits
* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html)

BIN
bytecode/etc/bytecode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,49 @@
<?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>

45
bytecode/pom.xml Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright (c) 2019 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.21.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bytecode</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,79 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
import com.iluwatar.bytecode.util.InstructionConverterUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The intention of Bytecode pattern is to give behavior the flexibility of data by encoding it as instructions
* for a virtual machine.
* 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.
*
* This pattern should be used when there is a need to define high number of behaviours and implementation engine
* is not a good choice because
* It is too lowe level
* 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 cant break the game,
* you need to sandbox it from the rest of the codebase.
*
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Main app method
* @param args command line args
*/
public static void main(String[] args) {
VirtualMachine vm = new VirtualMachine();
Wizard wizard = new Wizard();
wizard.setHealth(45);
wizard.setAgility(7);
wizard.setWisdom(11);
vm.getWizards()[0] = wizard;
interpretInstruction("LITERAL 0", vm);
interpretInstruction( "LITERAL 0", vm);
interpretInstruction( "GET_HEALTH", vm);
interpretInstruction( "LITERAL 0", vm);
interpretInstruction( "GET_AGILITY", vm);
interpretInstruction( "LITERAL 0", vm);
interpretInstruction( "GET_WISDOM ", vm);
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) {
InstructionConverterUtil converter = new InstructionConverterUtil();
vm.execute(converter.convertToByteCode(instruction));
LOGGER.info(instruction + String.format("%" + (12 - instruction.length()) + "s", "" ) + vm.getStack());
}
}

View File

@ -0,0 +1,65 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
/**
* Representation of instructions understandable by virtual machine
*/
public enum Instruction {
LITERAL(1),
SET_HEALTH(2),
SET_WISDOM (3),
SET_AGILITY(4),
PLAY_SOUND(5),
SPAWN_PARTICLES(6),
GET_HEALTH(7),
GET_AGILITY(8),
GET_WISDOM(9),
ADD(10),
DIVIDE (11);
private int value;
Instruction(int value) {
this.value = value;
}
public int getIntValue() {
return value;
}
/**
* Converts integer value to Instruction
* @param value value of instruction
* @return representation of the instruction
*/
public static Instruction getInstruction(int value) {
for (int i = 0; i < Instruction.values().length; i++) {
if (Instruction.values()[i].getIntValue() == value) {
return Instruction.values()[i];
}
}
throw new IllegalArgumentException("Invalid instruction value");
}
}

View File

@ -0,0 +1,142 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
import java.util.Stack;
/**
* Implementation of virtual machine
*/
public class VirtualMachine {
private Stack<Integer> stack = new Stack();
private Wizard[] wizards = new Wizard[2];
/**
* Constructor
*/
public VirtualMachine() {
for (int i = 0; i < wizards.length; i++) {
wizards[i] = new Wizard();
}
}
/**
* Executes provided bytecode
* @param bytecode to execute
*/
public void execute(int[] bytecode) {
for (int i = 0; i < bytecode.length; i++) {
Instruction instruction = Instruction.getInstruction(bytecode[i]);
int wizard;
int amount;
switch (instruction) {
case LITERAL:
// Read the next byte from the bytecode.
int value = bytecode[++i];
stack.push(value);
break;
case SET_AGILITY:
amount = stack.pop();
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:
int a = stack.pop();
int 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");
}
}
}
public Stack<Integer> getStack() {
return stack;
}
public void setHealth(int wizard, int amount) {
wizards[wizard].setHealth(amount);
}
public void setWisdom(int wizard, int amount) {
wizards[wizard].setWisdom(amount);
}
public void setAgility(int wizard, int amount) {
wizards[wizard].setAgility(amount);
}
public int getHealth(int wizard) {
return wizards[wizard].getHealth();
}
public int getWisdom(int wizard) {
return wizards[wizard].getWisdom();
}
public int getAgility(int wizard) {
return wizards[wizard].getAgility();
}
public Wizard[] getWizards() {
return wizards;
}
}

View File

@ -0,0 +1,83 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class represent game objects which properties can be changed by instructions interpreted by virtual machine
*/
public class Wizard {
private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class);
private int health;
private int agility;
private int wisdom;
private int numberOfPlayedSounds;
private int numberOfSpawnedParticles;
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public int getAgility() {
return agility;
}
public void setAgility(int agility) {
this.agility = agility;
}
public int getWisdom() {
return wisdom;
}
public void setWisdom(int wisdom) {
this.wisdom = wisdom;
}
public void playSound() {
LOGGER.info("Playing sound");
numberOfPlayedSounds++;
}
public void spawnParticles() {
LOGGER.info("Spawning particles");
numberOfSpawnedParticles++;
}
public int getNumberOfPlayedSounds() {
return numberOfPlayedSounds;
}
public int getNumberOfSpawnedParticles() {
return numberOfSpawnedParticles;
}
}

View File

@ -0,0 +1,76 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode.util;
import com.iluwatar.bytecode.Instruction;
/**
* Utility class used for instruction validation and conversion
*/
public class InstructionConverterUtil {
/**
* Converts instructions represented as String
*
* @param instructions to convert
* @return array of int representing bytecode
*/
public static int[] convertToByteCode(String instructions) {
if (instructions == null || instructions.trim().length() == 0) {
return new int[0];
}
String[] splitedInstructions = instructions.trim().split(" ");
int[] bytecode = new int[splitedInstructions.length];
for (int i = 0; i < splitedInstructions.length; i++) {
if (isValidInstruction(splitedInstructions[i])) {
bytecode[i] = Instruction.valueOf(splitedInstructions[i]).getIntValue();
} else if (isValidInt(splitedInstructions[i])) {
bytecode[i] = Integer.valueOf(splitedInstructions[i]);
} else {
throw new IllegalArgumentException("Invalid instruction or number: " + splitedInstructions[i]);
}
}
return bytecode;
}
private static boolean isValidInstruction(String instruction) {
try {
Instruction.valueOf(instruction);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
private static boolean isValidInt(String value) {
try {
Integer.parseInt(value);
return true;
} catch (NumberFormatException e) {
return false;
}
}
}

View File

@ -0,0 +1,37 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
import org.junit.jupiter.api.Test;
/**
* Application test
*/
public class AppTest {
@Test
public void test() {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,154 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode;
import org.junit.jupiter.api.Test;
import static com.iluwatar.bytecode.Instruction.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
/**
* Test for {@Link VirtualMachine}
*/
public class VirtualMachineTest {
@Test
public void testLiteral() {
int[] bytecode = new int[2];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = 10;
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(1, vm.getStack().size());
assertEquals(Integer.valueOf(10), vm.getStack().pop());
}
@Test
public void testSetHealth() {
int wizardNumber = 0;
int[] bytecode = new int[5];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
bytecode[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(50, vm.getWizards()[wizardNumber].getHealth());
}
@Test
public void testSetAgility() {
int wizardNumber = 0;
int[] bytecode = new int[5];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
bytecode[3] = 50; // agility amount
bytecode[4] = SET_AGILITY.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(50, vm.getWizards()[wizardNumber].getAgility());
}
@Test
public void testSetWisdom() {
int wizardNumber = 0;
int[] bytecode = new int[5];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
bytecode[3] = 50; // wisdom amount
bytecode[4] = SET_WISDOM.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(50, vm.getWizards()[wizardNumber].getWisdom());
}
@Test
public void testGetHealth() {
int wizardNumber = 0;
int[] bytecode = new int[8];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
bytecode[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
bytecode[5] = LITERAL.getIntValue();;
bytecode[6] = wizardNumber;
bytecode[7] = GET_HEALTH.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(Integer.valueOf(50), vm.getStack().pop());
}
@Test
public void testPlaySound() {
int wizardNumber = 0;
int[] bytecode = new int[3];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = PLAY_SOUND.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(0, vm.getStack().size());
assertEquals(1, vm.getWizards()[0].getNumberOfPlayedSounds());
}
@Test
public void testSpawnParticles() {
int wizardNumber = 0;
int[] bytecode = new int[3];
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = SPAWN_PARTICLES.getIntValue();
VirtualMachine vm = new VirtualMachine();
vm.execute(bytecode);
assertEquals(0, vm.getStack().size());
assertEquals(1, vm.getWizards()[0].getNumberOfSpawnedParticles());
}
@Test
public void testInvalidInstruction() {
int[] bytecode = new int[1];
bytecode[0] = 999;
VirtualMachine vm = new VirtualMachine();
assertThrows(IllegalArgumentException.class, () -> vm.execute(bytecode));
}
}

View File

@ -0,0 +1,63 @@
/**
* The MIT License
* Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.bytecode.util;
import com.iluwatar.bytecode.Instruction;
import com.iluwatar.bytecode.util.InstructionConverterUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Test for {@Link InstructionConverterUtil}
*/
public class InstructionConverterUtilTest {
@Test
public void testEmptyInstruction() {
String instruction = "";
int[] bytecode = InstructionConverterUtil.convertToByteCode(instruction);
Assertions.assertEquals(0, bytecode.length);
}
@Test
public void testInstructions() {
String instructions =
"LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND SPAWN_PARTICLES GET_HEALTH ADD DIVIDE";
int[] bytecode = InstructionConverterUtil.convertToByteCode(instructions);
Assertions.assertEquals(10, bytecode.length);
Assertions.assertEquals(Instruction.LITERAL.getIntValue(), bytecode[0]);
Assertions.assertEquals(35, bytecode[1]);
Assertions.assertEquals(Instruction.SET_HEALTH.getIntValue(), bytecode[2]);
Assertions.assertEquals(Instruction.SET_WISDOM.getIntValue(), bytecode[3]);
Assertions.assertEquals(Instruction.SET_AGILITY.getIntValue(), bytecode[4]);
Assertions.assertEquals(Instruction.PLAY_SOUND.getIntValue(), bytecode[5]);
Assertions.assertEquals(Instruction.SPAWN_PARTICLES.getIntValue(), bytecode[6]);
Assertions.assertEquals(Instruction.GET_HEALTH.getIntValue(), bytecode[7]);
Assertions.assertEquals(Instruction.ADD.getIntValue(), bytecode[8]);
Assertions.assertEquals(Instruction.DIVIDE.getIntValue(), bytecode[9]);
}
}

View File

@ -171,6 +171,7 @@
<module>priority-queue</module>
<module>commander</module>
<module>typeobjectpattern</module>
<module>bytecode</module>
</modules>
<repositories>