mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-04 20:46:28 +01:00
switch from tracking robot intelligence as a per-game boolean
implemented (when not smart) as trying to match the human's score to a per-robot value between 1 and 100 that gives the percentage of best moves to store before picking randomly from among them. So a 1 means save only the best move and always pick it; 100 means save all the best moves (how many are saved is compile-time configurable) and pick one of them. Because it's settable per-robot a smarter robot can be played against a dumber one (though I may choose not to make it settable per-robot on shipping versions.)
This commit is contained in:
parent
fc23080120
commit
f111b78714
8 changed files with 109 additions and 128 deletions
|
@ -564,7 +564,7 @@ board_canHint( const BoardCtxt* board )
|
|||
&& 0 < model_getNumTilesTotal( board->model, board->selPlayer );
|
||||
if ( canHint ) {
|
||||
LocalPlayer* lp = &board->gi->players[board->selPlayer];
|
||||
canHint = lp->isLocal && !lp->isRobot;
|
||||
canHint = lp->isLocal && !LP_IS_ROBOT(lp);
|
||||
}
|
||||
return canHint;
|
||||
}
|
||||
|
@ -1412,7 +1412,7 @@ chooseBestSelPlayer( BoardCtxt* board )
|
|||
for ( i = 0; i < nPlayers; ++i ) {
|
||||
LocalPlayer* lp = &board->gi->players[curTurn];
|
||||
|
||||
if ( !lp->isRobot && lp->isLocal ) {
|
||||
if ( !LP_IS_ROBOT(lp) && lp->isLocal ) {
|
||||
return curTurn;
|
||||
}
|
||||
curTurn = (curTurn + 1) % nPlayers;
|
||||
|
@ -1707,7 +1707,7 @@ board_requestHint( BoardCtxt* board,
|
|||
#ifdef XWFEATURE_SEARCHLIMIT
|
||||
lp, useTileLimits,
|
||||
#endif
|
||||
NO_SCORE_LIMIT,
|
||||
0, /* 0: not a robot */
|
||||
&canMove, &newMove );
|
||||
board_popTimerSave( board );
|
||||
|
||||
|
@ -2160,7 +2160,7 @@ askRevealTray( BoardCtxt* board )
|
|||
} else if ( !lp->isLocal ) {
|
||||
util_userError( board->util, ERR_NO_PEEK_REMOTE_TILES );
|
||||
#endif
|
||||
} else if ( lp->isRobot ) {
|
||||
} else if ( LP_IS_ROBOT(lp) ) {
|
||||
if ( reversed ) {
|
||||
util_userError( board->util, ERR_NO_PEEK_ROBOT_TILES );
|
||||
} else {
|
||||
|
|
|
@ -90,12 +90,11 @@ struct EngineCtxt {
|
|||
XP_Bool usePrev;
|
||||
XP_Bool searchInProgress;
|
||||
XP_Bool searchHorizontal;
|
||||
XP_Bool isRobot;
|
||||
XP_Bool isFirstMove;
|
||||
XP_U16 numRows, numCols;
|
||||
XP_U16 curRow;
|
||||
XP_U16 blankCount;
|
||||
XP_U16 targetScore;
|
||||
XP_U16 nMovesToSave;
|
||||
XP_U16 star_row;
|
||||
XP_Bool returnNOW;
|
||||
MoveIterationData miData;
|
||||
|
@ -202,7 +201,7 @@ engine_getScoreCache( EngineCtxt* engine, XP_U16 row )
|
|||
* turn it into a separate code module later.
|
||||
****************************************************************************/
|
||||
EngineCtxt*
|
||||
engine_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isRobot )
|
||||
engine_make( MPFORMAL XW_UtilCtxt* util )
|
||||
{
|
||||
EngineCtxt* result = (EngineCtxt*)XP_MALLOC( mpool, sizeof(*result) );
|
||||
XP_MEMSET( result, 0, sizeof(*result) );
|
||||
|
@ -211,8 +210,6 @@ engine_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isRobot )
|
|||
|
||||
result->util = util;
|
||||
|
||||
result->isRobot = isRobot;
|
||||
|
||||
engine_reset( result );
|
||||
|
||||
return result;
|
||||
|
@ -230,9 +227,9 @@ engine_writeToStream( EngineCtxt* XP_UNUSED(ctxt),
|
|||
|
||||
EngineCtxt*
|
||||
engine_makeFromStream( MPFORMAL XWStreamCtxt* XP_UNUSED_DBG(stream),
|
||||
XW_UtilCtxt* util, XP_Bool isRobot )
|
||||
XW_UtilCtxt* util )
|
||||
{
|
||||
EngineCtxt* engine = engine_make( MPPARM(mpool) util, isRobot );
|
||||
EngineCtxt* engine = engine_make( MPPARM(mpool) util );
|
||||
|
||||
/* All the engine's data seems to be used only in the process of finding a
|
||||
move. So if we're willing to have the set of moves found lost across
|
||||
|
@ -306,7 +303,7 @@ print_savedMoves( const EngineCtxt* engine, const char* label )
|
|||
int ii;
|
||||
int pos = 0;
|
||||
char buf[(NUM_SAVED_ENGINE_MOVES*10) + 3] = {0};
|
||||
for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES; ++ii ) {
|
||||
for ( ii = 0; ii < engine->nMovesToSave; ++ii ) {
|
||||
if ( 0 < engine->miData.savedMoves[ii].score ) {
|
||||
pos += XP_SNPRINTF( &buf[pos], VSIZE(buf)-pos, "[%d]: %d; ",
|
||||
ii, engine->miData.savedMoves[ii].score );
|
||||
|
@ -324,6 +321,8 @@ chooseMove( EngineCtxt* engine, PossibleMove** move )
|
|||
XP_U16 ii;
|
||||
PossibleMove* chosen;
|
||||
XP_Bool result;
|
||||
XP_Bool done;
|
||||
XP_Bool isRobot;
|
||||
|
||||
print_savedMoves( engine, "unsorted moves" );
|
||||
|
||||
|
@ -331,32 +330,40 @@ chooseMove( EngineCtxt* engine, PossibleMove** move )
|
|||
get picked up first. Don't sort if we're working for a robot; we've
|
||||
only been saving the single best move anyway. At least not until we
|
||||
start applying other criteria than score to moves. */
|
||||
if ( engine->isRobot ) {
|
||||
chosen = &engine->miData.savedMoves[0];
|
||||
} else {
|
||||
XP_Bool done = !move_cache_empty( engine );
|
||||
while ( !done ) { /* while so can break */
|
||||
done = XP_TRUE;
|
||||
PossibleMove* cur = engine->miData.savedMoves;
|
||||
for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES-1; ++ii ) {
|
||||
PossibleMove* next = cur + 1;
|
||||
if ( CMPMOVES( cur, next ) > 0 ) {
|
||||
PossibleMove tmp;
|
||||
XP_MEMCPY( &tmp, cur, sizeof(tmp) );
|
||||
XP_MEMCPY( cur, next, sizeof(*cur) );
|
||||
XP_MEMCPY( next, &tmp, sizeof(*next) );
|
||||
done = XP_FALSE;
|
||||
}
|
||||
cur = next;
|
||||
|
||||
done = !move_cache_empty( engine );
|
||||
isRobot = 0 < engine->nMovesToSave;
|
||||
while ( !done ) { /* while so can break */
|
||||
done = XP_TRUE;
|
||||
PossibleMove* cur = engine->miData.savedMoves;
|
||||
for ( ii = 0; ii < engine->nMovesToSave-1; ++ii ) {
|
||||
PossibleMove* next = cur + 1;
|
||||
if ( CMPMOVES( cur, next ) > 0 ) {
|
||||
PossibleMove tmp;
|
||||
XP_MEMCPY( &tmp, cur, sizeof(tmp) );
|
||||
XP_MEMCPY( cur, next, sizeof(*cur) );
|
||||
XP_MEMCPY( next, &tmp, sizeof(*next) );
|
||||
done = XP_FALSE;
|
||||
}
|
||||
if ( done ) {
|
||||
cur = next;
|
||||
}
|
||||
|
||||
if ( done ) {
|
||||
if ( !isRobot ) {
|
||||
init_move_cache( engine );
|
||||
print_savedMoves( engine, "sorted moves" );
|
||||
}
|
||||
print_savedMoves( engine, "sorted moves" );
|
||||
}
|
||||
|
||||
/* now pick the one we're supposed to return */
|
||||
chosen = next_from_cache( engine );
|
||||
if ( isRobot ) {
|
||||
XP_ASSERT( engine->miData.nInMoveCache <= NUM_SAVED_ENGINE_MOVES );
|
||||
XP_ASSERT( engine->miData.nInMoveCache <= engine->nMovesToSave );
|
||||
/* PENDING not nInMoveCache-1 below?? */
|
||||
chosen = &engine->miData.savedMoves[engine->miData.nInMoveCache];
|
||||
} else {
|
||||
chosen = next_from_cache( engine );
|
||||
}
|
||||
}
|
||||
|
||||
*move = chosen; /* set either way */
|
||||
|
@ -370,6 +377,27 @@ chooseMove( EngineCtxt* engine, PossibleMove** move )
|
|||
return result;
|
||||
} /* chooseMove */
|
||||
|
||||
/* Robot smartness is a number between 0 and 100, inclusive. 0 means a human
|
||||
* player who may want to iterate, so save all moves. If a robot player, we
|
||||
* want a random move within a range proportional to the 1-100 range, so we
|
||||
* figure out now what we'll be picking, save only that many moves and take
|
||||
* the worst of 'em in chooseMove().
|
||||
*/
|
||||
static void
|
||||
normalizeIQ( EngineCtxt* engine, XP_U16 iq )
|
||||
{
|
||||
if ( 0 == iq ) { /* human */
|
||||
engine->nMovesToSave = NUM_SAVED_ENGINE_MOVES; /* save 'em all */
|
||||
} else if ( 1 == iq ) { /* human */
|
||||
engine->nMovesToSave = 1;
|
||||
} else {
|
||||
XP_U16 count = NUM_SAVED_ENGINE_MOVES * iq / 100;
|
||||
engine->nMovesToSave = 1 + (XP_RANDOM() % count);
|
||||
}
|
||||
XP_LOGF( "%s: set nMovesToSave=%d (iq=%d; NUM_SAVED_ENGINE_MOVES=%d)",
|
||||
__func__, engine->nMovesToSave, iq, NUM_SAVED_ENGINE_MOVES );
|
||||
}
|
||||
|
||||
/* Return of XP_TRUE means that we ran to completion. XP_FALSE means we were
|
||||
* interrupted. Whether an actual move was found is indicated by what's
|
||||
* filled in in *newMove.
|
||||
|
@ -382,8 +410,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
|
|||
const BdHintLimits* searchLimits,
|
||||
XP_Bool useTileLimits,
|
||||
#endif
|
||||
XP_U16 targetScore, XP_Bool* canMoveP,
|
||||
MoveInfo* newMove )
|
||||
XP_U16 robotIQ, XP_Bool* canMoveP, MoveInfo* newMove )
|
||||
{
|
||||
XP_Bool result = XP_TRUE;
|
||||
XP_U16 star_row;
|
||||
|
@ -442,7 +469,7 @@ engine_findMove( EngineCtxt* engine, const ModelCtxt* model,
|
|||
util_engineStarting( engine->util,
|
||||
engine->rack[engine->blankTile] );
|
||||
|
||||
engine->targetScore = targetScore;
|
||||
normalizeIQ( engine, robotIQ );
|
||||
|
||||
if ( move_cache_empty( engine ) ) {
|
||||
set_search_limits( engine );
|
||||
|
@ -1141,7 +1168,9 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
|
|||
XP_Bool usePrev = engine->usePrev;
|
||||
XP_Bool foundEmpty = XP_FALSE;
|
||||
|
||||
if ( !engine->isRobot ) { /* robot doesn't ask for next hint.... */
|
||||
if ( 1 == engine->nMovesToSave ) { /* only saving one */
|
||||
mostest = 0;
|
||||
} else {
|
||||
mostest = -1;
|
||||
/* we're not interested if we've seen this */
|
||||
cmpVal = CMPMOVES( posmove, &engine->miData.lastSeenMove );
|
||||
|
@ -1154,7 +1183,7 @@ saveMoveIfQualifies( EngineCtxt* engine, PossibleMove* posmove )
|
|||
} else {
|
||||
XP_S16 ii;
|
||||
/* terminate i at 1 because mostest starts at 0 */
|
||||
for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES; ++ii ) {
|
||||
for ( ii = 0; ii < engine->nMovesToSave; ++ii ) {
|
||||
/* Find the mostest value move and overwrite it. Note that
|
||||
there might not be one, as all may have the same or higher
|
||||
scores and those that have the same score may compare
|
||||
|
@ -1222,7 +1251,7 @@ set_search_limits( EngineCtxt* engine )
|
|||
move as the limit; otherwise the lowest */
|
||||
if ( 0 < engine->miData.nInMoveCache ) {
|
||||
XP_U16 srcIndx = engine->usePrev
|
||||
? NUM_SAVED_ENGINE_MOVES-1 : engine->miData.bottom;
|
||||
? engine->nMovesToSave-1 : engine->miData.bottom;
|
||||
XP_MEMCPY( &engine->miData.lastSeenMove,
|
||||
&engine->miData.savedMoves[srcIndx],
|
||||
sizeof(engine->miData.lastSeenMove) );
|
||||
|
@ -1238,9 +1267,11 @@ set_search_limits( EngineCtxt* engine )
|
|||
static void
|
||||
init_move_cache( EngineCtxt* engine )
|
||||
{
|
||||
XP_U16 nInMoveCache = NUM_SAVED_ENGINE_MOVES;
|
||||
XP_U16 nInMoveCache = engine->nMovesToSave;
|
||||
XP_U16 ii;
|
||||
|
||||
XP_ASSERT( engine->nMovesToSave == NUM_SAVED_ENGINE_MOVES );
|
||||
|
||||
for ( ii = 0; ii < NUM_SAVED_ENGINE_MOVES; ++ii ) {
|
||||
if ( 0 == engine->miData.savedMoves[ii].score ) {
|
||||
--nInMoveCache;
|
||||
|
@ -1306,9 +1337,7 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score )
|
|||
XP_Bool qualifies = XP_FALSE;
|
||||
XP_Bool usePrev = engine->usePrev;
|
||||
|
||||
if ( score > engine->targetScore ) {
|
||||
/* drop it */
|
||||
} else if ( usePrev && score < engine->miData.lastSeenMove.score ) {
|
||||
if ( usePrev && score < engine->miData.lastSeenMove.score ) {
|
||||
/* drop it */
|
||||
} else if ( !usePrev && score > engine->miData.lastSeenMove.score
|
||||
/* || (score < engine->miData.lowestSavedScore) */ ) {
|
||||
|
@ -1322,7 +1351,7 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score )
|
|||
NUM_SAVED_ENGINE_MOVES moves in here* and doing a quick test on
|
||||
that. Or better, keeping the list in sorted order. */
|
||||
for ( ii = 0, savedMoves = engine->miData.savedMoves;
|
||||
ii < NUM_SAVED_ENGINE_MOVES; ++ii, ++savedMoves ) {
|
||||
ii < engine->nMovesToSave; ++ii, ++savedMoves ) {
|
||||
if ( savedMoves->score == 0 ) { /* empty slot */
|
||||
qualifies = XP_TRUE;
|
||||
} else if ( usePrev && score <= savedMoves->score ) {
|
||||
|
@ -1332,9 +1361,6 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score )
|
|||
qualifies = XP_TRUE;
|
||||
break;
|
||||
}
|
||||
if ( engine->isRobot ) { /* we look at only one for robot */
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//XP_LOGF( "%s(%d)->%d", __func__, score, qualifies );
|
||||
|
|
|
@ -38,17 +38,16 @@ typedef struct BdHintLimits {
|
|||
|
||||
XP_U16 engine_getScoreCache( EngineCtxt* engine, XP_U16 row );
|
||||
|
||||
EngineCtxt* engine_make( MPFORMAL XW_UtilCtxt* util, XP_Bool isRobot );
|
||||
EngineCtxt* engine_make( MPFORMAL XW_UtilCtxt* util );
|
||||
|
||||
void engine_writeToStream( EngineCtxt* ctxt, XWStreamCtxt* stream );
|
||||
EngineCtxt* engine_makeFromStream( MPFORMAL XWStreamCtxt* stream,
|
||||
XW_UtilCtxt* util, XP_Bool isRobot );
|
||||
XW_UtilCtxt* util );
|
||||
|
||||
void engine_init( EngineCtxt* ctxt );
|
||||
void engine_reset( EngineCtxt* ctxt );
|
||||
void engine_destroy( EngineCtxt* ctxt );
|
||||
|
||||
#define NO_SCORE_LIMIT 10000 /* for targetScore */
|
||||
XP_Bool engine_findMove( EngineCtxt* ctxt, const ModelCtxt* model,
|
||||
const DictionaryCtxt* dict, const Tile* tiles,
|
||||
XP_U16 nTiles, XP_Bool usePrev,
|
||||
|
@ -56,8 +55,7 @@ XP_Bool engine_findMove( EngineCtxt* ctxt, const ModelCtxt* model,
|
|||
const BdHintLimits* boardLimits,
|
||||
XP_Bool useTileLimits,
|
||||
#endif
|
||||
XP_U16 targetScore, XP_Bool* canMove,
|
||||
MoveInfo* result );
|
||||
XP_U16 robotIQ, XP_Bool* canMove, MoveInfo* result );
|
||||
XP_Bool engine_check( DictionaryCtxt* dict, Tile* buf, XP_U16 buflen );
|
||||
|
||||
#ifdef CPLUS
|
||||
|
|
|
@ -297,7 +297,6 @@ gi_initPlayerInfo( MPFORMAL CurGameInfo* gi, const XP_UCHAR* nameTemplate )
|
|||
gi->serverRole = SERVER_STANDALONE;
|
||||
gi->nPlayers = 2;
|
||||
gi->boardSize = 15;
|
||||
gi->robotSmartness = SMART_ROBOT;
|
||||
gi->gameSeconds = 25 * 60; /* 25 minute game is common? */
|
||||
|
||||
gi->confirmBTConnect = XP_TRUE;
|
||||
|
@ -312,7 +311,7 @@ gi_initPlayerInfo( MPFORMAL CurGameInfo* gi, const XP_UCHAR* nameTemplate )
|
|||
fp->name = copyString( mpool, buf );
|
||||
}
|
||||
|
||||
fp->isRobot = (i == 0); /* one robot */
|
||||
fp->robotIQ = (i == 0) ? 1 : 0; /* one robot */
|
||||
fp->isLocal = XP_TRUE;
|
||||
fp->secondsUsed = 0;
|
||||
}
|
||||
|
@ -367,7 +366,6 @@ gi_copy( MPFORMAL CurGameInfo* destGI, const CurGameInfo* srcGI )
|
|||
|
||||
destGI->hintsNotAllowed = srcGI->hintsNotAllowed;
|
||||
destGI->timerEnabled = srcGI->timerEnabled;
|
||||
destGI->robotSmartness = (XP_U8)srcGI->robotSmartness;
|
||||
destGI->phoniesAction = srcGI->phoniesAction;
|
||||
destGI->allowPickTiles = srcGI->allowPickTiles;
|
||||
|
||||
|
@ -378,7 +376,7 @@ gi_copy( MPFORMAL CurGameInfo* destGI, const CurGameInfo* srcGI )
|
|||
replaceStringIfDifferent( mpool, &destPl->password,
|
||||
srcPl->password );
|
||||
destPl->secondsUsed = srcPl->secondsUsed;
|
||||
destPl->isRobot = srcPl->isRobot;
|
||||
destPl->robotIQ = srcPl->robotIQ;
|
||||
destPl->isLocal = srcPl->isLocal;
|
||||
}
|
||||
} /* gi_copy */
|
||||
|
@ -391,7 +389,7 @@ gi_countLocalPlayers( const CurGameInfo* gi, XP_Bool humanOnly )
|
|||
const LocalPlayer* lp = gi->players;
|
||||
while ( nPlayers-- ) {
|
||||
if ( lp->isLocal ) {
|
||||
if ( humanOnly && lp->isRobot ) {
|
||||
if ( humanOnly && LP_IS_ROBOT(lp) ) {
|
||||
// skip
|
||||
} else {
|
||||
++count;
|
||||
|
@ -420,7 +418,9 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi )
|
|||
gi->boardSize = (XP_U8)stream_getBits( stream, 4 );
|
||||
gi->serverRole = (DeviceRole)stream_getBits( stream, 2 );
|
||||
gi->hintsNotAllowed = stream_getBits( stream, 1 );
|
||||
gi->robotSmartness = (XP_U8)stream_getBits( stream, 2 );
|
||||
if ( strVersion < STREAM_VERS_ROBOTIQ ) {
|
||||
(void)stream_getBits( stream, 2 );
|
||||
}
|
||||
gi->phoniesAction = (XWPhoniesChoice)stream_getBits( stream, 2 );
|
||||
gi->timerEnabled = stream_getBits( stream, 1 );
|
||||
|
||||
|
@ -459,7 +459,9 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi )
|
|||
}
|
||||
|
||||
pl->secondsUsed = stream_getU16( stream );
|
||||
pl->isRobot = stream_getBits( stream, 1 );
|
||||
pl->robotIQ = ( strVersion < STREAM_VERS_ROBOTIQ )
|
||||
? (XP_U8)stream_getBits( stream, 1 )
|
||||
: pl->robotIQ = stream_getU8( stream );
|
||||
pl->isLocal = stream_getBits( stream, 1 );
|
||||
}
|
||||
} /* gi_readFromStream */
|
||||
|
@ -476,7 +478,6 @@ gi_writeToStream( XWStreamCtxt* stream, const CurGameInfo* gi )
|
|||
stream_putBits( stream, 4, gi->boardSize );
|
||||
stream_putBits( stream, 2, gi->serverRole );
|
||||
stream_putBits( stream, 1, gi->hintsNotAllowed );
|
||||
stream_putBits( stream, 2, gi->robotSmartness );
|
||||
stream_putBits( stream, 2, gi->phoniesAction );
|
||||
stream_putBits( stream, 1, gi->timerEnabled );
|
||||
stream_putBits( stream, 1, gi->allowPickTiles );
|
||||
|
@ -491,7 +492,7 @@ gi_writeToStream( XWStreamCtxt* stream, const CurGameInfo* gi )
|
|||
stringToStream( stream, pl->name );
|
||||
stringToStream( stream, pl->password );
|
||||
stream_putU16( stream, pl->secondsUsed );
|
||||
stream_putBits( stream, 1, pl->isRobot );
|
||||
stream_putU8( stream, pl->robotIQ );
|
||||
stream_putBits( stream, 1, pl->isLocal );
|
||||
}
|
||||
} /* gi_writeToStream */
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define STREAM_VERS_ROBOTIQ 0x0E /* robots have different smarts */
|
||||
#define STREAM_VERS_DICTLANG 0x0D /* save dict lang code in CurGameInfo */
|
||||
#define STREAM_VERS_NUNDONE 0x0C /* save undone tile in model */
|
||||
#define STREAM_VERS_GAMESECONDS 0x0B /* save gameSeconds whether or not
|
||||
|
@ -48,16 +49,20 @@ extern "C" {
|
|||
#define STREAM_VERS_41B4 0x02
|
||||
#define STREAM_VERS_405 0x01
|
||||
|
||||
#define CUR_STREAM_VERS STREAM_VERS_DICTLANG
|
||||
#define CUR_STREAM_VERS STREAM_VERS_ROBOTIQ
|
||||
|
||||
typedef struct LocalPlayer {
|
||||
XP_UCHAR* name;
|
||||
XP_UCHAR* password;
|
||||
XP_U16 secondsUsed;
|
||||
XP_Bool isRobot;
|
||||
XP_Bool isLocal;
|
||||
XP_U8 robotIQ; /* 0 means not a robot; 1-100 means how
|
||||
dumb is it with 1 meaning very smart */
|
||||
} LocalPlayer;
|
||||
|
||||
#define LP_IS_ROBOT(lp) ((lp)->robotIQ != 0)
|
||||
#define LP_IS_LOCAL(lp) ((lp)->isLocal)
|
||||
|
||||
#define DUMB_ROBOT 0
|
||||
#define SMART_ROBOT 1
|
||||
|
||||
|
@ -75,7 +80,6 @@ typedef struct CurGameInfo {
|
|||
XP_Bool timerEnabled;
|
||||
XP_Bool allowPickTiles;
|
||||
XP_Bool allowHintRect;
|
||||
XP_U8 robotSmartness;
|
||||
XWPhoniesChoice phoniesAction;
|
||||
XP_Bool confirmBTConnect; /* only used for BT */
|
||||
} CurGameInfo;
|
||||
|
|
|
@ -198,7 +198,7 @@ cpToLP( NGValue value, const void* cbClosure )
|
|||
strAddr = &lp->password;
|
||||
break;
|
||||
case NG_COL_ROBOT:
|
||||
lp->isRobot = value.ng_bool;
|
||||
lp->robotIQ = value.ng_bool ? 1 : 0;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -591,7 +591,7 @@ loadPlayer( NewGameCtx* ngc, XP_U16 player, const LocalPlayer* lp )
|
|||
value.ng_cp = lp->password;
|
||||
(*ngc->setColProc)(closure, player, NG_COL_PASSWD, value );
|
||||
|
||||
value.ng_bool = lp->isRobot;
|
||||
value.ng_bool = LP_IS_ROBOT(lp);
|
||||
(*ngc->setColProc)(closure, player, NG_COL_ROBOT, value );
|
||||
}
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ drawScoreBoard( BoardCtxt* board )
|
|||
dp->dsi.name = emptyStringIfNull(lp->name);
|
||||
dp->dsi.selected = board->trayVisState != TRAY_HIDDEN
|
||||
&& ii==selPlayer;
|
||||
dp->dsi.isRobot = lp->isRobot;
|
||||
dp->dsi.isRobot = LP_IS_ROBOT(lp);
|
||||
dp->dsi.isRemote = !lp->isLocal;
|
||||
dp->dsi.nTilesLeft = (nTilesInPool > 0)? -1:
|
||||
model_getNumTilesTotal( model, ii );
|
||||
|
|
|
@ -45,9 +45,6 @@ extern "C" {
|
|||
|
||||
#define LOCAL_ADDR NULL
|
||||
|
||||
#define IS_ROBOT(p) ((p)->isRobot)
|
||||
#define IS_LOCAL(p) ((p)->isLocal)
|
||||
|
||||
enum {
|
||||
END_REASON_USER_REQUEST,
|
||||
END_REASON_OUT_OF_TILES,
|
||||
|
@ -309,7 +306,6 @@ server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
|
|||
{
|
||||
ServerCtxt* server;
|
||||
short i;
|
||||
CurGameInfo* gi = util->gameInfo;
|
||||
|
||||
server = server_make( MPPARM(mpool) model, comms, util );
|
||||
getNV( stream, &server->nv, nPlayers );
|
||||
|
@ -324,10 +320,8 @@ server_makeFromStream( MPFORMAL XWStreamCtxt* stream, ModelCtxt* model,
|
|||
player->deviceIndex = stream_getU8( stream );
|
||||
|
||||
if ( stream_getU8( stream ) != 0 ) {
|
||||
LocalPlayer* lp = &gi->players[i];
|
||||
player->engine = engine_makeFromStream( MPPARM(mpool)
|
||||
stream, util,
|
||||
lp->isRobot );
|
||||
stream, util );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -504,7 +498,7 @@ server_initClientConnection( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
continue;
|
||||
}
|
||||
|
||||
stream_putBits( stream, 1, lp->isRobot ); /* better not to send this */
|
||||
stream_putBits( stream, 1, LP_IS_ROBOT(lp) ); /* better not to send this */
|
||||
|
||||
/* The first nPlayers players are the ones we'll use. The local flag
|
||||
doesn't matter when for SERVER_ISCLIENT. */
|
||||
|
@ -635,43 +629,6 @@ robotTradeTiles( ServerCtxt* server, MoveInfo* newMove )
|
|||
} /* robotTradeTiles */
|
||||
#endif
|
||||
|
||||
#define FUDGE_RANGE 10
|
||||
#define MINIMUM_SCORE 5
|
||||
static XP_U16
|
||||
figureTargetScore( ServerCtxt* server, XP_U16 turn )
|
||||
{
|
||||
XP_S16 result = 1000;
|
||||
XP_S16 highScore = 0;
|
||||
ModelCtxt* model = server->vol.model;
|
||||
XP_U16 nPlayers = server->vol.gi->nPlayers;
|
||||
XP_U16 i;
|
||||
|
||||
XP_ASSERT( IS_ROBOT(&server->vol.gi->players[turn]) );
|
||||
|
||||
if ( 1 /* server->nHumanPlayers > 0 */ ) {
|
||||
result = 0;
|
||||
|
||||
/* find the highest score anybody but the current player has */
|
||||
for ( i = 0; i < nPlayers; ++i ) {
|
||||
if ( i != turn ) {
|
||||
XP_S16 score = model_getPlayerScore( model, i );
|
||||
XP_ASSERT( score >= 0 );
|
||||
if ( highScore < score ) {
|
||||
highScore = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = (XP_S16)(highScore - model_getPlayerScore( model, turn )
|
||||
+ (FUDGE_RANGE-(XP_RANDOM() % (FUDGE_RANGE*2))));
|
||||
if ( result < 0 ) {
|
||||
result = MINIMUM_SCORE;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} /* figureTargetScore */
|
||||
|
||||
static XWStreamCtxt*
|
||||
mkServerStream( ServerCtxt* server )
|
||||
{
|
||||
|
@ -697,7 +654,6 @@ makeRobotMove( ServerCtxt* server )
|
|||
XP_Bool timerEnabled = gi->timerEnabled;
|
||||
XP_Bool canMove;
|
||||
XP_U32 time = 0L; /* stupid compiler.... */
|
||||
XP_U16 targetScore = NO_SCORE_LIMIT;
|
||||
XW_UtilCtxt* util = server->vol.util;
|
||||
|
||||
if ( timerEnabled ) {
|
||||
|
@ -715,10 +671,6 @@ makeRobotMove( ServerCtxt* server )
|
|||
|
||||
tileSet = model_getPlayerTiles( model, turn );
|
||||
|
||||
if ( gi->robotSmartness == DUMB_ROBOT ) {
|
||||
targetScore = figureTargetScore( server, turn );
|
||||
}
|
||||
|
||||
XP_ASSERT( !!server_getEngineFor( server, turn ) );
|
||||
searchComplete = engine_findMove( server_getEngineFor( server, turn ),
|
||||
model, model_getDictionary( model ),
|
||||
|
@ -726,7 +678,8 @@ makeRobotMove( ServerCtxt* server )
|
|||
#ifdef XWFEATURE_SEARCHLIMIT
|
||||
NULL, XP_FALSE,
|
||||
#endif
|
||||
targetScore, &canMove, &newMove );
|
||||
server->vol.gi->players[turn].robotIQ,
|
||||
&canMove, &newMove );
|
||||
if ( searchComplete ) {
|
||||
const XP_UCHAR* str;
|
||||
XWStreamCtxt* stream = NULL;
|
||||
|
@ -806,7 +759,7 @@ robotMovePending( const ServerCtxt* server )
|
|||
if ( turn >= 0 && tileCountsOk(server) && NPASSES_OK(server) ) {
|
||||
CurGameInfo* gi = server->vol.gi;
|
||||
LocalPlayer* player = &gi->players[turn];
|
||||
result = IS_ROBOT(player) && IS_LOCAL(player);
|
||||
result = LP_IS_ROBOT(player) && LP_IS_LOCAL(player);
|
||||
}
|
||||
return result;
|
||||
} /* robotMovePending */
|
||||
|
@ -852,8 +805,8 @@ showPrevScore( ServerCtxt* server )
|
|||
|
||||
prevTurn = (server->nv.currentTurn + nPlayers - 1) % nPlayers;
|
||||
lp = &gi->players[prevTurn];
|
||||
wasRobot = lp->isRobot;
|
||||
wasLocal = lp->isLocal;
|
||||
wasRobot = LP_IS_ROBOT(lp);
|
||||
wasLocal = LP_IS_LOCAL(lp);
|
||||
|
||||
if ( wasLocal ) {
|
||||
XP_ASSERT( wasRobot );
|
||||
|
@ -1023,7 +976,7 @@ registerRemotePlayer( ServerCtxt* server, XWStreamCtxt* stream )
|
|||
lp = findFirstPending( server, &player );
|
||||
|
||||
/* get data from stream */
|
||||
lp->isRobot = stream_getBits( stream, 1 );
|
||||
lp->robotIQ = 1 == stream_getBits( stream, 1 )? 1 : 0;
|
||||
nameLen = stream_getBits( stream, NAME_LEN_NBITS );
|
||||
name = (XP_UCHAR*)XP_MALLOC( server->mpool, nameLen + 1 );
|
||||
stream_getBytes( stream, name, nameLen );
|
||||
|
@ -1060,8 +1013,8 @@ clearLocalRobots( ServerCtxt* server )
|
|||
|
||||
for ( i = 0; i < nPlayers; ++i ) {
|
||||
LocalPlayer* player = &gi->players[i];
|
||||
if ( IS_LOCAL( player ) ) {
|
||||
player->isRobot = XP_FALSE;
|
||||
if ( LP_IS_LOCAL( player ) ) {
|
||||
player->robotIQ = 0;
|
||||
}
|
||||
}
|
||||
} /* clearLocalRobots */
|
||||
|
@ -1409,8 +1362,7 @@ server_getEngineFor( ServerCtxt* server, XP_U16 playerNum )
|
|||
engine = player->engine;
|
||||
if ( !engine && server->vol.gi->players[playerNum].isLocal ) {
|
||||
engine = engine_make( MPPARM(server->mpool)
|
||||
server->vol.util,
|
||||
server->vol.gi->players[playerNum].isRobot );
|
||||
server->vol.util );
|
||||
player->engine = engine;
|
||||
}
|
||||
|
||||
|
@ -1542,7 +1494,7 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
|
|||
XP_ASSERT( !!pool );
|
||||
#ifdef FEATURE_TRAY_EDIT
|
||||
ask = server->vol.gi->allowPickTiles
|
||||
&& !server->vol.gi->players[playerNum].isRobot;
|
||||
&& !LP_IS_ROBOT(&server->vol.gi->players[playerNum]);
|
||||
#else
|
||||
ask = XP_FALSE;
|
||||
#endif
|
||||
|
@ -2060,7 +2012,7 @@ server_commitMove( ServerCtxt* server )
|
|||
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
|
||||
|
||||
#ifdef DEBUG
|
||||
if ( IS_ROBOT( &gi->players[turn] ) ) {
|
||||
if ( LP_IS_ROBOT( &gi->players[turn] ) ) {
|
||||
XP_ASSERT( model_checkMoveLegal( model, turn, (XWStreamCtxt*)NULL,
|
||||
(WordNotifierInfo*)NULL ) );
|
||||
}
|
||||
|
@ -2388,7 +2340,7 @@ server_handleUndo( ServerCtxt* server )
|
|||
++nUndone;
|
||||
XP_ASSERT( moveNum >= 0 );
|
||||
lastUndone = moveNum;
|
||||
if ( !IS_ROBOT(&gi->players[lastTurnUndone]) ) {
|
||||
if ( !LP_IS_ROBOT(&gi->players[lastTurnUndone]) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue