Modified manager output/protocol, added "basic" AI, made Asmodeus better
[progcomp2012.git] / 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
21 baseDirectory = "/home/sam/Documents/progcomp2012/"
22 resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores)
23 agentsDirectory = baseDirectory+"samples/" #Where agents are found (each agent has its own directory)
24 logDirectory = baseDirectory+"log/" #Where log files go
25 nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE
26 managerPath = baseDirectory+"manager/stratego" #Path to the manager program
27
28
29 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)} #Score dictionary
30
31 verbose = True
32
33
34 #Make necessary directories
35 if os.path.exists(resultsDirectory) == False:
36         os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
37 #Identify the round number by reading the results directory
38 roundNumber = len(os.listdir(resultsDirectory)) + 1
39 if roundNumber > 1:
40         roundNumber -= 1
41
42 if os.path.exists(logDirectory) == False:
43         os.mkdir(logDirectory) #Make the log directory if it didn't exist
44
45
46
47 if os.path.exists(logDirectory + "round"+str(roundNumber)) == False:
48         os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
49
50 print "Simulating ROUND " +str(roundNumber)
51 print "Identifying possible agents in \""+agentsDirectory+"\""
52
53 #Get all agent names from agentsDirectory
54 agentNames = os.listdir(agentsDirectory) 
55 agents = []
56 for name in agentNames:
57         #sys.stdout.write("\nLooking at Agent: \""+ str(name)+"\"... ")
58         if verbose:
59                 sys.stdout.write("Scan \""+name+"\"... ")
60         if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
61                 if verbose:
62                         sys.stdout.write(" Invalid! (Not a directory)\n")
63                 continue
64
65         if os.path.exists(agentsDirectory+name+"/info") == False: #Try and find the special "info" file in each directory; ignore if it doesn't exist
66                 if verbose:
67                         sys.stdout.write(" Invalid! (No \"info\" file found)\n")
68                 continue
69         if verbose:
70                 sys.stdout.write(" Valid!")
71         #sys.stdout.write("OK")
72         #Convert the array of names to an array of triples
73         #agents[0] - The name of the agent (its directory)
74         #agents[1] - The path to the program for the agent (typically agentsDirectory/agent/agent). Read from agentsDirectory/agent/info file
75         #agents[2] - The score the agent achieved in _this_ round. Begins at zero
76         agentExecutable = agentsDirectory+name+"/"+(open(agentsDirectory+name+"/info").readline().strip())
77         agents.append([name, agentExecutable, 0])
78         if verbose:
79                 sys.stdout.write(" (Run program \""+agentExecutable+"\")\n")
80
81 if len(agents) == 0:
82         print "Couldn't find any agents! Check paths (Edit this script) or generate \"info\" files for agents."
83         sys.exit(0)
84 if verbose:
85         print "Total: " + str(len(agents)) + " valid agents found (From "+str(len(agentNames))+" possibilities)"
86         print ""
87         print "Commencing ROUND " + str(roundNumber) + " combat! ("+str(nGames)+" games per pairing)"
88
89
90 normalGames = 0
91 draws = 0
92 aiErrors = 0
93 managerErrors = 0
94 #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.
95 for red in agents:  #for each agent playing as red,
96         for blue in agents: #against each other agent, playing as blue
97                 if red == blue:
98                         continue #Exclude battles against self
99                 for i in range(1, nGames/2 + 1):
100                         #Play a game and read the result. Note the game is logged to a file based on the agent's names
101                         if verbose:
102                                 sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ")
103                         logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i)
104                         outline = os.popen(managerPath + " -o " + logFile + " " + red[1] + " " + blue[1], "r").read()
105                         results = outline.split(' ')
106                         
107                         if len(results) != 6:
108                                 if verbose:
109                                         sys.stdout.write("Garbage output!       " + outline)
110                         else:
111                                 if results[1] == "RED":
112                                         red[2] += scores[results[2]][0]
113                                         blue[2] += scores[results[2]][1]
114                                 elif results[1] == "BLUE":
115                                         red[2] += scores[results[2]][1]
116                                         blue[2] += scores[results[2]][0]
117                                 elif results[1] == "BOTH":
118                                         red[2] += scores[results[2]][0]
119                                         blue[2] += scores[results[2]][1]
120                                         red[2] += scores[results[2]][1]
121                                         blue[2] += scores[results[2]][0]
122
123                         if verbose:
124                                 sys.stdout.write("      " + outline)
125                                 
126                                 
127                                 
128                                 
129
130                 
131 if verbose:
132         print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. "
133 if managerErrors != 0:
134         print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program."
135
136 if verbose:
137         print "" 
138 #We should now have complete score values.
139                 
140 if verbose:
141         sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
142
143 agents.sort(key = lambda e : e[2], reverse=True) #Sort the agents based on score
144
145 resultsFile = open(resultsDirectory+"round"+str(roundNumber)+".results", "w") #Create a file to store all the scores for this round
146 for agent in agents:
147         resultsFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the agent names and scores into the file, in descending order
148
149 if verbose:
150         sys.stdout.write(" Complete!\n")
151         sys.stdout.write("Updating total scores... ");
152
153 #Now update the total scores
154 if os.path.exists(resultsDirectory+"total.scores"):
155         if verbose:
156                 sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ")
157         totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file
158         for line in totalFile: #For all entries, 
159                 data = line.split(' ')
160                 for agent in agents:
161                         if agent[0] == data[0]:
162                                 agent.append(agent[2]) #Store the score achieved this round at the end of the list
163                                 agent[2] += int(data[1]) #Simply increment the current score by the recorded total score of the matching file entry
164                                 break
165         totalFile.close() #Close the file, so we can delete it
166         os.remove(resultsDirectory+"total.scores") #Delete the file
167         #Sort the agents again
168         agents.sort(key = lambda e : e[2], reverse=True)
169
170 else:
171         if verbose:
172                 sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ")
173 if verbose:
174         sys.stdout.write(" Complete!\n")
175         print "Finished writing results for ROUND " + str(roundNumber)
176         print ""
177
178
179 print "RESULTS FOR ROUND " + str(roundNumber)
180 print "Agent: [name, path, total_score, recent_score]"
181
182 totalFile = open(resultsDirectory+"total.scores", "w") #Recreate the file
183 for agent in agents:
184         totalFile.write(agent[0] + " " + str(agent[2]) +"\n") #Write the total scores in descending order
185         print "Agent: " + str(agent)
186
187
188 #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.
189

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