diff --git a/xwords4/linux/scripts/discon_ok2.py b/xwords4/linux/scripts/discon_ok2.py new file mode 100755 index 000000000..82bfa6c33 --- /dev/null +++ b/xwords4/linux/scripts/discon_ok2.py @@ -0,0 +1,986 @@ +#!/usr/bin/env python3 + +import re, os, sys, getopt, shutil, threading, requests, json, glob +import argparse, datetime, random, subprocess, time + +# LOGDIR=./$(basename $0)_logs +# APP_NEW="" +# DO_CLEAN="" +# APP_NEW_PARAMS="" +# NGAMES = 1 +g_UDP_PCT_START = 100 +# UDP_PCT_INCR=10 +# UPGRADE_ODDS="" +# NROOMS="" +# HOST="" +# PORT="" +# TIMEOUT="" +# SAVE_GOOD="" +# MINDEVS="" +# MAXDEVS="" +# ONEPER="" +# RESIGN_PCT=0 +g_DROP_N=0 +# MINRUN=2 # seconds +# ONE_PER_ROOM="" # don't run more than one device at a time per room +# USE_GTK="" +# UNDO_PCT=0 +# ALL_VIA_RQ=${ALL_VIA_RQ:-FALSE} +# SEED="" +# BOARD_SIZES_OLD=(15) +# BOARD_SIZES_NEW=(15) +g_NAMES = [None, 'Brynn', 'Ariela', 'Kati', 'Eric'] +# SEND_CHAT='' +# CORE_COUNT=$(ls core.* 2>/dev/null | wc -l) +# DUP_PACKETS='' +# HTTP_PCT=0 + +# declare -A PIDS +# declare -A APPS +# declare -A NEW_ARGS +# declare -a ARGS +# declare -A ARGS_DEVID +# declare -A ROOMS +# declare -A FILES +# declare -A LOGS +# declare -A MINEND +# ROOM_PIDS = {} +# declare -a APPS_OLD=() +# declare -a DICTS= # wants to be =() too? +# declare -A CHECKED_ROOMS + +# function cleanup() { +# APP="$(basename $APP_NEW)" +# while pidof $APP; do +# echo "killing existing $APP instances..." +# killall -9 $APP +# sleep 1 +# done +# echo "cleaning everything up...." +# if [ -d $LOGDIR ]; then +# mv $LOGDIR /tmp/${LOGDIR}_$$ +# fi +# if [ -e $(dirname $0)/../../relay/xwrelay.log ]; then +# mkdir -p /tmp/${LOGDIR}_$$ +# mv $(dirname $0)/../../relay/xwrelay.log /tmp/${LOGDIR}_$$ +# fi + +# echo "DELETE FROM games WHERE room LIKE 'ROOM_%';" | psql -q -t xwgames +# echo "DELETE FROM msgs WHERE NOT devid in (SELECT unnest(devids) from games);" | psql -q -t xwgames +# } + +# function connName() { +# LOG=$1 +# grep -a 'got_connect_cmd: connName' $LOG | \ +# tail -n 1 | \ +# sed 's,^.*connName: \"\(.*\)\" (reconnect=.)$,\1,' +# } + +# function check_room() { +# ROOM=$1 +# if [ -z ${CHECKED_ROOMS[$ROOM]:-""} ]; then +# NUM=$(echo "SELECT COUNT(*) FROM games "\ +# "WHERE NOT dead "\ +# "AND ntotal!=sum_array(nperdevice) "\ +# "AND ntotal != -sum_array(nperdevice) "\ +# "AND room='$ROOM'" | +# psql -q -t xwgames) +# NUM=$((NUM+0)) +# if [ "$NUM" -gt 0 ]; then +# echo "$ROOM in the DB has unconsummated games. Remove them." +# exit 1 +# else +# CHECKED_ROOMS[$ROOM]=1 +# fi +# fi +# } + +# print_cmdline() { +# local COUNTER=$1 +# local LOG=${LOGS[$COUNTER]} +# echo -n "New cmdline: " >> $LOG +# echo "${APPS[$COUNTER]} ${NEW_ARGS[$COUNTER]} ${ARGS[$COUNTER]}" >> $LOG +# } + +def pick_ndevs(args): + RNUM = random.randint(0, 99) + if RNUM > 90 and args.MAXDEVS >= 4: + NDEVS = 4 + elif RNUM > 75 and args.MAXDEVS >= 3: + NDEVS = 3 + else: + NDEVS = 2 + if NDEVS < args.MINDEVS: + NDEVS = args.MINDEVS + return NDEVS + +# # Given a device count, figure out how many local players per device. +# # "1 1" would be a two-device game with 1 each. "1 2 1" a +# # three-device game with four players total +def figure_locals(args, NDEVS): + NPLAYERS = pick_ndevs(args) + if NPLAYERS < NDEVS: NPLAYERS = NDEVS + + EXTRAS = 0 + if not args.ONEPER: + EXTRAS = NPLAYERS - NDEVS + + LOCALS = [] + for IGNORE in range(NDEVS): + COUNT = 1 + if EXTRAS > 0: + EXTRA = random.randint(0, EXTRAS) + if EXTRA > 0: + COUNT += EXTRA + EXTRAS -= EXTRA + LOCALS.append(COUNT) + assert 0 < sum(LOCALS) <= 4 + return LOCALS + +def player_params(args, NLOCALS, NPLAYERS, NAME_INDX): + assert 0 < NPLAYERS <= 4 + NREMOTES = NPLAYERS - NLOCALS + PARAMS = [] + while NLOCALS > 0 or NREMOTES > 0: + if 0 == random.randint(0, 2) and 0 < NLOCALS: + PARAMS += ['--robot', g_NAMES[NAME_INDX], '--robot-iq', str(random.randint(1,100))] + NLOCALS -= 1 + NAME_INDX += 1 + elif 0 < NREMOTES: + PARAMS += ['--remote-player'] + NREMOTES -= 1 + return PARAMS + +def logReaderStub(dev): dev.logReaderMain() + +class Device(): + sConnnameMap = {} + sHasLDevIDMap = {} + sConnNamePat = re.compile('.*got_connect_cmd: connName: "([^"]+)".*$') + sGameOverPat = re.compile('.*\[unused tiles\].*') + sTilesLeftPat = re.compile('.*pool_removeTiles: (\d+) tiles left in pool') + sRelayIDPat = re.compile('.*UPDATE games.*seed=(\d+),.*relayid=\'([^\']+)\'.*') + + def __init__(self, args, indx, app, params, room, db, log, nInGame): + self.indx = indx + self.args = args + self.pid = 0 + self.gameOver = False + self.app = app + self.params = params + self.room = room + self.db = db + self.logPath = log + self.nInGame = nInGame + # runtime stuff; init now + self.proc = None + self.connname = None + self.devID = '' + self.launchCount = 0 + self.allDone = False # when true, can be killed + self.nTilesLeft = -1 # negative means don't know + self.relayID = None + self.relaySeed = 0 + + with open(self.logPath, "w") as log: + log.write('New cmdline: ' + self.app + ' ' + (' '.join([str(p) for p in self.params]))) + log.write(os.linesep) + + def logReaderMain(self): + assert self and self.proc + stdout, stderr = self.proc.communicate() + # print('logReaderMain called; opening:', self.logPath, 'flag:', flag) + nLines = 0 + with open(self.logPath, 'a') as log: + for line in stderr.splitlines(): + nLines += 1 + log.write(line + os.linesep) + + # check for connname + if not self.connname: + match = Device.sConnNamePat.match(line) + if match: + self.connname = match.group(1) + if not self.connname in Device.sConnnameMap: + Device.sConnnameMap[self.connname] = set() + Device.sConnnameMap[self.connname].add(self) + + # check for game over + if not self.gameOver: + match = Device.sGameOverPat.match(line) + if match: self.gameOver = True + + # Check every line for tiles left + match = Device.sTilesLeftPat.match(line) + if match: self.nTilesLeft = int(match.group(1)) + + if not self.relayID: + match = Device.sRelayIDPat.match(line) + if match: + self.relaySeed = int(match.group(1)) + self.relayID = match.group(2) + + # print('logReaderMain done, wrote lines:', nLines, 'to', self.logPath); + + def launch(self): + args = [self.app] + [str(p) for p in self.params] + if self.devID: args.extend( ' '.split(self.devID)) + self.launchCount += 1 + # self.logStream = open(self.logPath, flag) + self.proc = subprocess.Popen(args, stdout = subprocess.DEVNULL, + stderr = subprocess.PIPE, universal_newlines = True) + self.pid = self.proc.pid + self.minEnd = datetime.datetime.now() + datetime.timedelta(seconds = self.args.MINRUN) + + # Now start a thread to read stdio + self.reader = threading.Thread(target = logReaderStub, args=(self,)) + self.reader.isDaemon = True + self.reader.start() + + def running(self): + return self.proc and not self.proc.poll() + + def minTimeExpired(self): + assert self.proc + return self.minEnd < datetime.datetime.now() + + def kill(self): + if self.proc.poll() is None: + self.proc.terminate() + self.proc.wait() + assert self.proc.poll() is not None + + self.reader.join() + self.reader = None + else: + print('NOT killing') + self.proc = None + self.check_game() + + def moveFiles(self): + assert not self.running() + shutil.move(self.logPath, self.args.LOGDIR + '/done') + shutil.move(self.db, self.args.LOGDIR + '/done') + + def send_dead(self): + JSON = json.dumps([{'relayID': self.relayID, 'seed': self.relaySeed}]) + url = 'http://%s/xw4/relay.py/kill' % (self.args.HOST) + req = requests.get(url, params = {'params' : JSON}) + + def getTilesCount(self): + result = None + if self.nTilesLeft != -1: + result = '%.2d:%.2d' % (self.indx, self.nTilesLeft) + return result + + def update_ldevid(self): + if not self.app in Device.sHasLDevIDMap: + hasLDevID = False + proc = subprocess.Popen([self.app, '--help'], stderr=subprocess.PIPE) + # output, err, = proc.communicate() + for line in proc.stderr.readlines(): + if b'--ldevid' in line: + hasLDevID = True + break + print('found --ldevid:', hasLDevID); + Device.sHasLDevIDMap[self.app] = hasLDevID + + if Device.sHasLDevIDMap[self.app]: + RNUM = random.randint(0, 99) + if not self.devID: + if RNUM < 30: + self.devID = '--ldevid LINUX_TEST_%.5d_' % (self.indx) + elif RNUM < 10: + self.devID += 'x' + + def check_game(self): + if self.gameOver and not self.allDone: + allDone = False + if len(Device.sConnnameMap[self.connname]) == self.nInGame: + allDone = True + for dev in Device.sConnnameMap[self.connname]: + if dev == self: continue + if not dev.gameOver: + allDone = False + break + + if allDone: + for dev in Device.sConnnameMap[self.connname]: + dev.allDone = True + + # print('Closing', self.connname, datetime.datetime.now()) + # for dev in Device.sConnnameMap[self.connname]: + # dev.kill() +# # kill_from_logs $OTHERS $KEY +# for ID in $OTHERS $KEY; do +# echo -n "${ID}:${LOGS[$ID]}, " +# kill_from_log ${LOGS[$ID]} || /bin/true +# send_dead $ID +# close_device $ID $DONEDIR "game over" +# done +# echo "" +# # XWRELAY_ERROR_DELETED may be old +# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then +# echo "deleting $LOG $(connName $LOG) b/c another resigned" +# kill_from_log $LOG || /bin/true +# close_device $KEY $DEADDIR "other resigned" +# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then +# echo "deleting $LOG $(connName $LOG) b/c another resigned" +# kill_from_log $LOG || /bin/true +# close_device $KEY $DEADDIR "other resigned" +# else +# maybe_resign $KEY +# fi +# } + + +def build_cmds(args): + devs = [] + COUNTER = 0 + PLAT_PARMS = [] + if not args.USE_GTK: + PLAT_PARMS += ['--curses', '--close-stdin'] + + for GAME in range(1, args.NGAMES + 1): + ROOM = 'ROOM_%.3d' % (GAME % args.NROOMS) + # check_room $ROOM + NDEVS = pick_ndevs(args) + LOCALS = figure_locals(args, NDEVS) # as array + NPLAYERS = sum(LOCALS) + assert(len(LOCALS) == NDEVS) + DICT = args.DICTS[GAME % len(args.DICTS)] + # make one in three games public + PUBLIC = [] + if random.randint(0, 3) == 0: PUBLIC = ['--make-public', '--join-public'] + DEV = 0 + for NLOCALS in LOCALS: + DEV += 1 + FILE="%s/GAME_%d_%d.sql3" % (args.LOGDIR, GAME, DEV) + LOG='%s/%d_%d_LOG.txt' % (args.LOGDIR, GAME, DEV) + # os.system("rm -f $LOG") # clear the log + + # APPS[$COUNTER]="$APP_NEW" + # NEW_ARGS[$COUNTER]="$APP_NEW_PARAMS" + BOARD_SIZE = ['--board-size', '15'] + # if [ 0 -lt ${#APPS_OLD[@]} ]; then + # # 50% chance of starting out with old app + # NAPPS=$((1+${#APPS_OLD[*]})) + # if [ 0 -lt $((RANDOM%$NAPPS)) ]; then + # APPS[$COUNTER]=${APPS_OLD[$((RANDOM%${#APPS_OLD[*]}))]} + # BOARD_SIZE="--board-size ${BOARD_SIZES_OLD[$((RANDOM%${#BOARD_SIZES_OLD[*]}))]}" + # NEW_ARGS[$COUNTER]="" + # fi + # fi + + PARAMS = player_params(args, NLOCALS, NPLAYERS, DEV) + PARAMS += PLAT_PARMS + PARAMS += BOARD_SIZE + ['--room', ROOM, '--trade-pct', args.TRADE_PCT, '--sort-tiles'] + if args.UNDO_PCT > 0: + PARAMS += ['--undo-pct', args.UNDO_PCT] + PARAMS += [ '--game-dict', DICT, '--relay-port', args.PORT, '--host', args.HOST] + PARAMS += ['--slow-robot', '1:3', '--skip-confirm'] + PARAMS += ['--db', FILE] + if random.randint(0,100) % 100 < g_UDP_PCT_START: + PARAMS += ['--use-udp'] + + PARAMS += ['--drop-nth-packet', g_DROP_N] + if random.randint(0, 100) < args.HTTP_PCT: + PARAMS += ['--use-http'] + + PARAMS += ['--split-packets', '2'] + if args.SEND_CHAT: + PARAMS += ['--send-chat', args.SEND_CHAT] + + if args.DUP_PACKETS: + PARAMS += ['--dup-packets'] + # PARAMS += ['--my-port', '1024'] + # PARAMS += ['--savefail-pct', 10] + + # With the --seed param passed, games with more than 2 + # devices don't get going. No idea why. This param is NOT + # passed in the old bash version of this script, so fixing + # it isn't a priority. + # PARAMS += ['--seed', args.SEED] + PARAMS += PUBLIC + if DEV > 1: + PARAMS += ['--force-channel', DEV - 1] + else: + PARAMS += ['--server'] + + # print('PARAMS:', PARAMS) + + dev = Device(args, COUNTER, args.APP_NEW, PARAMS, ROOM, FILE, LOG, len(LOCALS)) + dev.update_ldevid() + devs.append(dev) + + COUNTER += 1 + return devs + +# read_resume_cmds() { +# COUNTER=0 +# for LOG in $(ls $LOGDIR/*.txt); do +# echo "need to parse cmd and deal with changes" +# exit 1 +# CMD=$(head -n 1 $LOG) + +# ARGS[$COUNTER]=$CMD +# LOGS[$COUNTER]=$LOG +# PIDS[$COUNTER]=0 + +# set $CMD +# while [ $# -gt 0 ]; do +# case $1 in +# --file) +# FILES[$COUNTER]=$2 +# shift +# ;; +# --room) +# ROOMS[$COUNTER]=$2 +# shift +# ;; +# esac +# shift +# done +# COUNTER=$((COUNTER+1)) +# done +# ROOM_PIDS[$ROOM]=0 +# } + +# launch() { +# KEY=$1 +# LOG=${LOGS[$KEY]} +# APP="${APPS[$KEY]}" +# if [ -z "$APP" ]; then +# echo "error: no app set" +# exit 1 +# fi +# PARAMS="${NEW_ARGS[$KEY]} ${ARGS[$KEY]} ${ARGS_DEVID[$KEY]}" +# exec $APP $PARAMS >/dev/null 2>>$LOG +# } + +# # launch_via_rq() { +# # KEY=$1 +# # RELAYID=$2 +# # PIPE=${PIPES[$KEY]} +# # ../relay/rq -f $RELAYID -o $PIPE & +# # CMD="${CMDS[$KEY]}" +# # exec $CMD >/dev/null 2>>$LOG +# # } + +# send_dead() { +# ID=$1 +# DB=${FILES[$ID]} +# while :; do +# [ -f $DB ] || break # it's gone +# RES=$(echo 'select relayid, seed from games limit 1;' | sqlite3 -separator ' ' $DB || /bin/true) +# [ -n "$RES" ] && break +# sleep 0.2 +# done +# RELAYID=$(echo $RES | awk '{print $1}') +# SEED=$(echo $RES | awk '{print $2}') +# JSON="[{\"relayID\":\"$RELAYID\", \"seed\":$SEED}]" +# curl -G --data-urlencode params="$JSON" http://$HOST/xw4/relay.py/kill >/dev/null 2>&1 +# } + +# close_device() { +# ID=$1 +# MVTO=$2 +# REASON="$3" +# PID=${PIDS[$ID]} +# if [ $PID -ne 0 ]; then +# kill ${PIDS[$ID]} 2>/dev/null +# wait ${PIDS[$ID]} +# ROOM=${ROOMS[$ID]} +# [ ${ROOM_PIDS[$ROOM]} -eq $PID ] && ROOM_PIDS[$ROOM]=0 +# fi +# unset PIDS[$ID] +# unset ARGS[$ID] +# echo "closing game: $REASON" >> ${LOGS[$ID]} +# if [ -n "$MVTO" ]; then +# [ -f "${FILES[$ID]}" ] && mv ${FILES[$ID]} $MVTO +# mv ${LOGS[$ID]} $MVTO +# else +# rm -f ${FILES[$ID]} +# rm -f ${LOGS[$ID]} +# fi +# unset FILES[$ID] +# unset LOGS[$ID] +# unset ROOMS[$ID] +# unset APPS[$ID] +# unset ARGS_DEVID[$ID] + +# COUNT=${#ARGS[*]} +# echo "$COUNT devices left playing..." +# } + +# OBITS="" + +# kill_from_log() { +# LOG=$1 +# RELAYID=$(./scripts/relayID.sh --long $LOG) +# if [ -n "$RELAYID" ]; then +# OBITS="$OBITS -d $RELAYID" +# if [ 0 -eq $(($RANDOM%2)) ]; then +# ../relay/rq -a $HOST $OBITS 2>/dev/null || /bin/true +# OBITS="" +# fi +# return 0 # success +# fi +# echo "unable to send kill command for $LOG" +# return 1 +# } + +# maybe_resign() { +# if [ "$RESIGN_PCT" -gt 0 ]; then +# KEY=$1 +# LOG=${LOGS[$KEY]} +# if grep -aq XWRELAY_ALLHERE $LOG; then +# if [ $((${RANDOM}%100)) -lt $RESIGN_PCT ]; then +# echo "making $LOG $(connName $LOG) resign..." +# kill_from_log $LOG && close_device $KEY $DEADDIR "resignation forced" || /bin/true +# fi +# fi +# fi +# } + +# try_upgrade() { +# KEY=$1 +# if [ 0 -lt ${#APPS_OLD[@]} ]; then +# if [ $APP_NEW != "${APPS[$KEY]}" ]; then +# # one in five chance of upgrading +# if [ 0 -eq $((RANDOM % UPGRADE_ODDS)) ]; then +# APPS[$KEY]=$APP_NEW +# NEW_ARGS[$KEY]="$APP_NEW_PARAMS" +# print_cmdline $KEY +# fi +# fi +# fi +# } + +# try_upgrade_upd() { +# KEY=$1 +# CMD=${ARGS[$KEY]} +# if [ "${CMD/--use-udp/}" = "${CMD}" ]; then +# if [ $((RANDOM % 100)) -lt $UDP_PCT_INCR ]; then +# ARGS[$KEY]="$CMD --use-udp" +# echo -n "$(date +%r): " +# echo "upgrading key $KEY to use UDP" +# fi +# fi +# } + +# check_game() { +# KEY=$1 +# LOG=${LOGS[$KEY]} +# CONNNAME="$(connName $LOG)" +# OTHERS="" +# if [ -n "$CONNNAME" ]; then +# if grep -aq '\[unused tiles\]' $LOG ; then +# for INDX in ${!LOGS[*]}; do +# [ $INDX -eq $KEY ] && continue +# ALOG=${LOGS[$INDX]} +# CONNNAME2="$(connName $ALOG)" +# if [ "$CONNNAME2" = "$CONNNAME" ]; then +# if ! grep -aq '\[unused tiles\]' $ALOG; then +# OTHERS="" +# break +# fi +# OTHERS="$OTHERS $INDX" +# fi +# done +# fi +# fi + +# if [ -n "$OTHERS" ]; then +# echo -n "Closing $CONNNAME [$(date)]: " +# # kill_from_logs $OTHERS $KEY +# for ID in $OTHERS $KEY; do +# echo -n "${ID}:${LOGS[$ID]}, " +# kill_from_log ${LOGS[$ID]} || /bin/true +# send_dead $ID +# close_device $ID $DONEDIR "game over" +# done +# echo "" +# # XWRELAY_ERROR_DELETED may be old +# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then +# echo "deleting $LOG $(connName $LOG) b/c another resigned" +# kill_from_log $LOG || /bin/true +# close_device $KEY $DEADDIR "other resigned" +# elif grep -aq 'relay_error_curses(XWRELAY_ERROR_DEADGAME)' $LOG; then +# echo "deleting $LOG $(connName $LOG) b/c another resigned" +# kill_from_log $LOG || /bin/true +# close_device $KEY $DEADDIR "other resigned" +# else +# maybe_resign $KEY +# fi +# } + +# increment_drop() { +# KEY=$1 +# CMD=${ARGS[$KEY]} +# if [ "$CMD" != "${CMD/drop-nth-packet//}" ]; then +# DROP_N=$(echo $CMD | sed 's,^.*drop-nth-packet \(-*[0-9]*\) .*$,\1,') +# if [ $DROP_N -gt 0 ]; then +# NEXT_N=$((DROP_N+1)) +# ARGS[$KEY]=$(echo $CMD | sed "s,^\(.*drop-nth-packet \)$DROP_N\(.*\)$,\1$NEXT_N\2,") +# fi +# fi +# } + +def summarizeTileCounts(devs): + nDevs = len(devs) + strs = [dev.getTilesCount() for dev in devs] + strs = [s for s in strs if s] + nWithTiles = len(strs) + print('%s %d/%d %s' % (datetime.datetime.now().strftime("%H:%M:%S"), nDevs, nWithTiles, ' '.join(strs))) + +def countCores(): + return len(glob.glob1('/tmp',"core*")) + +def run_cmds(args, devs): + nCores = countCores() + endTime = datetime.datetime.now() + datetime.timedelta(seconds = args.TIMEOUT) + LOOPCOUNT = 0 + + while len(devs) > 0: + if countCores() > nCores: + print('core file count increased; exiting') + break + if datetime.datetime.now() > endTime: + print('outta time; outta here') + break + + LOOPCOUNT += 1 + if 0 == LOOPCOUNT % 20: summarizeTileCounts(devs) + + dev = random.choice(devs) + if not dev.running(): + if dev.allDone: + dev.moveFiles() + dev.send_dead() + devs.remove(dev) + else: +# if [ -n "$ONE_PER_ROOM" -a 0 -ne ${ROOM_PIDS[$ROOM]} ]; then +# continue +# fi +# try_upgrade $KEY +# try_upgrade_upd $KEY + dev.launch() +# PID=$! +# # renice doesn't work on one of my machines... +# renice -n 1 -p $PID >/dev/null 2>&1 || /bin/true +# PIDS[$KEY]=$PID +# ROOM_PIDS[$ROOM]=$PID +# MINEND[$KEY]=$(($NOW + $MINRUN)) + elif not dev.minTimeExpired(): + # print('sleeping...') + time.sleep(2) + else: + dev.kill() + # if g_DROP_N >= 0: dev.increment_drop() + # update_ldevid $KEY + + + # if we get here via a break, kill any remaining games + if devs: + print('stopping %d remaining games' % (len(devs))) + for dev in devs: + if dev.running(): dev.kill() + +# run_via_rq() { +# # launch then kill all games to give chance to hook up +# for KEY in ${!ARGS[*]}; do +# echo "launching $KEY" +# launch $KEY & +# PID=$! +# sleep 1 +# kill $PID +# wait $PID +# # add_pipe $KEY +# done + +# echo "now running via rq" +# # then run them +# while :; do +# COUNT=${#ARGS[*]} +# [ 0 -ge $COUNT ] && break + +# INDX=$(($RANDOM%COUNT)) +# KEYS=( ${!ARGS[*]} ) +# KEY=${KEYS[$INDX]} +# CMD=${ARGS[$KEY]} + +# RELAYID=$(./scripts/relayID.sh --short ${LOGS[$KEY]}) +# MSG_COUNT=$(../relay/rq -a $HOST -m $RELAYID 2>/dev/null | sed 's,^.*-- ,,') +# if [ $MSG_COUNT -gt 0 ]; then +# launch $KEY & +# PID=$! +# sleep 2 +# kill $PID || /bin/true +# wait $PID +# fi +# [ "$DROP_N" -ge 0 ] && increment_drop $KEY +# check_game $KEY +# done +# } # run_via_rq + +# function getArg() { +# [ 1 -lt "$#" ] || usage "$1 requires an argument" +# echo $2 +# } + +def mkParser(): + parser = argparse.ArgumentParser() + parser.add_argument('--send-chat', dest = 'SEND_CHAT', type = str, default = None, + help = 'the message to send') + + parser.add_argument('--app-new', dest = 'APP_NEW', default = './obj_linux_memdbg/xwords', + help = 'the app we\'ll use') + parser.add_argument('--num-games', dest = 'NGAMES', type = int, default = 1, help = 'number of games') + parser.add_argument('--num-rooms', dest = 'NROOMS', type = int, default = 0, + help = 'number of roooms (default to --num-games)') + parser.add_argument('--no-timeout', dest = 'TIMEOUT', default = False, action = 'store_true', + help = 'run forever (default proportional to number of games') + parser.add_argument('--log-root', dest='LOGROOT', default = '.', help = 'where logfiles go') + parser.add_argument('--dup-packets', dest = 'DUP_PACKETS', default = False, help = 'send all packet twice') + parser.add_argument('--use-gtk', dest = 'USE_GTK', default = False, action = 'store_true', + help = 'run games using gtk instead of ncurses') + # # + # # echo " [--clean-start] \\" >&2 + parser.add_argument('--game-dict', dest = 'DICTS', action = 'append', default = []) + # # echo " [--help] \\" >&2 + parser.add_argument('--host', dest = 'HOST', default = 'localhost', + help = 'relay hostname') + # # echo " [--max-devs ] \\" >&2 + parser.add_argument('--min-devs', dest = 'MINDEVS', type = int, default = 2, + help = 'No game will have fewer devices than this') + parser.add_argument('--max-devs', dest = 'MAXDEVS', type = int, default = 4, + help = 'No game will have more devices than this') + parser.add_argument('--min-run', dest = 'MINRUN', type = int, default = 2, + help = 'Keep each run alive at least this many seconds') + # # echo " [--new-app &2 + # # echo " [--new-app-args [arg*]] # passed only to new app \\" >&2 + # # echo " [--num-rooms ] \\" >&2 + # # echo " [--old-app &2 + parser.add_argument('--one-per', dest = 'ONEPER', default = False, + action = 'store_true', help = 'force one player per device') + parser.add_argument('--port', dest = 'PORT', default = 10997, type = int, \ + help = 'Port relay\'s on') + parser.add_argument('--resign-pct', dest = 'RESIGN_PCT', default = 0, type = int, \ + help = 'Odds of resigning [0..100]') + # # echo " [--no-timeout] # run until all games done \\" >&2 + parser.add_argument('--seed', type = int, dest = 'SEED', + default = random.randint(1, 1000000000)) + # # echo " [--send-chat \\" >&2 + # # echo " [--udp-incr ] \\" >&2 + # # echo " [--udp-start ] # default: $UDP_PCT_START \\" >&2 + # # echo " [--undo-pct ] \\" >&2 + parser.add_argument('--http-pct', dest = 'HTTP_PCT', default = 0, type = int, + help = 'pct of games to be using web api') + + parser.add_argument('--undo-pct', dest = 'UNDO_PCT', default = 0, type = int) + parser.add_argument('--trade-pct', dest = 'TRADE_PCT', default = 0, type = int) + + return parser + +# ####################################################### +# ##################### MAIN begins ##################### +# ####################################################### + +def parseArgs(): + args = mkParser().parse_args() + assignDefaults(args) + print(args) + return args + # print(options) + +# while [ "$#" -gt 0 ]; do +# case $1 in +# --udp-start) +# UDP_PCT_START=$(getArg $*) +# shift +# ;; +# --udp-incr) +# UDP_PCT_INCR=$(getArg $*) +# shift +# ;; +# --clean-start) +# DO_CLEAN=1 +# ;; +# --num-games) +# NGAMES=$(getArg $*) +# shift +# ;; +# --num-rooms) +# NROOMS=$(getArg $*) +# shift +# ;; +# --old-app) +# APPS_OLD[${#APPS_OLD[@]}]=$(getArg $*) +# shift +# ;; +# --log-root) +# [ -d $2 ] || usage "$1: no such directory $2" +# LOGDIR=$2/$(basename $0)_logs +# shift +# ;; +# --dup-packets) + # DUP_PACKETS=1 +# ;; +# --new-app) +# APP_NEW=$(getArg $*) +# shift +# ;; +# --new-app-args) +# APP_NEW_PARAMS="${2}" +# echo "got $APP_NEW_PARAMS" +# shift +# ;; +# --game-dict) +# DICTS[${#DICTS[@]}]=$(getArg $*) +# shift +# ;; +# --min-devs) +# MINDEVS=$(getArg $*) +# shift +# ;; +# --max-devs) +# MAXDEVS=$(getArg $*) +# shift +# ;; +# --min-run) +# MINRUN=$(getArg $*) +# [ $MINRUN -ge 2 -a $MINRUN -le 60 ] || usage "$1: n must be 2 <= n <= 60" +# shift +# ;; +# --one-per) +# ONEPER=TRUE +# ;; +# --host) +# HOST=$(getArg $*) +# shift +# ;; +# --port) +# PORT=$(getArg $*) +# shift +# ;; +# --seed) +# SEED=$(getArg $*) +# shift +# ;; +# --undo-pct) +# UNDO_PCT=$(getArg $*) +# shift +# ;; +# --http-pct) +# HTTP_PCT=$(getArg $*) +# [ $HTTP_PCT -ge 0 -a $HTTP_PCT -le 100 ] || usage "$1: n must be 0 <= n <= 100" +# shift +# ;; +# --send-chat) +# SEND_CHAT=$(getArg $*) +# shift +# ;; +# --resign-pct) +# RESIGN_PCT=$(getArg $*) +# [ $RESIGN_PCT -ge 0 -a $RESIGN_PCT -le 100 ] || usage "$1: n must be 0 <= n <= 100" +# shift +# ;; +# --no-timeout) +# TIMEOUT=0x7FFFFFFF +# ;; +# --help) +# usage +# ;; +# *) usage "unrecognized option $1" +# ;; +# esac +# shift +# done + +def assignDefaults(args): + if not args.NROOMS: args.NROOMS = args.NGAMES + if args.TIMEOUT: args.TIMEOUT = 100000000000 # huge number + if len(args.DICTS) == 0: args.DICTS.append('CollegeEng_2to8.xwd') + else: args.TIMEOUT = args.NGAMES * 60 + 500 + args.LOGDIR = os.path.basename(sys.argv[0]) + '_logs' + # Move an existing logdir aside + if os.path.exists(args.LOGDIR): + shutil.move(args.LOGDIR, '/tmp/' + args.LOGDIR + '_' + str(random.randint(0, 100000))) + for d in ['', 'done', 'dead',]: + os.mkdir(args.LOGDIR + '/' + d) +# [ -z "$SAVE_GOOD" ] && SAVE_GOOD=YES +# # [ -z "$RESIGN_PCT" -a "$NGAMES" -gt 1 ] && RESIGN_RATIO=1000 || RESIGN_RATIO=0 +# [ -z "$DROP_N" ] && DROP_N=0 +# [ -z "$USE_GTK" ] && USE_GTK=FALSE +# [ -z "$UPGRADE_ODDS" ] && UPGRADE_ODDS=10 +# #$((NGAMES/50)) +# [ 0 -eq $UPGRADE_ODDS ] && UPGRADE_ODDS=1 +# [ -n "$SEED" ] && RANDOM=$SEED +# [ -z "$ONEPER" -a $NROOMS -lt $NGAMES ] && usage "use --one-per if --num-rooms < --num-games" + +# [ -n "$DO_CLEAN" ] && cleanup + +# RESUME="" +# for FILE in $(ls $LOGDIR/*.{xwg,txt} 2>/dev/null); do +# if [ -e $FILE ]; then +# echo "Unfinished games found in $LOGDIR; continue with them (or discard)?" +# read -p " " ANSWER +# case "$ANSWER" in +# y|yes|Y|YES) +# RESUME=1 +# ;; +# *) +# ;; +# esac +# fi +# break +# done + +# if [ -z "$RESUME" -a -d $LOGDIR ]; then +# NEWNAME="$(basename $LOGDIR)_$$" +# (cd $(dirname $LOGDIR) && mv $(basename $LOGDIR) /tmp/${NEWNAME}) +# fi +# mkdir -p $LOGDIR + +# if [ "$SAVE_GOOD" = YES ]; then +# DONEDIR=$LOGDIR/done +# mkdir -p $DONEDIR +# fi +# DEADDIR=$LOGDIR/dead +# mkdir -p $DEADDIR + +# for VAR in NGAMES NROOMS USE_GTK TIMEOUT HOST PORT SAVE_GOOD \ +# MINDEVS MAXDEVS ONEPER RESIGN_PCT DROP_N ALL_VIA_RQ SEED \ +# APP_NEW; do +# echo "$VAR:" $(eval "echo \$${VAR}") 1>&2 +# done +# echo "DICTS: ${DICTS[*]}" +# echo -n "APPS_OLD: "; [ xx = "${APPS_OLD[*]+xx}" ] && echo "${APPS_OLD[*]}" || echo "" + +# echo "*********$0 starting: $(date)**************" +# STARTTIME=$(date +%s) +# [ -z "$RESUME" ] && build_cmds || read_resume_cmds +# if [ TRUE = "$ALL_VIA_RQ" ]; then +# run_via_rq +# else +# run_cmds +# fi + +# wait + +# SECONDS=$(($(date +%s)-$STARTTIME)) +# HOURS=$((SECONDS/3600)) +# SECONDS=$((SECONDS%3600)) +# MINUTES=$((SECONDS/60)) +# SECONDS=$((SECONDS%60)) +# echo "*********$0 finished: $(date) (took $HOURS:$MINUTES:$SECONDS)**************" + +def main(): + args = parseArgs() + devs = build_cmds(args) + run_cmds(args, devs) + +############################################################################## +if __name__ == '__main__': + main()