mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-05 20:45:49 +01:00
add test for rematch (once for now)
This commit is contained in:
parent
be9159fc35
commit
d021bb4029
4 changed files with 199 additions and 40 deletions
|
@ -135,6 +135,7 @@ static void relay_requestJoin_curses( void* closure, const XP_UCHAR* devID,
|
|||
XP_U16 nPlayersTotal, XP_U16 seed, XP_U16 lang );
|
||||
#endif
|
||||
|
||||
static XP_Bool rematch_and_save( CursesBoardGlobals* bGlobals, XP_U32* newGameID );
|
||||
static void disposeBoard( CursesBoardGlobals* bGlobals );
|
||||
static void initCP( CommonGlobals* cGlobals );
|
||||
static CursesBoardGlobals* commonInit( CursesBoardState* cbState,
|
||||
|
@ -595,6 +596,20 @@ enableDraw( CursesBoardGlobals* bGlobals, const cb_dims* dims )
|
|||
setupBoard( bGlobals );
|
||||
}
|
||||
|
||||
static CursesBoardGlobals*
|
||||
findOrOpenForGameID( CursesBoardState* cbState, XP_U32 gameID,
|
||||
const CurGameInfo* gi, const CommsAddrRec* returnAddr )
|
||||
{
|
||||
CursesBoardGlobals* result = NULL;
|
||||
sqlite3_int64 rowids[1];
|
||||
int nRowIDs = VSIZE(rowids);
|
||||
gdb_getRowsForGameID( cbState->params->pDb, gameID, rowids, &nRowIDs );
|
||||
if ( 1 == nRowIDs ) {
|
||||
result = findOrOpen( cbState, rowids[0], gi, returnAddr );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CursesBoardGlobals*
|
||||
findOrOpen( CursesBoardState* cbState, sqlite3_int64 rowid,
|
||||
const CurGameInfo* gi, const CommsAddrRec* returnAddr )
|
||||
|
@ -657,12 +672,7 @@ void
|
|||
cb_addInvite( CursesBoardState* cbState, XP_U32 gameID, XP_U16 forceChannel,
|
||||
const CommsAddrRec* destAddr )
|
||||
{
|
||||
sqlite3_int64 rowids[1];
|
||||
int nRowIDs = VSIZE(rowids);
|
||||
gdb_getRowsForGameID( cbState->params->pDb, gameID, rowids, &nRowIDs );
|
||||
XP_ASSERT( 1 == nRowIDs );
|
||||
|
||||
CursesBoardGlobals* bGlobals = findOrOpen( cbState, rowids[0], NULL, NULL );
|
||||
CursesBoardGlobals* bGlobals = findOrOpenForGameID( cbState, gameID, NULL, NULL );
|
||||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
CommsCtxt* comms = cGlobals->game.comms;
|
||||
|
||||
|
@ -676,6 +686,40 @@ cb_addInvite( CursesBoardState* cbState, XP_U32 gameID, XP_U16 forceChannel,
|
|||
comms_invite( comms, NULL_XWE, &nli, destAddr, XP_TRUE );
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
cb_makeRematch( CursesBoardState* cbState, XP_U32 gameID, XP_U32* newGameID )
|
||||
{
|
||||
CursesBoardGlobals* bGlobals = findOrOpenForGameID( cbState, gameID, NULL, NULL );
|
||||
XP_Bool success = rematch_and_save( bGlobals, newGameID );
|
||||
return success;
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
cb_makeMoveIf( CursesBoardState* cbState, XP_U32 gameID )
|
||||
{
|
||||
CursesBoardGlobals* bGlobals =
|
||||
findOrOpenForGameID( cbState, gameID, NULL, NULL );
|
||||
XP_Bool success = !!bGlobals;
|
||||
if ( success ) {
|
||||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
BoardCtxt* board = cGlobals->game.board;
|
||||
success = board_canHint( board );
|
||||
if ( success ) {
|
||||
XP_Bool ignored;
|
||||
success = board_requestHint( cGlobals->game.board, NULL_XWE,
|
||||
#ifdef XWFEATURE_SEARCHLIMIT
|
||||
XP_FALSE,
|
||||
#endif
|
||||
XP_FALSE, &ignored );
|
||||
if ( success ) {
|
||||
success = board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE, NULL );
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_RETURNF( "%s", boolToStr(success) );
|
||||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
kill_board( gpointer data )
|
||||
{
|
||||
|
@ -938,7 +982,6 @@ rematch_and_save_once( CursesBoardGlobals* bGlobals )
|
|||
{
|
||||
LOG_FUNC();
|
||||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
CursesBoardState* cbState = bGlobals->cbState;
|
||||
|
||||
int32_t alreadyDone;
|
||||
gchar key[128];
|
||||
|
@ -947,21 +990,37 @@ rematch_and_save_once( CursesBoardGlobals* bGlobals )
|
|||
&& 0 != alreadyDone ) {
|
||||
XP_LOGFF( "already rematched game %X", cGlobals->gi->gameID );
|
||||
} else {
|
||||
CursesBoardGlobals* bGlobalsNew = commonInit( cbState, -1, NULL );
|
||||
|
||||
XP_Bool success = game_makeRematch( &bGlobals->cGlobals.game, NULL_XWE,
|
||||
bGlobalsNew->cGlobals.util,
|
||||
&cGlobals->cp, &bGlobalsNew->cGlobals.procs,
|
||||
&bGlobalsNew->cGlobals.game, "newName" );
|
||||
if ( success ) {
|
||||
linuxSaveGame( &bGlobalsNew->cGlobals );
|
||||
if ( rematch_and_save( bGlobals, NULL ) ) {
|
||||
gdb_storeInt( cGlobals->params->pDb, key, 1 );
|
||||
}
|
||||
disposeBoard( bGlobalsNew );
|
||||
}
|
||||
LOG_RETURN_VOID();
|
||||
}
|
||||
|
||||
static XP_Bool
|
||||
rematch_and_save( CursesBoardGlobals* bGlobals, XP_U32* newGameID )
|
||||
{
|
||||
LOG_FUNC();
|
||||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
CursesBoardState* cbState = bGlobals->cbState;
|
||||
|
||||
CursesBoardGlobals* bGlobalsNew = commonInit( cbState, -1, NULL );
|
||||
|
||||
XP_Bool success = game_makeRematch( &bGlobals->cGlobals.game, NULL_XWE,
|
||||
bGlobalsNew->cGlobals.util,
|
||||
&cGlobals->cp, &bGlobalsNew->cGlobals.procs,
|
||||
&bGlobalsNew->cGlobals.game, "newName" );
|
||||
if ( success ) {
|
||||
if ( !!newGameID ) {
|
||||
*newGameID = bGlobalsNew->cGlobals.gi->gameID;
|
||||
}
|
||||
linuxSaveGame( &bGlobalsNew->cGlobals );
|
||||
}
|
||||
disposeBoard( bGlobalsNew );
|
||||
LOG_RETURNF( "%s", boolToStr(success) );
|
||||
return success;
|
||||
}
|
||||
|
||||
static void
|
||||
curses_util_notifyGameOver( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe), XP_S16 quitter )
|
||||
{
|
||||
|
|
|
@ -52,6 +52,9 @@ void cb_feedGame( CursesBoardState* cbState, XP_U32 gameID,
|
|||
const XP_U8* buf, XP_U16 len, const CommsAddrRec* from );
|
||||
void cb_addInvite( CursesBoardState* cbState, XP_U32 gameID, XP_U16 forceChannel,
|
||||
const CommsAddrRec* destAddr );
|
||||
XP_Bool cb_makeRematch( CursesBoardState* cbState, XP_U32 gameID, XP_U32* newGameID );
|
||||
XP_Bool cb_makeMoveIf( CursesBoardState* cbState, XP_U32 gameID );
|
||||
|
||||
void cb_closeAll( CursesBoardState* cbState );
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1447,6 +1447,30 @@ onGameSaved( CursesAppGlobals* aGlobals, sqlite3_int64 rowid, bool isNew )
|
|||
cgl_refreshOne( aGlobals->gameList, rowid, isNew );
|
||||
}
|
||||
|
||||
static XP_U32
|
||||
castGid( cJSON* obj )
|
||||
{
|
||||
XP_U32 gameID;
|
||||
sscanf( obj->valuestring, "%X", &gameID );
|
||||
return gameID;
|
||||
}
|
||||
|
||||
static XP_U32
|
||||
gidFromObject( cJSON* obj )
|
||||
{
|
||||
cJSON* tmp = cJSON_GetObjectItem( obj, "gid" );
|
||||
XP_ASSERT( !!tmp );
|
||||
return castGid( tmp );
|
||||
}
|
||||
|
||||
static void
|
||||
addGIDToObject( cJSON* obj, XP_U32 gid, const char* key )
|
||||
{
|
||||
char buf[16];
|
||||
sprintf( buf, "%08X", gid );
|
||||
cJSON_AddStringToObject( obj, key, buf );
|
||||
}
|
||||
|
||||
static XP_Bool
|
||||
makeGameFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
{
|
||||
|
@ -1457,11 +1481,9 @@ makeGameFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
gi.boardSize = 15;
|
||||
gi.traySize = 7;
|
||||
|
||||
cJSON* tmp = cJSON_GetObjectItem( args, "gid" );
|
||||
XP_ASSERT( !!tmp );
|
||||
sscanf( tmp->valuestring, "%X", &gi.gameID );
|
||||
gi.gameID = gidFromObject( args );
|
||||
|
||||
tmp = cJSON_GetObjectItem( args, "nPlayers" );
|
||||
cJSON* tmp = cJSON_GetObjectItem( args, "nPlayers" );
|
||||
XP_ASSERT( !!tmp );
|
||||
gi.nPlayers = tmp->valueint;
|
||||
|
||||
|
@ -1506,18 +1528,13 @@ inviteFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
/* XP_LOGFF( "(%s)", buf ); */
|
||||
/* } */
|
||||
|
||||
XP_U32 gameID;
|
||||
cJSON* tmp = cJSON_GetObjectItem( args, "gid" );
|
||||
XP_ASSERT( !!tmp );
|
||||
sscanf( tmp->valuestring, "%X", &gameID );
|
||||
XP_U32 gameID = gidFromObject( args );
|
||||
|
||||
tmp = cJSON_GetObjectItem( args, "channel" );
|
||||
cJSON* tmp = cJSON_GetObjectItem( args, "channel" );
|
||||
XP_ASSERT( !!tmp );
|
||||
XP_U16 channel = tmp->valueint;
|
||||
XP_LOGFF( "read channel: %X", channel );
|
||||
|
||||
/* CursesBoardState* cbState, XP_U32 gameID, XP_U16 forceChannel, */
|
||||
/* const CommsAddrRec* destAddr */
|
||||
CommsAddrRec destAddr = {0};
|
||||
cJSON* addr = cJSON_GetObjectItem( args, "addr" );
|
||||
XP_ASSERT( !!addr );
|
||||
|
@ -1541,6 +1558,28 @@ inviteFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
return XP_TRUE;
|
||||
}
|
||||
|
||||
static XP_Bool
|
||||
moveifFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
{
|
||||
XP_U32 gameID = gidFromObject( args );
|
||||
return cb_makeMoveIf( aGlobals->cbState, gameID );
|
||||
}
|
||||
|
||||
/* Return 'gid' of new game */
|
||||
static XP_U32
|
||||
rematchFromArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
{
|
||||
XP_U32 result = 0;
|
||||
|
||||
XP_U32 gameID = gidFromObject( args );
|
||||
|
||||
XP_U32 newGameID = 0;
|
||||
if ( cb_makeRematch( aGlobals->cbState, gameID, &newGameID ) ) {
|
||||
result = newGameID;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static cJSON*
|
||||
getGamesStateForArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
||||
{
|
||||
|
@ -1549,14 +1588,12 @@ getGamesStateForArgs( CursesAppGlobals* aGlobals, cJSON* args )
|
|||
LaunchParams* params = aGlobals->cag.params;
|
||||
cJSON* gids = cJSON_GetObjectItem(args, "gids" );
|
||||
for ( int ii = 0 ; ii < cJSON_GetArraySize(gids) ; ++ii ) {
|
||||
XP_U32 gameID;
|
||||
cJSON* gid = cJSON_GetArrayItem( gids, ii );
|
||||
sscanf( gid->valuestring, "%X", &gameID );
|
||||
XP_U32 gameID = castGid( cJSON_GetArrayItem( gids, ii ) );
|
||||
|
||||
GameInfo gib;
|
||||
if ( gdb_getGameInfoForGID( params->pDb, gameID, &gib ) ) {
|
||||
cJSON* item = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject( item, "gid", gid->valuestring );
|
||||
addGIDToObject( item, gameID, "gid" );
|
||||
cJSON_AddBoolToObject( item, "gameOver", gib.gameOver );
|
||||
cJSON_AddNumberToObject( item, "nPending", gib.nPending );
|
||||
cJSON_AddNumberToObject( item, "nMoves", gib.nMoves );
|
||||
|
@ -1631,6 +1668,15 @@ on_incoming_signal( GSocketService* XP_UNUSED(service),
|
|||
} else if ( 0 == strcmp( cmdStr, "invite" ) ) {
|
||||
XP_Bool success = inviteFromArgs( aGlobals, args );
|
||||
response = makeBoolObj( "success", success );
|
||||
} else if ( 0 == strcmp( cmdStr, "moveIf" ) ) {
|
||||
XP_Bool success = moveifFromArgs( aGlobals, args );
|
||||
response = makeBoolObj( "success", success );
|
||||
} else if ( 0 == strcmp( cmdStr, "rematch" ) ) {
|
||||
XP_U32 newGameID = rematchFromArgs( aGlobals, args );
|
||||
if ( 0 != newGameID ) {
|
||||
response = cJSON_CreateObject();
|
||||
addGIDToObject( response, newGameID, "newGid" );
|
||||
}
|
||||
} else if ( 0 == strcmp( cmdStr, "gamesState" ) ) {
|
||||
response = getGamesStateForArgs( aGlobals, args );
|
||||
} else {
|
||||
|
|
|
@ -37,10 +37,11 @@ class GuestGameInfo():
|
|||
|
||||
# Should be subclass of GuestGameInfo
|
||||
class HostedInfo():
|
||||
def __init__(self, guests):
|
||||
def __init__(self, guests, gid=None, invitesSent=False):
|
||||
self.guestNames = guests
|
||||
self.gid = '{:08X}'.format(random.randint(0, 0x7FFFFFFF))
|
||||
self.invitesSent = False
|
||||
self.gid = gid and gid or '{:08X}'.format(random.randint(1, 0x7FFFFFFF))
|
||||
assert len(self.gid) == 8
|
||||
self.invitesSent = invitesSent
|
||||
|
||||
def __str__(self):
|
||||
return 'gid: {}, guests: {}'.format(self.gid, self.guestNames)
|
||||
|
@ -165,6 +166,16 @@ class Device():
|
|||
while not self.endTime or not os.path.exists(self.cmdSocketName):
|
||||
time.sleep(0.2)
|
||||
|
||||
def moveOne(self):
|
||||
moved = False
|
||||
gids = [game.gid for game in self._allGames() if not self.gameOver(game.gid)]
|
||||
random.shuffle(gids)
|
||||
for gid in gids:
|
||||
response = self._sendWaitReply('moveIf', gid=gid)
|
||||
moved = response.get('success', False)
|
||||
if moved: break
|
||||
return moved
|
||||
|
||||
def _sendWaitReply(self, cmd, **kwargs):
|
||||
self.launchIfNot()
|
||||
|
||||
|
@ -229,15 +240,28 @@ class Device():
|
|||
if not stepped:
|
||||
if not self.endTime:
|
||||
self.launchIfNot()
|
||||
elif datetime.datetime.now() > self.endTime:
|
||||
self.quit()
|
||||
elif self.moveOne():
|
||||
pass
|
||||
else:
|
||||
now = datetime.datetime.now()
|
||||
if now > self.endTime:
|
||||
self.quit()
|
||||
else:
|
||||
# self._log('sleeping with {} to go'.format(self.endTime-now))
|
||||
time.sleep(0.5)
|
||||
# self._log('sleeping with {} to go'.format(self.endTime-now))
|
||||
time.sleep(0.5)
|
||||
stepped = True;
|
||||
|
||||
# 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):
|
||||
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, newGid, True))
|
||||
for guest in guests:
|
||||
Device.getFor(guest).expectInvite(newGid)
|
||||
|
||||
def invite(self, game):
|
||||
failed = False
|
||||
for ii in range(len(game.guestNames)):
|
||||
|
@ -365,6 +389,10 @@ class Device():
|
|||
Device._devs[host] = dev
|
||||
return dev
|
||||
|
||||
@staticmethod
|
||||
def playersIn(gid):
|
||||
return [dev.host for dev in Device.devsWith(gid)]
|
||||
|
||||
@staticmethod
|
||||
# return all devices (up to 4 of them) that are host or guest in a
|
||||
# game with <gid>"""
|
||||
|
@ -376,6 +404,16 @@ class Device():
|
|||
def getAll():
|
||||
return [dev for dev in Device._devs.values()]
|
||||
|
||||
@staticmethod
|
||||
def getFor(player):
|
||||
result = None
|
||||
for dev in Device.getAll():
|
||||
if dev.host == player:
|
||||
result = dev
|
||||
break;
|
||||
assert result
|
||||
return result
|
||||
|
||||
def addGameWith(self, guests):
|
||||
# self._log('addGameWith({})'.format(guests))
|
||||
hosted = HostedInfo(guests)
|
||||
|
@ -407,11 +445,22 @@ 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:
|
||||
|
@ -424,6 +473,8 @@ 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)
|
||||
|
|
Loading…
Add table
Reference in a new issue