Refactor ai-test-suite (python script).

This commit is contained in:
flix 2013-10-07 20:25:41 +02:00
parent 6472560ca7
commit 516154c3d1
11 changed files with 680 additions and 787 deletions

View file

@ -32,6 +32,7 @@ Version 1.11.6+dev:
configuration work again (suspected version 1.7.4 regression).
* Move_Leader_To_Keep CA will now move all leaders back to a keep
(multiple leader support for recruitment).
* Refactored the AI-Test-Suite completely (/utils/ai_test/).
* Campaigns:
* Legend of Wesmere:
* Removed now redundant MP code.

View file

@ -1,17 +1,73 @@
[default]
path_to_wesnoth_binary=../../wesnoth-debug ../..
arguments_to_wesnoth_binary=--log-info=ai/testing,mp/connect --nogui --multiplayer --controller1=ai --controller2=ai
number_of_tests=10000
ai_config1=ai/ais/formula_ai.cfg
ai_config2=ai/ais/default_ai.cfg
faction1=
faction2=
db_ip=127.0.0.1
db_port=5432
db_name=org.wesnoth.ai.test
db_user=wesnoth_ai_test_user
db_pass=PASSWORD
map1=multiplayer_Weldyn_Channel
map2=multiplayer_The_Freelands
map3=multiplayer_Den_of_Onis
map4=multiplayer_Fallenstar_Lake
# The title is only used when using the database.
# Include in the title some hints of the version
# of the AI you are using
# (e.g. "V3 Combat Analysis weighted twice")
title = Untitled
path_to_wesnoth_binary = ../../cmake_build_dir/wesnoth
# this is the first parts of arguments
# the second part is added by the script
# the first argument should be the
# relative path to <data-directory>
additional_arguments = ../../ --controller=1:ai --controller=2:ai --parm 1:gold:200 --parm 2:gold:200
# This is the number of tests when
# structured_test = no
number_of_tests = 2
# Repeats each test n times using the --repeat argument.
# The idea behind this is that repeated games will run
# without executing wesnoth over and over again.
# When n is high enough it will save ~30 % execution time.
# The number of total games is
# number_of_tests * repeats.
repeat = 1
# If you encounter problems with macros
# (e.g. "Macro/file 'AI_CA_GOTO' is missing")
# add the following line at the beginning
# of the AI's cfg-file:
# {core/macros}
ai_config1 = ai/ais/ai_default_rca.cfg
ai_config2 = ai/dev/idle_ai.cfg
# leave empty for random fractions
# Possible factions are: Drakes, Rebels, Undead, Northerners, Knalgan Alliance, Loyalists
faction1 =
faction2 =
randomize_sides = yes
# When set to 'yes' the factions will be not randomized.
# Instead each faction-combination will play <repeat> times.
# <number_of_tests>, <faction1>, <faction2> will then be ignored.
structured_test = no
# Comment this line out when you don't want to create logfiles.
# See here for time-format conventions: http://docs.python.org/2/library/time.html#time.strftime
log_file = ai_test__%Y_%m_%d__%H-%M__myAI_vs_default.log
# uncomment this line if you want to save
# the results in a sqlite3 Database
# Note that we should have write access to this file.
# Also it is convenient to save the file outside the git repo.
# sqlite_file = ../../../ai_test_db/ai_test.sqlite
map = multiplayer_Weldyn_Channel
#map = multiplayer_The_Freelands
#map = multiplayer_Den_of_Onis
#map = multiplayer_Fallenstar_Lake

View file

