Repository: iluwatar/java-design-patterns
Issue: Bytecode pattern
Labels: epic: pattern

http://gameprogrammingpatterns.com/bytecode.html

Issue comments:
Hey @iluwatar, I would love to work on this if no one else has claimed it yet.
Ok @abhishek7 
@abhishek7  are you still implementing this?
@iluwatar apologies for the delay, did not have a chance to make additional progress on it. Feel free to give it to someone else. If I do have time, I'll open up a PR and reference the issue accordingly.
The issue is free to take again
Hi @iluwatar, if nobody is working on this then I can try to implement this pattern.
Ok @pzawitowski 

Pull request review comments:

Patch that resolved the issue:
diff --git a/bytecode/README.md b/bytecode/README.md
new file mode 100644
index 000000000000..d2fc45e1e2d1
--- /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.ucls b/bytecode/etc/bytecode.ucls
new file mode 100644
index 000000000000..3ec390458956
--- /dev/null
+++ b/bytecode/etc/bytecode.ucls
@@ -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>
\ No newline at end of file
diff --git a/bytecode/pom.xml b/bytecode/pom.xml
new file mode 100644
index 000000000000..e9e6247f8eb2
--- /dev/null
+++ b/bytecode/pom.xml
@@ -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>
\ 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 000000000000..90b97bd33308
--- /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 000000000000..2ceb66e3bdc2
--- /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 000000000000..aedafe514dce
--- /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<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;
+  }
+}
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 000000000000..ca47fd28f873
--- /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 000000000000..202784d5a96c
--- /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/pom.xml b/pom.xml
index f305e97d983e..c55ed6492211 100644
--- a/pom.xml
+++ b/pom.xml
@@ -171,6 +171,7 @@
         <module>priority-queue</module>
 	    <module>commander</module>
 	    <module>typeobjectpattern</module>
+        <module>bytecode</module>
     </modules>
 
     <repositories>


Test patch:
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 000000000000..014ec787569c
--- /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 000000000000..e4379c9ede38
--- /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 000000000000..4743ac109e12
--- /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]);
+  }
+
+}