From ac335e7c423d067effae82cc80db518f896271b9 Mon Sep 17 00:00:00 2001
From: Sam Moore <matches@ucc.asn.au>
Date: Sat, 28 Apr 2012 14:27:01 +0800
Subject: [PATCH] [PATCH] Remove arguments, patch vixen, add hunter AI

The arguments stuff has been causing errors for everyone.
No one uses arguments for their AI, so I removed it.

sulix now beats vixen, so I have patched it to not make illegal moves.

hunter uses the same sort of idea as vixen, but seems to do slightly better.

Enjoy
[SZM]
---
 agents/asmodeus/path.py       |   2 +-
 agents/hunter/basic_python.py | 363 ++++++++++++++++++++++++++++++++++
 agents/hunter/default.scores  |   1 +
 agents/hunter/hunter.path.py  | 320 ++++++++++++++++++++++++++++++
 agents/hunter/hunter.py       | 286 +++++++++++++++++++++++++++
 agents/hunter/path.py         |   1 +
 agents/hunter/path.pyc        | Bin 0 -> 2099 bytes
 agents/vixen/vixen.py         |  15 +-
 judge/manager/program.cpp     |   6 +-
 9 files changed, 986 insertions(+), 8 deletions(-)
 create mode 100755 agents/hunter/basic_python.py
 create mode 100644 agents/hunter/default.scores
 create mode 100755 agents/hunter/hunter.path.py
 create mode 100755 agents/hunter/hunter.py
 create mode 120000 agents/hunter/path.py
 create mode 100644 agents/hunter/path.pyc

diff --git a/agents/asmodeus/path.py b/agents/asmodeus/path.py
index 3f08979..3a4527e 100644
--- a/agents/asmodeus/path.py
+++ b/agents/asmodeus/path.py
@@ -32,7 +32,7 @@ class PathFinder:
 		up = (start[0], start[1]-1)
 		down = (start[0], start[1]+1)
 		choices = [left, right, up, down]
-		choices.sort(key = lambda e : random.randint(0,5))
+		choices.sort(key = lambda e : (e[0] - end[0])**2.0 + (e[1] - end[1])**2.0 )
 		options = []
 		for point in choices:
 			option = [point, self.pathFind(point,end,board)]
