Small changes to several things
authorSam Moore <matches@ucc.asn.au>
Tue, 20 Dec 2011 04:10:47 +0000 (12:10 +0800)
committerSam Moore <matches@ucc.asn.au>
Tue, 20 Dec 2011 04:10:47 +0000 (12:10 +0800)
Changed combat outcomes for equivelant ranks:
Up until now, victor was randomly chosen
Changed so that result is always "BOTHDIE"

Updated manual page for manager
Added section on unbuffered stdin/stdout

Modified sample agents to take into account MULTIPLIER
There is no way for a human player to move the scout multiple spaces yet.
Sample agents still play each other fine, but since none of them actually move scouts multiple spaces,
this doesn't prove the new code works.
TODO: Test properly (add scout movement to asmodeus and see if everything still works?)

Updated webpage.
Added section on unbuffered stdin/stdout
Added some other pointless waffle. Mmmm waffle.

TODO:
Make simulate.py keep track of the round number properly.
I'm sure this is simple to do, but I can't be bothered right now.

progcomp/agents/basic_cpp/basic_cpp.cpp
progcomp/agents/basic_python/basic_python.py
progcomp/judge/manager/manual.txt
progcomp/judge/manager/stratego.cpp
progcomp/judge/simulator/simulate.py
progcomp/web/index.html

index ab00826..4312489 100644 (file)
@@ -333,9 +333,21 @@ bool BasicAI::InterpretResult()
        
 
        Direction dir = Helper::StrToDir(tokens[2]);
-       string & outcome = tokens[3];
 
-       int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir);
+       //We might want to actually check for the multiplier in the sample agents! 20/12/11
+       unsigned int outIndex = 3;
+       int multiplier = atoi(tokens[outIndex].c_str());
+       if (multiplier == 0)
+               multiplier = 1;
+       else
+               outIndex += 1;
+       
+
+
+       string & outcome = tokens[outIndex];
+
+
+       int x2 = x; int y2 = y; Helper::MoveInDirection(x2,y2,dir, multiplier);
 
        Piece * attacker = board->Get(x,y);
        if (attacker == NULL)
@@ -357,12 +369,14 @@ bool BasicAI::InterpretResult()
                        //cerr << "No defender!\n";
                        return false;
                }
+               if (tokens.size() < outIndex+2)
+                       return false;
+
 
                board->Set(x2,y2, attacker);
                board->Set(x,y,NULL);
                attacker->x = x2; attacker->y = y2;
-               
-               attacker->rank = Piece::GetRank(tokens[4][0]);
+               attacker->rank = Piece::GetRank(tokens[outIndex+1][0]);
                ForgetUnit(defender);
        }
        else if (outcome == "DIES")
@@ -372,10 +386,12 @@ bool BasicAI::InterpretResult()
                        //cerr << "No defender!\n";
                        return false;
                }
-               
+               if (tokens.size() < outIndex+3)
+                       return false;
+
 
                board->Set(x,y,NULL);
-               defender->rank = Piece::GetRank(tokens[5][0]);
+               defender->rank = Piece::GetRank(tokens[outIndex+2][0]);
                ForgetUnit(attacker);
 
                
index 024ae55..849c6f7 100755 (executable)
@@ -20,7 +20,15 @@ import random
 
 ranks = ['B','1','2','3','4','5','6','7','8','9','s','F', '?', '+']
 
-def move(x, y, direction):
+def is_integer(s):
+       """ Using exceptions for this feels... wrong..."""
+       try:
+               int(s)
+               return True
+       except ValueError:
+               return False
+
+def move(x, y, direction, multiplier):
        """ Moves point (x,y) in direction, returns a pair """
        if direction == "UP":
                return (x,y-1)
@@ -203,9 +211,16 @@ class BasicAI:
                #sys.stderr.write("     Board position " + str(x) + " " + str(y) + " is OK!\n")         
 
                direction = result[2].strip()
+
+               multiplier = 1
                outcome = result[3].strip()
