mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2024-12-28 09:58:30 +01:00
improve rematch test
This commit is contained in:
parent
c726477878
commit
c31bc3c0eb
2 changed files with 76 additions and 42 deletions
|
@ -1692,6 +1692,9 @@ on_incoming_signal( GSocketService* XP_UNUSED(service),
|
|||
if ( success ) {
|
||||
addGIDToObject( &response, newGameID, "newGid" );
|
||||
}
|
||||
} else if ( 0 == strcmp( cmdStr, "getStates" ) ) {
|
||||
cJSON* gids = getGamesStateForArgs( aGlobals, args );
|
||||
addObjectToObject( &response, "states", gids );
|
||||
} else {
|
||||
success = XP_FALSE;
|
||||
XP_ASSERT(0);
|
||||
|
|
|
@ -5,6 +5,7 @@ import argparse, datetime, json, os, random, shutil, signal, \
|
|||
|
||||
g_NAMES = ['Brynn', 'Ariela', 'Kati', 'Eric']
|
||||
gDone = False
|
||||
gGamesMade = 0
|
||||
|
||||
def log(args, msg):
|
||||
if args.VERBOSE:
|
||||
|
@ -31,22 +32,33 @@ def chooseNames(nPlayers):
|
|||
result.append(players.pop(indx))
|
||||
return result
|
||||
|
||||
class GuestGameInfo():
|
||||
def __init__(self, gid):
|
||||
class GameInfo():
|
||||
def __init__(self, gid, rematchLevel):
|
||||
self.gid = gid
|
||||
|
||||
# Should be subclass of GuestGameInfo
|
||||
class HostedInfo():
|
||||
def __init__(self, guestNames, **kwargs):
|
||||
self.guestNames = guestNames
|
||||
self.gid = kwargs.get('gid')
|
||||
self.needsInvite = kwargs.get('needsInvite', True)
|
||||
self.state = {}
|
||||
assert isinstance(rematchLevel, int)
|
||||
self.rematchLevel = rematchLevel
|
||||
|
||||
def setGid(self, gid):
|
||||
# set only once!
|
||||
assert 8 == len(gid) and not self.gid
|
||||
self.gid = gid
|
||||
|
||||
def gameOver(self):
|
||||
return self.state.get('gameOver', False)
|
||||
|
||||
class GuestGameInfo(GameInfo):
|
||||
def __init__(self, gid, rematchLevel):
|
||||
super().__init__(gid, rematchLevel)
|
||||
|
||||
class HostGameInfo(GameInfo):
|
||||
def __init__(self, guestNames, **kwargs):
|
||||
super().__init__(kwargs.get('gid'), kwargs.get('rematchLevel'))
|
||||
self.guestNames = guestNames
|
||||
self.needsInvite = kwargs.get('needsInvite', True)
|
||||
global gGamesMade;
|
||||
gGamesMade += 1
|
||||
|
||||
def __str__(self):
|
||||
return 'gid: {}, guests: {}'.format(self.gid, self.guestNames)
|
||||
|
||||
|
@ -102,13 +114,13 @@ class GameStatus():
|
|||
dev = Device._devs.get(player)
|
||||
initial = player[0]
|
||||
arg2 = -1
|
||||
status = dev.gameStates.get(gid)
|
||||
if status:
|
||||
if status.get('gameOver', False):
|
||||
gameState = dev.gameFor(gid).state
|
||||
if gameState:
|
||||
if gameState.get('gameOver', False):
|
||||
initial = initial.lower()
|
||||
arg2 = status.get('nPending', 0)
|
||||
arg2 = gameState.get('nPending', 0)
|
||||
else:
|
||||
arg2 = status.get('nTiles')
|
||||
arg2 = gameState.get('nTiles')
|
||||
lineTxt += '{}{: 3}'.format(initial, arg2)
|
||||
results.append(lineTxt)
|
||||
return ' '.join(results)
|
||||
|
@ -136,13 +148,12 @@ class Device():
|
|||
self.mqttDevID = None
|
||||
self.smsNumber = args.WITH_SMS and '{}_phone'.format(host) or None
|
||||
self.host = host
|
||||
self.hostedGames = [] # array of HostedInfo for each game I host
|
||||
self.hostedGames = [] # array of HostGameInfo for each game I host
|
||||
self.guestGames = []
|
||||
self.script = '{}/{}.sh'.format(Device._logdir, host)
|
||||
self.dbName = '{}/{}.db'.format(Device._logdir, host)
|
||||
self.logfile = '{}/{}_log.txt'.format(Device._logdir, host)
|
||||
self.cmdSocketName = '{}/{}.sock'.format(Device._logdir, host)
|
||||
self.gameStates = {}
|
||||
self._keyCur = 10000 * (1 + g_NAMES.index(host))
|
||||
|
||||
def init(self):
|
||||
|
@ -251,7 +262,7 @@ class Device():
|
|||
if not self.endTime:
|
||||
self.launchIfNot()
|
||||
elif datetime.datetime.now() > self.endTime:
|
||||
self.quit()
|
||||
self.rematchOrQuit()
|
||||
elif self.moveOne():
|
||||
pass
|
||||
else:
|
||||
|
@ -261,16 +272,20 @@ class Device():
|
|||
|
||||
# I may be a guest or a host in this game. Rematch works either
|
||||
# way. But how I figure out the other players differs.
|
||||
def rematch(self, gid):
|
||||
def rematch(self, game):
|
||||
gid = game.gid
|
||||
newGid = self._sendWaitReply('rematch', gid=gid).get('newGid')
|
||||
if newGid:
|
||||
guests = Device.playersIn(gid)
|
||||
guests.remove(self.host)
|
||||
self._log('rematch: new host: {}; new guest[s]: {}, gid: {}'.format(self.host, guests, newGid))
|
||||
|
||||
self.hostedGames.append(HostedInfo(guests, needsInvite=False, gid=newGid))
|
||||
rematchLevel = game.rematchLevel - 1
|
||||
assert rematchLevel >= 0 # fired
|
||||
self.hostedGames.append(HostGameInfo(guests, needsInvite=False, gid=newGid,
|
||||
rematchLevel=rematchLevel))
|
||||
for guest in guests:
|
||||
Device.getFor(guest).expectInvite(newGid)
|
||||
Device.getFor(guest).expectInvite(newGid, rematchLevel)
|
||||
|
||||
def invite(self, game):
|
||||
failed = False
|
||||
|
@ -284,13 +299,13 @@ class Device():
|
|||
channel=ii+1, addr=addr)
|
||||
|
||||
if response['success']:
|
||||
guestDev.expectInvite(game.gid)
|
||||
guestDev.expectInvite(game.gid, game.rematchLevel)
|
||||
else:
|
||||
failed = True
|
||||
if not failed: game.needsInvite = False
|
||||
|
||||
def expectInvite(self, gid):
|
||||
self.guestGames.append(GuestGameInfo(gid))
|
||||
def expectInvite(self, gid, rematchLevel):
|
||||
self.guestGames.append(GuestGameInfo(gid, rematchLevel))
|
||||
self.launchIfNot()
|
||||
|
||||
# Return true only if all games I host are finished on all games.
|
||||
|
@ -311,7 +326,7 @@ class Device():
|
|||
def gameOver(self, gid):
|
||||
result = False
|
||||
"""Is the game is over for *this* device"""
|
||||
gameState = self.gameStates.get(gid, None)
|
||||
gameState = self.gameFor(gid).state
|
||||
if gameState:
|
||||
result = gameState.get('gameOver', False) and 0 == gameState.get('nPending', 1)
|
||||
# if result: self._log('gameOver({}) => {}'.format(gid, result))
|
||||
|
@ -329,6 +344,32 @@ class Device():
|
|||
withGid = [game for game in self._allGames() if gid == game.gid]
|
||||
return 0 < len(withGid)
|
||||
|
||||
def gameFor(self, gid):
|
||||
for game in self._allGames():
|
||||
if gid == game.gid:
|
||||
return game
|
||||
|
||||
def rematchOrQuit(self):
|
||||
if self.endTime:
|
||||
gids = [game.gid for game in self._allGames() if not self.gameOver(game.gid)]
|
||||
response = self._sendWaitReply('getStates', gids=gids)
|
||||
|
||||
anyRematched = False
|
||||
for obj in response.get('states', []):
|
||||
game = self.gameFor(obj.get('gid'))
|
||||
game.state = obj
|
||||
|
||||
if game.gameOver() and 0 < game.rematchLevel:
|
||||
self.rematch(game)
|
||||
game.rematchLevel = 0 # so won't be used again
|
||||
anyRematched = True
|
||||
|
||||
if not anyRematched:
|
||||
response = self._sendWaitReply('quit')
|
||||
self.watcher.join()
|
||||
self.watcher = None
|
||||
assert not self.endTime
|
||||
|
||||
def quit(self):
|
||||
if self.endTime:
|
||||
allGames = self._allGames()
|
||||
|
@ -336,8 +377,7 @@ class Device():
|
|||
response = self._sendWaitReply('quit', gids=gids)
|
||||
|
||||
for obj in response.get('states', []):
|
||||
gid = obj.get('gid')
|
||||
self.gameStates[gid] = obj
|
||||
self.gameFor(obj.get('gid')).state = obj
|
||||
|
||||
# wait for the thing to actually die
|
||||
self.watcher.join()
|
||||
|
@ -422,7 +462,8 @@ class Device():
|
|||
return result
|
||||
|
||||
def addGameWith(self, guests):
|
||||
self.hostedGames.append(HostedInfo(guests, needsInvite=True))
|
||||
self.hostedGames.append(HostGameInfo(guests, needsInvite=True,
|
||||
rematchLevel=self.args.REMATCH_LEVEL))
|
||||
for guest in guests:
|
||||
Device.deviceFor(self.args, guest) # in case this device never hosts
|
||||
|
||||
|
@ -450,22 +491,11 @@ def openOnExit(args):
|
|||
subprocess.Popen([str(arg) for arg in appargs], stdout = subprocess.DEVNULL,
|
||||
stderr = subprocess.DEVNULL, universal_newlines = True)
|
||||
|
||||
# Pick a game that's joined -- all invites accepted -- and call
|
||||
# rematch on it. Return True if successful
|
||||
def testRematch():
|
||||
for dev in Device.getAll():
|
||||
for gid, status in dev.gameStates.items():
|
||||
if 2 < status.get('nMoves', 0):
|
||||
dev.rematch(gid)
|
||||
return True
|
||||
return False
|
||||
|
||||
def mainLoop(args, devs):
|
||||
startCount = len(devs)
|
||||
|
||||
startTime = datetime.datetime.now()
|
||||
nextStallCheck = startTime + datetime.timedelta(seconds = 20)
|
||||
rematchTested = False
|
||||
|
||||
while 0 < len(devs):
|
||||
if gDone:
|
||||
|
@ -478,8 +508,6 @@ def mainLoop(args, devs):
|
|||
devs.remove(dev)
|
||||
log(args, 'removed dev for {}; {} devs left'.format(dev.host, len(devs)))
|
||||
|
||||
if not rematchTested: rematchTested = testRematch()
|
||||
|
||||
now = datetime.datetime.now()
|
||||
if devs and now > nextStallCheck:
|
||||
nextStallCheck = now + datetime.timedelta(seconds = 10)
|
||||
|
@ -601,8 +629,8 @@ def mkParser():
|
|||
parser.add_argument('--board-size', dest = 'BOARD_SIZE', type = int, default = 15,
|
||||
help = 'Use <n>x<n> size board')
|
||||
|
||||
# parser.add_argument('--rematch-limit-secs', dest = 'REMATCH_SECS', type = int, default = 0,
|
||||
# help = 'rematch games that end within this many seconds of script launch')
|
||||
parser.add_argument('--rematch-level', dest = 'REMATCH_LEVEL', type = int, default = 0,
|
||||
help = 'rematch games down to this ancestry/depth')
|
||||
|
||||
# envpat = 'DISCON_COREPAT'
|
||||
# parser.add_argument('--core-pat', dest = 'CORE_PAT', default = os.environ.get(envpat),
|
||||
|
@ -666,6 +694,9 @@ def main():
|
|||
dev.quit()
|
||||
mainLoop(args, devs)
|
||||
|
||||
elapsed = datetime.datetime.now() - startTime
|
||||
print('played {} games in {}'.format(gGamesMade, elapsed))
|
||||
|
||||
if args.OPEN_ON_EXIT: openOnExit(args)
|
||||
|
||||
##############################################################################
|
||||
|
|
Loading…
Reference in a new issue