diff --git a/Malmo/samples/Python_examples/CMakeLists.txt b/Malmo/samples/Python_examples/CMakeLists.txt
index 516bb4809..f7b14af65 100755
--- a/Malmo/samples/Python_examples/CMakeLists.txt
+++ b/Malmo/samples/Python_examples/CMakeLists.txt
@@ -35,6 +35,7 @@ set( SOURCES
mission_quit_command_example.py
MultiMaze.py
mob_fun.py
+ moving_target_test.py
overclock_test.py
patchwork_quilt.py
quit_from_reaching_position_test.py
diff --git a/Malmo/samples/Python_examples/moving_target_test.py b/Malmo/samples/Python_examples/moving_target_test.py
new file mode 100755
index 000000000..0d3f872c6
--- /dev/null
+++ b/Malmo/samples/Python_examples/moving_target_test.py
@@ -0,0 +1,171 @@
+# ------------------------------------------------------------------------------------------------
+# Copyright (c) 2016 Microsoft Corporation
+#
+# 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.
+# ------------------------------------------------------------------------------------------------
+
+# Sample to demonstrate use of MovingTargetDecorator.
+# Creates two moving targets - one which moves as fast as possible, and one which is turn-based, and
+# will wait for the agent to take its turn.
+
+import MalmoPython
+import os
+import random
+import sys
+import time
+import json
+import random
+import errno
+
+def GetMissionXML(summary):
+ return '''
+
+
+ ''' + summary + '''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ random
+ turnbased
+
+
+
+
+
+
+
+
+
+ random
+ 1
+
+
+
+
+
+ 0.1
+ random
+ random
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+ Chevy
+
+
+
+
+
+
+
+
+
+
+
+
+
+ '''
+
+recordingsDirectory="MovingTargetRecordings"
+try:
+ os.makedirs(recordingsDirectory)
+except OSError as exception:
+ if exception.errno != errno.EEXIST: # ignore error if already existed
+ raise
+
+sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) # flush print output immediately
+
+validate = True
+my_client_pool = MalmoPython.ClientPool()
+my_client_pool.add(MalmoPython.ClientInfo("127.0.0.1", 10000))
+
+agent_host = MalmoPython.AgentHost()
+try:
+ agent_host.parse( sys.argv )
+except RuntimeError as e:
+ print 'ERROR:',e
+ print agent_host.getUsage()
+ exit(1)
+if agent_host.receivedArgument("help"):
+ print agent_host.getUsage()
+ exit(0)
+
+if agent_host.receivedArgument("test"):
+ num_reps = 1
+else:
+ num_reps = 30000
+
+for iRepeat in range(num_reps):
+ my_mission = MalmoPython.MissionSpec(GetMissionXML("Moving target #" + str(iRepeat)),validate)
+ my_mission_record = MalmoPython.MissionRecordSpec()
+ max_retries = 3
+ for retry in range(max_retries):
+ try:
+ # Attempt to start the mission:
+ agent_host.startMission( my_mission, my_client_pool, my_mission_record, 0, "movingTargetTestExperiment" )
+ break
+ except RuntimeError as e:
+ if retry == max_retries - 1:
+ print "Error starting mission",e
+ print "Is the game running?"
+ exit(1)
+ else:
+ time.sleep(2)
+
+ world_state = agent_host.getWorldState()
+ while not world_state.has_mission_begun:
+ time.sleep(0.1)
+ world_state = agent_host.getWorldState()
+
+ # main loop:
+ turn_key = ""
+ while world_state.is_mission_running:
+ world_state = agent_host.getWorldState()
+ if world_state.number_of_observations_since_last_state > 0:
+ msg = world_state.observations[-1].text
+ ob = json.loads(msg)
+ new_turn_key = ob.get(u'turn_key', "")
+ turn_index = ob.get(u'turn_number',0)
+ if len(new_turn_key) > 0 and new_turn_key != turn_key:
+ if agent_host.receivedArgument("test"):
+ nb = random.choice(["movenorth","movesouth","moveeast","movewest"])
+ else:
+ nb = raw_input('Enter command: ')
+ agent_host.sendCommand(nb, str(new_turn_key))
+ turn_key = new_turn_key
+
+ # mission has ended.
+ time.sleep(0.5) # Give the mod a little time to prepare for the next mission.
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
index 3fcd0a0a7..acb012e1e 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlerInterfaces/IWorldDecorator.java
@@ -19,8 +19,11 @@
package com.microsoft.Malmo.MissionHandlerInterfaces;
+import java.util.ArrayList;
import java.util.List;
+
import net.minecraft.world.World;
+
import com.microsoft.Malmo.Schemas.MissionInit;
/** Interface for objects which can determine the world structure for the Minecraft mission.
@@ -59,4 +62,17 @@ public DecoratorException(String message)
/** Called once after the mission ends - use for any necessary mission cleanup.
*/
public void cleanup();
+
+ /** Used by the turn scheduler - if decorator matches this string, it must acknowledge and take its turn.
+ * @param nextAgentName - string to match against
+ * @return true if matching
+ */
+ public boolean targetedUpdate(String nextAgentName);
+
+ /** Used by the turn scheduler - if the decorator wants to be part of the turn schedule, it must add a name
+ * and a requested slot (can be null) to these arrays.
+ * @param participants
+ * @param participantSlots
+ */
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots);
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AnimationDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AnimationDecoratorImplementation.java
index 1234e273e..d33cd69b7 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AnimationDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/AnimationDecoratorImplementation.java
@@ -19,6 +19,7 @@
package com.microsoft.Malmo.MissionHandlers;
+import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -182,4 +183,16 @@ public void prepare(MissionInit missionInit)
public void cleanup()
{
}
+
+ @Override
+ public boolean targetedUpdate(String nextAgentName)
+ {
+ return false; // Does nothing.
+ }
+
+ @Override
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots)
+ {
+ // Does nothing.
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/BuildBattleDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/BuildBattleDecoratorImplementation.java
index 236d6f461..72378f69a 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/BuildBattleDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/BuildBattleDecoratorImplementation.java
@@ -315,4 +315,17 @@ public void cleanup()
MinecraftForge.EVENT_BUS.unregister(this);
FMLCommonHandler.instance().bus().unregister(this);
}
+
+ @Override
+ public boolean targetedUpdate(String nextAgentName)
+ {
+ return false; // Does nothing.
+ }
+
+ @Override
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots)
+ {
+ // Does nothing.
+ }
+
}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
index 44c2a9571..1ff4e7cb6 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/ClassroomDecoratorImplementation.java
@@ -1529,4 +1529,16 @@ public void prepare(MissionInit missionInit)
public void cleanup()
{
}
+
+ @Override
+ public boolean targetedUpdate(String nextAgentName)
+ {
+ return false; // Does nothing.
+ }
+
+ @Override
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots)
+ {
+ // Does nothing.
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
index 9b08e517e..db3e7149c 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/DrawingDecoratorImplementation.java
@@ -19,6 +19,7 @@
package com.microsoft.Malmo.MissionHandlers;
+import java.util.ArrayList;
import java.util.List;
import net.minecraft.server.MinecraftServer;
@@ -82,4 +83,16 @@ public void prepare(MissionInit missionInit)
public void cleanup()
{
}
+
+ @Override
+ public boolean targetedUpdate(String nextAgentName)
+ {
+ return false; // Does nothing.
+ }
+
+ @Override
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots)
+ {
+ // Does nothing.
+ }
}
\ No newline at end of file
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
index 7b106e9c2..7fd43bfae 100755
--- a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MazeDecoratorImplementation.java
@@ -31,7 +31,6 @@
import net.minecraft.world.World;
import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
-import com.microsoft.Malmo.Schemas.AgentHandlers;
import com.microsoft.Malmo.Schemas.AgentQuitFromReachingPosition;
import com.microsoft.Malmo.Schemas.AgentSection;
import com.microsoft.Malmo.Schemas.BlockOrItemSpec;
@@ -795,4 +794,16 @@ public void prepare(MissionInit missionInit)
public void cleanup()
{
}
+
+ @Override
+ public boolean targetedUpdate(String nextAgentName)
+ {
+ return false; // Does nothing.
+ }
+
+ @Override
+ public void getTurnParticipants(ArrayList participants, ArrayList participantSlots)
+ {
+ // Does nothing.
+ }
}
diff --git a/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MovingTargetDecoratorImplementation.java b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MovingTargetDecoratorImplementation.java
new file mode 100755
index 000000000..7801e48dc
--- /dev/null
+++ b/Minecraft/src/main/java/com/microsoft/Malmo/MissionHandlers/MovingTargetDecoratorImplementation.java
@@ -0,0 +1,325 @@
+package com.microsoft.Malmo.MissionHandlers;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+
+import net.minecraft.block.properties.IProperty;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.BlockPos;
+import net.minecraft.world.World;
+
+import com.microsoft.Malmo.MalmoMod;
+import com.microsoft.Malmo.MalmoMod.MalmoMessageType;
+import com.microsoft.Malmo.MissionHandlerInterfaces.IWorldDecorator;
+import com.microsoft.Malmo.Schemas.BlockType;
+import com.microsoft.Malmo.Schemas.Colour;
+import com.microsoft.Malmo.Schemas.DrawBlock;
+import com.microsoft.Malmo.Schemas.DrawBlockBasedObjectType;
+import com.microsoft.Malmo.Schemas.MissionInit;
+import com.microsoft.Malmo.Schemas.MovingTargetDecorator;
+import com.microsoft.Malmo.Schemas.Pos;
+import com.microsoft.Malmo.Schemas.UnnamedGridDefinition;
+import com.microsoft.Malmo.Schemas.Variation;
+import com.microsoft.Malmo.Utils.BlockDrawingHelper;
+import com.microsoft.Malmo.Utils.BlockDrawingHelper.XMLBlockState;
+import com.microsoft.Malmo.Utils.MinecraftTypeHelper;
+
+public class MovingTargetDecoratorImplementation extends HandlerBase implements IWorldDecorator
+{
+ private Random rng;
+ private MovingTargetDecorator targetParams;
+ private UnnamedGridDefinition arenaBounds;
+ private XMLBlockState blockType;
+ private ArrayDeque path = new ArrayDeque();
+ private ArrayDeque originalPath = new ArrayDeque();
+ private BlockPos startPos;
+ private int timeSinceLastUpdate = 0;
+ private int speedInTicks = 10;
+ private int pathSize = 2;
+ private boolean mustWaitTurn = false;
+ private boolean isOurTurn = false;
+ private String guid = UUID.randomUUID().toString();
+
+ @Override
+ public boolean parseParameters(Object params)
+ {
+ if (params == null || !(params instanceof MovingTargetDecorator))
+ return false;
+ this.targetParams = (MovingTargetDecorator)params;
+ this.arenaBounds = this.targetParams.getArenaBounds();
+ DrawBlockBasedObjectType targetBlock = this.targetParams.getBlockType();
+ this.blockType = (targetBlock != null) ? new XMLBlockState(targetBlock.getType(), targetBlock.getColour(), targetBlock.getFace(), targetBlock.getVariant()) : null;
+ Pos pos = this.targetParams.getStartPos();
+ int xPos = pos.getX().intValue();
+ int yPos = pos.getY().intValue();
+ int zPos = pos.getZ().intValue();
+ // Check start pos lies within arena:
+ xPos = Math.min(this.arenaBounds.getMax().getX(), Math.max(this.arenaBounds.getMin().getX(), xPos));
+ yPos = Math.min(this.arenaBounds.getMax().getY(), Math.max(this.arenaBounds.getMin().getY(), yPos));
+ zPos = Math.min(this.arenaBounds.getMax().getZ(), Math.max(this.arenaBounds.getMin().getZ(), zPos));
+ this.startPos = new BlockPos(xPos, yPos, zPos);
+ if (this.targetParams.getUpdateSpeed() == null || this.targetParams.getUpdateSpeed().equals("turnbased"))
+ {
+ this.mustWaitTurn = true;
+ }
+ else
+ {
+ this.speedInTicks = Integer.parseInt(this.targetParams.getUpdateSpeed());
+ }
+ createRNG();
+ return true;
+ }
+
+ @Override
+ public void buildOnWorld(MissionInit missionInit) throws DecoratorException
+ {
+ this.path.add(this.startPos);
+ World world = MinecraftServer.getServer().getEntityWorld();
+ this.originalPath.add(world.getBlockState(this.startPos));
+ BlockDrawingHelper drawContext = new BlockDrawingHelper();
+ drawContext.beginDrawing(world);
+ drawContext.setBlockState(world, this.startPos, this.blockType);
+ drawContext.endDrawing(world);
+ }
+
+ @Override
+ public boolean getExtraAgentHandlers(List