Merge branch 'android_branch' into android_bt

This commit is contained in:
Eric House 2012-05-03 21:44:35 -07:00
commit d59b19514b
16 changed files with 418 additions and 233 deletions

View file

@ -198,6 +198,7 @@ typedef struct CommonPrefs {
XP_Bool sortNewTiles; /* applies to all games */
#ifdef XWFEATURE_SLOW_ROBOT
XP_U16 robotThinkMin, robotThinkMax;
XP_U16 robotTradePct;
#endif
XP_Bool showColors; /* applies to all games */
XP_Bool allowPeek; /* applies to all games */

View file

@ -250,7 +250,7 @@ putOneBit( MemStreamCtxt* stream, XP_U16 bit )
if ( stream->curWritePos == stream->nBytesWritten ) {
stream_putU8( (XWStreamCtxt*)stream, 0 ); /* increments curPos */
} else {
++stream->curWritePos;
stream->buf[stream->curWritePos++] = 0;
}
}

View file

@ -71,6 +71,8 @@ static void buildModelFromStack( ModelCtxt* model, StackCtxt* stack,
MovePrintFuncPre mpfpr,
MovePrintFuncPost mpfpo, void* closure );
static void setPendingCounts( ModelCtxt* model, XP_S16 turn );
static XP_S16 setContains( const TrayTileSet* tiles, Tile tile );
static void removeTile( TrayTileSet* tiles, XP_U16 index );
static void loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
XP_U16 version, PlayerCtxt* pc );
static void writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
@ -290,6 +292,13 @@ model_destroy( ModelCtxt* model )
XP_FREE( model->vol.mpool, model );
} /* model_destroy */
XP_U32
model_getHash( const ModelCtxt* model )
{
XP_ASSERT( !!model->vol.stack );
return stack_getHash( model->vol.stack );
}
#ifdef STREAM_VERS_BIGBOARD
void
model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses, XP_U16 nBonuses )
@ -787,39 +796,31 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
XP_U16 nMovesSought, XP_U16* turnP, XP_S16* moveNumP )
{
StackCtxt* stack = model->vol.stack;
StackEntry entry;
XP_U16 turn = 0;
Tile blankTile = dict_getBlankTile( model_getDictionary(model) );
XP_Bool success = XP_TRUE;
XP_Bool success;
XP_S16 moveSought = !!moveNumP ? *moveNumP : -1;
XP_U16 nMovesUndone;
XP_U16 nStackEntries;
XP_U16 nStackEntries = stack_getNEntries( stack );
nStackEntries = stack_getNEntries( stack );
if ( nStackEntries < nMovesSought ) {
return XP_FALSE;
} else if ( nStackEntries <= model->nPlayers ) {
return XP_FALSE;
}
for ( nMovesUndone = 0; success && nMovesUndone < nMovesSought; ) {
success = stack_popEntry( stack, &entry );
if ( success ) {
++nMovesUndone;
if ( moveSought < 0 ) {
moveSought = entry.moveNum - 1;
} else if ( moveSought-- != entry.moveNum ) {
if ( (0 <= moveSought && moveSought >= nStackEntries)
|| ( nStackEntries < nMovesSought )
|| ( nStackEntries <= model->nPlayers ) ) {
success = XP_FALSE;
} else {
XP_U16 nMovesUndone = 0;
XP_U16 turn;
StackEntry entry;
Tile blankTile = dict_getBlankTile( model_getDictionary(model) );
for ( ; ; ) {
success = stack_popEntry( stack, &entry );
if ( !success ) {
break;
}
++nMovesUndone;
turn = entry.playerNum;
model_resetCurrentTurn( model, turn );
if ( entry.moveType == MOVE_TYPE ) {
/* get the tiles out of player's tray and back into the
pool */
replaceNewTiles( model, pool, turn, &entry.u.move.newTiles);
@ -839,27 +840,36 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
}
} else if ( entry.moveType == PHONY_TYPE ) {
/* nothing to do, since nothing happened */
} else {
XP_ASSERT( entry.moveType == ASSIGN_TYPE );
success = XP_FALSE;
break;
}
/* exit if we've undone what we're supposed to. If the sought
stack height has been provided, use that. Otherwise go by move
count. */
if ( 0 <= moveSought ) {
if ( moveSought == entry.moveNum ) {
break;
}
} else if ( nMovesSought == nMovesUndone ) {
break;
}
}
/* Find the first MOVE still on the stack and highlight its tiles since
they're now the most recent move. Trades and lost turns ignored. */
nStackEntries = stack_getNEntries( stack );
for ( ; ; ) {
for ( nStackEntries = stack_getNEntries( stack );
0 < nStackEntries; --nStackEntries ) {
StackEntry entry;
if ( nStackEntries == 0 ||
!stack_getNthEntry( stack, nStackEntries - 1, &entry ) ) {
if ( !stack_getNthEntry( stack, nStackEntries - 1, &entry ) ) {
break;
}
if ( entry.moveType == MOVE_TYPE ) {
if ( entry.moveType == ASSIGN_TYPE ) {
break;
} else if ( entry.moveType == MOVE_TYPE ) {
XP_U16 nTiles = entry.u.move.moveInfo.nTiles;
XP_U16 col, row;
XP_U16* varies;
@ -883,16 +893,20 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
XP_FALSE );
}
break;
} else if ( entry.moveType == ASSIGN_TYPE ) {
break;
} else {
--nStackEntries; /* look at the next one */
}
}
/* We fail if we didn't undo as many as requested UNLESS the lower
limit is the trumping target/test. */
if ( 0 <= moveSought ) {
if ( moveSought != entry.moveNum ) {
success = XP_FALSE;
}
} else {
if ( nMovesUndone != nMovesSought ) {
success = XP_FALSE;
}
}
if ( success ) {
if ( !!turnP ) {
@ -903,11 +917,12 @@ model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
}
} else {
while ( nMovesUndone-- ) {
/* undo isn't enough here: pool's got tiles in it!! */
/* redo isn't enough here: pool's got tiles in it!! */
XP_ASSERT( 0 );
(void)stack_redo( stack, NULL );
}
}
}
return success;
} /* model_undoLatestMoves */
@ -963,10 +978,11 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
* assert that it's in the tray, remove it from the tray, and place it on the
* board.
*/
void
XP_Bool
model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
XWStreamCtxt* stream )
{
XP_Bool success = XP_TRUE;
XP_U16 numTiles, ii;
Tile blank = dict_getBlankTile( model_getDictionary(model) );
XP_U16 nColsNBits;
@ -978,40 +994,57 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
#endif
model_resetCurrentTurn( model, playerNum );
if ( success ) {
numTiles = (XP_U16)stream_getBits( stream, NTILES_NBITS );
XP_LOGF( "%s: numTiles=%d", __func__, numTiles );
for ( ii = 0; ii < numTiles; ++ii ) {
XP_S16 foundAt;
Tile moveTile;
Tile tileFace = (Tile)stream_getBits( stream, TILE_NBITS );
XP_U16 col = (XP_U16)stream_getBits( stream, nColsNBits );
XP_U16 row = (XP_U16)stream_getBits( stream, nColsNBits );
XP_Bool isBlank = stream_getBits( stream, 1 );
Tile tileFaces[numTiles];
XP_U16 cols[numTiles];
XP_U16 rows[numTiles];
XP_Bool isBlanks[numTiles];
Tile moveTiles[numTiles];
TrayTileSet curTiles = *model_getPlayerTiles( model, playerNum );
/* This code gets called both for the server, which has all the
tiles in its tray, and for a client, which has "EMPTY" tiles
only. If it's the empty case, we stuff a real tile into the
tray before falling through to the normal case */
for ( ii = 0; success && ii < numTiles; ++ii ) {
tileFaces[ii] = (Tile)stream_getBits( stream, TILE_NBITS );
cols[ii] = (XP_U16)stream_getBits( stream, nColsNBits );
rows[ii] = (XP_U16)stream_getBits( stream, nColsNBits );
isBlanks[ii] = stream_getBits( stream, 1 );
if ( isBlank ) {
moveTile = blank;
if ( isBlanks[ii] ) {
moveTiles[ii] = blank;
} else {
moveTile = tileFace;
moveTiles[ii] = tileFaces[ii];
}
foundAt = model_trayContains( model, playerNum, moveTile );
XP_S16 index = setContains( &curTiles, moveTiles[ii] );
if ( 0 <= index ) {
removeTile( &curTiles, index );
} else {
success = XP_FALSE;
}
}
if ( success ) {
for ( ii = 0; ii < numTiles; ++ii ) {
XP_S16 foundAt = model_trayContains( model, playerNum, moveTiles[ii] );
if ( foundAt == -1 ) {
XP_ASSERT( EMPTY_TILE == model_getPlayerTile(model, playerNum, 0));
XP_ASSERT( EMPTY_TILE == model_getPlayerTile(model, playerNum,
0));
/* Does this ever happen? */
XP_LOGF( "%s: found empty tile and it's ok", __func__ );
(void)model_removePlayerTile( model, playerNum, -1 );
model_addPlayerTile( model, playerNum, -1, moveTile );
model_addPlayerTile( model, playerNum, -1, moveTiles[ii] );
}
model_moveTrayToBoard( model, playerNum, col, row, foundAt, tileFace );
model_moveTrayToBoard( model, playerNum, cols[ii], rows[ii], foundAt,
tileFaces[ii] );
}
}
}
return success;
} /* model_makeTurnFromStream */
void
@ -1090,25 +1123,11 @@ model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts,
XP_S16
model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile )
{
PlayerCtxt* player;
XP_S16 i;
XP_S16 result = -1;
XP_ASSERT( turn >= 0 );
XP_ASSERT( turn < model->nPlayers );
player = &model->players[turn];
/* search from top down so don't pull out of below divider */
for ( i = player->trayTiles.nTiles - 1; i >= 0 ; --i ) {
Tile playerTile = player->trayTiles.tiles[i];
if ( playerTile == tile ) {
result = i;
break;
}
}
return result;
const TrayTileSet* tiles = model_getPlayerTiles( model, turn );
return setContains( tiles, tile );
} /* model_trayContains */
XP_U16
@ -1531,7 +1550,7 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
XP_U16 ii;
PlayerCtxt* player;
PendingTile* pt;
XP_S16 score;
XP_S16 score = -1;
XP_Bool inLine, isHorizontal;
const Tile* newTilesP;
XP_U16 nTiles;
@ -1551,7 +1570,7 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
player = &model->players[turn];
if ( useStack ) {
MoveInfo moveInfo;
MoveInfo moveInfo = {0};
inLine = tilesInLine( model, turn, &isHorizontal );
XP_ASSERT( inLine );
normalizeMoves( model, turn, isHorizontal, &moveInfo );
@ -1603,15 +1622,14 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
while ( nTiles-- ) {
model_addPlayerTile( model, turn, -1, *newTilesP++ );
}
return score;
} /* commitTurn */
void
XP_Bool
model_commitTurn( ModelCtxt* model, XP_S16 turn, TrayTileSet* newTiles )
{
(void)commitTurn( model, turn, newTiles, (XWStreamCtxt*)NULL,
(WordNotifierInfo*)NULL, XP_TRUE );
XP_S16 score = commitTurn( model, turn, newTiles, NULL, NULL, XP_TRUE );
return 0 <= score;
} /* model_commitTurn */
/* Given a rack of new tiles and of old, remove all the old from the tray and
@ -2316,7 +2334,7 @@ model_getPlayersLastScore( ModelCtxt* model, XP_S16 player,
switch ( entry.moveType ) {
case MOVE_TYPE:
scoreLastMove( model, &entry.u.move.moveInfo,
nEntries - which - 1, expl, explLen );
nEntries - which, expl, explLen );
break;
case TRADE_TYPE:
nTiles = entry.u.trade.oldTiles.nTiles;
@ -2405,6 +2423,32 @@ writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
}
} /* writePlayerCtxt */
static void
removeTile( TrayTileSet* tiles, XP_U16 index )
{
XP_U16 ii;
--tiles->nTiles;
for ( ii = index; ii < tiles->nTiles; ++ii ) {
tiles->tiles[ii] = tiles->tiles[ii+1];
}
}
static XP_S16
setContains( const TrayTileSet* tiles, Tile tile )
{
XP_S16 result = -1;
XP_S16 ii;
/* search from top down so don't pull out of below divider */
for ( ii = tiles->nTiles - 1; ii >= 0 ; --ii ) {
Tile playerTile = tiles->tiles[ii];
if ( playerTile == tile ) {
result = ii;
break;
}
}
return result;
}
#ifdef CPLUS
}
#endif

View file

@ -114,6 +114,7 @@ void model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream );
void model_setSize( ModelCtxt* model, XP_U16 boardSize );
void model_destroy( ModelCtxt* model );
XP_U32 model_getHash( const ModelCtxt* model );
void model_setNPlayers( ModelCtxt* model, XP_U16 numPlayers );
XP_U16 model_getNPlayers( const ModelCtxt* model );
@ -181,7 +182,7 @@ void model_getCurrentMoveTile( ModelCtxt* model, XP_S16 turn, XP_S16* index,
Tile* tile, XP_U16* col, XP_U16* row,
XP_Bool* isBlank );
void model_commitTurn( ModelCtxt* model, XP_S16 player,
XP_Bool model_commitTurn( ModelCtxt* model, XP_S16 player,
TrayTileSet* newTiles );
void model_commitRejectedPhony( ModelCtxt* model, XP_S16 player );
void model_makeTileTrade( ModelCtxt* model, XP_S16 player,
@ -198,7 +199,7 @@ void model_trayToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream );
void model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream);
void model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
XP_Bool model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
XWStreamCtxt* stream );
void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
const MoveInfo* newMove );

