4 simulate.py - simulation script for the 2012 UCC Programming Competition
5 NOTE: This is not the manager program for a stratego game
6 It merely calls the manager program as appropriate, and records results
7 Plays exactly ONE round, but does not overwrite previously played rounds
8 eg: run once to generate round1.results, twice to generate round2.results etc
9 Also generates total.scores based on results from every round.
12 author Sam Moore (matches) [SZM]
13 website http://matches.ucc.asn.au/stratego
15 git git.ucc.asn.au/progcomp2012.git
22 #Global variables/arguments
24 baseDirectory = "../.." #Base directory for results, logs, agents
25 nGames = 2 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE. If nGames <= 1, then no games will be played (useful for dry run?)
28 if len(sys.argv) >= 2:
29 nRounds = int(sys.argv[1])
30 if len(sys.argv) >= 3:
31 nGames = int(sys.argv[2])
33 print "Warning: nGames should be even. "+str(nGames)+" specified, but only " + str(int(nGames/2) * 2)+" will be played!"
34 if len(sys.argv) >= 4:
35 baseDirectory = sys.argv[3]
36 if len(sys.argv) >= 6:
37 print "Useage: " +sys.argv[0] + " [nRounds=1] [nGames=10] [baseDirectory=\""+baseDirectory+"\"] [managerPath=baseDirectory+\"/judge/manager/stratego\"]"
40 resultsDirectory = baseDirectory+"/results/" #Where results will go (results are in the form of text files of agent names and scores)
41 logDirectory = baseDirectory+"/log/" #Where log files go (direct output of manager program)
42 agentsDirectory = baseDirectory+"/agents/" #Where agents are found (each agent has its own subdirectory within this directory)
43 managerPath = baseDirectory+"/judge/manager/stratego" #Path to the executable that plays the games
44 if len(sys.argv) >= 5:
45 managerPath = sys.argv[5]
48 #Score dictionary - Tuple is of the form: (end score, other score, other result) where end is the player on whose turn the result occurs, other is the other player, other result indicates what to record the outcome as for the other player.
49 scores = {"VICTORY":(3,1, "DEFEAT"), "DEFEAT":(1,3, "VICTORY"), "SURRENDER":(1,3, "VICTORY"), "DRAW":(2,2, "DRAW"), "DRAW_DEFAULT":(1,1, "DRAW_DEFAULT"), "ILLEGAL":(-1,2, "DEFAULT"), "DEFAULT":(2,-1, "ILLEGAL"), "BOTH_ILLEGAL":(-1,-1, "BOTH_ILLEGAL"), "INTERNAL_ERROR":(0,0, "INTERNAL_ERROR"), "BAD_SETUP":(0,0,"BAD_SETUP")}
52 #Verbose - print lots of useless stuff about what you are doing (kind of like matches in irc...)
54 makePrettyResults = False
57 #Check the manager program exists TODO: And is executable!
58 if os.path.exists(managerPath) == False:
59 print "Manager program at \""+managerPath+"\" doesn't exist!"
62 #Make necessary directories
64 if os.path.exists(resultsDirectory) == False:
65 os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
66 #Identify the round number by reading the results directory
67 totalRounds = len(os.listdir(resultsDirectory)) + 1
71 if os.path.exists(logDirectory) == False:
72 os.mkdir(logDirectory) #Make the log directory if it didn't exist
75 startTime = time() #Record time at which simulation starts
78 for roundNumber in range(totalRounds, totalRounds + nRounds):
80 if os.path.exists(logDirectory + "round"+str(roundNumber)) == False:
81 os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
84 print "Simulating ROUND " +str(roundNumber)
85 print "Identifying possible agents in \""+agentsDirectory+"\""
87 #Get all agent names from agentsDirectory
88 #TODO: Move this part outside the loop? It only has to happen once
89 agentNames = os.listdir(agentsDirectory)
91 for name in agentNames:
92 #sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ")
94 sys.stdout.write("Scan \""+name+"\"... ")
95 if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
97 sys.stdout.write(" Invalid! (Not a directory)\n")
100 if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist
102 sys.stdout.write(" Invalid! (No \"info\" file found)\n")
107 agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
109 if os.path.exists(agentExecutable) == False:
111 sys.stdout.write(" Invalid! (File \""+agentExecutable+"\" does not exist!)\n")
116 sys.stdout.write(" Valid! (To run: \""+agentExecutable+"\")\n")
118 #Convert array of valid names into array of dictionaries containing information about each agent
119 #I'm starting to like python...
120 agents.append({"name":name, "path":agentExecutable,"score":[0], "totalScore":0, "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "INTERNAL_ERROR":[]})
122 print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents."
125 print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)"
127 print "Commencing ROUND " + str(roundNumber) + " combat! This could take a while... "
134 #This double for loop simulates a round robin, with each agent getting the chance to play as both red and blue against every other agent.
136 for red in agents: #for each agent playing as red,
137 for blue in agents: #against each other agent, playing as blue
139 continue #Exclude battles against self
141 for i in range(1, nGames/2 + 1):
142 #Play a game and read the result. Note the game is logged to a file based on the agent's names
144 sys.stdout.write("Agents: \""+red["name"]+"\" and \""+blue["name"]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ")
145 logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(i)
146 outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read()
147 results = outline.split(' ')
149 if len(results) != 6:
151 sys.stdout.write("Garbage output! \"" + outline + "\"\n")
152 red["manager_errors"].append((gameID, blue["name"]))
156 if results[1] == "RED":
159 elif results[1] == "BLUE":
162 if results[1] == "BOTH":
165 endColour["score"].insert(0,endColour["score"][0] + scores[results[2]][0])
166 endColour[results[2]].append((otherColour["name"], gameID, scores[results[2]][0]))
167 otherColour["score"].insert(0, otherColour["score"][0] + scores[results[2]][1])
168 otherColour[scores[results[2]][2]].append((endColour["name"], gameID, scores[results[2]][1]))
171 sys.stdout.write(" Result \"")
172 for ii in range(1, len(results)):
173 sys.stdout.write(results[ii].strip())
174 if ii < (len(results) - 1):
175 sys.stdout.write(" ")
176 sys.stdout.write("\"\n")
179 print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. "
180 if managerErrors != 0:
181 print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program."
185 #We should now have complete score values.
188 sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
190 agents.sort(key = lambda e : e["score"], reverse=True) #Sort the agents based on score
192 resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
194 resultsFile.write(agent["name"] + " " + str(agent["score"]) +"\n") #Write the agent names and scores into the file, in descending order
197 sys.stdout.write(" Complete!\n")
198 sys.stdout.write("Updating total scores... ");
200 #Now update the total scores
201 if os.path.exists(resultsDirectory+"total.scores"):
203 sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ")
204 totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file
205 for line in totalFile: #For all entries,
206 data = line.split(' ')
208 if agent["name"] == data[0]:
209 agent["totalScore"] = int(data[1]) + agent["score"][0] #Simply increment the current score by the recorded total score of the matching file entry
211 totalFile.close() #Close the file, so we can delete it
212 os.remove(resultsDirectory+"total.scores") #Delete the file
213 #Sort the agents again
214 agents.sort(key = lambda e : e["totalScore"], reverse=True)
218 sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ")
220 sys.stdout.write(" Complete!\n")
221 print "Finished writing results for ROUND " + str(roundNumber)
225 print "RESULTS FOR ROUND " + str(roundNumber)
227 totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
229 totalFile.write(agent["name"] + " " + str(agent["totalScore"]) +"\n") #Write the total scores in descending order
230 print "Agent: " + str(agent)
233 #I just want to say the even though I still think python is evil, it is much better than bash. Using bash makes me cry.
236 print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds."
238 if makePrettyResults:
240 print "Now creating prettiful .html files..."