@ -1,183 +1,346 @@
#!/usr/bin/env python
from subprocess import Popen,PIPE
from subprocess import Popen, PIPE
from time import clock, time
from pyPgSQL import PgSQL
import datetime
import sqlite3
import ConfigParser
import os
import string
import random
import sys
class GameResult:
ai_config1 = ''
ai_config2 = ''
ai_ident1 = ''
ai_ident2 = ''
duration = '0'
faction1 = ''
faction2 = ''
is_success = 'false'
local_modifications = 'false'
map = ''
repo_release = '0'
test = 'default'
end_turn = '0'
version_string = ''
winner_side = '0'
# Wording conversations:
# We have one 'Test'
# One 'Test' includes multiple 'Executions'
# One 'Execution' has one or more 'Games'
def __init__(self, _ai_config1, _ai_config2, _faction1, _faction2, _map,
_test):
self.ai_config1 = _ai_config1
self.ai_config2 = _ai_config2
self.faction1 = _faction1
self.faction2 = _faction2
self.map = _map
self.test = _test
class Test:
ai_config1 = ''
ai_config2 = ''
ai_ident1 = ''
ai_ident2 = ''
map = ''
version_string = ''
faction1 = ''
faction2 = ''
time = ''
test_id = 0
title = ''
game_results = []
def __init__(self, _ai_config1, _ai_config2, _faction1, _faction2, _map, _title):
self.ai_config1 = _ai_config1
self.ai_config2 = _ai_config2
self.faction1 = _faction1
self.faction2 = _faction2
self.map = _map
self.title = _title
today = datetime.datetime.now()
self.time = today.strftime('%Y-%m-%d %H:%M')
def filter_non_printable(str):
return ''.join(c for c in str if ord(c) > 31 or ord(c) == 9)
return ''.join(c for c in str if ord(c) > 31 or ord(c) == 9)
def construct_command_line(cfg, ai1, ai2, f1, f2,map):
wesnoth = cfg.get('default', 'path_to_wesnoth_binary')
options= cfg.get('default', 'arguments_to_wesnoth_binary')
ai_config1= '--ai_config1=' + ai1
ai_config2= '--ai_config2=' +ai2
if not map:
optmap = ''
else:
optmap = '--scenario=' + map
return '%s %s %s %s %s' % (wesnoth, options, optmap, ai_config1, ai_config2)
def construct_command_line(cfg, test, switched_side):
wesnoth = cfg.get('default', 'path_to_wesnoth_binary')
options = cfg.get('default', 'additional_arguments')
repeats = cfg.getint('default', 'repeat')
repeats_param = '--multiplayer-repeat ' + str(repeats)
if repeats > 1:
print 'Be patient, ' + str(repeats) + ' repeats are going to take a while.'
side1 = test.ai_config1 if not switched_side else test.ai_config2
side2 = test.ai_config2 if not switched_side else test.ai_config1
faction1 = test.faction1 if not switched_side else test.faction2
faction2 = test.faction2 if not switched_side else test.faction1
ai_param1 = '--ai-config 1:' + side1 if side1 else ''
ai_param2 = '--ai-config 2:' + side2 if side2 else ''
faction_param1 = '--side 1:"' + faction1 + '"' if faction1 else ''
faction_param2 = '--side 2:"' + faction2 + '"' if faction2 else ''
map_param = '--scenario=' + test.map if test.map else ''
if len(sys.argv) > 1 and sys.argv[1] == '-p':
gui = '--debug'
else:
gui = '--nogui'
statics = '--log-info=ai/testing,mp/connect/engine --multiplayer'
return (wesnoth + ' ' + options + ' ' + map_param + ' ' + ai_param1 + ' ' + ai_param2 +
' ' + faction_param1 + ' ' + faction_param2 + ' ' + gui + ' ' + repeats_param + ' ' + statics)
def do_filter(str, substring):
n = str.find(substring)
if n > -1:
return n, str[n + len(substring):].strip()
return n, ''
n = str.find(substring)
if (n > -1):
temp = str[n + len(substring):].strip()
c = temp.find(',')
if (c > -1):
return n, temp[:c].strip()
else:
return n, temp
return n, ''
def run_game(cfg,game_result):
command_line = construct_command_line(cfg, game_result.ai_config1, game_result.ai_config2, game_result.faction1, game_result.faction2, game_result.map)
print 'Running: ' + command_line
start = time()
p = Popen(command_line, shell=True, bufsize=10000000, stdout=PIPE, stderr=PIPE)
# outlines = p.stdout.readlines()
outerrlines = p.stderr.readlines()
print 'Finished'
for line in outerrlines:
str = filter_non_printable(line.strip())
n,s = do_filter(str, 'info ai/testing: WINNER:')
if n > -1:
#print 'AND THE WINNER IS: '+s
game_result.winner_side = s
game_result.is_success = 'true'
continue
def run_game(cfg, test, switched_side):
command_line = construct_command_line(cfg, test, switched_side)
print 'Running: ' + command_line
n, s = do_filter(str, 'info ai/testing: VERSION:')
if n > -1:
#print 'AND THE VERSION IS: '+s
game_result.version_string = s
n1 = s.rfind('(')
n2 = s.rfind(')')
if -1 < n1 < n2:
sz = s[n1+1:n2]
#parse local_modifications
n3 = sz.rfind('M')
if n3 > -1:
sz = sz[:n3]
game_result.local_modifications = 1
#parse repo_release
game_result.repo_release = sz
continue
game_results = []
game_result = None
faction1 = ''
faction2 = ''
debugout = ''
n,s = do_filter(str ,'info ai/testing: GAME_END_TURN:')
if n > -1:
#print 'AND THE VICTORY_TURN IS: '+s
game_result.end_turn = s
continue
p = Popen(command_line, shell=True, bufsize=10000000, stdout=PIPE, stderr=PIPE)
n, s = do_filter(str, 'info ai/testing: AI_IDENTIFIER1:')
if n > -1:
#print 'AND THE AI_IDENTIFIER1 IS: '+s
game_result.ai_ident1 = s.strip()
continue
for line in p.stderr:
l = filter_non_printable(line.strip())
debugout += l + '\n'
n, s = do_filter(str, 'info ai/testing: AI_IDENTIFIER2:')
if n > -1:
#print 'AND THE AI_IDENTIFIER2 IS: '+s
game_result.ai_ident2 = s.strip()
continue
n, s = do_filter(l , 'side 1: faction=')
if (n > -1):
faction1 = s
continue
n, s = do_filter(str, 'info mp/connect: FACTION1:')
if n > -1:
#print 'AND THE FACTION1 IS: '+s
game_result.faction1 = s
continue
n, s = do_filter(l , 'side 2: faction=')
if (n > -1):
faction2 = s
continue
n, s = do_filter(str, 'info mp/connect: FACTION2:')
if n > -1:
#print 'AND THE FACTION2 IS: '+s
game_result.faction2 = s
continue
n, s = do_filter(l , 'info ai/testing: VERSION:')
if (n > -1):
test.version_string = s
continue
game_result.duration = time() - start
if (game_result.is_success=='false'):
print 'Warning: not success!'
print '===================='
print 'stderr:'
for line in outerrlines:
print filter_non_printable(line.strip())
print '===================='
n, s = do_filter(l , 'info ai/testing: AI_IDENTIFIER1:')
if(n > -1):
if switched_side:
test.ai_ident2 = s
else:
test.ai_ident1 = s
return game_result
# this is the first line of a game.
# We'll do some initializations here.
def save_result(cfg,game_result):
print 'Saving to DB....'
query = 'insert into game(ai_config1,ai_config2,ai_ident1,ai_ident2,duration,faction1,faction2,is_success,local_modifications,map,repo_release,test,end_turn,version_string,winner_side) values (%s,%s,%s,%s,cast(%s as double precision),%s,%s,cast(%s as boolean),cast(%s as boolean),%s,cast(%s as int),%s,cast(%s as int),%s,cast(%s as int))'
db_ip = cfg.get('default','db_ip')
db_port = cfg.getint('default','db_port')
db_name = cfg.get('default','db_name')
db_user = cfg.get('default','db_user')
db_pass = cfg.get('default','db_pass')
game_result = {}
game_result['switched_side'] = switched_side
game_result['is_success'] = False
continue
dbconnection = PgSQL.connect(database=db_name,host=db_ip,port=db_port,user=db_user,password=db_pass)
cu = dbconnection.cursor()
cu.execute(query, game_result.ai_config1, game_result.ai_config2, game_result.ai_ident1, game_result.ai_ident2, game_result.duration, game_result.faction1, game_result.faction2, game_result.is_success, game_result.local_modifications, game_result.map, game_result.repo_release, game_result.test, game_result.end_turn, game_result.version_string, game_result.winner_side)
cu.execute('commit')
dbconnection.close()
print 'Saved to DB'
n, s = do_filter(l , 'info ai/testing: AI_IDENTIFIER2:')
if(n > -1):
if switched_side:
test.ai_ident1 = s
else:
test.ai_ident2 = s
continue
def maps(cfg):
mp = 1
while 1:
try:
yield cfg.get('default', 'map' + repr(mp))
mp += 1
except:
return
n, s = do_filter(l , 'info ai/testing: WINNER:')
if (n > -1):
if (int(s) == 1) != switched_side:
winner = 1
else:
winner = 2
print 'AND THE WINNER IS: ' + str(winner)
if game_result.has_key('winner'):
game_result['is_success'] = False
break
game_result['winner'] = winner
game_result['is_success'] = True
continue
def tests(cfg):
ai1 = cfg.get('default', 'ai_config1').strip()
ai2 = cfg.get('default', 'ai_config2').strip()
f1 = cfg.get('default', 'faction1').strip()
f2 = cfg.get('default', 'faction2').strip()
n = cfg.getint('default', 'number_of_tests')
maplist = []
for map in maps(cfg):
maplist.append(map)
random.seed()
for i in xrange(n):
map = random.choice(maplist)
d = random.randint(0, 1)
print 'TEST: map '+map+' i='+str(i)+' d='+str(d)
if not d:
game_result = GameResult(ai1, ai2, f1, f2, map, 'default')
else:
game_result = GameResult(ai2, ai1, f2, f1, map, 'default')
yield game_result
n, s = do_filter(l , 'info ai/testing: DRAW:')
if(n > -1):
print 'AND THE WINNER IS: DRAW'
if game_result.has_key('winner'):
game_result['is_success'] = False
break
game_result['winner'] = 0
game_result['is_success'] = True
continue
n, s = do_filter(l , 'info ai/testing: GAME_END_TURN:')
if (n > -1):
# this is the last line printed in a game
# so we do some checking here and adding
# game_result to game_results.
print 'AND THE VICTORY_TURN IS: ' + s
if game_result.has_key('end_turn'):
game_result['is_success'] = False
break
game_result['end_turn'] = int(s)
game_result['faction1'] = faction1 if not switched_side else faction2
game_result['faction2'] = faction2 if not switched_side else faction1
if not game_result['is_success']:
print_error(debugout)
game_results.append(game_result)
continue
n, s = do_filter(l , 'error')
if(n > -1):
# forward errors from stderr.
print 'stderr give: error ' + s
continue
if game_result is None or not game_result['is_success']:
print_error(debugout)
return game_results
def print_error(debugout):
print 'Warning: not success!'
print '===================='
print 'stderr:'
print debugout
print '===================='
def save_result_logfile(cfg, test, game_result, log_file):
print 'Saving to log file....'
log_file.write('"' + test.ai_config1 + '", "' +
test.ai_config2 + '", "' +
test.ai_ident1 + '", "' +
test.ai_ident2 + '", "' +
str(game_result['switched_side']) + '", "' +
game_result['faction1'] + '", "' +
game_result['faction2'] + '", "' +
str(game_result['is_success']) + '", "' +
test.map + '", "' +
str(game_result['end_turn']) + '", "' +
str(test.version_string) + '", "' +
str(game_result['winner']) + '"\n');
log_file.flush();
print 'Saved to log file'
def save_result_database(cfg, test, game_result, sqlite_file):
print 'Saving to DB....'
query = ('INSERT INTO games("test_id","faction1","faction2","switched_side","is_success","end_turn","winner")' +
'VALUES (?,?,?,?,?,?,?)')
conn = sqlite3.connect(sqlite_file)
cur = conn.cursor()
cur.execute(query, (
test.test_id,
game_result['faction1'],
game_result['faction2'],
game_result['switched_side'],
game_result['is_success'],
game_result['end_turn'],
game_result['winner']))
conn.commit()
conn.close()
print 'Saved to DB'
def executions(cfg, test):
structured = cfg.getboolean('default', 'structured_test')
if structured:
factions = ['Drakes', 'Rebels', 'Undead', 'Northerners', 'Knalgan Alliance', 'Loyalists']
i = 1
for faction1 in factions:
for faction2 in factions:
print 'EXECUTION: ' + str(i) + '/36 --- ' + faction1 + ' against ' + faction2
test.faction1 = faction1
test.faction2 = faction2
game_results = run_game(cfg, test, False)
yield game_results
i = i + 1
test.faction1 = 'structured'
test.faction2 = 'structured'
else:
n = cfg.getint('default', 'number_of_tests')
randomize = cfg.getboolean('default', 'randomize_sides')
random.seed()
for i in range(0, n):
switched_side = (random.randint(0, 1) == 1) if randomize else False
print 'EXECUTION ' + str(i + 1)
game_results = run_game(cfg, test, switched_side)
yield game_results
if test.faction1 == '':
test.faction1 = 'random'
if test.faction2 == '':
test.faction2 = 'random'
# main
cfg = ConfigParser.ConfigParser()
cfg.read('ai_test.cfg')
for test in tests(cfg):
game_result = run_game(cfg, test)
save_result(cfg,game_result)
ai1 = cfg.get('default', 'ai_config1').strip()
ai2 = cfg.get('default', 'ai_config2').strip()
faction1 = cfg.get('default', 'faction1').strip()
faction2 = cfg.get('default', 'faction2').strip()
map = cfg.get('default', 'map').strip()
if cfg.has_option('default', 'title'):
title = cfg.get('default', 'title')
else:
title = ''
test = Test(ai1, ai2, faction1, faction2, map, title)
# only 'test the test' with GUI / start one game then exit
if len(sys.argv) > 1 and sys.argv[1] == '-p':
executions(cfg, test).next()
sys.exit(0)
log_file = None
if cfg.has_option('default', 'log_file'):
log_file = open(datetime.datetime.now().strftime(cfg.get('default', 'log_file').strip()) , 'w')
log_file.write('"ai_config1", "ai_config2", "ai_ident1", "ai_ident2", "switched_side", ' +
'"faction1", "faction2", "is_success", "local_modifications", ' +
'"map", "repo_release", "end_turn", "version_string", "winner_side"\n');
log_file.flush();
sqlite_file = None
if cfg.has_option('default', 'sqlite_file'):
sqlite_file = cfg.get('default', 'sqlite_file')
conn = sqlite3.connect(sqlite_file)
cur = conn.cursor()
cur.execute('INSERT INTO tests ("title","ai_config1","ai_config2","map","time") ' +
'VALUES ("' + test.title + '","' + test.ai_config1 + '","' + test.ai_config2 + '","' +
test.map + '","' + test.time + '")')
test.test_id = cur.lastrowid
conn.commit()
conn.close();
# the following variables are for generating a print output only
total = 0
ai1_won = 0
ai2_won = 0
draw = 0
for game_results in executions(cfg, test):
for game_result in game_results:
if log_file:
save_result_logfile(cfg, test, game_result, log_file)
if sqlite_file:
save_result_database(cfg, test, game_result, sqlite_file)
total = total + 1
if game_result['winner'] == 0:
draw = draw + 1
elif game_result['winner'] == 1:
ai1_won = ai1_won + 1
elif game_result['winner'] == 2:
ai2_won = ai2_won + 1
print '\n=====Status====='
print 'Total games: ' + str(total)
print 'AI1(' + ai1 + ') won: ' + str(ai1_won) + "/" + str(ai1_won * 100 / total) + '%'
print 'AI2(' + ai2 + ') won: ' + str(ai2_won) + "/" + str(ai2_won * 100 / total) + '%'
print 'Draws: ' + str(draw) + "/" + str(draw * 100 / total) + '%\n'
if sqlite_file:
conn = sqlite3.connect(sqlite_file)
cur = conn.cursor()
query = "UPDATE tests SET ai_ident1 = ?, ai_ident2 = ?, version = ? , faction1 = ?, faction2 = ? WHERE id = ?;"
cur.execute(query, (test.ai_ident1,
test.ai_ident2,
test.version_string,
test.faction1,
test.faction2,
test.test_id))
conn.commit()
conn.close();