View file

@ -57,6 +57,68 @@ stack_init( StackCtxt* stack )
shrunk to fit as soon as we serialize/deserialize anyway. */
} /* stack_init */
#ifdef STREAM_VERS_BIGBOARD
static XP_U32
augmentHash( XP_U32 hash, XP_U8* ptr, XP_U16 len )
{
XP_ASSERT( 0 < len );
// see http://en.wikipedia.org/wiki/Jenkins_hash_function
XP_U16 ii;
for ( ii = 0; ii < len; ++ii ) {
hash += *ptr++;
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
static XP_U32
augmentFor( XP_U32 hash, const StackEntry* entry )
{
switch( entry->moveType ) {
case ASSIGN_TYPE:
hash = augmentHash( hash, (XP_U8*)&entry->u.assign,
sizeof(entry->u.assign) );
break;
case MOVE_TYPE:
hash = augmentHash( hash, (XP_U8*)&entry->u.move,
sizeof(entry->u.move) );
break;
case TRADE_TYPE:
hash = augmentHash( hash, (XP_U8*)&entry->u.trade,
sizeof(entry->u.trade) );
break;
case PHONY_TYPE:
hash = augmentHash( hash, (XP_U8*)&entry->u.phony,
sizeof(entry->u.phony) );
break;
}
return hash;
}
XP_U32
stack_getHash( StackCtxt* stack )
{
XP_U32 hash = 0L;
XP_U16 nn, nEntries = stack->nEntries;
for ( nn = 0; nn < nEntries; ++nn ) {
StackEntry entry;
XP_MEMSET( &entry, 0, sizeof(entry) );
if ( !stack_getNthEntry( stack, nn, &entry ) ) {
XP_ASSERT( 0 );
}
hash = augmentFor( hash, &entry );
// XP_LOGF( "hash after %d: %.8X", nn, (unsigned int)hash );
}
XP_ASSERT( 0 != hash );
// LOG_RETURNF( "%.8X", (unsigned int)hash );
return hash;
}
#endif
void
stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile )
{

View file

@ -69,6 +69,7 @@ StackCtxt* stack_make( MPFORMAL VTableMgr* vtmgr );
void stack_destroy( StackCtxt* stack );
void stack_init( StackCtxt* stack );
XP_U32 stack_getHash( StackCtxt* stack );
void stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile );
void stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream );

View file

@ -280,7 +280,7 @@ void
normalizeMoves( ModelCtxt* model, XP_S16 turn, XP_Bool isHorizontal,
MoveInfo* moveInfo )
{
XP_S16 lowCol, i, j, thisCol; /* unsigned is a problem on palm */
XP_S16 lowCol, ii, jj, thisCol; /* unsigned is a problem on palm */
PlayerCtxt* player = &model->players[turn];
XP_U16 nTiles = player->nPending;
XP_S16 lastTaken;
@ -291,27 +291,29 @@ normalizeMoves( ModelCtxt* model, XP_S16 turn, XP_Bool isHorizontal,
moveInfo->nTiles = (XP_U8)nTiles;
lastTaken = -1;
for ( i = 0; i < nTiles; ++i ) {
for ( ii = 0; ii < nTiles; ++ii ) {
lowCol = 100; /* high enough to always be changed */
for ( j = 0; j < nTiles; ++j ) {
pt = &player->pendingTiles[j];
for ( jj = 0; jj < nTiles; ++jj ) {
pt = &player->pendingTiles[jj];
thisCol = isHorizontal? pt->col:pt->row;
if (thisCol < lowCol && thisCol > lastTaken ) {
lowCol = thisCol;
lowIndex = j;
lowIndex = jj;
}
}
/* we've found the next to transfer (4 bytes smaller without a temp
local ptr. */
pt = &player->pendingTiles[lowIndex];
lastTaken = lowCol;
moveInfo->tiles[i].varCoord = (XP_U8)lastTaken;
moveInfo->tiles[ii].varCoord = (XP_U8)lastTaken;
moveInfo->tiles[i].tile = pt->tile;
moveInfo->tiles[ii].tile = pt->tile;
}
if ( 0 < nTiles ) {
pt = &player->pendingTiles[0];
moveInfo->commonCoord = isHorizontal? pt->row:pt->col;
}
} /* normalizeMoves */
static XP_Bool

View file

@ -208,6 +208,22 @@ pool_removeTiles( PoolContext* pool, const TrayTileSet* tiles )
XP_LOGF( "%s: %d tiles left in pool", __func__, pool->numTilesLeft );
} /* pool_removeTiles */
XP_Bool
pool_containsTiles( const PoolContext* pool, const TrayTileSet* tiles )
{
XP_Bool allThere = XP_TRUE;
XP_U16 ii;
XP_U8 counts[pool->numFaces];
XP_MEMCPY( counts, pool->lettersLeft, sizeof(counts) );
/* In case we have duplicates, make count of each type */
for ( ii = 0; allThere && ii < tiles->nTiles; ++ii ) {
allThere = 0 < counts[tiles->tiles[ii]]--;
}
return allThere;
}
XP_U16
pool_getNTilesLeft( PoolContext* pool )
{

View file

@ -28,6 +28,8 @@ void pool_requestTiles( PoolContext* pool, Tile* tiles,
/*in out*/ XP_U8* maxNum );
void pool_replaceTiles( PoolContext* pool, const TrayTileSet* tiles );
void pool_removeTiles( PoolContext* pool, const TrayTileSet* tiles );
XP_Bool pool_containsTiles( const PoolContext* pool,
const TrayTileSet* tiles );
XP_U16 pool_getNTilesLeft( PoolContext* pool );
XP_U16 pool_getNTilesLeftFor( PoolContext* pool, Tile tile );

View file

@ -92,6 +92,7 @@ typedef struct ServerNonvolatiles {
#endif
#ifdef XWFEATURE_SLOW_ROBOT
XP_U16 robotThinkMin, robotThinkMax; /* not saved (yet) */
XP_U16 robotTradePct;
#endif
RemoteAddress addresses[MAX_NUM_PLAYERS];
@ -516,6 +517,7 @@ server_prefsChanged( ServerCtxt* server, CommonPrefs* cp )
#ifdef XWFEATURE_SLOW_ROBOT
server->nv.robotThinkMin = cp->robotThinkMin;
server->nv.robotThinkMax = cp->robotThinkMax;
server->nv.robotTradePct = cp->robotTradePct;
#endif
} /* server_prefsChanged */
@ -784,11 +786,20 @@ makeRobotMove( ServerCtxt* server )
XP_Bool canMove;
XP_U32 time = 0L; /* stupid compiler.... */
XW_UtilCtxt* util = server->vol.util;
XP_Bool forceTrade = XP_FALSE;
if ( timerEnabled ) {
time = util_getCurSeconds( util );
}
#ifdef XWFEATURE_SLOW_ROBOT
if ( 0 != server->nv.robotTradePct
&& (server_countTilesInPool( server ) >= MAX_TRAY_TILES) ) {
XP_U16 pct = XP_RANDOM() % 100;
forceTrade = pct < server->nv.robotTradePct ;
}
#endif
turn = server->nv.currentTurn;
XP_ASSERT( turn >= 0 );
@ -798,6 +809,7 @@ makeRobotMove( ServerCtxt* server )
paranoid. PENDING(ehouse) */
model_resetCurrentTurn( model, turn );
if ( !forceTrade ) {
tileSet = model_getPlayerTiles( model, turn );
XP_ASSERT( !!server_getEngineFor( server, turn ) );
@ -809,12 +821,14 @@ makeRobotMove( ServerCtxt* server )
#endif
server->vol.gi->players[turn].robotIQ,
&canMove, &newMove );
if ( searchComplete ) {
}
if ( forceTrade || searchComplete ) {
const XP_UCHAR* str;
XWStreamCtxt* stream = NULL;
XP_Bool trade = (newMove.nTiles == 0) && canMove &&
(server_countTilesInPool( server ) >= MAX_TRAY_TILES);
XP_Bool trade = forceTrade ||
((newMove.nTiles == 0) && canMove &&
(server_countTilesInPool( server ) >= MAX_TRAY_TILES));
server->vol.showPrevMove = XP_TRUE;
if ( server->nv.showRobotScores ) {
@ -1891,6 +1905,14 @@ sendMoveTo( ServerCtxt* server, XP_U16 devIndex, XP_U16 turn,
stream = messageStreamWithHeader( server, devIndex, code );
#ifdef STREAM_VERS_BIGBOARD
if ( STREAM_VERS_BIGBOARD <= stream_getVersion( stream ) ) {
XP_U32 hash = model_getHash( server->vol.model );
// XP_LOGF( "%s: adding hash %x", __func__, (unsigned int)hash );
stream_putU32( stream, hash );
}
#endif
stream_putBits( stream, PLAYERNUM_NBITS, turn ); /* who made the move */
traySetToStream( stream, newTiles );
@ -1923,34 +1945,53 @@ sendMoveTo( ServerCtxt* server, XP_U16 devIndex, XP_U16 turn,
stream_destroy( stream );
} /* sendMoveTo */
static void
static XP_Bool
readMoveInfo( ServerCtxt* server, XWStreamCtxt* stream,
XP_U16* whoMovedP, XP_Bool* isTradeP,
TrayTileSet* newTiles, TrayTileSet* tradedTiles,
XP_Bool* legalP )
{
XP_U16 whoMoved = stream_getBits( stream, PLAYERNUM_NBITS );
XP_Bool success = XP_TRUE;
XP_Bool legalMove = XP_TRUE;
XP_Bool isTrade;
#ifdef STREAM_VERS_BIGBOARD
if ( STREAM_VERS_BIGBOARD <= stream_getVersion( stream ) ) {
XP_U32 hashReceived = stream_getU32( stream );
success = hashReceived == model_getHash( server->vol.model );
if ( !success ) {
XP_LOGF( "%s: hash mismatch: dropping move", __func__ );
}
}
#endif
if ( success ) {
XP_U16 whoMoved = stream_getBits( stream, PLAYERNUM_NBITS );
traySetFromStream( stream, newTiles );
success = pool_containsTiles( server->pool, newTiles );
if ( success ) {
isTrade = stream_getBits( stream, 1 );
if ( isTrade ) {
traySetFromStream( stream, tradedTiles );
XP_LOGF( "%s: got trade of %d tiles", __func__, tradedTiles->nTiles );
XP_LOGF( "%s: got trade of %d tiles", __func__,
tradedTiles->nTiles );
} else {
legalMove = stream_getBits( stream, 1 );
model_makeTurnFromStream( server->vol.model, whoMoved, stream );
success = model_makeTurnFromStream( server->vol.model,
whoMoved, stream );
getPlayerTime( server, stream, whoMoved );
}
if ( success ) {
pool_removeTiles( server->pool, newTiles );
*whoMovedP = whoMoved;
*isTradeP = isTrade;
*legalP = legalMove;
}
}
}
return success;
} /* readMoveInfo */
static void
@ -2006,6 +2047,7 @@ makeMoveReportIf( ServerCtxt* server, XWStreamCtxt** wordsStream )
static XP_Bool
reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
{
XP_Bool success;
ModelCtxt* model = server->vol.model;
XP_U16 whoMoved;
XP_U16 nTilesMoved = 0; /* trade case */
@ -2022,11 +2064,12 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
XP_ASSERT( gi->serverRole == SERVER_ISSERVER );
readMoveInfo( server, stream, &whoMoved, &isTrade, &newTiles,
success = readMoveInfo( server, stream, &whoMoved, &isTrade, &newTiles,
&tradedTiles, &isLegalMove ); /* modifies model */
XP_ASSERT( isLegalMove ); /* client should always report as true */
XP_ASSERT( !success || isLegalMove ); /* client should always report as true */
isLegalMove = XP_TRUE;
if ( success ) {
if ( isTrade ) {
sendMoveToClientsExcept( server, whoMoved, XP_TRUE, &newTiles,
@ -2053,11 +2096,11 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
server->vol.showPrevMove = XP_TRUE;
mvStream = makeMoveReportIf( server, &wordsStream );
model_commitTurn( model, whoMoved, &newTiles );
success = model_commitTurn( model, whoMoved, &newTiles );
resetEngines( server );
}
if ( isLegalMove ) {
if ( success && isLegalMove ) {
XP_U16 nTilesLeft = model_getNumTilesTotal( model, whoMoved );
if ( (gi->phoniesAction == PHONIES_DISALLOW) && (nTilesMoved > 0) ) {
@ -2090,8 +2133,8 @@ reflectMoveAndInform( ServerCtxt* server, XWStreamCtxt* stream )
if ( doRequest ) {
util_requestTime( server->vol.util );
}
return XP_TRUE;
}
return success;
} /* reflectMoveAndInform */
static XP_Bool
@ -2110,9 +2153,10 @@ reflectMove( ServerCtxt* server, XWStreamCtxt* stream )
moveOk = XWSTATE_INTURN == server->nv.gameState;
XP_ASSERT( moveOk ); /* message permanently lost if dropped here! */
if ( moveOk ) {
readMoveInfo( server, stream, &whoMoved, &isTrade, &newTiles,
moveOk = readMoveInfo( server, stream, &whoMoved, &isTrade, &newTiles,
&tradedTiles, &isLegal ); /* modifies model */
}
if ( moveOk ) {
if ( isTrade ) {
model_makeTileTrade( model, whoMoved, &tradedTiles, &newTiles );
pool_replaceTiles( server->pool, &tradedTiles );
@ -2466,7 +2510,8 @@ sendUndoToClientsExcept( ServerCtxt* server, XP_U16 skip,
static XP_Bool
reflectUndos( ServerCtxt* server, XWStreamCtxt* stream, XW_Proto code )
{
XP_U16 nUndone, lastUndone;
XP_U16 nUndone;
XP_S16 lastUndone;
XP_U16 turn;
ModelCtxt* model = server->vol.model;
XP_Bool success = XP_TRUE;
@ -2475,7 +2520,7 @@ reflectUndos( ServerCtxt* server, XWStreamCtxt* stream, XW_Proto code )
lastUndone = stream_getU16( stream );
success = model_undoLatestMoves( model, server->pool, nUndone, &turn,
NULL );
&lastUndone );
if ( success ) {
if ( code == XWPROTO_UNDO_INFO_CLIENT ) { /* need to inform */

View file

@ -94,7 +94,8 @@ DEFINES += -DTEXT_MODEL
DEFINES += -DXWFEATURE_WALKDICT
DEFINES += -DXWFEATURE_WALKDICT_FILTER
DEFINES += -DXWFEATURE_DICTSANITY
#DEFINES += -DMAX_ROWS=32
# MAX_ROWS controls STREAM_VERS_BIGBOARD and with it move hashing
DEFINES += -DMAX_ROWS=32
ifdef CURSES_CELL_HT
DEFINES += -DCURSES_CELL_HT=$(CURSES_CELL_HT)

View file

@ -1709,6 +1709,7 @@ cursesmain( XP_Bool isServer, LaunchParams* params )
#ifdef XWFEATURE_SLOW_ROBOT
g_globals.cGlobals.cp.robotThinkMin = params->robotThinkMin;
g_globals.cGlobals.cp.robotThinkMax = params->robotThinkMax;
g_globals.cGlobals.cp.robotTradePct = params->robotTradePct;
#endif
setupCursesUtilCallbacks( &g_globals, params->util );

View file

@ -2284,6 +2284,7 @@ gtkmain( LaunchParams* params, int argc, char *argv[] )
#ifdef XWFEATURE_SLOW_ROBOT
globals.cGlobals.cp.robotThinkMin = params->robotThinkMin;
globals.cGlobals.cp.robotThinkMax = params->robotThinkMax;
globals.cGlobals.cp.robotTradePct = params->robotTradePct;
#endif
#ifdef XWFEATURE_CROSSHAIRS
globals.cGlobals.cp.hideCrosshairs = params->hideCrosshairs;

View file

@ -433,6 +433,7 @@ typedef enum {
#endif
#ifdef XWFEATURE_SLOW_ROBOT
,CMD_SLOWROBOT
,CMD_TRADEPCT
#endif
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
,CMD_GTK
@ -517,6 +518,7 @@ static CmdInfoRec CmdInfoRecs[] = {
#endif
#ifdef XWFEATURE_SLOW_ROBOT
,{ CMD_SLOWROBOT, true, "slow-robot", "make robot slower to test network" }
,{ CMD_TRADEPCT, true, "trade-pct", "what pct of the time should robot trade" }
#endif
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES
,{ CMD_GTK, false, "gtk", "use GTK for display" }
@ -1581,6 +1583,12 @@ main( int argc, char** argv )
usage(argv[0], "bad param" );
}
break;
case CMD_TRADEPCT:
mainParams.robotTradePct = atoi( optarg );
if ( mainParams.robotTradePct < 0 || mainParams.robotTradePct > 100 ) {
usage(argv[0], "must be 0 <= n <= 100" );
}
break;
#endif
#if defined PLATFORM_GTK && defined PLATFORM_NCURSES

View file

@ -84,9 +84,9 @@ typedef struct LaunchParams {
XP_Bool hideCrosshairs;
#endif
#ifdef XWFEATURE_SLOW_ROBOT
XP_U16 robotThinkMin, robotThinkMax;
XP_U16 robotTradePct;
#endif
DeviceRole serverRole;

View file

@ -159,7 +159,7 @@ build_cmds() {
fi
PARAMS="$(player_params $NLOCALS $NPLAYERS $DEV)"
PARAMS="$PARAMS $BOARD_SIZE --room $ROOM"
PARAMS="$PARAMS $BOARD_SIZE --room $ROOM --trade-pct 20"
PARAMS="$PARAMS --game-dict $DICT --port $PORT --host $HOST "
PARAMS="$PARAMS --file $FILE --slow-robot 1:3 --skip-confirm"
PARAMS="$PARAMS --drop-nth-packet $DROP_N $PLAT_PARMS"