"""
marlboro_tourney.py
A simple tournament engine for the google ants competition
as done by the Marlboro College programming workshop.
Use:
1st edit the list of bots and maps in main(), at the end of this file.
Then run this from the command line to get the tourney results, e.g.
$ python marlboro_tourney.py
playing tournament . . . . . .
-- bots --
python ../python_starter_package/MyBot.py
python sample_bots/python/HunterBot.py
python sample_bots/python/GreedyBot.py
-- maps --
maps/example/tutorial1.map
maps/maze/maze_02p_02.map
-- games --
0 HunterBot.py 3 MyBot.py 0 tutorial1.map
1 HunterBot.py 1 MyBot.py 1 maze_02p_02.map
2 MyBot.py 0 GreedyBot.py 3 tutorial1.map
3 MyBot.py 0 GreedyBot.py 3 maze_02p_02.map
4 HunterBot.py 3 GreedyBot.py 0 tutorial1.map
5 HunterBot.py 3 GreedyBot.py 0 maze_02p_02.map
-- ranks --
HunterBot.py 3
GreedyBot.py 0
MyBot.py -3
The games line shows (bot, score) as given by the google AI engine.
The games (n.replay, replay.n.html) are stored as JSON and
in a browser-viewable form in the game folder, currently ./log_marlboro/ .
Jim M | Feb 2012 | MIT License
"""
# I'm keeping things simple at this point:
# Two player maps only
# Each bot plays all other bots, on every map.
# Score +1 for win, -1 for loss.
#import subprocess
import commands
import json
import sys
import os
import re
engine = "./playgame.py"
log_folder = "log_marlboro"
def shortname(path):
""" convert file path (perhaps with leading command
to just the file, e.g. "python a/b/c/ddd.py" to "ddd.py" """
return re.compile("\s|/").split(path)[-1]
def update_winloss(winloss, winner, loser):
""" modify the winloss dict
by incrementing winner and decrementing loser """
winloss[winner] = winloss.get(winner, 0) + 1
winloss[loser] = winloss.get(loser, 0) - 1
return winloss
def get_ranks(games, print_rankings=False):
""" return wins-losses for each bot given the list of games played
as returned by play_tourney.
If print_rankings=True, also print winloss, high to low. """
winloss = {}
for (bot1, score1, bot2, score2, m) in games:
if score1 > score2:
winloss = update_winloss(winloss, bot1, bot2)
elif score1 < score2:
winloss = update_winloss(winloss, bot2, bot1)
if print_rankings:
print "-- ranks --"
for bot in sorted(winloss, key=winloss.get, reverse=True):
print " %-12s %4i" % (bot, winloss[bot])
return winloss
def print_games(games):
""" print 'em, one per line; see play_tourney for their format. """
print "-- games --"
# print "games = " + str(games)
index = 0
for (bot1, score1, bot2, score2, mapp) in games:
print " %4i %-12s %4i %-12s %4i %16s" % \
(index, bot1, score1, bot2, score2, shortname(mapp))
index += 1
def check_log_folder():
""" Create log folder if it doesn't exist. """
if not os.path.exists(log_folder):
os.mkdir(log_folder)
def play_tourney(bots, maps, print_progress=False):
""" Call play_game for all maps & bots combos.
Return list of games played,
each as tuple (bot1, score1, bot2, score2, map)
If print_progress=True, print a period for each game played. """
games = []
index = 0
check_log_folder()
if print_progress:
print "playing tournament ",
sys.stdout.flush()
for bot1 in bots:
for bot2 in bots:
if bot1 < bot2:
for mapp in maps:
result = play_game(bot1, bot2, mapp, index)
((bot1name, score1), (bot2name, score2)) = result.items()
games.append((bot1name, score1, bot2name, score2,
shortname(mapp)))
index += 1
if print_progress:
print ". ",
sys.stdout.flush()
if print_progress:
print
return games
def play_game(bot1, bot2, the_map, index=0, turns=1000):
""" Play game index=n; put log in ./log_marlboro/n.replay,
and return result as {bot1:score1, bot2, score2} """
# command to run a game is similar to that in play_marlboro_game, i.e.
# ./playgame.py \
# --game=1 --log_dir=game_logs \
# -R --nolaunch --verbose --player_seed=42 --end_wait=0.25 --turns=1000 \
# --map_file=maps/example/tutorial1.map "$@" \
# "ruby ../sam_isaac/explore.rb" \
# "python sample_bots/python/HunterBot.py"
### take 1
### this version failed in spite of numerous attempts :
#### python command line stuff sucks.
# options = '-R --nolaunch --player_seed=42 --end_wait=0.24'
# dir = os.getcwd()
# command = 'cd ' + dir + ' ; ' + engine + ' ' +
# ' --game=' + str(index) + ' --turns=' + str(turns) + \
# ' --log_dir=' + log_folder + ' --map_file=' + map + \
# ' "' + bot1 + '" ' + ' "' + bot2 + '" '
# print "command = '%s'" % command
# status = subprocess.call(command)
### take 2
### this one also failed; cwd=dir is apparently not setting working folder
# options = ['-R', '--nolaunch', '--player_seed=42', '--end_wait=0.24']
# args = options + ['--game=' + str(index), '--turns=' + str(turns),
# '--log_dir=' + log_folder,
# '--map_file=' + map,
# '"' + bot1 + '" ', ' "' + bot2 + '"']
# dir = os.getcwd()
# print "args = " + str(args)
# print "engine = '" + engine + "'"
# print "dir = '" + dir + "'"
# status = subprocess.Popen([engine] + args, shell=True, cwd=dir).wait()
# print "status = " + str(status)
### take 3
### using the deprecated (but much simpler) commands.getoutput("cmd -arg1")
### Finally! This one works (with python 2.6.1 on Mac OS anyway)
options = '-R --nolaunch --player_seed=42 --end_wait=0.24'
dir = os.getcwd()
command = 'cd ' + dir + ' ; ' + engine + ' ' + options + \
' --game=' + str(index) + ' --turns=' + str(turns) + \
' --log_dir=' + log_folder + ' --map_file=' + the_map + \
' "' + bot1 + '" ' + ' "' + bot2 + '" '
# print "command = '%s'" % command
output = commands.getoutput(command)
# print "output = '%s'" % output
logfile = log_folder + "/" + str(index) + ".replay"
json_result = json.load(open(logfile))
# One issue is that json returns unicode (part of JSON spec);
# its strings render as e.g. u"MyBot.py" rather than "MyBot.py".
# Ah yes - more pythonic "goodness".
# manual conversion: u"foo".encode('utf-8') gives plain old "foo"
result = {}
for (who, what) in zip(json_result['playernames'], json_result['score']):
if who != None: # None => bot crashed
result[who.encode('utf-8')] = what
for bot in (shortname(bot1), shortname(bot2)): # Fill in score=0 if crashed.
if not result.has_key(bot):
result[bot] = 0
return result
def test_play_game():
print "Playing game 0 ..."
result = play_game(bots[0], bots[1], maps[0], 0)
print "Done. Scores are " + str(result)
# test_play_game()
def main():
""" play the tourney; print results """
###### Modify these two lists #######
# Paths are relative to this tools/ folder.
# Two player maps only
maps = ["maps/example/tutorial1.map",
"maps/maze/maze_02p_02.map",
]
bots = ["python ../alex/DumbAnts.py",
"ruby ../sam_isaac/aggress-v1.rb",
"java -jar ../ryan/TheBot.jar",
"python ../jay/TestBotRandom.py",
"python ../chad/ChadBot.py",
"python ../jacob/antbot.py",
]
#####################################
games = play_tourney(bots, maps, print_progress=True)
print "-- bots --"
for bot in bots:
print " " + bot
print "-- maps --"
for mapp in maps:
print " " + mapp
print_games(games)
get_ranks(games, print_rankings=True)
main()
syntax highlighted by Code2HTML, v. 0.93pm6