View file

@ -1,18 +0,0 @@
[default]
path_to_wesnoth_binary=../../wesnoth
arguments_to_wesnoth_binary=--log-info=ai/testing,mp/connect --nogui --multiplayer --controller1=ai --controller2=ai
number_of_tests=400
ai_config1=ai/ais/default_ai.cfg
ai_config2=ai/ais/default_ai.cfg
faction1=
faction2=
db_ip=127.0.0.1
db_port=5432
db_name=org.wesnoth.ai.test
db_user=wesnoth_ai_test_user
db_pass=PASSWORD
log_file=ai_test__%A_%d_%B_%Y_%I-%M%p.log
map1=multiplayer_Weldyn_Channel
map2=multiplayer_The_Freelands
map3=multiplayer_Den_of_Onis
map4=multiplayer_Fallenstar_Lake

View file

@ -1,178 +0,0 @@
#!/usr/bin/env python
from subprocess import Popen,PIPE
from time import clock, time
from datetime import datetime
import ConfigParser
import os
import string
import random
class GameResult:
ai_config1 = ''
ai_config2 = ''
ai_ident1 = ''
ai_ident2 = ''
duration = '0'
faction1 = ''
faction2 = ''
is_success = 'false'
local_modifications = 'false'
map = ''
repo_release = '0'
test = 'default'
end_turn = '0'
version_string = ''
winner_side = '0'
def __init__(self,_ai_config1,_ai_config2,_faction1,_faction2,_map,_test):
self.ai_config1 = _ai_config1
self.ai_config2 = _ai_config2
self.faction1 = _faction1
self.faction2 = _faction2
self.map = _map
self.test = _test
def filter_non_printable(str):
return ''.join(c for c in str if ord(c) > 31 or ord(c) == 9)
def construct_command_line(cfg,ai1,ai2,f1,f2,map):
wesnoth = cfg.get('default','path_to_wesnoth_binary')
options= cfg.get('default','arguments_to_wesnoth_binary')
ai_config1='--ai_config1='+ai1
ai_config2='--ai_config2='+ai2
if (map==''):
optmap=''
else:
optmap='--scenario='+map
return wesnoth+' '+options+' '+optmap+' '+ai_config1+' '+ai_config2
def do_filter(str,substring):
n = str.find(substring)
if (n>-1):
return n,str[n+len(substring):].strip()
return n,''
def run_game(cfg,game_result):
command_line = construct_command_line(cfg,game_result.ai_config1,game_result.ai_config2, game_result.faction1, game_result.faction2, game_result.map)
print 'Running: '+command_line
start = time()
p = Popen(command_line, shell=True, bufsize=10000000, stdout=PIPE, stderr=PIPE)
# outlines = p.stdout.readlines()
outerrlines = p.stderr.readlines()
print 'Finished'
for line in outerrlines:
str = filter_non_printable(line.strip())
n,s = do_filter(str,'info ai/testing: WINNER:')
if (n>-1):
print 'AND THE WINNER IS: '+s
game_result.winner_side = s
game_result.is_success = 'true'
continue
n,s = do_filter(str,'info ai/testing: VERSION:')
if (n>-1):
#print 'AND THE VERSION IS: '+s
game_result.version_string = s
n1 = s.rfind('(')
n2 = s.rfind(')')
if ((n1>-1) and (n2>-1) and (n2>n1)):
sz = s[n1+1:n2]
#parse local_modifications
n3 = sz.rfind('M')
if (n3>-1):
sz = sz[:n3]
game_result.local_modifications = 1
#parse repo_release
game_result.repo_release = sz
continue
n,s = do_filter(str,'info ai/testing: GAME_END_TURN:')
if (n>-1):
print 'AND THE VICTORY_TURN IS: '+s
game_result.end_turn = s
continue
n,s = do_filter(str,'info ai/testing: AI_IDENTIFIER1:')
if (n>-1):
#print 'AND THE AI_IDENTIFIER1 IS: '+s
game_result.ai_ident1 = s.strip()
continue
n,s = do_filter(str,'info ai/testing: AI_IDENTIFIER2:')
if (n>-1):
#print 'AND THE AI_IDENTIFIER2 IS: '+s
game_result.ai_ident2 = s.strip()
continue
n,s = do_filter(str,'info mp/connect: FACTION1:')
if (n>-1):
#print 'AND THE FACTION1 IS: '+s
game_result.faction1 = s
continue
n,s = do_filter(str,'info mp/connect: FACTION2:')
if (n>-1):
#print 'AND THE FACTION2 IS: '+s
game_result.faction2 = s
continue
game_result.duration = time() - start
if (game_result.is_success=='false'):
print 'Warning: not success!'
print '===================='
print 'stderr:'
for line in outerrlines:
print filter_non_printable(line.strip())
print '===================='
return game_result
def save_result(cfg,log_file,game_result):
print 'Saving to log file....'
print 'game duration: '+str(game_result.duration);
log_file.write('"'+game_result.ai_config1+'", "'+game_result.ai_config2+'", "'+game_result.ai_ident1+'", "'+game_result.ai_ident2+'", "'+ str(game_result.duration)+'", "'+game_result.faction1+'", "'+game_result.faction2+'", "'+str(game_result.is_success)+'", "'+str(game_result.local_modifications)+'", "'+game_result.map+'", "'+str(game_result.repo_release)+'", "'+str(game_result.test)+'", "'+str(game_result.end_turn)+'", "'+str(game_result.version_string)+'", "'+str(game_result.winner_side)+'"\n');
log_file.flush();
print 'Saved to log file'
def maps(cfg):
mp = 1
while 1:
try:
yield cfg.get('default','map' + repr(mp));
mp= mp+1
except:
return
def tests(cfg):
ai1=cfg.get('default','ai_config1').strip()
ai2=cfg.get('default','ai_config2').strip()
f1=cfg.get('default','faction1').strip()
f2=cfg.get('default','faction2').strip()
n=cfg.getint('default','number_of_tests')
maplist = []
for map in maps(cfg):
maplist.append(map)
random.seed()
for i in range(0, n):
map = random.choice(maplist)
d = random.randint(0,1)
print 'TEST: map '+map+' i='+str(i)+' d='+str(d)
if (d==0):
game_result = GameResult(ai1,ai2,f1,f2,map,'default')
else:
game_result = GameResult(ai2,ai1,f2,f1,map,'default')
yield game_result
# main
cfg = ConfigParser.ConfigParser()
cfg.read('ai_test.cfg')
log_file = open(datetime.now().strftime(cfg.get('default','log_file').strip()) , 'w')
log_file.write('"ai_config1"'+', '+'"ai_config2"'+', '+'"ai_ident1"'+', '+'"ai_ident2"'+', '+ '"duration"'+', '+'"faction1"'+', '+'"faction2"'+', '+'"is_success"'+', '+'"local_modifications"'+', '+'"map"'+', '+'"repo_release"'+', '+'"test"'+', '+'"end_turn"'+', '+'"version_string"'+', '+'"winner_side"'+'\n');
log_file.flush();
for test in tests(cfg):
game_result = run_game(cfg,test)
save_result(cfg,log_file,game_result)