diff --git a/agents/hunter/basic_python.py b/agents/hunter/basic_python.py
new file mode 100755
index 0000000..b94b857
--- /dev/null
+++ b/agents/hunter/basic_python.py
@@ -0,0 +1,363 @@
+#!/usr/bin/python -u
+
+#NOTE: The -u option is required for unbuffered stdin/stdout.
+#	If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out.
+
+"""
+ basic_python.py - A sample Stratego AI for the UCC Programming Competition 2012
+
+ Written in python, the slithery language 
+ Simply makes random moves, as long as possible
+
+ author Sam Moore (matches) [SZM]
+ website http://matches.ucc.asn.au/stratego
+ email progcomp@ucc.asn.au or matches@ucc.asn.au
+ git git.ucc.asn.au/progcomp2012.git
+"""
+
+import sys
+import random
+
+ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+']
+
+def is_integer(s):
+	""" Using exceptions for this feels... wrong..."""
+	try:
+		int(s)
+		return True
+	except ValueError:
+		return False
+
+def move(x, y, direction, multiplier):
+	""" Moves point (x,y) in direction, returns a pair """
+	if direction == "UP":
+		return (x,y-multiplier)
+	elif direction == "DOWN":
+		return (x,y+multiplier)
+	elif direction == "LEFT":
+		return (x-multiplier, y)
+	elif direction == "RIGHT":
+		return (x+multiplier, y)
+	return (x,y)
+
+
+
+def oppositeColour(colour):
+	""" Returns the opposite colour to that given """
+	if colour == "RED":
+		return "BLUE"
+	elif colour == "BLUE":
+		return "RED"
+	else:
+		return "NONE"
+
+class Piece:
+	""" Class representing a piece 
+		Pieces have colour, rank and co-ordinates	
+	"""
+	def __init__(self, colour, rank, x, y):
+		self.colour = colour
+		self.rank = rank
+		self.x = x
+		self.y = y
+		self.lastMoved = -1
+		self.beenRevealed = False
+		self.positions = [(x, y)]
+
+	def copy(self):
+		p = Piece(self.colour, self.rank, self.x, self.y)
+		p.lastMoved = self.lastMoved
+		p.beenRevealed = self.beenRevealed
+		p.positions = []
+		for pos in self.positions:
+			p.positions.append((pos[0], pos[1]))
+		return p
+
+	def mobile(self):
+		return self.rank != 'F' and self.rank != 'B' and self.rank != '?' and self.rank != '+'
+
+	def valuedRank(self):
+		if ranks.count(self.rank) > 0:
+			return len(ranks) - 2 - ranks.index(self.rank)
+		else:
+			return 0
+	
+
+def valuedRank(rank):
+	if ranks.count(rank) > 0 and rank != '?':
+		return len(ranks) - 2 - ranks.index(rank)
+	else:
+		return 0
+
+
+
+class BasicAI:
+	"""
+		BasicAI class to play a game of stratego
+ 		Implements the protocol correctly. Stores the state of the board in self.board
+		Only makes random moves.
+		Override method "MakeMove" for more complex moves
+	"""
+	def __init__(self):	
+		""" Constructs the BasicAI agent, and starts it playing the game """
+		#sys.stderr.write("BasicAI __init__ here...\n");
+		self.turn = 0
+		self.board = []
+		self.units = []
+		self.enemyUnits = []
+
+		self.totalAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
+		self.totalEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
+		self.hiddenEnemies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
+		self.hiddenAllies = {'B':6,'1':1,'2':1,'3':2,'4':3,'5':4,'6':4,'7':4,'8':5,'9':8,'s':1,'F':1}
+		self.lastMoved = None
+
+	def LegalPosition(self, x, y):
+		return x >= 0 and y >= 0 and x < len(self.board) and y < len(self.board[x])
+		
+
+	def Setup(self):
+		""" Implements Setup part of protocol. Always uses the same setup. Override to create custom setups """
+		#sys.stderr.write("BasicAI Setup here...\n");
+		setup = sys.stdin.readline().split(' ')
+		if len(setup) != 4:
+			sys.stderr.write("BasicAI setup fails, expected 4 tokens, got " + str(len(setup)) + " "+str(setup) + "\n")
+		self.colour = setup[0]
+		self.opponentName = setup[1]
+		self.width = int(setup[2])
+		self.height = int(setup[3])
+		for x in range(0, self.width):
+			self.board.append([])
+			for y in range(0, self.height):		
+				self.board[x].append(None)
+		if self.colour == "RED":
+			print "FB8sB479B8\nBB31555583\n6724898974\n967B669999"
+		elif self.colour == "BLUE":
+			print "967B669999\n6724898974\nBB31555583\nFB8sB479B8"
+		return True
+
+	def MoveCycle(self):
+		#sys.stderr.write("BasicAI MakeMove here...\n");
+		if self.InterpretResult() == False or self.ReadBoard() == False or self.MakeMove() == False:
+			return False
+		self.turn += 1
+		return self.InterpretResult()
+
+	def MakeMove(self):
+		""" Randomly moves any moveable piece, or prints "NO_MOVE" if there are none """
+		#TODO: Over-ride this function in base classes with more complex move behaviour
+
+		#sys.stderr.write("BasicAI MakeMove here...\n")
+		#self.debugPrintBoard()
+
+		if len(self.units) <= 0:
+			return False
+
+		index = random.randint(0, len(self.units)-1)
+		startIndex = index
+
+		directions = ("UP", "DOWN", "LEFT", "RIGHT")
+		while True:
+			piece = self.units[index]
+			if piece != None and piece.mobile():
+				dirIndex = random.randint(0, len(directions)-1)
+				startDirIndex = dirIndex
+				
+				while True:
+					#sys.stderr.write("Trying index " + str(dirIndex) + "\n")
+					p = move(piece.x, piece.y, directions[dirIndex],1)
+					if p[0] >= 0 and p[0] < self.width and p[1] >= 0 and p[1] < self.height:
+						target = self.board[p[0]][p[1]]
+						if target == None or (target.colour != piece.colour and target.colour != "NONE" and target.colour != "BOTH"):	
+							print str(piece.x) + " " + str(piece.y) + " "+directions[dirIndex]
+							return True
+					dirIndex = (dirIndex + 1) % len(directions)
+					if startDirIndex == dirIndex:
+						break
+
+			index = (index + 1) % len(self.units)
+			if startIndex == index:
+				print "NO_MOVE"
+				return True
+							
+			
+	def ReadBoard(self):
+		""" Reads in the board. 
+			On the very first turn, sets up the self.board structure
+			On subsequent turns, the board is simply read, but the self.board structure is not updated here.
+		"""
+		#sys.stderr.write("BasicAI ReadBoard here...\n");
+		for y in range(0,self.height):
+			row = sys.stdin.readline().strip()
+			if len(row) < self.width:
+				sys.stderr.write("Row has length " + str(len(row)) + " vs " + str(self.width) + "\n")
+				return False
+			for x in range(0,self.width):
+				if self.turn == 0:
+					if row[x] == '.':
+						pass
+					elif row[x] == '#':
+						self.board[x][y] = Piece(oppositeColour(self.colour), '?',x,y)
+						self.enemyUnits.append(self.board[x][y])
+					elif row[x] == '+':
+						self.board[x][y] = Piece("NONE", '+', x, y)
+					else:
+						self.board[x][y] = Piece(self.colour, row[x],x,y)
+						self.units.append(self.board[x][y])
+				else:
+					pass
+		return True
+		
+
+	def InterpretResult(self, string = None):
+		""" Interprets the result of a move, and updates the board. 
+			The very first move is ignored. 
+			On subsequent moves, the self.board structure is updated
+		"""
+		#sys.stderr.write("BasicAI InterpretResult here...\n")
+		if string == None:
+			string = sys.stdin.readline()
+
+		result = string.split(' ')
+
+		#sys.stderr.write("	Read status line \"" + str(result) + "\"\n")
+		if self.turn == 0:
+			return True
+
+		if result[0].strip() == "QUIT": #Make sure we exit when the manager tells us to!
+			return False
+
+		if result[0].strip() == "NO_MOVE": #No move was made, don't need to update anything
+			return True
+
+		if len(result) < 4: #Should be at least 4 tokens (X Y DIRECTION OUTCOME) in any other case
+			return False
+
+		x = int(result[0].strip())
+		y = int(result[1].strip())
+
+
+		#sys.stderr.write("	Board position " + str(x) + " " + str(y) + " is OK!\n")		
+
+		direction = result[2].strip()
+
+		multiplier = 1
+		outcome = result[3].strip()
+		outIndex = 3
+		if is_integer(outcome):
+			multiplier = int(outcome)
+			outcome = result[4].strip()
+			outIndex = 4
+		
+		p = move(x,y,direction, multiplier)
+
+		#Determine attacking piece
+		attacker = self.board[x][y]
+		self.board[x][y] = None
+
+		if attacker == None:
+			return False
+
+		lastMoved = attacker
+
+		defender = self.board[p[0]][p[1]]
+
+		#Update attacker's position (Don't overwrite the board yet though)
+
+		attacker.x = p[0]
+		attacker.y = p[1]
+		attacker.positions.insert(0, (attacker.x, attacker.y))
+
+		
+		#Determine ranks of pieces if supplied
+		if len(result) >= outIndex + 3:
+			if defender == None:
+				return False
+			attacker.rank = result[outIndex+1].strip()
+			if attacker.beenRevealed == False:
+				if attacker.colour == self.colour:
+					self.hiddenAllies[attacker.rank] -= 1
+				elif attacker.colour == oppositeColour(self.colour):
+					self.hiddenEnemies[attacker.rank] -= 1
+			attacker.beenRevealed = True
+			defender.rank = result[outIndex+2].strip()
+			if defender.beenRevealed == False:
+				if defender.colour == self.colour:
+					self.hiddenAllies[defender.rank] -= 1
+				elif defender.colour == oppositeColour(self.colour):
+					self.hiddenEnemies[defender.rank] -= 1
+
+			defender.beenRevealed = True
+
+			
+		
+		if outcome == "OK":
+			self.board[p[0]][p[1]] = attacker
+			
+		elif outcome == "KILLS":
+			self.board[p[0]][p[1]] = attacker
+
+			if defender.colour == self.colour:
+				self.totalAllies[defender.rank] -= 1
+				self.units.remove(defender)
+			elif defender.colour == oppositeColour(self.colour):
+				self.totalEnemies[defender.rank] -= 1
+				self.enemyUnits.remove(defender)
+	
+		elif outcome == "DIES":
+			if attacker.colour == self.colour:
+				self.totalAllies[attacker.rank] -= 1
+				self.units.remove(attacker)
+			elif attacker.colour == oppositeColour(self.colour):
+				self.totalEnemies[attacker.rank] -= 1
+				self.enemyUnits.remove(attacker)
+
+		elif outcome == "BOTHDIE":
+			self.board[p[0]][p[1]] = None
+
+			if defender.colour == self.colour:
+				self.totalAllies[defender.rank] -= 1
+				self.units.remove(defender)
+			elif defender.colour == oppositeColour(self.colour):
+				self.totalEnemies[defender.rank] -= 1
+				self.enemyUnits.remove(defender)
+
+			if attacker.colour == self.colour:
+				self.totalAllies[attacker.rank] -= 1
+				self.units.remove(attacker)
+			elif attacker.colour == oppositeColour(self.colour):
+				self.totalEnemies[attacker.rank] -= 1
+				self.enemyUnits.remove(attacker)
+
+		elif outcome == "FLAG":
+			#sys.stderr.write("	Game over!\n")
+			return False
+		elif outcome == "ILLEGAL":
+			#sys.stderr.write("	Illegal move!\n")
+			return False
+		else:
+			#sys.stderr.write("	Don't understand outcome \"" + outcome + "\"!\n");
+			return False
+
+		#sys.stderr.write("	Completed interpreting move!\n");		
+		return True
+
+	def debugPrintBoard(self):
+		""" For debug purposes only. Prints the board to stderr.
+			Does not indicate difference between allied and enemy pieces
+			Unknown (enemy) pieces are shown as '?'
+ 		"""
+		for y in range(0, self.height):
+			for x in range(0, self.width):
+				if self.board[x][y] == None:
+					sys.stderr.write(".");
+				else:
+					sys.stderr.write(str(self.board[x][y].rank));
+			sys.stderr.write("\n")
+
+if __name__ == "__main__":
+	basicAI = BasicAI()
+	if basicAI.Setup():
+		while basicAI.MoveCycle():
+			pass
+
diff --git a/agents/hunter/default.scores b/agents/hunter/default.scores
new file mode 100644
index 0000000..a3cc91e
--- /dev/null
+++ b/agents/hunter/default.scores
@@ -0,0 +1 @@
+9 10 9 8 7 6 5 4 9 2 5 9
diff --git a/agents/hunter/hunter.path.py b/agents/hunter/hunter.path.py
new file mode 100755
index 0000000..0a488f1
--- /dev/null
+++ b/agents/hunter/hunter.path.py
@@ -0,0 +1,320 @@
+#!/usr/bin/python -u
+
+#NOTE: The -u option is required for unbuffered stdin/stdout.
+#	If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out.
+
+'''
+ khaos.py - A sample Stratego AI for the UCC Programming Competition 2012
+
+ The name describes the state of this file :S
+
+ Written in python, the slithery language 
+
+ author Sam Moore (matches) [SZM]
+ website http://matches.ucc.asn.au/stratego
+ email progcomp@ucc.asn.au or matches@ucc.asn.au
+ git git.ucc.asn.au/progcomp2012.git
+'''
+
+import os
+
+from basic_python import *
+from path import *
+
+def OppositeDirection(direction):
+	if direction == "UP":
+		return "DOWN"
+	elif direction == "DOWN":
+		return "UP"
+	elif direction == "LEFT":
+		return "RIGHT"
+	elif direction == "RIGHT":
+		return "LEFT"
+	else:
+		assert(False)
+	return "ERROR"
+
+class Hunter(BasicAI):
+	" Python based AI of DEATH "
+	def __init__(self, scoresFilename=None):
+		if scoresFilename == None:
+			scoresFilename = "default.scores"
+		BasicAI.__init__(self)
+		
+		scoresFile = open(scoresFilename, "r")
+		self.scoreTable = []
+		for i in scoresFile.readline().strip().split(' '):
+			self.scoreTable.append(float(i))
+		scoresFile.close()
+
+		self.maxdepth = 1
+		self.recursiveConsider = {"allies" : 2, "enemies" : 2}
+		self.paths = {}
+		
+
+	def PositionLegal(self, x, y, unit = None):
+		if x >= 0 and x < len(self.board) and y >= 0 and y < len(self.board[x]):
+			if unit == None:
+				return True
+			else:
+				return self.board[x][y] == None or self.board[x][y].colour == oppositeColour(unit.colour)
+		else:
+			return False
+
+	def BestMove(self, maxdepth = 1):
+
+		moveList = []
+
+		
+		if maxdepth < self.maxdepth:
+			#sys.stderr.write("Recurse!\n")
+			considerAllies = self.recursiveConsider["allies"]
+			considerEnemies = self.recursiveConsider["enemies"]
+		else:
+			considerAllies = len(self.units)+1
+			considerEnemies = len(self.enemyUnits)+1
+
+		for enemy in self.enemyUnits[0:considerEnemies]:
+			for ally in self.units[0:considerAllies]:
+				moveList.append(self.DesiredMove(ally, enemy))
+
+		for desiredMove in moveList:
+			if desiredMove[0] == "NO_MOVE" or desiredMove[2] == None:
+				desiredMove[1] = -2.0
+
+		
+			
+
+		if maxdepth > 1:
+			for desiredMove in moveList:
+				if desiredMove[2] == None or desiredMove[1] < 0.0:
+					continue
+				p = move(desiredMove[3].x, desiredMove[3].y, desiredMove[2][0], 1)
+				if self.board[p[0]][p[1]] == None:
+					x = desiredMove[3].x
+					y = desiredMove[3].y
+					result = desiredMove[0] + " OK"
+					self.InterpretResult(result)
+					bestRecurse = self.BestMove(maxdepth-1)
+					if bestRecurse != None:
+						desiredMove[1] += bestRecurse[1]# / float(max(1.0, maxdepth))
+					self.board[desiredMove[3].x][desiredMove[3].y] = None
+					self.board[x][y] = desiredMove[3]
+					desiredMove[3].x = x
+					desiredMove[3].y = y
+
+		
+
+		
+		if len(moveList) <= 0:
+			return None
+		moveList.sort(key = lambda e : e[1], reverse = True)			
+		return moveList[0]
+				
+
+	def GetPath(self, ally, enemy):
+		#Attempts to do the minimum required work to reconstruct a path
+		return PathFinder().pathFind((ally.x, ally.y), (enemy.x, enemy.y), self.board)
+		if (ally in self.paths.keys()) == False: 
+			self.paths.update({ally : {}})	
+			#sys.stderr.write("Update keys are " + str(self.paths.keys()) + "\n")	
+		#sys.stderr.write("Keys are " + str(self.paths.keys()) + "\n")	
+
+		if (enemy in self.paths[ally].keys()) == False: #No path exists; compute a new one
+			path = PathFinder().pathFind((ally.x, ally.y), (enemy.x, enemy.y), self.board)				
+			if path != False:
+				self.paths[ally].update({enemy : [path, (ally.x, ally.y), (enemy.x, enemy.y)]})
+			return path
+
+		oldPath = self.paths[ally][enemy]
+		if oldPath[1][0] != ally.x or oldPath[1][1] != ally.y or oldPath[2][0] != enemy.x or oldPath[2][1] != enemy.y:
+			#The pieces involved have moved. Recompute the path 
+			path = PathFinder().pathFind((ally.x, ally.y), (enemy.x, enemy.y), self.board)
+			if path != False:
+				self.paths[ally][enemy] = [path, (ally.x, ally.y), (enemy.x, enemy.y)]
+			return path
+
+		if len(oldPath[0]) > 1:
+			#The pieces involved haven't moved, check to see if the path is blocked
+			p = move(ally.x, ally.y, oldPath[0][0], 1) #Look forward one move
+			if self.PositionLegal(p[0], p[1]) and self.board[p[0]][p[1]] != None: #If the position is blocked...
+				path = PathFinder().pathFind((ally.x, ally.y), (enemy.x, enemy.y), self.board)  #Compute new path
+				if path != False:
+					self.paths[ally][enemy] = [path, (ally.x, ally.y), (enemy.x, enemy.y)]
+				return path
+		return False
+		
+	def DesiredMove(self, ally, enemy):
+		""" Determine desired move of allied piece, towards or away from enemy, with score value """
+		scaleFactor = 1.0
+		if ally.rank == 'F' or ally.rank == 'B':
+			return ["NO_MOVE", 0, None, ally, enemy]
+		
+		actionScores = {"ATTACK" : 0, "RETREAT" : 0}
+		if enemy.rank == '?':
+			for i in range(0, len(ranks)):
+				prob = self.rankProbability(enemy, ranks[i])
+				if prob > 0:
+					desiredAction = self.DesiredAction(ally, ranks[i])
+					actionScores[desiredAction[0]] += prob* (desiredAction[1] / 2.0)
+			if len(enemy.positions) <= 1 and ally.rank != '8':
+				scaleFactor *= (1.0 - float(valuedRank(ally.rank)) / float(valuedRank('1')))**2.0
+			elif len(enemy.positions) > 1 and ally.rank == '8':
+				scaleFactor *= 0.05
+			#elif len(enemy.positions) > 1:
+			#	scaleFactor *= (1.0 - float(valuedRank(ally.rank)) / float(valuedRank('1')))**0.25
+			#	scaleFactor = max(0.05, scaleFactor)
+		else:
+			desiredAction = self.DesiredAction(ally, enemy.rank)
+			actionScores[desiredAction[0]] += desiredAction[1]
+		
+
+		desiredAction = sorted(actionScores.items(), key = lambda e : e[1], reverse = True)[0]
+		direction = None
+		path = self.GetPath(ally, enemy)
+
+		
+		if path != False and len(path) > 0:
+			if desiredAction[0] == "RETREAT":
+				#sys.stderr.write("Recommend retreat! "+ally.rank + " from " + enemy.rank+"\n")
+				direction = OppositeDirection(path[0])
+				p = move(ally.x, ally.y, direction, 1)
+				if self.PositionLegal(p[0], p[1], ally) == False:
+					path = None
+				scaleFactor = 0.05 * scaleFactor
+			else:
+				direction = path[0]
+			if desiredAction[1] > 0.0 and path != None:
+				scaleFactor = scaleFactor / float(len(path))
+			return [str(ally.x) + " " + str(ally.y) + " " + direction, desiredAction[1] * scaleFactor, path, ally, enemy]
+
+		#directions = {"RIGHT" : enemy.x - ally.x, "LEFT" : ally.x - enemy.x, "DOWN" : enemy.y - ally.y, "UP" : ally.y - enemy.y}
+		#if desiredAction[0] == "RETREAT":
+		#	for key in directions.keys():
+		#		directions[key] = -directions[key]
+
+		#while direction == None:
+		#	d = sorted(directions.items(), key = lambda e : e[1], reverse = True)
+		#	p = move(ally.x, ally.y, d[0][0], 1)
+		#	if self.PositionLegal(p[0], p[1]) and (self.board[p[0]][p[1]] == None or self.board[p[0]][p[1]] == enemy):
+		#		direction = d[0][0]
+		#		scaleFactor *= (1.0 - float(max(d[0][1], 0.0)) / 10.0)**2.0
+		#	else:
+		#		del directions[d[0][0]]
+		#		if len(directions.keys()) <= 0:
+		#			break
+
+		if abs(enemy.x - ally.x) >= abs(enemy.y - ally.y):
+			if enemy.x > ally.x:
+				direction = "RIGHT"
+			elif enemy.x < ally.x:
+				direction = "LEFT"		
+		else:
+			if enemy.y > ally.y:
+				direction = "DOWN"
+			elif enemy.y < ally.y:
+				direction = "UP"
+		if direction == None:
+			return ["NO_MOVE", 0, [], ally, enemy]
+		return [str(ally.x) + " " + str(ally.y) + " " + direction, desiredAction[1], None, ally, enemy]			
+			
+
+	def DesiredAction(self, ally, enemyRank):
+		if enemyRank == 'F':
+			return ["ATTACK", 1.0]
+		if ally.rank == '8' and enemyRank == 'B':
+			return ["ATTACK", 0.9]
+		if ally.rank == '1' and enemyRank == 's':
+			return ["RETREAT", 0.9]
+		if ally.rank == 's' and enemyRank == '1':
+			return ["ATTACK", 0.6]
+		if enemyRank == 'B':
+			return ["RETREAT", 0.0]
+		if ally.rank == enemyRank:
+			return ["ATTACK", 0.1]
+		if valuedRank(ally.rank) > valuedRank(enemyRank):
+			return ["ATTACK", float(self.scoreTable[ranks.index(enemyRank)]) * (0.1 + 1.0/float(self.scoreTable[ranks.index(ally.rank)]))]
+		else:
+			return ["RETREAT", float(self.scoreTable[ranks.index(ally.rank)]) / 10.0]
+		
+
+	def MakeMove(self):
+		if len(self.units) < 20:
+			self.maxdepth = 1
+		bestMove = self.BestMove(self.maxdepth)
+
+
+		if bestMove == None:
+			#sys.stderr.write("Khaos makes random move!\n")
+			return BasicAI.MakeMove(self)
+		
+		#sys.stderr.write("Board state before move: \n")
+		#self.debugPrintBoard()
+		
+		#sys.stderr.write("Best move is \"" + bestMove[0] + "\" with score " +  str(bestMove[1]) + " as part of path " +str(bestMove[2]) + " ...\n")
+		#sys.stderr.write("	 Ally with rank " + bestMove[3].rank + " is targeting unit at " + str((bestMove[4].x, bestMove[4].y)) + " rank " + bestMove[4].rank + "\n")
+		
+		sys.stdout.write(bestMove[0] + "\n")
+		#self.paths[bestMove[3]][bestMove[4]].pop(0)
+
+		return True
+
+
+
+	def rankProbability(self, target, targetRank):
+
+		if targetRank == '+' or targetRank == '?':
+			return 0.0
+		if target.rank == targetRank:
+			return 1.0
+		elif target.rank != '?':
+			return 0.0
+
+		total = 0.0
+		for rank in ranks:
+			if rank == '+' or rank == '?':
+				continue
+			elif rank == 'F' or rank == 'B':
+				if target.lastMoved < 0:
+					total += self.hiddenEnemies[rank]
+			else:
+				total += self.hiddenEnemies[rank]
+
+		if total == 0.0:
+			return 0.0
+		return float(float(self.hiddenEnemies[targetRank]) / float(total))
+
+	def InterpretResult(self, string=None):
+		if BasicAI.InterpretResult(self, string) == False:
+			return False
+
+
+		if self.maxdepth > 1:
+			if self.lastMoved != None and self.lastMoved.colour == self.colour and self.lastMoved.alive == False:
+				self.units.sort(key = lambda e : valuedRank(e.rank), reverse = True)
+			elif self.lastMoved != None and self.lastMoved.colour == oppositeColour(self.colour) and self.lastMoved.alive == True:
+				oldRank = self.lastMoved.rank
+				self.lastMoved.rank = '1'
+				self.enemyUnits.sort(key = lambda e : valuedRank(e.rank), reverse = True)
+				self.lastMoved.rank = oldRank
+			
+		
+		return True			
+				
+		
+if __name__ == "__main__":
+	if len(sys.argv) > 1:
+		hunter = Hunter(sys.argv[1])
+	else:
+		string = ""
+		path = sys.argv[0].split('/')
+		for i in range(0, len(path)-1):
+			string += path[i] + "/"
+		string += "default.scores"
+		
+		
+		hunter = Hunter(string)
+	if hunter.Setup():
+		while hunter.MoveCycle():
+			pass
+
diff --git a/agents/hunter/hunter.py b/agents/hunter/hunter.py
new file mode 100755
index 0000000..2f591bf
--- /dev/null
+++ b/agents/hunter/hunter.py
@@ -0,0 +1,286 @@
+#!/usr/bin/python -u
+
+#NOTE: The -u option is required for unbuffered stdin/stdout.
+#	If stdin/stdout are buffered, the manager program will not recieve any messages and assume that the agent has timed out.
+
+'''
+ khaos.py - A sample Stratego AI for the UCC Programming Competition 2012
+
+ The name describes the state of this file :S
+
+ Written in python, the slithery language 
+
+ author Sam Moore (matches) [SZM]
+ website http://matches.ucc.asn.au/stratego
+ email progcomp@ucc.asn.au or matches@ucc.asn.au
+ git git.ucc.asn.au/progcomp2012.git
+'''
+
+import os
+
+from basic_python import *
+from path import *
+
+def OppositeDirection(direction):
+	if direction == "UP":
+		return "DOWN"
+	elif direction == "DOWN":
+		return "UP"
+	elif direction == "LEFT":
+		return "RIGHT"
+	elif direction == "RIGHT":
+		return "LEFT"
+	else:
+		assert(False)
+	return "ERROR"
+
+class Hunter(BasicAI):
+	" Python based AI of DEATH "
+	def __init__(self, scoresFilename=None):
+		if scoresFilename == None:
+			scoresFilename = "default.scores"
+		BasicAI.__init__(self)
+		
+		scoresFile = open(scoresFilename, "r")
+		self.scoreTable = []
+		for i in scoresFile.readline().strip().split(' '):
+			self.scoreTable.append(float(i))
+		scoresFile.close()
+
+		self.maxdepth = 1
+		self.recursiveConsider = {"allies" : 5, "enemies" : 5}
+
+		
+
+	def PositionLegal(self, x, y, unit = None):
+		if x >= 0 and x < len(self.board) and y >= 0 and y < len(self.board[x]):
+			if unit == None:
+				return True
+			else:
+				return self.board[x][y] == None or self.board[x][y].colour == oppositeColour(unit.colour)
+		else:
+			return False
+
+	def BestMove(self, maxdepth = 1):
+
+		moveList = []
+
+		
+		if maxdepth < self.maxdepth:
+			#sys.stderr.write("Recurse!\n")
+			considerAllies = self.recursiveConsider["allies"]
+			considerEnemies = self.recursiveConsider["enemies"]
+		else:
+			considerAllies = len(self.units)+1
+			considerEnemies = len(self.enemyUnits)+1
+
+		for enemy in self.enemyUnits[0:considerEnemies]:
+			for ally in self.units[0:considerAllies]:
+				moveList.append(self.DesiredMove(ally, enemy))
+
+		for desiredMove in moveList:
+			if desiredMove[0] == "NO_MOVE" or desiredMove[2] == None:
+				desiredMove[1] = -2.0
+
+		
+			
+
+		if maxdepth > 1:
+			for desiredMove in moveList:
+				if desiredMove[2] == None or desiredMove[1] < 0.0:
+					continue
+				p = move(desiredMove[3].x, desiredMove[3].y, desiredMove[2][0], 1)
+				if self.board[p[0]][p[1]] == None:
+					x = desiredMove[3].x
+					y = desiredMove[3].y
+					result = desiredMove[0] + " OK"
+					self.InterpretResult(result)
+					bestRecurse = self.BestMove(maxdepth-1)
+					if bestRecurse != None:
+						desiredMove[1] += bestRecurse[1]# / float(max(1.0, maxdepth))
+					self.board[desiredMove[3].x][desiredMove[3].y] = None
+					self.board[x][y] = desiredMove[3]
+					desiredMove[3].x = x
+					desiredMove[3].y = y
+
+		for desiredMove in moveList:
+			if desiredMove[1] > 0.0:
+				desiredMove[1] = desiredMove[1] / float(len(desiredMove[2]))
+		
+		if len(moveList) <= 0:
+			return None
+		moveList.sort(key = lambda e : e[1], reverse = True)			
+		return moveList[0]
+				
+						
+	def DesiredMove(self, ally, enemy):
+		""" Determine desired move of allied piece, towards or away from enemy, with score value """
+		scaleFactor = 1.0
+		if ally.rank == 'F' or ally.rank == 'B':
+			return ["NO_MOVE", 0, None, ally, enemy]
+		
+		actionScores = {"ATTACK" : 0, "RETREAT" : 0}
+		if enemy.rank == '?':
+			for i in range(0, len(ranks)):
+				prob = self.rankProbability(enemy, ranks[i])
+				if prob > 0:
+					desiredAction = self.DesiredAction(ally, ranks[i])
+					actionScores[desiredAction[0]] += prob* (desiredAction[1] / 2.0)
+			if len(enemy.positions) <= 1 and ally.rank != '8':
+				scaleFactor *= (1.0 - float(valuedRank(ally.rank)) / float(valuedRank('1')))**2.0
+			elif len(enemy.positions) > 1 and ally.rank == '8':
+				scaleFactor *= 0.05
+			#elif len(enemy.positions) > 1:
+			#	scaleFactor *= (1.0 - float(valuedRank(ally.rank)) / float(valuedRank('1')))**0.25
+			#	scaleFactor = max(0.05, scaleFactor)
+		else:
+			desiredAction = self.DesiredAction(ally, enemy.rank)
+			actionScores[desiredAction[0]] += desiredAction[1]
+		
+
+		desiredAction = sorted(actionScores.items(), key = lambda e : e[1], reverse = True)[0]
+		direction = None
+		#path = PathFinder().pathFind((ally.x, ally.y), (enemy.x, enemy.y), self.board)
+		
+		#if path != False and len(path) > 0:
+		#	if desiredAction[0] == "RETREAT":
+				#sys.stderr.write("Recommend retreat! "+ally.rank + " from " + enemy.rank+"\n")
+		#		direction = OppositeDirection(path[0])
+		#		p = move(ally.x, ally.y, direction, 1)
+		#		if self.PositionLegal(p[0], p[1], ally) == False:
+		#			path = None
+		#		scaleFactor = 0.05 * scaleFactor
+		#	else:
+		#		direction = path[0]
+
+		#	return [str(ally.x) + " " + str(ally.y) + " " + direction, desiredAction[1] * scaleFactor, path, ally, enemy]
+
+		directions = {"RIGHT" : enemy.x - ally.x, "LEFT" : ally.x - enemy.x, "DOWN" : enemy.y - ally.y, "UP" : ally.y - enemy.y}
+		if desiredAction[0] == "RETREAT":
+			for key in directions.keys():
+				directions[key] = -directions[key]
+
+		while direction == None:
+			d = sorted(directions.items(), key = lambda e : e[1], reverse = True)
+			p = move(ally.x, ally.y, d[0][0], 1)
+			if self.PositionLegal(p[0], p[1]) and (self.board[p[0]][p[1]] == None or self.board[p[0]][p[1]] == enemy):
+				direction = d[0][0]
+				scaleFactor *= (1.0 - float(max(d[0][1], 0.0)) / 10.0)**2.0
+			else:
+				del directions[d[0][0]]
+				if len(directions.keys()) <= 0:
+					break
+
+		#if abs(enemy.x - ally.x) >= abs(enemy.y - ally.y):
+		#	if enemy.x > ally.x:
+		#		direction = "RIGHT"
+		#	elif enemy.x < ally.x:
+		#
+		#else:
+		#	if enemy.y > ally.y:
+		#		direction = "DOWN"
+		#	elif enemy.y < ally.y:
+		#		direction = "UP"
+		if direction == None:
+			return ["NO_MOVE", 0, [], ally, enemy]
+		return [str(ally.x) + " " + str(ally.y) + " " + direction, desiredAction[1], [direction], ally, enemy]			
+			
+
+	def DesiredAction(self, ally, enemyRank):
+		if enemyRank == 'F':
+			return ["ATTACK", 1.0]
+		if ally.rank == '8' and enemyRank == 'B':
+			return ["ATTACK", 0.9]
+		if ally.rank == '1' and enemyRank == 's':
+			return ["RETREAT", 0.9]
+		if ally.rank == 's' and enemyRank == '1':
+			return ["ATTACK", 0.6]
+		if enemyRank == 'B':
+			return ["RETREAT", 0.0]
+		if ally.rank == enemyRank:
+			return ["ATTACK", 0.1]
+		if valuedRank(ally.rank) > valuedRank(enemyRank):
+			return ["ATTACK", float(self.scoreTable[ranks.index(enemyRank)]) * (0.1 + 1.0/float(self.scoreTable[ranks.index(ally.rank)]))]
+		else:
+			return ["RETREAT", float(self.scoreTable[ranks.index(ally.rank)]) / 10.0]
+		
+
+	def MakeMove(self):
+		if len(self.units) < 20:
+			self.maxdepth = 1
+		bestMove = self.BestMove(self.maxdepth)
+
+
+		if bestMove == None:
+			#sys.stderr.write("Khaos makes random move!\n")
+			return BasicAI.MakeMove(self)
+		
+		#sys.stderr.write("Board state before move: \n")
+		#self.debugPrintBoard()
+		
+		sys.stderr.write("Best move is \"" + bestMove[0] + "\" with score " +  str(bestMove[1]) + " as part of path " +str(bestMove[2]) + " ...\n")
+		sys.stderr.write("	 Ally with rank " + bestMove[3].rank + " is targeting unit at " + str((bestMove[4].x, bestMove[4].y)) + " rank " + bestMove[4].rank + "\n")
+		sys.stdout.write(bestMove[0] + "\n")
+
+
+		return True
+
+
+
+	def rankProbability(self, target, targetRank):
+
+		if targetRank == '+' or targetRank == '?':
+			return 0.0
+		if target.rank == targetRank:
+			return 1.0
+		elif target.rank != '?':
+			return 0.0
+
+		total = 0.0
+		for rank in ranks:
+			if rank == '+' or rank == '?':
+				continue
+			elif rank == 'F' or rank == 'B':
+				if target.lastMoved < 0:
+					total += self.hiddenEnemies[rank]
+			else:
+				total += self.hiddenEnemies[rank]
+
+		if total == 0.0:
+			return 0.0
+		return float(float(self.hiddenEnemies[targetRank]) / float(total))
+
+	def InterpretResult(self, string=None):
+		if BasicAI.InterpretResult(self, string) == False:
+			return False
+
+
+		if self.maxdepth > 1:
+			if self.lastMoved != None and self.lastMoved.colour == self.colour and self.lastMoved.alive == False:
+				self.units.sort(key = lambda e : valuedRank(e.rank), reverse = True)
+			elif self.lastMoved != None and self.lastMoved.colour == oppositeColour(self.colour) and self.lastMoved.alive == True:
+				oldRank = self.lastMoved.rank
+				self.lastMoved.rank = '1'
+				self.enemyUnits.sort(key = lambda e : valuedRank(e.rank), reverse = True)
+				self.lastMoved.rank = oldRank
+			
+		
+		return True			
+				
+		
+if __name__ == "__main__":
+	if len(sys.argv) > 1:
+		hunter = Hunter(sys.argv[1])
+	else:
+		string = ""
+		path = sys.argv[0].split('/')
+		for i in range(0, len(path)-1):
+			string += path[i] + "/"
+		string += "default.scores"
+		
+		
+		hunter = Hunter(string)
+	if hunter.Setup():
+		while hunter.MoveCycle():
+			pass
+
diff --git a/agents/hunter/path.py b/agents/hunter/path.py
new file mode 120000
index 0000000..1d82284
--- /dev/null
+++ b/agents/hunter/path.py
@@ -0,0 +1 @@
+../asmodeus/path.py
\ No newline at end of file
diff --git a/agents/hunter/path.pyc b/agents/hunter/path.pyc
new file mode 100644
index 0000000000000000000000000000000000000000..9a0db59306fc5e64589611be34615581f932ea4f
GIT binary patch
literal 2099
zcmb_dO^+Kz5Un12{IR=1B&%rGArcZ?tQ>GyBu-HjCCi2{Ing*MuoAK+Gqd(M_RQe!
z1y=H&76IZ6|B4Go{sw;l-mCV<=CG$_?C$BVu6p&Vy1M7De{O93{rMly5?MYW-jC31
z4_%7Ck=G)}>#>nzBWWOMUDBHTByu70%`xUi23;EG|KjPpEexN>ZhVv%X*OTO8;BpH
z**oYm00T@2;~_YxNi^sNSWc+S#>o>jdjs7_WFazCLW3@s``WuEhTrElcUkHMb(&<;
zlP=z(o&6Z2-ng7)JsZz@hh=g)%L-?E)w~=f<*Yim|KUL|9;s}6TDWZ9s}RHo)tPI-
zbU4h5+zp3xWeG2)9V8s=pix>wvwwo{0)ZLn{Hhu3OadwIh@mj>LcAyp#UP-8K}b3N
zhISWfI;rcb3%KgR05}n;o_ZjlyP7O&)SrY>-CyG`_1;|zyxtO}<MruU!MUj3H-6uP
zC8XYKhi}mBq#~2rB~qSpxTL%$8OZ+9;v|ss`U(aRdza*D#tbcGSl*|8W2L{5?@`{A
z1)yLBg{F+^a^8|fOY#QG@E4I2kgUtktVhF?p10-4FQ{?&?GriQk_G-C445s!rZ8<a
z83A}(7TX#hlELt9BKEfA@U+d$n(uxyY%eKPXKCom-WX=Cz1l9nnSQ(4y7wAgUAWD6
z6_f&IGH-D`ZOBC+rucoc#&m>N8#KK^@}@!KYJ-+HIC#^bd9?weAti*3z*ckzT^{B>
z>QgIJ1aXVJ&d!q60p$tUMYB9*NHZ!$D<7|YpzKn#se2Xe4Z1{8P;8ezk5K>1lj=_}
zr<os8IV(~+kn0}f2h(`=Dvck$Pb>ni1EH4zrEj5g>sU|+{;Kf=NlSAu%?f1~@#0fz
z*X0*AfpiZ|c{wKb%TJD8I68<vd-~}M6qv|Y&voVS*~@;n%@t)toutIArP@dF)Mil&
zGS%&uMdliq#Z`qos?3&ir}rA~I9}AMkQX+ayDo3Eh|Uzvx_Is)+UxXH8P7e&G&}MB
z=J{ysl-+5i=xO;wp_g(pF7qU_IxZ`hmxWc3s?5Qam|p4=$WYYLHFi0=1rP>cAEOg<
zF9?H95Sp*iJ_^G6+h%|HyJb42VVcq~@0gqBcCc%9p?llhHJ4h@<vuLG07X2@hC}_n
z4TrNbJ)L42v2fJurV6fI3Z%~L=$EEp+M&m$E3H0zW;J{BxJb)cMD_pAyGHxK$M=wB
Kpe%OGPVGNyH+VY$

literal 0
HcmV?d00001

diff --git a/agents/vixen/vixen.py b/agents/vixen/vixen.py
index e4dee3f..669df83 100755
--- a/agents/vixen/vixen.py
+++ b/agents/vixen/vixen.py
@@ -44,20 +44,25 @@ class Vixen(BasicAI):
 			if unit.mobile() == False:
 				continue
 
-			scores = {"LEFT":0, "RIGHT":0, "UP":0, "DOWN":0}
+			scores = {"LEFT":None, "RIGHT":None, "UP":None, "DOWN":None}
 			
-
 			for target in self.enemyUnits:
 				if target == unit:
 					continue
 				path = PathFinder().pathFind((unit.x, unit.y), (target.x, target.y), self.board)
 				if path == False or len(path) == 0:
 					continue
-				#moveList.append({"unit":unit, "direction":path[0], "score":self.CalculateScore(unit, target, path)})
+				if scores[path[0]] == None:
+					scores[path[0]] = 0 
+
 				scores[path[0]] += self.CalculateScore(unit, target, path)
 
-			bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
-			if bestScore[1] > -100.0:
+			for d in scores.keys():
+				if scores[d] == None:
+					del scores[d]
+
+			if len(scores.items()) > 0: 
+				bestScore = sorted(scores.items(), key = lambda e : e[1], reverse=True)[0]
 				moveList.append({"unit":unit, "direction":bestScore[0], "score":bestScore[1]})
 			
 			
diff --git a/judge/manager/program.cpp b/judge/manager/program.cpp
index 5ea1591..c936de3 100644
--- a/judge/manager/program.cpp
+++ b/judge/manager/program.cpp
@@ -32,7 +32,7 @@ Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0
 {
 	
 		
-	
+	/*
 	vector<char*> args;
 	if (executablePath[0] != '"')
 		args.push_back((char*)executablePath);
@@ -70,6 +70,7 @@ Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0
 		for (unsigned int i=0; i < args.size(); ++i)
 			arguments[i] = args[i];
 	}
+	*/
 	//See if file exists and is executable...
 	if (access(executablePath, X_OK) != 0)
 	{
@@ -100,7 +101,8 @@ Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0
 
 		if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file
 		{
-			execv(executablePath,arguments); ///Replace process with desired executable
+			execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
+			//execv(executablePath,arguments); ///Replace process with desired executable
 		}
 		perror("execv error:\n");
 		fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
-- 
GitLab