simulate.py 13.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
#!/usr/bin/python -u

'''
 simulate.py - simulation script for the 2012 UCC Programming Competition
	NOTE: This is not the manager program for a stratego game
	It merely calls the manager program as appropriate, and records results
	Plays exactly ONE round, but does not overwrite previously played rounds
	eg: run once to generate round1.results, twice to generate round2.results etc
	Also generates total.scores based on results from every round.
Sam Moore's avatar
Sam Moore committed
10
11

	Now (sortof) generates .html files to display results in a prettiful manner.
12
13
14
15
16
17
18
19
20
21
	

 author Sam Moore (matches) [SZM]
 website http://matches.ucc.asn.au/stratego
 email [email protected] or [email protected]
 git git.ucc.asn.au/progcomp2012.git
'''

import os
import sys
Sam Moore's avatar
Sam Moore committed
22
from time import time
23

Samuel Moore's avatar
Samuel Moore committed
24
#Global variables/arguments
25

Samuel Moore's avatar
Samuel Moore committed
26
27
baseDirectory = "../.." #Base directory for results, logs, agents
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?)
Sam Moore's avatar
Sam Moore committed
28
nRounds = 1
29

Samuel Moore's avatar
Samuel Moore committed
30
if len(sys.argv) >= 2:
Sam Moore's avatar
Sam Moore committed
31
	nRounds = int(sys.argv[1])
Samuel Moore's avatar
Samuel Moore committed
32
33
34
35
36
37
38
39
if len(sys.argv) >= 3:
	nGames = int(sys.argv[2])
	if nGames % 2 != 0:
		print "Warning: nGames should be even. "+str(nGames)+" specified, but only " + str(int(nGames/2) * 2)+" will be played!"
if len(sys.argv) >= 4:
	baseDirectory = sys.argv[3]
if len(sys.argv) >= 6:
	print "Useage: " +sys.argv[0] + " [nRounds=1] [nGames=10] [baseDirectory=\""+baseDirectory+"\"] [managerPath=baseDirectory+\"/judge/manager/stratego\"]"
Sam Moore's avatar
Sam Moore committed
40
41
	sys.exit(1)

Samuel Moore's avatar
Samuel Moore committed
42
43
44
45
46
47
48
49
50
51
resultsDirectory = baseDirectory+"/results/" #Where results will go (results are in the form of text files of agent names and scores)
logDirectory = baseDirectory+"/log/" #Where log files go (direct output of manager program)
agentsDirectory = baseDirectory+"/agents/" #Where agents are found (each agent has its own subdirectory within this directory)
managerPath = baseDirectory+"/judge/manager/stratego" #Path to the executable that plays the games
if len(sys.argv) >= 5:
	managerPath = sys.argv[5] 


#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.
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")}
Sam Moore's avatar
Sam Moore committed
52

53

Sam Moore's avatar
Sam Moore committed
54
#Verbose - print lots of useless stuff about what you are doing (kind of like matches talking on irc...)
55
verbose = True
Sam Moore's avatar
Sam Moore committed
56

57
58


Samuel Moore's avatar
Samuel Moore committed
59
60
61
62
63
#Check the manager program exists TODO: And is executable!
if os.path.exists(managerPath) == False:
	print "Manager program at \""+managerPath+"\" doesn't exist!"
	sys.exit(1)

64
#Make necessary directories
Samuel Moore's avatar
Samuel Moore committed
65

66
67
68
if os.path.exists(resultsDirectory) == False:
	os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
#Identify the round number by reading the results directory
Sam Moore's avatar
Sam Moore committed
69
70
71
totalRounds = len(os.listdir(resultsDirectory)) + 1
if totalRounds > 1:
	totalRounds -= 1
72
73
74
75
76

if os.path.exists(logDirectory) == False:
	os.mkdir(logDirectory) #Make the log directory if it didn't exist


Samuel Moore's avatar
Samuel Moore committed
77
78
startTime = time() #Record time at which simulation starts

Sam Moore's avatar
Sam Moore committed
79
80
81
82
83
84
85
86
87
88
89
90
91
if verbose:
	if nRounds > 1:
		print "Simulating " + str(nRounds) + " rounds (" + str(totalRounds) + " to " + str(totalRounds + nRounds-1) + ")"
	else:
		print "Simulating one round."
	print ""
	print "Identifying possible agents in \""+agentsDirectory+"\""

#Get all agent names from agentsDirectory
#TODO: Move this part outside the loop? It only has to happen once
agentNames = os.listdir(agentsDirectory) 
agents = []
for name in agentNames:
92
	if verbose:
Sam Moore's avatar
Sam Moore committed
93
94
		sys.stdout.write("Scan \""+name+"\"... ")
	if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
95
		if verbose:
Sam Moore's avatar
Sam Moore committed
96
97
			sys.stdout.write(" Invalid! (Not a directory)\n")
		continue
98

Sam Moore's avatar
Sam Moore committed
99
100
101
102
	if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist	
		if verbose:
			sys.stdout.write(" Invalid! (No \"info\" file found)\n")
		continue
Sam Moore's avatar
Sam Moore committed
103

Sam Moore's avatar
Sam Moore committed
104
	agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
Sam Moore's avatar
Sam Moore committed
105
	
Sam Moore's avatar
Sam Moore committed
106
107
108
109
	if os.path.exists(agentExecutable) == False:
		if verbose:
			sys.stdout.write(" Invalid! (Path: \""+agentExecutable+"\" does not exist!)\n")
		continue
Sam Moore's avatar
Sam Moore committed
110
111
112


	if verbose:
Sam Moore's avatar
Sam Moore committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
		sys.stdout.write(" Valid! (Path: \""+agentExecutable+"\")\n")

	#Convert array of valid names into array of dictionaries containing information about each agent
	#I'm starting to like python...
	agents.append({"name":name, "path":agentExecutable,"score":[0], "totalScore":0, "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "INTERNAL_ERROR":[], "ALL":[]})	

if len(agents) == 0:
	print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents."
	sys.exit(0)
if verbose:
	print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)"
	print ""

#Prepare the pretty .html files if they don't exist
htmlDir = resultsDirectory + "pretty/"
if os.path.exists(htmlDir) == False:
	os.mkdir(htmlDir)
if os.path.exists(htmlDir) == False:
	print "Couldn't create directory \""+htmlDir+"\"."
	sys.exit(1)

for agent in agents:
	if os.path.exists(htmlDir+agent["name"] + ".html") == False:
		agentFile = open(htmlDir+agent["name"] + ".html", "w")
		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")
		agentFile.close()

	os.rename(htmlDir+agent["name"] + ".html", "tmpfile")
	
	oldFile = open("tmpfile")
	agentFile = open(htmlDir+agent["name"] + ".html", "w")
	for line in oldFile:
		if line.strip() == "</body>":
			break
147
		agentFile.write(line.strip() + "\n")
Sam Moore's avatar
Sam Moore committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
	oldFile.close()
	agentFile.close()
	os.remove("tmpfile")

#Do each round...
totalGames = nGames/2 * len(agents) * (len(agents)-1)
for roundNumber in range(totalRounds, totalRounds + nRounds):

	if os.path.exists(logDirectory + "round"+str(roundNumber)) == False:
		os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs

	
	print "Commencing ROUND " + str(roundNumber) + " combat!"
	print "Total: " + str(totalGames) + " games to be played. This could take a while... (Estimate 60s/game)"

Sam Moore's avatar
Sam Moore committed
163
164
165
166


	managerErrors = 0
	#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.
Sam Moore's avatar
Sam Moore committed
167
	gameNumber = 0
Sam Moore's avatar
Sam Moore committed
168
169
170
171
	for red in agents:  #for each agent playing as red,
		for blue in agents: #against each other agent, playing as blue
			if red == blue:
				continue #Exclude battles against self
Sam Moore's avatar
Sam Moore committed
172
173
			gameNumber += 1
			gameID = str(roundNumber) + "." + str(gameNumber)
Sam Moore's avatar
Sam Moore committed
174
175
176
			for i in range(1, nGames/2 + 1):
				#Play a game and read the result. Note the game is logged to a file based on the agent's names
				if verbose:
Sam Moore's avatar
Sam Moore committed
177
					sys.stdout.write("Agents: \""+red["name"]+"\" and \""+blue["name"]+"\" playing game (ID: " + gameID + ") ... ")
Samuel Moore's avatar
Samuel Moore committed
178
179
				logFile = logDirectory + "round"+str(roundNumber) + "/"+red["name"]+".vs."+blue["name"]+"."+str(i)
				outline = os.popen(managerPath + " -o " + logFile + " " + red["path"] + " " + blue["path"], "r").read()
Sam Moore's avatar
Sam Moore committed
180
181
182
183
184
				results = outline.split(' ')
			
				if len(results) != 6:
					if verbose:
						sys.stdout.write("Garbage output! \"" + outline + "\"\n")
Sam Moore's avatar
Sam Moore committed
185
186
187
188
					red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0]))
					blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0]))
					red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR"))
					blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR"))
Sam Moore's avatar
Sam Moore committed
189
190
					managerErrors += 1
				else:
Samuel Moore's avatar
Samuel Moore committed
191

Sam Moore's avatar
Sam Moore committed
192
					if results[1] == "RED":
Samuel Moore's avatar
Samuel Moore committed
193
194
						endColour = red
						otherColour = blue
Sam Moore's avatar
Sam Moore committed
195
196
						endStr = "RED"
						otherStr = "BLUE"
Sam Moore's avatar
Sam Moore committed
197
					elif results[1] == "BLUE":
Samuel Moore's avatar
Samuel Moore committed
198
199
						endColour = blue
						otherColour = red
Sam Moore's avatar
Sam Moore committed
200
201
202
203
						endStr = "BLUE"
						otherStr = "RED"


Samuel Moore's avatar
Samuel Moore committed
204
					if results[1] == "BOTH":
Sam Moore's avatar
Sam Moore committed
205
206
207
208
209
						red["INTERNAL_ERROR"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0]))
						blue["INTERNAL_ERROR"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0]))
						red["ALL"].append((blue["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "RED"))
						blue["ALL"].append((red["name"], gameID, scores["INTERNAL_ERROR"][0], "INTERNAL_ERROR", "BLUE"))
						managerErrors += 1
Samuel Moore's avatar
Samuel Moore committed
210
211
212
					else:
						endColour["score"].insert(0,endColour["score"][0] + scores[results[2]][0])
						endColour[results[2]].append((otherColour["name"], gameID, scores[results[2]][0]))
Sam Moore's avatar
Sam Moore committed
213
						endColour["ALL"].append((otherColour["name"], gameID, scores[results[2]][0], results[2], endStr))
Samuel Moore's avatar
Samuel Moore committed
214
215
						otherColour["score"].insert(0, otherColour["score"][0] + scores[results[2]][1])
						otherColour[scores[results[2]][2]].append((endColour["name"], gameID, scores[results[2]][1]))
Sam Moore's avatar
Sam Moore committed
216
						otherColour["ALL"].append((endColour["name"], gameID, scores[results[2]][1], scores[results[2]][2], otherStr))
Samuel Moore's avatar
Samuel Moore committed
217

Sam Moore's avatar
Sam Moore committed
218
					
Sam Moore's avatar
Sam Moore committed
219
220
221
222
223
224
225
					if verbose:
						sys.stdout.write(" Result \"")
						for ii in range(1, len(results)):
							sys.stdout.write(results[ii].strip())
							if ii < (len(results) - 1):
								sys.stdout.write(" ")
						sys.stdout.write("\"\n")
226
		
Sam Moore's avatar
Sam Moore committed
227
	if verbose:
Sam Moore's avatar
Sam Moore committed
228
		print "Completed combat. Total of " + str(gameNumber) + " games played. "
Sam Moore's avatar
Sam Moore committed
229
	if managerErrors != 0:
Sam Moore's avatar
Sam Moore committed
230
		print "WARNING: Registered "+str(managerErrors)+" errors. Check the manager program."
231

232
	if verbose:
Sam Moore's avatar
Sam Moore committed
233
234
235
		print "" 
	#We should now have complete score values.
		
236
	if verbose:
Sam Moore's avatar
Sam Moore committed
237
		sys.stdout.write("Creating raw results files for ROUND " + str(roundNumber) + "... ")
238

Samuel Moore's avatar
Samuel Moore committed
239
	agents.sort(key = lambda e : e["score"], reverse=True) #Sort the agents based on score
Sam Moore's avatar
Sam Moore committed
240
241
242
	
	resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
	for agent in agents:
Samuel Moore's avatar
Samuel Moore committed
243
		resultsFile.write(agent["name"] + " " + str(agent["score"]) +"\n") #Write the agent names and scores into the file, in descending order
244

Sam Moore's avatar
Sam Moore committed
245
246
247
248
249
250
251
252
253
254
255
256
	if verbose:
		sys.stdout.write(" Complete!\n")
		sys.stdout.write("Updating total scores... ");
	
	#Now update the total scores
	if os.path.exists(resultsDirectory+"total.scores"):
		if verbose:
			sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ")
		totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file
		for line in totalFile: #For all entries, 
			data = line.split(' ')
			for agent in agents:
Samuel Moore's avatar
Samuel Moore committed
257
258
				if agent["name"] == data[0]:
					agent["totalScore"] = int(data[1]) + agent["score"][0] #Simply increment the current score by the recorded total score of the matching file entry
Sam Moore's avatar
Sam Moore committed
259
260
261
262
					break
		totalFile.close() #Close the file, so we can delete it
		os.remove(resultsDirectory+"total.scores") #Delete the file
		#Sort the agents again
Samuel Moore's avatar
Samuel Moore committed
263
		agents.sort(key = lambda e : e["totalScore"], reverse=True)
Sam Moore's avatar
Sam Moore committed
264
265
266
267
268
269
270
271
272

	else:
		if verbose:
			sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ")
	if verbose:
		sys.stdout.write(" Complete!\n")
		print "Finished writing results for ROUND " + str(roundNumber)
		print ""
	
Sam Moore's avatar
Sam Moore committed
273
274
	if verbose:	
		print "RESULTS FOR ROUND " + str(roundNumber)
275

Sam Moore's avatar
Sam Moore committed
276
	totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
Sam Moore's avatar
Sam Moore committed
277
	for agent in agents:	
Samuel Moore's avatar
Samuel Moore committed
278
		totalFile.write(agent["name"] + " " + str(agent["totalScore"]) +"\n") #Write the total scores in descending order
Sam Moore's avatar
Sam Moore committed
279
280
		if verbose:
			print "Agent: " + str(agent)
281

Sam Moore's avatar
Sam Moore committed
282
283
	if verbose:
		print "Updating pretty .html files... "
284
285


Samuel Moore's avatar
Samuel Moore committed
286

Sam Moore's avatar
Sam Moore committed
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
	for agent in agents:
		agentFile = open(htmlDir + agent["name"]+".html", "a")
		agentFile.write("<h2> Round " + str(roundNumber) + "</h2>\n")
		agentFile.write("<h3> Summary </h3>\n")
		agentFile.write("<table border=\"0\" cellpadding=\"10\">\n")
		agentFile.write("<tr> <th> Score </th> <th> Wins </th> <th> Losses </th> <th> Draws </th> <th> Illegal </th> <th> Errors </th></tr>\n")
		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")

		agentFile.write("</table>\n")

		agentFile.write("<h3> Detailed </h3>\n")
		agentFile.write("<table border=\"0\" cellpadding=\"10\">\n")
		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")
		
		for index in range(0, len(agent["ALL"])):
			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")
		agentFile.write("</table>\n")
		agentFile.close()	
	

if verbose:
	print "Finalising .html files... "
for agent in agents:
	agentFile = open(htmlDir + agent["name"]+".html", "a")
	agentFile.write("</body>\n<!-- Results file for \"" + agent["name"] + "\" autogenerated by \"" + sys.argv[0] + "\" at time " + str(time()) + " -->\n</html>\n\n")
	agentFile.close()

Samuel Moore's avatar
Samuel Moore committed
314
315
316
	
if verbose:
	print "Done!"
Sam Moore's avatar
Sam Moore committed
317
318
319

endTime = time()
print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds."
Sam Moore's avatar
Sam Moore committed
320
sys.exit(0)