* Added bytecode pattern * Diagram changed and added licence information * Added bytecode module to main pom. * Fixed missing dependency error
This commit is contained in:
committed by
Ilkka Seppälä
parent
7f6067f19f
commit
318f811fea
79
bytecode/src/main/java/com/iluwatar/bytecode/App.java
Normal file
79
bytecode/src/main/java/com/iluwatar/bytecode/App.java
Normal 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 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());
|
||||
}
|
||||
}
|
@ -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");
|
||||
}
|
||||
}
|
142
bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
Normal file
142
bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
Normal 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;
|
||||
}
|
||||
}
|
83
bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
Normal file
83
bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user