diff --git a/bytecode/README.md b/bytecode/README.md new file mode 100644 index 000000000..d2fc45e1e --- /dev/null +++ b/bytecode/README.md @@ -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 +game’s implementation language isn’t a good fit because: + +* 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. +* 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. + +## Credits + +* [Game programming patterns](http://gameprogrammingpatterns.com/bytecode.html) diff --git a/bytecode/etc/bytecode.png b/bytecode/etc/bytecode.png new file mode 100644 index 000000000..31b6bc6ed Binary files /dev/null and b/bytecode/etc/bytecode.png differ diff --git a/bytecode/etc/bytecode.ucls b/bytecode/etc/bytecode.ucls new file mode 100644 index 000000000..3ec390458 --- /dev/null +++ b/bytecode/etc/bytecode.ucls @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/bytecode/pom.xml b/bytecode/pom.xml new file mode 100644 index 000000000..e9e6247f8 --- /dev/null +++ b/bytecode/pom.xml @@ -0,0 +1,45 @@ + + + + + java-design-patterns + com.iluwatar + 1.21.0-SNAPSHOT + + 4.0.0 + + bytecode + + + org.junit.jupiter + junit-jupiter-engine + test + + + + \ No newline at end of file diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/App.java b/bytecode/src/main/java/com/iluwatar/bytecode/App.java new file mode 100644 index 000000000..90b97bd33 --- /dev/null +++ b/bytecode/src/main/java/com/iluwatar/bytecode/App.java @@ -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 can’t 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()); + } +} diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java new file mode 100644 index 000000000..2ceb66e3b --- /dev/null +++ b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java @@ -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"); + } +} diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java new file mode 100644 index 000000000..aedafe514 --- /dev/null +++ b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java @@ -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 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 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; + } +} diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java new file mode 100644 index 000000000..ca47fd28f --- /dev/null +++ b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java @@ -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; + } +} diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java new file mode 100644 index 000000000..202784d5a --- /dev/null +++ b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java @@ -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; + } + } + + +} diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java new file mode 100644 index 000000000..014ec7875 --- /dev/null +++ b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java @@ -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); + } +} diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java new file mode 100644 index 000000000..e4379c9ed --- /dev/null +++ b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java @@ -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)); + } +} diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java new file mode 100644 index 000000000..4743ac109 --- /dev/null +++ b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java @@ -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]); + } + +} diff --git a/pom.xml b/pom.xml index f305e97d9..c55ed6492 100644 --- a/pom.xml +++ b/pom.xml @@ -171,6 +171,7 @@ priority-queue commander typeobjectpattern + bytecode