[PATCH] Remove arguments, patch vixen, add hunter AI
authorSam Moore <[email protected]>
Sat, 28 Apr 2012 06:27:01 +0000 (14:27 +0800)
committerSam Moore <[email protected]>
Sat, 28 Apr 2012 06:27:01 +0000 (14:27 +0800)
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
agents/hunter/basic_python.py [new file with mode: 0755]
agents/hunter/default.scores [new file with mode: 0644]
agents/hunter/hunter.path.py [new file with mode: 0755]
agents/hunter/hunter.py [new file with mode: 0755]
agents/hunter/path.py [new symlink]
agents/hunter/path.pyc [new file with mode: 0644]
agents/vixen/vixen.py
judge/manager/program.cpp

index 3f08979..3a4527e 100644 (file)
@@ -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 (executable)
index 0000000..b94b857
--- /dev/null
@@ -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
+ 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 (file)
index 0000000..a3cc91e
--- /dev/null
@@ -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 (executable)
index 0000000..0a488f1
--- /dev/null
@@ -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
+ 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 (executable)
index 0000000..2f591bf
--- /dev/null
@@ -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
+ 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 (symlink)
index 0000000..1d82284
--- /dev/null
@@ -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 (file)
index 0000000..9a0db59
Binary files /dev/null and b/agents/hunter/path.pyc differ
index e4dee3f..669df83 100755 (executable)
@@ -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]})
                        
                        
index 5ea1591..c936de3 100644 (file)
@@ -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);

UCC git Repository :: git.ucc.asn.au