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.
11 Now (sortof) generates .html files to display results in a prettiful manner.
14 author Sam Moore (matches) [SZM]
15 website http://matches.ucc.asn.au/stratego
17 git git.ucc.asn.au/progcomp2012.git
24 #Global variables/arguments
26 baseDirectory = "../.." #Base directory for results, logs, agents
27 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?)
30 if len(sys.argv) >= 2:
31 nRounds = int(sys.argv[1])
32 if len(sys.argv) >= 3:
33 nGames = int(sys.argv[2])
35 print "Warning: nGames should be even. "+str(nGames)+" specified, but only " + str(int(nGames/2) * 2)+" will be played!"
36 if len(sys.argv) >= 4:
37 baseDirectory = sys.argv[3]
38 if len(sys.argv) >= 6:
39 print "Useage: " +sys.argv[0] + " [nRounds=1] [nGames=10] [baseDirectory=\""+baseDirectory+"\"] [managerPath=baseDirectory+\"/judge/manager/stratego\"]"
42 resultsDirectory = baseDirectory+"/results/" #Where results will go (results are in the form of text files of agent names and scores)
43 logDirectory = baseDirectory+"/log/" #Where log files go (direct output of manager program)
44 agentsDirectory = baseDirectory+"/agents/" #Where agents are found (each agent has its own subdirectory within this directory)
45 managerPath = baseDirectory+"/judge/manager/stratego" #Path to the executable that plays the games
46 if len(sys.argv) >= 5:
47 managerPath = sys.argv[5]
50 #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.
51 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")}
54 #Verbose - print lots of useless stuff about what you are doing (kind of like matches talking on irc...)
59 #Check the manager program exists TODO: And is executable!
60 if os.path.exists(managerPath) == False:
61 print "Manager program at \""+managerPath+"\" doesn't exist!"
64 #Make necessary directories
66 if os.path.exists(resultsDirectory) == False:
67 os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
68 #Identify the round number by reading the results directory
69 totalRounds = len(os.listdir(resultsDirectory)) + 1
73 if os.path.exists(logDirectory) == False:
74 os.mkdir(logDirectory) #Make the log directory if it didn't exist
77 startTime = time() #Record time at which simulation starts
81 print "Simulating " + str(nRounds) + " rounds (" + str(totalRounds) + " to " + str(totalRounds + nRounds-1) + ")"
83 print "Simulating one round."
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:
93 sys.stdout.write("Scan \""+name+"\"... ")
94 if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
96 sys.stdout.write(" Invalid! (Not a directory)\n")
99 if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist
101 sys.stdout.write(" Invalid! (No \"info\" file found)\n")
104 agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
106 if os.path.exists(agentExecutable) == False:
108 sys.stdout.write(" Invalid! (Path: \""+agentExecutable+"\" does not exist!)\n")
113 sys.stdout.write(" Valid! (Path: \""+agentExecutable+"\")\n")
115 #Convert array of valid names into array of dictionaries containing information about each agent
116 #I'm starting to like python...
117 agents.append({"name":name, "path":agentExecutable,"score":[0], "totalScore":0, "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "INTERNAL_ERROR":[], "ALL":[]})
120 print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents."
123 print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)"
126 #Prepare the pretty .html files if they don't exist
127 htmlDir = resultsDirectory + "pretty/"
128 if os.path.exists(htmlDir) == False:
130 if os.path.exists(htmlDir) == False:
131 print "Couldn't create directory \""+htmlDir+"\"."
135 if os.path.exists(htmlDir+agent["name"] + ".html") == False:
136 agentFile = open(htmlDir+agent["name"] + ".html", "w")
137 agentFile.write("<html>\n<head>\n <title> " + agent["name"] + " results</title>\n</head>\n<body>\n<h1> Results for " + agent["name"]+" </h1>\n</body>\n</html>\n")
140 os.rename(htmlDir+agent["name"] + ".html", "tmpfile")
142 oldFile = open("tmpfile")
143 agentFile = open(htmlDir+agent["name"] + ".html", "w")
145 if line.strip() == "</body>":
147 agentFile.write(line.strip() + "\n")
153 totalGames = nGames/2 * len(agents) * (len(agents)-1)
154 for roundNumber in range(totalRounds, totalRounds + nRounds):
156 if os.path.exists(logDirectory + "round"+str(roundNumber)) == False:
157 os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
160 print "Commencing ROUND " + str(roundNumber) + " combat!"
161 print "Total: " + str(totalGames) + " games to be played. This could take a while... (Estimate 60s/game)"
166 #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.
168 for red in agents: #for each agent playing as red,
169 for blue in agents: #against each other agent, playing as blue
171 continue #Exclude battles against self
173 gameID = str(roundNumber) + "." + str(gameNumber)
174 for i in range(1, nGames/2 + 1):
175 #Play a game and read the result. Note the game is logged to a file based on the agent's names
177 sys.stdout.write("Agents: \""+red["name"]+"\" and \""+blue["name"]+"\" playing game (ID: " + gameID + ") ... ")
178 logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(i)
179 outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read()
180 results = outline.split(' ')
182 if len(results) != 6:
184 sys.stdout.write("Garbage output! \"" + outline + "\"\n")
185 red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0]))
186 blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0]))
187 red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR"))
188 blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR"))
192 if results[1] == "RED":
197 elif results[1] == "BLUE":
204 if results[1] == "BOTH":
205 red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0]))
206 blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0]))
207 red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "RED"))
208 blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "BLUE"))
211 endColour["score"].insert(0,endColour["score"][0] + scores[results[2]][0])
212 endColour[results[2]].append((otherColour["name"], gameID, scores[results[2]][0]))
213 endColour["ALL"].append((otherColour["name"], gameID, scores[results[2]][0], results[2], endStr))
214 otherColour["score"].insert(0, otherColour["score"][0] + scores[results[2]][1])
215 otherColour[scores[results[2]][2]].append((endColour["name"], gameID, scores[results[2]][1]))
216 otherColour["ALL"].append((endColour["name"], gameID, scores[results[2]][1], scores[results[2]][2], otherStr))
220 sys.stdout.write(" Result \"")
221 for ii in range(1, len(results)):
222 sys.stdout.write(results[ii].strip())
223 if ii < (len(results) - 1):
224 sys.stdout.write(" ")
225 sys.stdout.write("\"\n")
228 print "Completed combat. Total of " + str(gameNumber) + " games played. "
229 if managerErrors != 0:
230 print "WARNING: Registered "+str(managerErrors)+" errors. Check the manager program."
234 #We should now have complete score values.
237 sys.stdout.write("Creating raw results files for ROUND " + str(roundNumber) + "... ")
239 agents.sort(key = lambda e : e["score"], reverse=True) #Sort the agents based on score
241 resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
243 resultsFile.write(agent["name"] + " " + str(agent["score"]) +"\n") #Write the agent names and scores into the file, in descending order
246 sys.stdout.write(" Complete!\n")
247 sys.stdout.write("Updating total scores... ");
249 #Now update the total scores
250 if os.path.exists(resultsDirectory+"total.scores"):
252 sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ")
253 totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file
254 for line in totalFile: #For all entries,
255 data = line.split(' ')
257 if agent["name"] == data[0]:
258 agent["totalScore"] = int(data[1]) + agent["score"][0] #Simply increment the current score by the recorded total score of the matching file entry
260 totalFile.close() #Close the file, so we can delete it
261 os.remove(resultsDirectory+"total.scores") #Delete the file
262 #Sort the agents again
263 agents.sort(key = lambda e : e["totalScore"], reverse=True)
267 sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ")
269 sys.stdout.write(" Complete!\n")
270 print "Finished writing results for ROUND " + str(roundNumber)
274 print "RESULTS FOR ROUND " + str(roundNumber)
276 totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
278 totalFile.write(agent["name"] + " " + str(agent["totalScore"]) +"\n") #Write the total scores in descending order
280 print "Agent: " + str(agent)
283 print "Updating pretty .html files... "
288 agentFile = open(htmlDir + agent["name"]+".html", "a")
289 agentFile.write("<h2> Round " + str(roundNumber) + "</h2>\n")
290 agentFile.write("<h3> Summary </h3>\n")
291 agentFile.write("<table border=\"0\" cellpadding=\"10\">\n")
292 agentFile.write("<tr> <th> Score </th> <th> Wins </th> <th> Losses </th> <th> Draws </th> <th> Illegal </th> <th> Errors </th></tr>\n")
293 agentFile.write("<tr> <td> "+str(agent["score"][0])+" </td> <td> "+str(len(agent["VICTORY"]))+" </td> <td> "+str(len(agent["DEFEAT"]))+" </td> <td> "+str(len(agent["DRAW"]))+" </td> <td> "+str(len(agent["ILLEGAL"]))+" </td> <td> " +str(len(agent["INTERNAL_ERROR"]))+" </td> </tr>\n")
295 agentFile.write("</table>\n")
297 agentFile.write("<h3> Detailed </h3>\n")
298 agentFile.write("<table border=\"0\" cellpadding=\"10\">\n")
299 agentFile.write("<tr> <th> Game ID </th> <th> Opponent </th> <th> Played as </th> <th> Outcome </th> <th> Score </th> <th> Accumulated Score </th> </tr> </th>\n")
301 for index in range(0, len(agent["ALL"])):
302 agentFile.write("<tr> <td> " + str(agent["ALL"][index][1]) + " </td> <td> <a href="+agent["ALL"][index][0]+".html>"+agent["ALL"][index][0] + " </a> </td> <td> " + agent["ALL"][index][4] + " </td> <td> " + agent["ALL"][index][3] + " </td> <td> " + str(agent["ALL"][index][2]) + "</td> <td> " + str(agent["score"][len(agent["score"])-index -2]) + " </td> </tr> </th>\n")
303 agentFile.write("</table>\n")
308 print "Finalising .html files... "
310 agentFile = open(htmlDir + agent["name"]+".html", "a")
311 agentFile.write("</body>\n<!-- Results file for \"" + agent["name"] + "\" autogenerated by \"" + sys.argv[0] + "\" at time " + str(time()) + " -->\n</html>\n\n")
319 print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds."