simulate.py 8.18 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/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.
	

 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
20
from time import time
21

Sam Moore's avatar
Sam Moore committed
22
baseDirectory = "/home/sam/Documents/progcomp2012/progcomp/"
23
resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores)
Sam Moore's avatar
Sam Moore committed
24
agentsDirectory = baseDirectory+"agents/" #Where agents are found (each agent has its own directory)
25
26
logDirectory = baseDirectory+"log/" #Where log files go
nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE
Sam Moore's avatar
Sam Moore committed
27
managerPath = baseDirectory+"judge/manager/stratego" #Path to the manager program
28

Sam Moore's avatar
Sam Moore committed
29
nRounds = 1
30

Sam Moore's avatar
Sam Moore committed
31
32
33
34
35
36
37
38
39
40
time()

if len(sys.argv) == 2:
	nRounds = int(sys.argv[1])
elif len(sys.argv) != 1:
	print "Useage: simulate.py [nRounds]"
	sys.exit(1)


scores = {"VICTORY":(3,1), "DEFEAT":(1,3), "SURRENDER":(0,3), "DRAW":(2,2), "DRAW_DEFAULT":(1,1), "ILLEGAL":(-1,2), "DEFAULT":(2,-1), "BOTH_ILLEGAL":(-1,-1), "INTERNAL_ERROR":(0,0), "BAD_SETUP":(0,0)} #Score dictionary
41

42
verbose = True
43
44
45
46
47
48


#Make necessary directories
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
49
50
51
totalRounds = len(os.listdir(resultsDirectory)) + 1
if totalRounds > 1:
	totalRounds -= 1
52
53
54
55

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

Sam Moore's avatar
Sam Moore committed
56
startTime = time()
57

Sam Moore's avatar
Sam Moore committed
58
for roundNumber in range(totalRounds, totalRounds + nRounds):
59

Sam Moore's avatar
Sam Moore committed
60
61
	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
62

63
	if verbose:
Sam Moore's avatar
Sam Moore committed
64
65
66
67
68
69
70
71
		print "Simulating ROUND " +str(roundNumber)
		print "Identifying possible agents in \""+agentsDirectory+"\""

	#Get all agent names from agentsDirectory
	agentNames = os.listdir(agentsDirectory) 
	agents = []
	for name in agentNames:
		#sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ")
72
		if verbose:
Sam Moore's avatar
Sam Moore committed
73
74
75
76
77
			sys.stdout.write("Scan \""+name+"\"... ")
		if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
			if verbose:
				sys.stdout.write(" Invalid! (Not a directory)\n")
			continue
78

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

	
85

Sam Moore's avatar
Sam Moore committed
86
87
88
89
90
91
92
		#Convert the array of names to an array of triples
		#agents[0] - The name of the agent (its directory)
		#agents[1] - The path to the program for the agent (typically agentsDirectory/agent/agent). Read from agentsDirectory/agent/info file
		#agents[2] - The score the agent achieved in _this_ round. Begins at zero
		agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
	
		if os.path.exists(agentExecutable) == False:
93
			if verbose:
Sam Moore's avatar
Sam Moore committed
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
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
147
148
149
150
151
152
153
154
				sys.stdout.write(" Invalid! (File \""+agentExecutable+"\" does not exist!)\n")
			continue


		if verbose:
			sys.stdout.write(" Valid! (To run: \""+agentExecutable+"\")\n")
		agents.append([name, agentExecutable, 0])
	
	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 ""
		print "Commencing ROUND " + str(roundNumber) + " combat! This could take a while... "


	normalGames = 0
	draws = 0
	aiErrors = 0
	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.
	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
			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:
					sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ")
				logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i)
				outline = os.popen(managerPath + " -o " + logFile + " " + red[1] + " " + blue[1], "r").read()
				results = outline.split(' ')
			
				if len(results) != 6:
					if verbose:
						sys.stdout.write("Garbage output! \"" + outline + "\"\n")
					managerErrors += 1
				else:
					if results[1] == "RED":
						red[2] += scores[results[2]][0]
						blue[2] += scores[results[2]][1]
						normalGames += 1
					elif results[1] == "BLUE":
						red[2] += scores[results[2]][1]
						blue[2] += scores[results[2]][0]
						normalGames += 1
					elif results[1] == "BOTH":
						red[2] += scores[results[2]][0]
						blue[2] += scores[results[2]][1]
						red[2] += scores[results[2]][1]
						blue[2] += scores[results[2]][0]
						draws += 1
					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")
				
155
156
157
158
				
				
				
				
159

160
		
Sam Moore's avatar
Sam Moore committed
161
162
163
164
	if verbose:
		print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. "
	if managerErrors != 0:
		print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program."
165

166
	if verbose:
Sam Moore's avatar
Sam Moore committed
167
168
169
		print "" 
	#We should now have complete score values.
		
170
	if verbose:
Sam Moore's avatar
Sam Moore committed
171
		sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
172

Sam Moore's avatar
Sam Moore committed
173
174
175
176
177
	agents.sort(key = lambda e : e[2], reverse=True) #Sort the agents based on score
	
	resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
	for agent in agents:
		resultsFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the agent names and scores into the file, in descending order
178

Sam Moore's avatar
Sam Moore committed
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
	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:
				if agent[0] == data[0]:
					agent.append(agent[2]) #Store the score achieved this round at the end of the list
					agent[2] += int(data[1]) #Simply increment the current score by the recorded total score of the matching file entry
					break
		totalFile.close() #Close the file, so we can delete it
		os.remove(resultsDirectory+"total.scores") #Delete the file
		#Sort the agents again
		agents.sort(key = lambda e : e[2], reverse=True)

	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 ""
	
	
	print "RESULTS FOR ROUND " + str(roundNumber)
	print "Agent: [name, path, total_score, recent_score]"
211

Sam Moore's avatar
Sam Moore committed
212
213
214
215
	totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
	for agent in agents:
		totalFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the total scores in descending order
		print "Agent: " + str(agent)
216
217


Sam Moore's avatar
Sam Moore committed
218
	#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.
219

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