Commit baf69b8d authored by Sam Moore's avatar Sam Moore
Browse files

Fixed bugs, minor changes

Fixed segfault in manager caused by attempt to print invalid setups to log
Fixed SIGPIPE in manager caused by attempt to message non-existant programs.

Although I previously fixed a similar SIGPIPE, It is also possible for a file to exist but not have executable permissions set.
Controllers set to use such files as executables were returning true for Valid(), but were in fact, not valid at all.

Use the access function (thanks stack overflow!) to check for executable permissions
and existence in Program::Program. If they aren't set, or file doesn't exist, set
pid to -1 which is an "invalid" controller.

Discovered python trick which allows me to get rid of stupid "run.py" files for the python AIs.

Modified the simulate script to take the number of rounds as an argument.
Also made its output slightly prettier.

Currently testing simulation of 10 rounds on my laptop at home.

"./simulate 10; shutdown -h -P now"

I hope it doesn't set the desk on fire while I'm asleep... :S

Oh, and the VM is finally setup, hooray!
parent 56b7e695
...@@ -77,5 +77,9 @@ class Asmodeus(BasicAI): ...@@ -77,5 +77,9 @@ class Asmodeus(BasicAI):
else: else:
return self.suicideScores[attacker.rank] return self.suicideScores[attacker.rank]
if __name__ == "__main__":
asmodeus = Asmodeus()
if asmodeus.Setup():
while asmodeus.MoveCycle():
pass
#!/usr/bin/python -u
from asmodeus import *
asmodeus = Asmodeus()
if asmodeus.Setup():
while asmodeus.MoveCycle():
pass
...@@ -287,8 +287,9 @@ class BasicAI: ...@@ -287,8 +287,9 @@ class BasicAI:
sys.stderr.write(str(self.board[x][y].rank)); sys.stderr.write(str(self.board[x][y].rank));
sys.stderr.write("\n") sys.stderr.write("\n")
#basicAI = BasicAI() if __name__ == "__main__":
#if basicAI.Setup(): basicAI = BasicAI()
# while basicAI.MoveCycle(): if basicAI.Setup():
# pass while basicAI.MoveCycle():
pass
#!/usr/bin/python -u
from basic_python import *
basicAI = BasicAI()
if basicAI.Setup():
while basicAI.MoveCycle():
pass
...@@ -89,10 +89,14 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName) ...@@ -89,10 +89,14 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName)
if (!red->Valid()) if (!red->Valid())
{ {
logMessage("Controller for Player RED is invalid!\n"); logMessage("Controller for Player RED is invalid!\n");
if (!red->HumanController())
logMessage("Check that program \"%s\" exists and has executable permissions set.\n", redName);
} }
if (!blue->Valid()) if (!blue->Valid())
{ {
logMessage("Controller for Player BLUE is invalid!\n"); logMessage("Controller for Player BLUE is invalid!\n");
if (!blue->HumanController())
logMessage("Check that program \"%s\" exists and has executable permissions set.\n", blueName);
} }
if (!red->Valid()) if (!red->Valid())
{ {
...@@ -132,37 +136,57 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName) ...@@ -132,37 +136,57 @@ Piece::Colour Game::Setup(const char * redName, const char * blueName)
} }
else else
{ {
logMessage("Player RED gave an invalid setup!\n"); //logMessage("Player RED gave an invalid setup!\n");
result = Piece::RED; result = Piece::RED;
} }
} }
else if (blueSetup != MovementResult::OK) else if (blueSetup != MovementResult::OK)
{ {
logMessage("Player BLUE gave an invalid setup!\n"); //logMessage("Player BLUE gave an invalid setup!\n");
result = Piece::BLUE; result = Piece::BLUE;
} }
logMessage("%s RED SETUP\n", red->name.c_str()); logMessage("%s RED SETUP\n", red->name.c_str());
for (int y=0; y < 4; ++y) if (redSetup == MovementResult::OK)
{ {
for (int x=0; x < theBoard.Width(); ++x) for (int y=0; y < 4; ++y)
{ {
if (theBoard.GetPiece(x, y) != NULL) for (int x=0; x < theBoard.Width(); ++x)
logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]); {
else if (theBoard.GetPiece(x, y) != NULL)
logMessage("."); logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
} else
logMessage("\n"); logMessage(".");
} }
logMessage("\n");
}
}
else
{
logMessage("INVALID!\n");
}
logMessage("%s BLUE SETUP\n", blue->name.c_str()); logMessage("%s BLUE SETUP\n", blue->name.c_str());
for (int y=0; y < 4; ++y) if (blueSetup == MovementResult::OK)
{
for (int y=0; y < 4; ++y)
{
for (int x=0; x < theBoard.Width(); ++x)
{
if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
else
logMessage(".");
}
logMessage("\n");
}
}
else
{ {
for (int x=0; x < theBoard.Width(); ++x) logMessage("INVALID!\n");
logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]); }
logMessage("\n");
}
return result; return result;
......
...@@ -253,7 +253,7 @@ void PrintResults(const MovementResult & result, string & buffer) ...@@ -253,7 +253,7 @@ void PrintResults(const MovementResult & result, string & buffer)
s << "DRAW_DEFAULT "; s << "DRAW_DEFAULT ";
break; break;
case MovementResult::BAD_SETUP: case MovementResult::BAD_SETUP:
s << "BOTH_ILLEGAL "; s << "BAD_SETUP ";
break; break;
default: default:
s << "INTERNAL_ERROR "; s << "INTERNAL_ERROR ";
......
...@@ -22,13 +22,8 @@ using namespace std; ...@@ -22,13 +22,8 @@ using namespace std;
*/ */
Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0) Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
{ {
//See if file exists... //See if file exists and is executable...
FILE * file = fopen(executablePath, "r"); if (access(executablePath, X_OK) != 0)
if (file != NULL)
{
fclose(file);
}
else
{ {
pid = -1; pid = -1;
return; return;
...@@ -55,10 +50,11 @@ Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0 ...@@ -55,10 +50,11 @@ Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0
//If your wrapped program is not written in C/C++, you will probably have a problem //If your wrapped program is not written in C/C++, you will probably have a problem
if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file
execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
//fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
//exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
} }
else else
{ {
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "thread_util.h" #include "thread_util.h"
#include <string> #include <string>
#include <unistd.h> //Needed to check permissions
/** /**
* A wrapping class for an external program, which can exchange messages with the current process through stdin/stdout * A wrapping class for an external program, which can exchange messages with the current process through stdin/stdout
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
# Not used for building simulate.py # Not used for building simulate.py
# Used for building/removing results # Used for building/removing results
BASEDIR = /home/sam/Documents/progcomp2012/ BASEDIR = /home/sam/Documents/progcomp2012/progcomp
RESULTSDIR = /home/sam/Documents/progcomp2012/results RESULTSDIR = /home/sam/Documents/progcomp2012/progcomp/results
LOGDIR = /home/sam/Documents/progcomp2012/log LOGDIR = /home/sam/Documents/progcomp2012/progcomp/log
AGENTSDIR = /home/sam/Documents/progcomp2012/samples AGENTSDIR = /home/sam/Documents/progcomp2012/progcomp/agents
MANAGER = /home/sam/Documents/progcomp2012/manager/stratego MANAGER = /home/sam/Documents/progcomp2012/progcomp/judge/manager/stratego
......
...@@ -17,16 +17,27 @@ ...@@ -17,16 +17,27 @@
import os import os
import sys import sys
from time import time
baseDirectory = "/home/sam/Documents/progcomp2012/" baseDirectory = "/home/sam/Documents/progcomp2012/progcomp/"
resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores) resultsDirectory = baseDirectory+"results/" #Where results will go (results are in the form of text files of agent names and scores)
agentsDirectory = baseDirectory+"samples/" #Where agents are found (each agent has its own directory) agentsDirectory = baseDirectory+"agents/" #Where agents are found (each agent has its own directory)
logDirectory = baseDirectory+"log/" #Where log files go 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 nGames = 10 #Number of games played by each agent against each opponent. Half will be played as RED, half as BLUE
managerPath = baseDirectory+"manager/stratego" #Path to the manager program managerPath = baseDirectory+"judge/manager/stratego" #Path to the manager program
nRounds = 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)} #Score dictionary 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
verbose = True verbose = True
...@@ -35,155 +46,177 @@ verbose = True ...@@ -35,155 +46,177 @@ verbose = True
if os.path.exists(resultsDirectory) == False: if os.path.exists(resultsDirectory) == False:
os.mkdir(resultsDirectory) #Make the results directory if it didn't exist os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
#Identify the round number by reading the results directory #Identify the round number by reading the results directory
roundNumber = len(os.listdir(resultsDirectory)) + 1 totalRounds = len(os.listdir(resultsDirectory)) + 1
if roundNumber > 1: if totalRounds > 1:
roundNumber -= 1 totalRounds -= 1
if os.path.exists(logDirectory) == False: if os.path.exists(logDirectory) == False:
os.mkdir(logDirectory) #Make the log directory if it didn't exist os.mkdir(logDirectory) #Make the log directory if it didn't exist
startTime = time()
for roundNumber in range(totalRounds, totalRounds + nRounds):
if os.path.exists(logDirectory + "round"+str(roundNumber)) == False: 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 os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
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)+"\"... ")
if verbose: if verbose:
sys.stdout.write("Scan \""+name+"\"... ") print "Simulating ROUND " +str(roundNumber)
if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories 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)+"\"... ")
if verbose: if verbose:
sys.stdout.write(" Invalid! (Not a directory)\n") sys.stdout.write("Scan \""+name+"\"... ")
continue if os.path.isdir(agentsDirectory+name) == False: #Remove non-directories
if verbose:
sys.stdout.write(" Invalid! (Not a directory)\n")
continue
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 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
if verbose:
sys.stdout.write(" Valid!")
#sys.stdout.write("OK")
#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())
agents.append([name, agentExecutable, 0])
if verbose:
sys.stdout.write(" (Run program \""+agentExecutable+"\")\n")
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... ("+str(nGames)+" games per pairing * " + str(len(agents) * len(agents)-1) + " pairings = " + str((len(agents) * len(agents)-1) * nGames) + " games)"
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: if verbose:
sys.stdout.write("Agents: \""+red[0]+"\" and \""+blue[0]+"\" playing game " + str(i) + "/"+str(nGames/2) + "... ") sys.stdout.write(" Invalid! (No \"info\" file found)\n")
logFile = logDirectory + "round"+str(roundNumber) + "/"+red[0]+"_vs_"+blue[0]+"_"+str(i) continue
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)
else:
if results[1] == "RED":
red[2] += scores[results[2]][0]
blue[2] += scores[results[2]][1]
elif results[1] == "BLUE":
red[2] += scores[results[2]][1]
blue[2] += scores[results[2]][0]
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]
#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:
if verbose: if verbose:
sys.stdout.write(" " + outline) 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")
if verbose: if verbose:
print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. " print "Completed combat. Total of " + str(normalGames + draws + aiErrors + managerErrors) + " games played. "
if managerErrors != 0: if managerErrors != 0:
print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program." print "WARNING: Recieved "+str(managerErrors)+" garbage outputs. Check the manager program."
if verbose:
print ""
#We should now have complete score values.
if verbose:
sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
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
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: if verbose:
sys.stdout.write(" Reading from \""+resultsDirectory+"total.scores\" to update scores... ") print ""
totalFile = open(resultsDirectory+"total.scores", "r") #Try to open the total.scores file #We should now have complete score values.
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: if verbose:
sys.stdout.write(" First round - creating \""+resultsDirectory+"total.scores\"... ") sys.stdout.write("Creating results files for ROUND " + str(roundNumber) + "... ")
if verbose:
sys.stdout.write(" Complete!\n")
print "Finished writing results for ROUND " + str(roundNumber)
print ""
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
print "RESULTS FOR ROUND " + str(roundNumber) if verbose:
print "Agent: [name, path, total_score, recent_score]" 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")