View file

@ -1,189 +0,0 @@
--
-- PostgreSQL database dump
--
-- Started on 2009-04-28 03:24:33 EEST
-- In March 2013, svn_release -> repo_release
SET client_encoding = 'UTF8';
SET standard_conforming_strings = off;
SET check_function_bodies = false;
SET client_min_messages = warning;
SET escape_string_warning = off;
--
-- TOC entry 296 (class 2612 OID 45054)
-- Name: plpgsql; Type: PROCEDURAL LANGUAGE; Schema: -; Owner: pgsql
--
CREATE PROCEDURAL LANGUAGE plpgsql;
ALTER PROCEDURAL LANGUAGE plpgsql OWNER TO pgsql;
SET search_path = public, pg_catalog;
SET default_tablespace = '';
SET default_with_oids = false;
--
-- TOC entry 1469 (class 1259 OID 44782)
-- Dependencies: 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 6
-- Name: game; Type: TABLE; Schema: public; Owner: wesnoth_ai_test_user; Tablespace:
--
CREATE TABLE game (
id bigint NOT NULL,
repo_release integer DEFAULT 0 NOT NULL,
datetime timestamp with time zone DEFAULT now() NOT NULL,
winner_side integer DEFAULT 0 NOT NULL,
ai_config1 text DEFAULT ''::text NOT NULL,
ai_config2 text DEFAULT ''::text NOT NULL,
local_modifications boolean DEFAULT false NOT NULL,
turn integer DEFAULT 1 NOT NULL,
ai_ident1 text DEFAULT ''::text NOT NULL,
ai_ident2 text DEFAULT ''::text NOT NULL,
is_success boolean DEFAULT true NOT NULL,
version_string text DEFAULT ''::text NOT NULL,
duration double precision DEFAULT 0 NOT NULL,
map text DEFAULT ''::text NOT NULL,
faction1 text DEFAULT ''::text NOT NULL,
faction2 text DEFAULT ''::text NOT NULL,
test text DEFAULT 'default'::text NOT NULL,
CONSTRAINT winner_side_gte_0 CHECK ((winner_side >= 0))
);
ALTER TABLE public.game OWNER TO wesnoth_ai_test_user;
--
-- TOC entry 1470 (class 1259 OID 45085)
-- Dependencies: 1546 6
-- Name: games_side; Type: VIEW; Schema: public; Owner: wesnoth_ai_test_user
--
CREATE VIEW games_side AS
SELECT game.id, game.repo_release, game.datetime, game.duration, game.map, 1 AS my_side, CASE WHEN (game.winner_side = 1) THEN 1 WHEN (game.winner_side = 2) THEN -1 ELSE 0 END AS outcome, CASE WHEN (game.winner_side = 1) THEN 1 ELSE 0 END AS win, CASE WHEN (game.winner_side = 0) THEN 1 ELSE 0 END AS draw, CASE WHEN (game.winner_side = 2) THEN 1 ELSE 0 END AS loss, CASE WHEN (game.winner_side = 1) THEN game.turn ELSE 0 END AS win_turns, CASE WHEN (game.winner_side = 2) THEN game.turn ELSE 0 END AS loss_turns, game.is_success, game.version_string, game.ai_config1 AS ai_config_me, game.ai_config2 AS ai_config_enemy, game.ai_ident1 AS ai_ident_me, game.ai_ident2 AS ai_ident_enemy, game.local_modifications, game.turn, game.faction1 AS faction_me, game.faction2 AS faction_enemy FROM game WHERE (game.repo_release <> 0) UNION SELECT game.id, game.repo_release, game.datetime, game.duration, game.map, 2 AS my_side, CASE WHEN (game.winner_side = 1) THEN -1 WHEN (game.winner_side = 2) THEN 1 ELSE 0 END AS outcome, CASE WHEN (game.winner_side = 2) THEN 1 ELSE 0 END AS win, CASE WHEN (game.winner_side = 0) THEN 1 ELSE 0 END AS draw, CASE WHEN (game.winner_side = 1) THEN 1 ELSE 0 END AS loss, CASE WHEN (game.winner_side = 2) THEN game.turn ELSE 0 END AS win_turns, CASE WHEN (game.winner_side = 1) THEN game.turn ELSE 0 END AS loss_turns, game.is_success, game.version_string, game.ai_config2 AS ai_config_me, game.ai_config1 AS ai_config_enemy, game.ai_ident2 AS ai_ident_me, game.ai_ident1 AS ai_ident_enemy, game.local_modifications, game.turn, game.faction1 AS faction_me, game.faction2 AS faction_enemy FROM game WHERE (game.repo_release <> 0);
ALTER TABLE public.games_side OWNER TO wesnoth_ai_test_user;
--
-- TOC entry 20 (class 1255 OID 45064)
-- Dependencies: 296 6
-- Name: avg_from(bigint, bigint); Type: FUNCTION; Schema: public; Owner: pgsql
--
CREATE FUNCTION avg_from(bigint, bigint) RETURNS double precision
AS $_$
BEGIN
IF $2=0 THEN
RETURN 0;
ELSE
RETURN cast($1 as double precision)/$2;
END IF;
END;
$_$
LANGUAGE plpgsql IMMUTABLE;
ALTER FUNCTION public.avg_from(bigint, bigint) OWNER TO pgsql;
--
-- TOC entry 1468 (class 1259 OID 44780)
-- Dependencies: 1469 6
-- Name: game_id_seq; Type: SEQUENCE; Schema: public; Owner: wesnoth_ai_test_user
--
CREATE SEQUENCE game_id_seq
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
ALTER TABLE public.game_id_seq OWNER TO wesnoth_ai_test_user;
--
-- TOC entry 1765 (class 0 OID 0)
-- Dependencies: 1468
-- Name: game_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: wesnoth_ai_test_user
--
ALTER SEQUENCE game_id_seq OWNED BY game.id;
--
-- TOC entry 1738 (class 2604 OID 44785)
-- Dependencies: 1469 1468 1469
-- Name: id; Type: DEFAULT; Schema: public; Owner: wesnoth_ai_test_user
--
ALTER TABLE game ALTER COLUMN id SET DEFAULT nextval('game_id_seq'::regclass);
--
-- TOC entry 1757 (class 2606 OID 44794)
-- Dependencies: 1469 1469
-- Name: pk_game; Type: CONSTRAINT; Schema: public; Owner: wesnoth_ai_test_user; Tablespace:
--
ALTER TABLE ONLY game
ADD CONSTRAINT pk_game PRIMARY KEY (id);
--
-- TOC entry 1762 (class 0 OID 0)
-- Dependencies: 6
-- Name: public; Type: ACL; Schema: -; Owner: pgsql
--
REVOKE ALL ON SCHEMA public FROM PUBLIC;
REVOKE ALL ON SCHEMA public FROM pgsql;
GRANT ALL ON SCHEMA public TO pgsql;
GRANT ALL ON SCHEMA public TO PUBLIC;
--
-- TOC entry 1763 (class 0 OID 0)
-- Dependencies: 1469
-- Name: game; Type: ACL; Schema: public; Owner: wesnoth_ai_test_user
--
REVOKE ALL ON TABLE game FROM PUBLIC;
REVOKE ALL ON TABLE game FROM wesnoth_ai_test_user;
GRANT ALL ON TABLE game TO wesnoth_ai_test_user;
GRANT SELECT ON TABLE game TO wesnoth_ai_test_viewer;
--
-- TOC entry 1764 (class 0 OID 0)
-- Dependencies: 1470
-- Name: games_side; Type: ACL; Schema: public; Owner: wesnoth_ai_test_user
--
REVOKE ALL ON TABLE games_side FROM PUBLIC;
REVOKE ALL ON TABLE games_side FROM wesnoth_ai_test_user;
GRANT ALL ON TABLE games_side TO wesnoth_ai_test_user;
GRANT SELECT ON TABLE games_side TO wesnoth_ai_test_viewer;
--
-- TOC entry 1766 (class 0 OID 0)
-- Dependencies: 1468
-- Name: game_id_seq; Type: ACL; Schema: public; Owner: wesnoth_ai_test_user
--
REVOKE ALL ON SEQUENCE game_id_seq FROM PUBLIC;
REVOKE ALL ON SEQUENCE game_id_seq FROM wesnoth_ai_test_user;
GRANT ALL ON SEQUENCE game_id_seq TO wesnoth_ai_test_user;
GRANT SELECT ON SEQUENCE game_id_seq TO wesnoth_ai_test_viewer;
-- Completed on 2009-04-28 03:24:36 EEST
--
-- PostgreSQL database dump complete
--