+               outIndex = 3
+               if is_integer(outcome):
+                       multiplier = int(outcome)
+                       outcome = result[4].strip()
+                       outIndex = 4
                
-               p = move(x,y,direction)
+               p = move(x,y,direction, multiplier)
 
                
 
@@ -229,7 +244,7 @@ class BasicAI:
 
 
                        self.board[p[0]][p[1]] = self.board[x][y]
-                       self.board[x][y].rank = result[4].strip()
+                       self.board[x][y].rank = result[outIndex+1].strip()
 
                        self.board[x][y] = None
                        
@@ -242,7 +257,7 @@ class BasicAI:
                        elif self.board[x][y].colour == oppositeColour(self.colour):
                                self.enemyUnits.remove(self.board[x][y])
 
-                       self.board[p[0]][p[1]].rank = result[5].strip()
+                       self.board[p[0]][p[1]].rank = result[outIndex+2].strip()
                        self.board[x][y] = None
                elif outcome == "BOTHDIE":
                        if self.board[p[0]][p[1]] == None:
index 3237241..120a6aa 100644 (file)
@@ -20,9 +20,19 @@ DESCRIPTION
                NOTES
                        1. There is no plan to support AI programs named "human". Deal with it.
                        2. The graphical interface for human players is... basic. Deal with it.
-
        blue_player
                As red_player, except for controlling the Blue player.
+
+A WARNING ABOUT BUFFERING
+       The AI programs must unbuffer their stdin and stdout streams, otherwise it will be seen to be non-responsive.
+       If you C and you know a way to force the process started by exec() to have unbuffered stdin/stdout, please email the author.
+
+       In C or C++, unbuffering is accomplished with the following lines, which should appear near the start of main()
+               setbuf(stdin, NULL);
+               setbuf(stdout, NULL);
+       In python, unbuffering is accomplished by passing the -u switch to the interpreter, ie: The first line of a script reads:
+               #!/usr/bin/python -u
+       
        
 OPTIONS
        -g
@@ -74,7 +84,7 @@ OPTIONS
                
 
 GAME RULES
-               Each player controls up to 40 pieces on the Board. The pieces consist of the following:
+               Each player controls up to 40 pieces on the Board. The pieces are represented by the following characters:
 
                Piece   Name            Rank    Number  Abilities
                1       Marshal         1       1       Dies if attacked by Spy
@@ -205,11 +215,7 @@ EXIT/OUTPUT
        If possible, stratego will print the message "QUIT" to both AI programs, and they should exit as soon as possible.
        
 
-BUGS
-       WARNING:
-       stratego has been observed to segfault occassionally after the end of a game. It is not yet known what is causing these errors.
-       They appear to occur most often when the result is a draw, however they have been observed to occur under all exit conditions except the Illegal case. The result is still printed to stdout. However this bug _must_ be fixed before stratego can be used by simulation scripts.       
-
+BUGS   
        stratego is still a work in progress. Report another bug to the AUTHOR (see below).
 
 AUTHORS
@@ -227,4 +233,7 @@ NOTES
  
        3. IRC Channel
           irc://irc.ucc.asn.au #progcomp
+
+THIS PAGE LAST UPDATED
+       20/12/11 by Sam Moore
        
