Fixed bugs, minor changes
[progcomp2012.git] / progcomp / judge / simulator / simulate.py
1 #!/usr/bin/python -u
2
3 '''
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.
10         
11
12  author Sam Moore (matches) [SZM]
13  website http://matches.ucc.asn.au/stratego
14  email progcomp@ucc.asn.au or matches@ucc.asn.au
15  git git.ucc.asn.au/progcomp2012.git
16 '''
17
18 import os
19 import sys
20 from time import time
21
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)
24 agentsDirectory = baseDirectory+"agents/" #Where agents are found (each agent has its own directory)
25 logDirectory = baseDirectory+"log/" #Where log files go
26 nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE
27 managerPath = baseDirectory+"judge/manager/stratego" #Path to the manager program
28
29 nRounds = 1
30
31 time()
32
33 if len(sys.argv) == 2:
34         nRounds = int(sys.argv[1])
35 elif len(sys.argv) != 1:
36         print "Useage: simulate.py [nRounds]"
37         sys.exit(1)
38
39
40 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 #Make necessary directories
46 if os.path.exists(resultsDirectory) == False:
47         os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
48 #Identify the round number by reading the results directory
49 totalRounds = len(os.listdir(resultsDirectory)) + 1
50 if totalRounds > 1:
51         totalRounds -= 1
52
53 if os.path.exists(logDirectory) == False:
54         os.mkdir(logDirectory) #Make the log directory if it didn't exist
55
56 startTime = time()
57
58 for roundNumber in range(totalRounds, totalRounds + nRounds):
59
60         if os.path.exists(logDirectory + "round"+str(roundNumber)) == False:
61                 os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
62
63         if verbose:
64                 print "Simulating ROUND " +str(roundNumber)
65                 print "Identifying possible agents in \""+agentsDirectory+"\""
66
67         #Get all agent names from agentsDirectory
68         agentNames = os.listdir(agentsDirectory) 
69         agents = []
70         for name in agentNames:
71                 #sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ")
72                 if verbose:
73                         sys.stdout.write("Scan \""+name+"\"... ")
74                 if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
75                         if verbose:
76                                 sys.stdout.write(" Invalid! (Not a directory)\n")
77                         continue
78
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:
81                                 sys.stdout.write(" Invalid! (No \"info\" file found)\n")
82                         continue
83
84         
85
86                 #Convert the array of names to an array of triples
87                 #agents[0] - The name of the agent (its directory)
88                 #agents[1] - The path to the program for the agent (typically agentsDirectory/agent/agent). Read from agentsDirectory/agent/info file
89                 #agents[2] - The score the agent achieved in _this_ round. Begins at zero
90                 agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
91         
92                 if os.path.exists(agentExecutable) == False:
93                         if verbose:
94                                 sys.stdout.write(" Invalid! (File \""+agentExecutable+"\" does not exist!)\n")
95                         continue
96
97
98                 if verbose:
99                         sys.stdout.write(" Valid! (To run: \""+agentExecutable+"\")\n")
100                 agents.append([name, agentExecutable, 0])
101         
102         if len(agents) == 0:
103                 print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents."
104                 sys.exit(0)
105         if verbose:
106                 print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)"
107                 print ""
108                 print "Commencing ROUND " + str(roundNumber) + " combat! This could take a while... "
109
110
111         normalGames = 0
112         draws = 0
113         aiErrors = 0
114         managerErrors = 0
115         #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.
116         for red in agents:  #for each agent playing as red,
117                 for blue in agents: #against each other agent, playing as blue
118                         if red == blue:
119                                 continue #Exclude battles against self
120                         for i in range(1, nGames/2 + 1):
121                                 #Play a game and read the result. Note the game is logged to a file based on the agent's names
122                                 if verbose:
123                                         sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ")
124                                 logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i)
125                                 outline = os.popen(managerPath + " -o " + logFile + " " + red[1] + " " + blue[1], "r").read()
126                                 results = outline.split(' ')
127                         
128                                 if len(results) != 6:
129                                         if verbose:
130                                                 sys.stdout.write("Garbage output! \"" + outline + "\"\n")
131                                         managerErrors += 1
132                                 else:
133                                         if results[1] == "RED":
134                                                 red[2] += scores[results[2]][0]
135                                                 blue[2] += scores[results[2]][1]
136                                                 normalGames += 1
137                                         elif results[1] == "BLUE":
138                                                 red[2] += scores[results[2]][1]
139                                                 blue[2] += scores[results[2]][0]
140                                                 normalGames += 1
141                                         elif results[1] == "BOTH":
142                                                 red[2] += scores[results[2]][0]
143                                                 blue[2] += scores[results[2]][1]
144                                                 red[2] += scores[results[2]][1]
145                                                 blue[2] += scores[results[2]][0]
146                                                 draws += 1
147                                         if verbose:
148                                                 sys.stdout.write(" Result \"")
149                                                 for ii in range(1, len(results)):
150                                                         sys.stdout.write(results[ii].strip())
151                                                         if ii < (len(results) - 1):
152                                                                 sys.stdout.write(" ")
153                                                 sys.stdout.write("\"\n")
154                                 
155                                 
156                                 
157                                 
158                                 
159
160                 
161         if verbose:
162                 print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. "
163         if managerErrors != 0:
164                 print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program."
165
166         if verbose:
167                 print "" 
168         #We should now have complete score values.
169                 
170         if verbose:
171                 sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
172
173         agents.sort(key = lambda e : e[2], reverse=True) #Sort the agents based on score
174         
175         resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
176         for agent in agents:
177                 resultsFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the agent names and scores into the file, in descending order
178
179         if verbose:
180                 sys.stdout.write(" Complete!\n")
181                 sys.stdout.write("Updating total scores... ");
182         
183         #Now update the total scores
184         if os.path.exists(resultsDirectory+"total.scores"):
185                 if verbose:
186                         sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ")
187                 totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file
188                 for line in totalFile: #For all entries, 
189                         data = line.split(' ')
190                         for agent in agents:
191                                 if agent[0] == data[0]:
192                                         agent.append(agent[2]) #Store the score achieved this round at the end of the list
193                                         agent[2] += int(data[1]) #Simply increment the current score by the recorded total score of the matching file entry
194                                         break
195                 totalFile.close() #Close the file, so we can delete it
196                 os.remove(resultsDirectory+"total.scores") #Delete the file
197                 #Sort the agents again
198                 agents.sort(key = lambda e : e[2], reverse=True)
199
200         else:
201                 if verbose:
202                         sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ")
203         if verbose:
204                 sys.stdout.write(" Complete!\n")
205                 print "Finished writing results for ROUND " + str(roundNumber)
206                 print ""
207         
208         
209         print "RESULTS FOR ROUND " + str(roundNumber)
210         print "Agent: [name, path, total_score, recent_score]"
211
212         totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
213         for agent in agents:
214                 totalFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the total scores in descending order
215                 print "Agent: " + str(agent)
216
217
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
220 endTime = time()
221 print "Completed simulating " + str(nRounds) + " rounds in " + str(endTime - startTime) + " seconds."
222 sys.exit(0)

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