Binary file not shown.

View file

@ -1,11 +1,12 @@
/*
AI Batch testing suite
Copyright (C) 2009 by Yurii Chernyi <terraninfo@terraninfo.net>
Copyright (C) 2009 - 2013 by Yurii Chernyi <terraninfo@terraninfo.net>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
@ -15,35 +16,30 @@
REQUIRES:
Wesnoth
Postgresql database for storing data
PHP-enabled web server for web frontend
INSTALLATION:
1. Create a role, two users and a database - one for uploader script with INSERT priv (and ability to use the sequence used for generating IDs), and another for web frontend with SELECT priv.
---------------------
CREATE ROLE wesnoth_ai_test_viewer
NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
CREATE ROLE wesnoth_ai_test_user LOGIN
PASSWORD 'YOUR_PASSWORD_FOR_TEST_USER'
NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
GRANT wesnoth_ai_test_viewer TO wesnoth_ai_test_user;
CREATE ROLE wesnoth_ai_test_viewer_impl LOGIN
PASSWORD 'YOUR_PASSWORD_FOR_WEB_FRONTEND_USER'
NOSUPERUSER INHERIT NOCREATEDB NOCREATEROLE;
GRANT wesnoth_ai_test_viewer TO wesnoth_ai_test_viewer_impl;
CREATE DATABASE "org.wesnoth.ai.test"
WITH OWNER = wesnoth_ai_test_user
ENCODING = 'UTF8';
---------------------
2. Restore ai_test_db.backup to that newly created DB to create the DB schema and set privilegies.
3. place wesnoth_ai_test.php at a php-enabled web server
4. Modify passwords/paths in ai_test.cfg and wesnoth_ai_test.php
Python
(optional) PHP-enabled web server for web frontend
USAGE:
1. Edit ai_test.cfg. (Read comments there for explanations)
2. Run "python ai_test.pyi [-p]" to run the tests.
USAGE WITH SQLITE DATABASE:
1. Copy ai_test_empty.sqlite to somewhere outside the git repo.
2. Give ai_test_empty.sqlite write access.
3. place wesnoth.php and wesnoth_test.php at a php-enabled web server.
4. Edit BOTH php files and change the variable $sqlitefile.
5. Edit ai_test.cfg. (Don't forget to change 'sqlite_file' there).
6. Run "python ai_test.py [-p]".
TIPPS:
- Before you start testing use the "-p" parameter
to play a test-game with gui. Then you may want to use
:inspect to see if the ais are set up correctly.
- 100 tests are nothing, 500 tests are good to see some
trends. I recommend to run at least 1000 tests.
- You can make use of your multicores and run multiple
tests at the same time. In Linux the 'System Monitor'
is your friend. You can set the process priority there
to 'Very Low' or pause the process and continue it later.
- In linux you can pause the tests by pressing Ctrl + z.
You can continue the process by running 'fg'.

80
utils/ai_test/wesnoth.php Normal file
View file

@ -0,0 +1,80 @@
<?
$sqlitefile = 'RELATIVE PATH TO SQLITE-FILE';
?>
<html>
<head>
<title>Wesnoth AI Testing Statistics</title>
<link rel="stylesheet" type="text/css" href="http://wiki.wesnoth.org/skins/glamdrol/main.css">
<style>
thead th, thead td {
text-align: center;
}
tbody th, tbody td {
text-align: center;
}
tfoot th, tfoot td {
text-align: center;
}
table {
margin: auto;
}
div {
text-align:center;
}
</style>
</head>
<body>
<div id="main">
<?php
$db = new SQLite3($sqlitefile);
if (!$db) {
print("Connection Failed.");
exit;
} else {
# print("Connection Ok!");
}
?>
<h2>Ai-Test Suite - Test results</h2>
<table>
<tr>
<th>title</th>
<th>ai_ident1</th>
<th>ai_ident2</th>
<th>games</th>
<th>1 won</th>
<th>2 won</th>
<th>draws</th>
</tr>
<?
$query = "SELECT test_id, title, ai_config1, ai_config2, ai_ident1, ai_ident2, map, tests.faction1, tests.faction2, time,
count(games.id) as total_games,
sum(case when winner = 1 then 1 else 0 end) as side1_won,
sum(case when winner = 2 then 1 else 0 end) as side2_won,
sum(case when winner = 0 then 1 else 0 end) as draw,
sum(case when winner = 1 then 1 else 0 end) * 100.0 / count(games.id) as side1_won_p,
sum(case when winner = 2 then 1 else 0 end) * 100.0 / count(games.id) as side2_won_p,
sum(case when winner = 0 then 1 else 0 end) * 100.0 / count(games.id) as draw_p,
avg(games.end_turn) as avg_end_turn
FROM tests, games
WHERE tests.id = games.test_id
GROUP BY test_id
ORDER BY test_id DESC";
$result = $db->query($query) or die('Query failed');
while ($row = $result->fetchArray())
{
$title = (strlen($row['title']) > 30) ? substr($row['title'], 0, 30) . "..." : $row['title'];
echo "<tr>";
echo "<td><a href=wesnoth_test.php?id=" . $row['test_id'] . ">{$title}</a></td>";
echo "<td>{$row['ai_ident1']}</td>";
echo "<td>{$row['ai_ident2']}</td>";
echo "<td>{$row['total_games']}</td>";
echo "<td>" . round($row['side1_won_p'], 2) . "%</td>";
echo "<td>" . round($row['side2_won_p'], 2) . "%</td>";
echo "<td>" . round($row['draw_p'], 2) . "%</td>";
echo "</tr>";
}
?>
</table>
</div>
</body>
</html>

View file

@ -1,203 +0,0 @@
<html>
<head>
<title>Wesnoth AI Testing Statistics</title>
</head>
<body>
<?php
$database = pg_connect("host=127.0.0.1 dbname=org.wesnoth.ai.test user=wesnoth_ai_test_viewer_impl password=PASSWORD");
if (!$database) {
print("Connection Failed.");
exit;
} else {
# print("Connection Ok!");
}
?>
<h2>Latest repository AI wins % graph:</h2>
<?php
$query = "select ai_ident_me,avg_from(sum(win)*100,count(*))::bigint as win_percent from games_side where repo_release=(select max(repo_release) from games_side) group by ai_ident_me order by ai_ident_me;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
$a = "";
$b = "";
$i = true;
while($myrow = pg_fetch_assoc($result)) {
if ($i) {
$a=$a.$myrow['ai_ident_me'];
$b=$b.$myrow['win_percent'];
$i=false;
} else {
$a=$a."|".$myrow['ai_ident_me'];
$b=$b.",".$myrow['win_percent'];
}
}
printf("<img src=\"http://chart.apis.google.com/chart?cht=p3&amp;chd=t:$b&amp;chs=500x200&amp;chl=$a\" alt=\"win percentages\" title=\"win percentages\"");
?>
<h2>By AI:</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, repo_release order by ai_ident_me, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
<h2>By AI and side</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Side</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, my_side, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, my_side, repo_release order by ai_ident_me, my_side, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['my_side'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
<h2>By AI and map</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Map</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, map, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, map, repo_release order by ai_ident_me, map, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['map'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
<h2>By AI and own faction</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Own faction</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, faction_me, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, faction_me, repo_release order by ai_ident_me, faction_me, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['faction_me'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
<h2>By AI and enemy faction</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Enemy faction</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, faction_enemy, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, faction_enemy, repo_release order by ai_ident_me, faction_enemy, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['faction_enemy'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
<h2>By AI and factions</h2>
<table border=1>
<tr>
<th>AI</th>
<th>Own faction</th>
<th>Enemy faction</th>
<th>Repository_Revision</th>
<th>Win %</th>
<th>Games</th>
<th>Wins</th>
<th>Losses</th>
<th>Avg. turns to win</th>
<th>Avg. turns to lose</th>
</tr>
<?php
$query = "select ai_ident_me, faction_me, faction_enemy, repo_release, avg_from(sum(win)*100,count(*)) as win_percent, count(*) as games, sum(win) as wins, sum(draw) as draws, sum(loss) as losses, avg_from(sum(win_turns),sum(win)) as avg_win_turns, avg_from(sum(loss_turns),sum(loss)) as avg_loss_turns from games_side group by ai_ident_me, faction_me, faction_enemy ,repo_release order by ai_ident_me, faction_me, faction_enemy, repo_release desc;";
$result = pg_query($query);
if (!$result) {
echo pg_last_error();
exit();
}
while($myrow = pg_fetch_assoc($result)) {
printf ("<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%.1f</td><td>%d</td><td>%d</td><td>%d</td><td>%.1f</td><td>%.1f</td></tr>",
$myrow['ai_ident_me'],$myrow['faction_me'],$myrow['faction_enemy'],$myrow['repo_release'],$myrow['win_percent'],$myrow['games'],$myrow['wins'],$myrow['losses'],$myrow['avg_win_turns'],$myrow['avg_loss_turns']);
}
?>
</table>
</body>

View file

@ -0,0 +1,185 @@
<?
$sqlitefile = 'RELATIVE PATH TO SQLITE-FILE';
$db = new SQLite3($sqlitefile);
if (!$db) {
print("Connection Failed.");
exit;
} else {
# print("Connection Ok!");
}
$id = $_GET["id"];
$query = "SELECT * FROM tests WHERE id = $id";
$result = $db->query($query) or die('Query failed');
$row = $result->fetchArray();
$title = $row["title"];
$ai_config1 = $row["ai_config1"];
$ai_config2 = $row["ai_config2"];
$ai_ident1 = $row["ai_ident1"];
$ai_ident2 = $row["ai_ident2"];
$map = $row["map"];
$version = $row["version"];
$faction1 = $row["faction1"];
$faction2 = $row["faction2"];
$time = $row["time"];
$query = 'SELECT faction1, faction2,
count(test_id) as total_games,
sum(case when winner = 1 then 1 else 0 end) as side1_won,
sum(case when winner = 2 then 1 else 0 end) as side2_won,
sum(case when winner = 0 then 1 else 0 end) as draw,
sum(case when winner = 1 then 1 else 0 end) * 100. / count(test_id) as side1_won_p,
sum(case when winner = 2 then 1 else 0 end) * 100. / count(test_id) as side2_won_p,
sum(case when winner = 0 then 1 else 0 end) * 100. / count(test_id) as draw_p,
avg(end_turn) as avg_end_turn
FROM "games"
WHERE test_id = ' . $id . '
GROUP BY faction1, faction2';
$result = $db->query($query) or die('Query failed');
?>
<html>
<head>
<title>Wesnoth AI Testing Statistics</title>
<link rel="stylesheet" type="text/css" href="http://wiki.wesnoth.org/skins/glamdrol/main.css">
<style>
thead th, thead td {
text-align: center;
}
tbody th, tbody td {
text-align: center;
}
tfoot th, tfoot td {
text-align: center;
}
table {
margin: auto;
}
div {
text-align:center;
}
</style>
</head>
<body>
<div id="main">
<h2><?echo $title;?></h2>
<script type='text/javascript' src='https://www.google.com/jsapi'></script>
<script type='text/javascript'>
google.load('visualization', '1', {packages:['table']});
google.setOnLoadCallback(drawTable);
function drawTable() {
var data = new google.visualization.DataTable();
data.addColumn('string', 'Side1 / Side2');
data.addColumn('number', 'Drakes');
data.addColumn('number', 'Rebels');
data.addColumn('number', 'Undead');
data.addColumn('number', 'Northerners');
data.addColumn('number', 'Knalgan Alliance');
data.addColumn('number', 'Loyalists');
data.addRow(['Drakes', null, null, null, null, null, null]);
data.addRow(['Rebels', null, null, null, null, null, null]);
data.addRow(['Undead', null, null, null, null, null, null]);
data.addRow(['Northerners', null, null, null, null, null, null]);
data.addRow(['Knalgan Alliance', null, null, null, null, null, null]);
data.addRow(['Loyalists', null, null, null, null, null, null]);
<?
while ($row = $result->fetchArray()) {
echo "data.setCell(toId('".$row['faction1']."')-1, toId('".$row['faction2']."'), ".$row['side1_won_p'].", '".round($row['side1_won_p'],2)."% / ".round($row['side2_won_p'],2)."% / ".$row['total_games']."');";
}
?>
var table = new google.visualization.Table(document.getElementById('table_div'));
var formatter = new google.visualization.ColorFormat();
formatter.addGradientRange(-0.001, 100.0001, 'black', '#FF3333', '#33FF33');
formatter.format(data, 1);
formatter.format(data, 2);
formatter.format(data, 3);
formatter.format(data, 4);
formatter.format(data, 5);
formatter.format(data, 6);
table.draw(data, {showRowNumber: false, allowHtml: true});
}
function toId(faction) {
switch(faction) {
case 'Drakes': return 1;
case 'Rebels': return 2;
case 'Undead': return 3;
case 'Northerners': return 4;
case 'Knalgan Alliance': return 5;
case 'Loyalists': return 6;
}
}
</script>
<div id="table_div"> </div>
</div>
<?
$query = "SELECT test_id,
count(games.id) as total_games,
sum(case when winner = 1 then 1 else 0 end) as side1_won,
sum(case when winner = 2 then 1 else 0 end) as side2_won,
sum(case when winner = 0 then 1 else 0 end) as draw,
sum(case when winner = 1 then 1 else 0 end) * 100.0 / count(games.id) as side1_won_p,
sum(case when winner = 2 then 1 else 0 end) * 100.0 / count(games.id) as side2_won_p,
sum(case when winner = 0 then 1 else 0 end) * 100.0 / count(games.id) as draw_p,
avg(games.end_turn) as avg_end_turn
FROM games
WHERE test_id = " . $id . "
GROUP BY test_id";
$result = $db->query($query) or die('Query failed');
$row = $result->fetchArray()
?>
<table>
<tr>
<td><b>ai_ident1</b></td>
<td><?echo $ai_ident1;?></td>
</tr>
<tr>
<td><b>ai_ident2</b></td>
<td><?echo $ai_ident2;?></td>
</tr>
<tr>
<td><b>ai_config1</b></td>
<td><?echo $ai_config1;?></td>
</tr>
<tr>
<td><b>ai_config2</b></td>
<td><?echo $ai_config2;?></td>
</tr>
<tr>
<td><b>map</b></td>
<td><?echo $map;?></td>
</tr>
<tr>
<td><b>time</b></td>
<td><?echo $time;?></td>
</tr>
<tr>
<td><b>version</b></td>
<td><?echo $version;?></td>
</tr>
<tr>
<td><b>total games</b></td>
<td><?echo $row['total_games'];?></td>
</tr>
<tr>
<td><b>side1 won</b></td>
<td><?echo $row['side1_won'] . " / " . round($row['side1_won_p'], 2) . "%";?></td>
</tr>
<tr>
<td><b>side2 won</b></td>
<td><?echo $row['side2_won'] . " / " . round($row['side2_won_p'], 2) . "%";?></td>
</tr>
<tr>
<td><b>draws</b></td>
<td><?echo $row['draw'] . " / " . round($row['draw_p'], 2) . "%";?></td>
</tr>
<tr>
<td><b>average end turn</b></td>
<td><?echo round($row['avg_end_turn'], 1);?></td>
</tr>
</table>
</body>
</html>