index f2df0ff..62da170 100644 (file)
@@ -200,7 +200,9 @@ void Board::PrintPretty(FILE * stream, const Piece::Colour & reveal)
 
 /**
  * Draw the board state to graphics
- * @param reveal - Pieces matching this colour will be revealed. All others will be shown as blank coloured squares.
+ * @param reveal - Pieces matching this colour will be revealed. If Piece::BOTH, all pieces will be revealed
+ * @param showRevealed - If true, then all pieces that have taken part in combat will be revealed, regardless of colour.
+ *                      If false, only pieces matching the colour reveal will be revealed
  */
 void Board::Draw(const Piece::Colour & reveal, bool showRevealed)
 {
@@ -291,12 +293,12 @@ Piece * Board::GetPiece(int x, int y)
 }
 
 /**
- * Moves a piece at a specified position in the specified direction, handles combat if necessary
+ * Moves a piece at a specified position in the specified direction, handles combat if necessary, updates state of the board
  * @param x - x-coord of the piece
  * @param y - y-coord of the piece
  * @param direction - Direction in which to move (UP, DOWN, LEFT or RIGHT)
  * @param colour - Colour which the piece must match for the move to be valid
- * @returns A MovementResult which indicates the result of the move - OK is good, VICTORY means that a flag was captured, anything else is an error
+ * @returns A MovementResult which indicates the result of the move
  */
 MovementResult Board::MovePiece(int x, int y, const Direction & direction, int multiplier,const Piece::Colour & colour)
 {
@@ -413,13 +415,15 @@ MovementResult Board::MovePiece(int x, int y, const Direction & direction, int m
                        board[x2][y2] = target;
                        return MovementResult(MovementResult::KILLS, attackerType, defenderType);
                }
-               else if (target->operator==(*defender) && rand() % 2 == 0)
+               else if (target->operator==(*defender))// && rand() % 2 == 0)
                {
                        RemovePiece(defender);
+                       RemovePiece(target);
                        delete defender;
+                       delete target;
                        board[x][y] = NULL;
-                       board[x2][y2] = target; 
-                       return MovementResult(MovementResult::KILLS, attackerType, defenderType);
+                       board[x2][y2] = NULL;   
+                       return MovementResult(MovementResult::BOTH_DIE, attackerType, defenderType);
                }
                else
                {
index 4429e4a..9b79194 100755 (executable)
@@ -65,10 +65,16 @@ if os.path.exists(managerPath) == False:
 
 if os.path.exists(resultsDirectory) == False:
        os.mkdir(resultsDirectory) #Make the results directory if it didn't exist
+''' 
+       Obselete older version doesn't work with new .html files
 #Identify the round number by reading the results directory
 totalRounds = len(os.listdir(resultsDirectory)) + 1
 if totalRounds > 1:
        totalRounds -= 1
+'''
+
+totalRounds = 1
+#TODO: Fix this bit!
 
 if os.path.exists(logDirectory) == False:
        os.mkdir(logDirectory) #Make the log directory if it didn't exist
@@ -179,7 +185,7 @@ for roundNumber in range(totalRounds, totalRounds + nRounds):
                os.mkdir(logDirectory + "round"+str(roundNumber)) #Check there is a directory for this round's logs
 
        for agent in agents:
-               agent.update({"name":agent["name"], "path":agent["path"],  "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[], "totalScore":0, "Wins":0, "Losses":0, "Draws":0, "Illegal":0, "Errors":0})
+               agent.update({"name":agent["name"], "path":agent["path"],  "score":[0], "VICTORY":[], "DEFEAT":[], "DRAW":[], "ILLEGAL":[], "DEFAULT":[], "INTERNAL_ERROR":[], "SURRENDER":[], "DRAW_DEFAULT":[], "BOTH_ILLEGAL":[], "BAD_SETUP":[], "ALL":[]})
 
        
        print "Commencing ROUND " + str(roundNumber) + " combat!"
@@ -343,7 +349,20 @@ for roundNumber in range(totalRounds, totalRounds + nRounds):
                agentFile.write("</table>\n")
                agentFile.close()       
 
-               
+       #Update round file
+       roundFile = open(htmlDir + "round"+str(roundNumber)+".html", "w")
+       roundFile.write("<html>\n<head>\n <title> Round " +str(roundNumber)+ " Overview </title>\n</head>\n<body>\n")
+       roundFile.write("<h1> Round " +str(roundNumber)+ " Overview </h1>\n")
+       roundFile.write("<table border=\"0\" cellpadding=\"10\">\n")
+       roundFile.write("<tr> <th> Name </th> <th> Score </th> <th> Total Score </th> </tr>\n")
+       agents.sort(key = lambda e : e["score"][0], reverse=True)
+       for agent in agents:
+               roundFile.write("<tr> <td> <a href="+agent["name"]+".html>"+agent["name"] + " </a> </td> <td> " + str(agent["score"][0]) + " </td> <td> " + str(agent["totalScore"]) + " </td> </tr>\n")
+       roundFile.write("</table>\n</body>\n<!-- Results file for Round " + str(roundNumber) + " autogenerated by \"" + sys.argv[0] + "\" at time " + str(time()) + " -->\n</html>\n\n")
+       roundFile.close()
+
+
+       
        
 
 if verbose:
@@ -356,6 +375,20 @@ for agent in agents:
        agentFile.write("</body>\n<!-- Results file for \"" + agent["name"] + "\" autogenerated by \"" + sys.argv[0] + "\" at time " + str(time()) + " -->\n</html>\n\n")
        agentFile.close()
 
+       if os.path.exists(htmlDir + "total.html") == True:
+               os.remove(htmlDir + "total.html") #Delete the file
+
+totalFile = open(htmlDir + "total.html", "w")
+totalFile.write("<html>\n<head>\n <title> Total Overview </title>\n</head>\n<body>\n")
+totalFile.write("<h1> Total Overview </h1>\n")
+totalFile.write("<table border=\"0\" cellpadding=\"10\">\n")
+totalFile.write("<tr> <th> Name </th> <th> Total Score </th> </tr>\n")
+agents.sort(key = lambda e : e["totalScore"], reverse=True)
+for agent in agents:
+       totalFile.write("<tr> <td> <a href="+agent["name"]+".html>"+agent["name"] + " </a> </td> <td> " + str(agent["totalScore"]) + " </td> </tr>\n")
+totalFile.write("</table>\n</body>\n<!-- Total Results file autogenerated by \"" + sys.argv[0] + "\" at time " + str(time()) + " -->\n</html>\n\n")
+totalFile.close()
+
        
 if verbose:
        print "Done!"
index 2a7fba8..ff1fea5 100644 (file)
@@ -45,6 +45,8 @@
 
 <p> <b> Warning:</b> The accuracy of the above file depends on how recently I pulled it from git. To ensure you get the latest version, find it under "manager/manual.txt" in the <a href="http://git.ucc.asn.au/?p=progcomp2012.git;a=summary"/>git repository</a> </p>
 
+<p> <b> Another Warning:</b> AI programs <b>must</b> unbuffer stdin and stdout themselves. This is explained in the manual page, but I figured no one would read it. It is fairly simple to unbuffer stdin/stdout in C/C++ and python, I have not investigated other languages yet. </p>
+
 <h2> Long Term Scoring </h2>
 <p> <b> WARNING: Work in progress </b> </p>
 <p> It is currently planned to store all the AIs on a virtual machine, and periodically run a script to play a round robin </p>
 
 <h2> Submissions </h2>
 <p> We (I?) are now accepting test submissions. </p>
-<p> Please email matches@ attaching your submission source code </p>
+<p> You must submit the full source code, and build instructions or makefile(s) for your program. </p>
+<p> Also include the name of the executable or script, the name of the AI, your name, and optionally a description of your AI and its tactics. </p>
+<p> Please email matches@ if you have a submission. </p>
+
+<h2> Dates </h2>
+<p> The competition will run in 2012. The exact dates have not been determined. I will consider whether it is worth waiting until freshers have a chance to make submissions March/April, or running the competition earlier in January/February. </p>
+
+<h2> Involvement </h2>
+<p> If you want to help set up the competition, email matches@ </p>
+<p> There isn't much left to do, but all help is appreciated. Maybe you can suggest something, and volunteer to implement it! </p>
+<p> The most useful thing you can do is actually write an AI, and test the manager program. Please report any bugs (in the manager program, not in your AI!) to matches@ (you can feel free to fix them, but won't be able to push unless you email matches). </p>
 
-<p> <b>Last webpage update: 12/12/11</b></p>
+<p> <b>Last webpage update: 20/12/11</b></p>
 </body>
 
 </html>

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