toward making tile picking work through rotations

Make face-up tile picker util method return void and add mechanism for
passing in selected tiles asynchronously, as has been done recently with
the rest of the once-blocking util callbacks. Works perfectly in the gtk
case. Likely crashes in curses (if picking face-up option is on.) In
java all the callbacks are there but rather than put up a UI we pretend
the user says "pick 'em for me" each time. Putting up a UI is next.
This commit is contained in:
Eric House 2017-03-09 20:36:14 -08:00
parent 70b3aaa263
commit 6e5973c55b
19 changed files with 385 additions and 131 deletions

View file

@ -1869,16 +1869,32 @@ public class BoardDelegate extends DelegateBase
}
@Override
public int userPickTileTray( int playerNum, String[] texts,
String[] curTiles, int nPicked )
public void informNeedPickTiles( final boolean isInitial,
final int playerNum, int nToPick,
String[] texts, int[] counts )
{
String curTilesStr = TextUtils.join( ", ", curTiles );
boolean canUndoTiles = 0 < nPicked;
waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK,
UtilCtxt.PICKER_PICKALL, texts, curTilesStr,
canUndoTiles );
return m_resultCode;
post( new Runnable() {
@Override
public void run() {
int[] noNewTiles = new int[0];
if ( isInitial ) {
handleViaThread( JNICmd.CMD_TILES_PICKED, playerNum, noNewTiles );
} else {
handleViaThread( JNICmd.CMD_COMMIT, true, true, noNewTiles );
}
}
} );
}
// public int userPickTileTray( int playerNum, String[] texts,
// String[] curTiles, int nPicked )
// {
// String curTilesStr = TextUtils.join( ", ", curTiles );
// boolean canUndoTiles = 0 < nPicked;
// waitBlockingDialog( DlgID.PICK_TILE_REQUESTTRAY_BLK,
// UtilCtxt.PICKER_PICKALL, texts, curTilesStr,
// canUndoTiles );
// return m_resultCode;
// }
@Override
public void informNeedPassword( int player, String name )

View file

@ -72,6 +72,7 @@ public class JNIThread extends Thread {
CMD_KEYUP,
CMD_TIMER_FIRED,
CMD_COMMIT,
CMD_TILES_PICKED,
CMD_JUGGLE,
CMD_FLIP,
CMD_TOGGLE_TRAY,
@ -552,9 +553,17 @@ public class JNIThread extends Thread {
? false : (Boolean)args[0];
boolean turnConfirmed = args.length < 2
? false : (Boolean)args[1];
int[] newTiles = args.length < 3 ? null : (int[])args[2];
draw = XwJNI.board_commitTurn( m_jniGamePtr, phoniesConfirmed,
turnConfirmed );
turnConfirmed, newTiles );
break;
case CMD_TILES_PICKED:
int playerNum = (Integer)args[0];
int[] tiles = (int[])args[1];
XwJNI.server_tilesPicked( m_jniGamePtr, playerNum, tiles );
break;
case CMD_JUGGLE:
draw = XwJNI.board_juggleTray( m_jniGamePtr );
break;

View file

@ -38,8 +38,9 @@ public interface UtilCtxt {
public static final int PICKER_BACKUP = -2;
void notifyPickTileBlank( int playerNum, int col, int row, String[] texts );
int userPickTileTray( int playerNum, String[] tiles,
String[] curTiles, int nPicked );
void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
String[] texts, int[] counts );
void informNeedPassword( int player, String name );

View file

@ -54,11 +54,10 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "userPickTileBlank" );
}
public int userPickTileTray( int playerNum, String[] texts,
String[] curTiles, int nPicked )
public void informNeedPickTiles( boolean isInitial, int playerNum, int nToPick,
String[] texts, int[] counts )
{
subclassOverride( "userPickTileTray" );
return 0;
subclassOverride( "informNeedPickTiles" );
}
public void informNeedPassword( int player, String name )

View file

@ -300,7 +300,8 @@ public class XwJNI {
public static native boolean board_toggle_showValues( GamePtr gamePtr );
public static native boolean board_commitTurn( GamePtr gamePtr,
boolean phoniesConfirmed,
boolean turnConfirmed );
boolean turnConfirmed,
int[] newTiles );
public static native boolean board_flip( GamePtr gamePtr );
public static native boolean board_replaceTiles( GamePtr gamePtr );
@ -358,6 +359,8 @@ public class XwJNI {
public static native void server_reset( GamePtr gamePtr );
public static native void server_handleUndo( GamePtr gamePtr );
public static native boolean server_do( GamePtr gamePtr );
public static native void server_tilesPicked( GamePtr gamePtr, int player, int[] tiles );
public static native String server_formatDictCounts( GamePtr gamePtr, int nCols );
public static native boolean server_getGameIsOver( GamePtr gamePtr );
public static native String server_writeFinalScores( GamePtr gamePtr );

View file

@ -1,4 +1,4 @@
/* -*-mode: C; compile-command: "cd ..; ../scripts/ndkbuild.sh -j3"; -*- */
/* -*-mode: C; compile-command: "find-and-gradle.sh insXwdDeb"; -*- */
/*
* Copyright 2001-2010 by Eric House (xwords@eehouse.org). All rights
* reserved.

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-ant.sh debug install"; -*- */
/* -*- compile-command: "find-and-gradle.sh insXw4Deb"; -*- */
/*
* Copyright 2001-2014 by Eric House (xwords@eehouse.org). All rights
* Copyright 2001 - 2017 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -162,24 +162,23 @@ and_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
UTIL_CBK_TAIL();
}
static XP_S16
and_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** tileFaces,
XP_U16 nTiles )
static void
and_util_informNeedPickTiles( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 player, XP_U16 nToPick,
XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts )
{
XP_S16 result = -1;
UTIL_CBK_HEADER("userPickTileTray",
"(I[Ljava/lang/String;[Ljava/lang/String;I)I" );
jobject jtexts = makeStringArray( env, nTiles, tileFaces );
jobject jcurtiles = makeStringArray( env, pi->nCurTiles, pi->curTiles );
result = (*env)->CallIntMethod( env, util->jutil, mid,
playerNum, jtexts, jcurtiles,
pi->thisPick );
deleteLocalRefs( env, jtexts, jcurtiles, DELETE_NO_REF );
UTIL_CBK_HEADER("informNeedPickTiles",
"(ZII[Ljava/lang/String;[I)V" );
jobject jtexts = makeStringArray( env, nFaces, faces );
jobject jcounts = makeIntArray( env, nFaces, counts );
(*env)->CallVoidMethod( env, util->jutil, mid, isInitial, player,
nToPick, jtexts, jcounts );
deleteLocalRefs( env, jtexts, jcounts, DELETE_NO_REF );
UTIL_CBK_TAIL();
return result;
} /* and_util_userPickTile */
} /* and_util_informNeedPickTiles */
static void
and_util_informNeedPassword( XW_UtilCtxt* uc, XP_U16 player,
@ -707,7 +706,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
SET_PROC(notifyMove);
SET_PROC(notifyTrade);
SET_PROC(notifyPickTileBlank);
SET_PROC(userPickTileTray);
SET_PROC(informNeedPickTiles);
SET_PROC(informNeedPassword);
SET_PROC(trayHiddenChange);
SET_PROC(yOffsetChange);

View file

@ -220,6 +220,21 @@ envForMe( EnvThreadInfo* ti, const char* caller )
return result;
}
static void
tilesArrayToTileSet( JNIEnv* env, jintArray jtiles, TrayTileSet* tset )
{
if ( jtiles != NULL ) {
jsize nTiles = (*env)->GetArrayLength( env, jtiles );
int tmp[MAX_TRAY_TILES];
getIntsFromArray( env, tmp, jtiles, nTiles, XP_FALSE );
tset->nTiles = nTiles;
for ( int ii = 0; ii < nTiles; ++ii ) {
tset->tiles[ii] = tmp[ii];
}
}
}
#ifdef GAMEPTR_IS_OBJECT
static JNIState*
getState( JNIEnv* env, GamePtrType gamePtr )
@ -1282,13 +1297,21 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1toggle_1showValues
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn
(JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed,
jboolean turnConfirmed)
( JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed,
jboolean turnConfirmed, jintArray jNewTiles )
{
jboolean result;
XWJNI_START();
TrayTileSet* newTilesP = NULL;
TrayTileSet newTiles;
if ( jNewTiles != NULL ) {
tilesArrayToTileSet( env, jNewTiles, &newTiles );
newTilesP = &newTiles;
}
result = board_commitTurn( state->game.board, phoniesConfirmed,
turnConfirmed );
turnConfirmed, newTilesP );
XWJNI_END();
return result;
}
@ -1356,6 +1379,17 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1do
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_server_1tilesPicked
( JNIEnv* env, jclass C, GamePtrType gamePtr, jint player, jintArray jNewTiles )
{
XWJNI_START();
TrayTileSet newTiles;
tilesArrayToTileSet( env, jNewTiles, &newTiles );
server_tilesPicked( state->game.server, player, &newTiles );
XWJNI_END();
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_board_1resetEngine
(JNIEnv* env, jclass C, GamePtrType gamePtr )

View file

@ -1026,17 +1026,19 @@ boardNotifyTrade( BoardCtxt* board, const TrayTileSet* tiles )
XP_Bool
board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
XP_Bool turnConfirmed /* includes trade */ )
XP_Bool turnConfirmed /* includes trade */,
TrayTileSet* newTiles )
{
XP_Bool result = XP_FALSE;
const XP_S16 turn = server_getCurrentTurn( board->server, NULL );
PerTurnInfo* pti = board->pti + turn;
ModelCtxt* model = board->model;
if ( board->gameOver || turn < 0 ) {
/* do nothing */
} else if ( turn != board->selPlayer ) {
util_userError( board->util, ERR_NOT_YOUR_TURN );
} else if ( 0 == model_getNumTilesTotal( board->model, turn ) ) {
} else if ( 0 == model_getNumTilesTotal( model, turn ) ) {
/* game's over but still undoable so turn hasn't changed; do
nothing */
} else if ( phoniesConfirmed || turnConfirmed || checkRevealTray( board ) ) {
@ -1051,11 +1053,15 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
TrayTileSet selTiles;
getSelTiles( board, traySelBits, &selTiles );
if ( turnConfirmed ) {
/* server_commitTrade() changes selPlayer, so board_endTrade
must be called first() */
(void)board_endTrade( board );
if ( !server_askPickTiles( board->server, turn, newTiles,
selTiles.nTiles ) ) {
/* server_commitTrade() changes selPlayer, so board_endTrade
must be called first() */
(void)board_endTrade( board );
(void)server_commitTrade( board->server, &selTiles );
(void)server_commitTrade( board->server, &selTiles,
newTiles );
}
} else {
boardNotifyTrade( board, &selTiles );
}
@ -1081,13 +1087,13 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
info.proc = saveBadWords;
info.closure = &bwl;
}
legal = model_checkMoveLegal( board->model, turn, stream,
legal = model_checkMoveLegal( model, turn, stream,
warn? &info:(WordNotifierInfo*)NULL);
}
if ( 0 < bwl.bwi.nWords && !phoniesConfirmed ) {
bwl.bwi.dictName =
dict_getShortName( model_getPlayerDict( board->model, turn ) );
dict_getShortName( model_getPlayerDict( model, turn ) );
util_notifyIllegalWords( board->util, &bwl.bwi, turn, XP_FALSE );
} else {
/* Hide the tray so no peeking. Leave it hidden even if user
@ -1099,14 +1105,20 @@ board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
}
if ( board->skipCommitConfirm || turnConfirmed ) {
result = server_commitMove( board->server ) || result;
/* invalidate all tiles in case we'll be drawing this tray
again rather than some other -- as is the case in a
two-player game where one's a robot. We really only
need the selected tiles and the rightmost (in case it's
showing points-this-turn), but this is easier. */
board_invalTrayTiles( board, ALLTILES );
pti->traySelBits = 0x00;
XP_U16 nToPick = MAX_TRAY_TILES -
model_getNumTilesInTray( model, turn );
if ( !server_askPickTiles( board->server, turn, newTiles,
nToPick ) ) {
result = server_commitMove( board->server, newTiles )
|| result;
/* invalidate all tiles in case we'll be drawing this tray
again rather than some other -- as is the case in a
two-player game where one's a robot. We really only
need the selected tiles and the rightmost (in case it's
showing points-this-turn), but this is easier. */
board_invalTrayTiles( board, ALLTILES );
pti->traySelBits = 0x00;
}
} else {
util_notifyMove( board->util, stream );
}

View file

@ -177,7 +177,7 @@ XP_Bool board_setBlankValue( BoardCtxt* board, XP_U16 XP_UNUSED(player),
void board_resetEngine( BoardCtxt* board );
XP_Bool board_commitTurn( BoardCtxt* board, XP_Bool phoniesConfirmed,
XP_Bool turnConfirmed );
XP_Bool turnConfirmed, TrayTileSet* newTiles );
void board_pushTimerSave( BoardCtxt* board );
void board_popTimerSave( BoardCtxt* board );

View file

@ -34,6 +34,7 @@
#include "pool.h"
#include "engine.h"
#include "strutils.h"
#include "dbgutil.h"
#include "LocalizedStrIncludes.h"
@ -132,6 +133,8 @@ struct ServerCtxt {
/******************************* prototypes *******************************/
static void assignTilesToAll( ServerCtxt* server );
static void makePoolOnce( ServerCtxt* server );
static void resetEngines( ServerCtxt* server );
static void nextTurn( ServerCtxt* server, XP_S16 nxtTurn );
@ -142,6 +145,9 @@ static void badWordMoveUndoAndTellUser( ServerCtxt* server,
static XP_Bool tileCountsOk( const ServerCtxt* server );
static void setTurn( ServerCtxt* server, XP_S16 turn );
static XWStreamCtxt* mkServerStream( ServerCtxt* server );
static void fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
const TrayTileSet* tradedTiles,
TrayTileSet* resultTiles );
#ifndef XWFEATURE_STANDALONE_ONLY
static XWStreamCtxt* messageStreamWithHeader( ServerCtxt* server,
@ -900,7 +906,7 @@ makeRobotMove( ServerCtxt* server )
if ( trade ) {
TrayTileSet oldTiles = *model_getPlayerTiles( model, turn );
XP_LOGF( "%s: robot trading %d tiles", __func__, oldTiles.nTiles );
result = server_commitTrade( server, &oldTiles );
result = server_commitTrade( server, &oldTiles, NULL );
/* Quick hack to fix gremlin bug where all-robot game seen none
able to trade for tiles to move and blowing the undo stack.
@ -933,7 +939,7 @@ makeRobotMove( ServerCtxt* server )
server->nv.prevMoveStream = stream;
server->nv.prevWordsStream = wordsStream;
}
result = server_commitMove( server );
result = server_commitMove( server, NULL );
} else {
result = XP_FALSE;
}
@ -1047,6 +1053,64 @@ showPrevScore( ServerCtxt* server )
SETSTATE( server, server->nv.stateAfterShow );
} /* showPrevScore */
void
server_tilesPicked( ServerCtxt* server, XP_U16 player,
const TrayTileSet* newTilesP )
{
TrayTileSet newTiles = *newTilesP;
pool_removeTiles( server->pool, &newTiles );
fetchTiles( server, player, MAX_TRAY_TILES, NULL, &newTiles );
model_assignPlayerTiles( server->vol.model, player, &newTiles );
util_requestTime( server->vol.util );
}
static void
informNeedPickTiles( ServerCtxt* server, XP_Bool initial, XP_U16 turn,
XP_U16 nToPick )
{
ModelCtxt* model = server->vol.model;
DictionaryCtxt* dict = model_getDictionary(model);
XP_U16 nFaces = dict_numTileFaces( dict );
XP_U16 counts[MAX_UNIQUE_TILES];
const XP_UCHAR* faces[MAX_UNIQUE_TILES];
XP_U16 nLeft = pool_getNTilesLeft( server->pool );
if ( nLeft < nToPick ) {
nToPick = nLeft;
}
for ( Tile tile = 0; tile < nFaces; ++tile ) {
faces[tile] = dict_getTileString( dict, tile );
counts[tile] = pool_getNTilesLeftFor( server->pool, tile );
}
util_informNeedPickTiles( server->vol.util, initial, turn,
nToPick, nFaces, faces, counts );
}
static XP_Bool
askedForTiles( ServerCtxt* server )
{
XP_Bool asked = XP_FALSE;
CurGameInfo* gi = server->vol.gi;
if ( gi->serverRole == SERVER_STANDALONE && gi->allowPickTiles ) {
XP_U16 nPlayers = gi->nPlayers;
ModelCtxt* model = server->vol.model;
makePoolOnce( server );
for ( XP_U16 turn = 0; !asked && turn < nPlayers; ++turn ) {
LocalPlayer* player = &gi->players[turn];
XP_U16 nTiles = model_getNumTilesInTray( model, turn );
if ( nTiles == 0 && !LP_IS_ROBOT(player) ) {
informNeedPickTiles( server, XP_TRUE, turn, MAX_TRAY_TILES );
asked = XP_TRUE;
}
}
}
LOG_RETURNF( "%s", boolToStr(asked));
return asked;
}
XP_Bool
server_do( ServerCtxt* server )
{
@ -1062,10 +1126,12 @@ server_do( ServerCtxt* server )
case XWSTATE_BEGIN:
if ( server->nv.pendingRegistrations == 0 ) { /* all players on
device */
assignTilesToAll( server );
SETSTATE( server, XWSTATE_INTURN );
setTurn( server, 0 );
moreToDo = XP_TRUE;
if ( !askedForTiles( server ) ) {
assignTilesToAll( server );
SETSTATE( server, XWSTATE_INTURN );
setTurn( server, 0 );
moreToDo = XP_TRUE;
}
}
break;
@ -1359,8 +1425,8 @@ client_readInitialMessage( ServerCtxt* server, XWStreamCtxt* stream )
dict_unref( newDict ); /* new owner will have ref'd */
XP_ASSERT( !server->pool );
pool = server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) );
pool_initFromDict( server->pool, model_getDictionary(model));
makePoolOnce( server );
pool = server->pool;
/* now read the assigned tiles for each player from the stream, and
remove them from the newly-created local pool. */
@ -1755,6 +1821,23 @@ curTrayAsTexts( ServerCtxt* server, XP_U16 turn, const TrayTileSet* notInTray,
*nUsedP = nUsed;
} /* curTrayAsTexts */
/**
* Return true (after calling util_informPickTiles()) IFF allowPickTiles is
* TRUE and the tile set passed in is NULL. If it doesn't contain as many
* tiles as are needed that's cool: server code will later interpret that as
* meaning the remainder should be assigned randomly as usual.
*/
XP_Bool
server_askPickTiles( ServerCtxt* server, XP_U16 turn, TrayTileSet* newTiles,
XP_U16 nToPick )
{
XP_Bool asked = newTiles == NULL && server->vol.gi->allowPickTiles;
if ( asked ) {
informNeedPickTiles( server, XP_FALSE, turn, nToPick );
}
return asked;
}
/* Get tiles for one user. If picking is available, let user pick until
* cancels. Otherwise, and after cancel, pick for 'im.
*/
@ -1763,7 +1846,7 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
const TrayTileSet* tradedTiles, TrayTileSet* resultTiles )
{
XP_Bool ask;
XP_U16 nSoFar = 0;
XP_U16 nSoFar = resultTiles->nTiles;
XP_U16 nLeft;
PoolContext* pool = server->pool;
TrayTileSet oneTile;
@ -1796,17 +1879,17 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
#ifdef FEATURE_TRAY_EDIT /* good compiler would note ask==0, but... */
/* First ask until cancelled */
for ( nSoFar = 0; ask && nSoFar < nToFetch; ) {
for ( ; ask && nSoFar < nToFetch; ) {
const XP_UCHAR* texts[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES];
XP_S16 chosen;
XP_U16 nUsed = MAX_UNIQUE_TILES;
// XP_ASSERT(0); /* should no longer happen!!! */
model_packTilesUtil( server->vol.model, pool,
XP_TRUE, &nUsed, texts, tiles );
chosen = util_userPickTileTray( server->vol.util, &pi, playerNum,
texts, nUsed );
chosen = PICKER_PICKALL; /*util_userPickTileTray( server->vol.util,
&pi, playerNum, texts, nUsed );*/
if ( chosen == PICKER_PICKALL ) {
ask = XP_FALSE;
@ -1846,6 +1929,18 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
resultTiles->nTiles = (XP_U8)nSoFar;
} /* fetchTiles */
static void
makePoolOnce( ServerCtxt* server )
{
ModelCtxt* model = server->vol.model;
XP_ASSERT( model_getDictionary(model) != NULL );
if ( server->pool == NULL ) {
server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) );
XP_STATUSF( "initing pool" );
pool_initFromDict( server->pool, model_getDictionary(model));
}
}
static void
assignTilesToAll( ServerCtxt* server )
{
@ -1855,12 +1950,7 @@ assignTilesToAll( ServerCtxt* server )
XP_U16 nPlayers = server->vol.gi->nPlayers;
XP_ASSERT( server->vol.gi->serverRole != SERVER_ISCLIENT );
XP_ASSERT( model_getDictionary(model) != NULL );
if ( server->pool == NULL ) {
server->pool = pool_make( MPPARM_NOCOMMA(server->mpool) );
XP_STATUSF( "initing pool" );
pool_initFromDict( server->pool, model_getDictionary(model));
}
makePoolOnce( server );
XP_STATUSF( "assignTilesToAll" );
@ -1871,9 +1961,11 @@ assignTilesToAll( ServerCtxt* server )
numAssigned = MAX_TRAY_TILES;
}
for ( ii = 0; ii < nPlayers; ++ii ) {
TrayTileSet newTiles;
fetchTiles( server, ii, numAssigned, NULL, &newTiles );
model_assignPlayerTiles( model, ii, &newTiles );
if ( 0 == model_getNumTilesInTray( model, ii ) ) {
TrayTileSet newTiles = {0};
fetchTiles( server, ii, numAssigned, NULL, &newTiles );
model_assignPlayerTiles( model, ii, &newTiles );
}
sortTilesIf( server, ii );
}
@ -2345,16 +2437,20 @@ reflectMove( ServerCtxt* server, XWStreamCtxt* stream )
* if it was legal -- but only if DISALLOW is set.
*/
XP_Bool
server_commitMove( ServerCtxt* server )
server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
{
XP_S16 turn = server->nv.currentTurn;
ModelCtxt* model = server->vol.model;
CurGameInfo* gi = server->vol.gi;
TrayTileSet newTiles;
TrayTileSet newTiles = {0};
XP_U16 nTilesMoved;
XP_Bool isLegalMove = XP_TRUE;
XP_Bool isClient = gi->serverRole == SERVER_ISCLIENT;
if ( !!newTilesP ) {
newTiles = *newTilesP;
}
#ifdef DEBUG
if ( LP_IS_ROBOT( &gi->players[turn] ) ) {
XP_ASSERT( model_checkMoveLegal( model, turn, (XWStreamCtxt*)NULL,
@ -2408,9 +2504,13 @@ server_commitMove( ServerCtxt* server )
} /* server_commitMove */
XP_Bool
server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles )
server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles,
TrayTileSet* newTilesP )
{
TrayTileSet newTiles;
TrayTileSet newTiles = {0};
if ( !!newTilesP ) {
newTiles = *newTilesP;
}
XP_U16 turn = server->nv.currentTurn;
fetchTiles( server, turn, oldTiles->nTiles, oldTiles, &newTiles );

View file

@ -102,12 +102,18 @@ XP_U32 server_getLastMoveTime( const ServerCtxt* server );
/* Signed in case no dictionary available */
XP_S16 server_countTilesInPool( ServerCtxt* server );
XP_Bool server_askPickTiles( ServerCtxt* server, XP_U16 player,
TrayTileSet* newTiles, XP_U16 nToPick );
void server_tilesPicked( ServerCtxt* server, XP_U16 player,
const TrayTileSet* newTiles );
XP_U16 server_getPendingRegs( const ServerCtxt* server );
XP_Bool server_do( ServerCtxt* server );
XP_Bool server_commitMove( ServerCtxt* server );
XP_Bool server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles );
XP_Bool server_commitMove( ServerCtxt* server, TrayTileSet* newTiles );
XP_Bool server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles,
TrayTileSet* newTiles );
/* call this when user wants to end the game */
void server_endGame( ServerCtxt* server );

View file

@ -460,7 +460,7 @@ handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider )
}
#endif
} else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
result = board_commitTurn( board, XP_FALSE, XP_FALSE );
result = board_commitTurn( board, XP_FALSE, XP_FALSE, NULL );
#if defined XWFEATURE_TRAYUNDO_ALL
} else if ( index < 0 ) { /* other empty area */
/* it better be true */

View file

@ -101,14 +101,14 @@ typedef struct UtilVtable {
void (*m_util_notifyMove)( XW_UtilCtxt* uc, XWStreamCtxt* stream );
void (*m_util_notifyTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles,
XP_U16 nTiles );
/* return of < 0 means computer should pick */
void (*m_util_notifyPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum,
XP_U16 col, XP_U16 row,
const XP_UCHAR** tileFaces,
XP_U16 nTiles );
XP_S16 (*m_util_userPickTileTray)( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles );
void (*m_util_informNeedPickTiles)( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 player, XP_U16 nToPick,
XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts );
void (*m_util_informNeedPassword)( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR* name );
@ -236,9 +236,11 @@ struct XW_UtilCtxt {
(uc)->vtable->m_util_notifyTrade((uc), (tx), (nt))
#define util_notifyPickTileBlank( uc, c, r, n, tx, nt ) \
(uc)->vtable->m_util_notifyPickTileBlank( (uc), (c), (r), (n), (tx), (nt) )
#define util_userPickTileTray( uc, w, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileTray( (uc), (w), (n), (tx), (nt) )
(uc)->vtable->m_util_notifyPickTileBlank( (uc), (c), (r), (n), (tx), (nt) )
#define util_informNeedPickTiles( uc, ii, pl, np, nt, fc, cn ) \
(uc)->vtable->m_util_informNeedPickTiles( (uc), (ii), (pl), (np), (nt), (fc), (cn) )
#define util_informNeedPassword( uc, pn, n ) \
(uc)->vtable->m_util_informNeedPassword( (uc), (pn), (n) )

View file

@ -243,22 +243,26 @@ curses_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
// return index;
} /* util_userPickTile */
static XP_S16
curses_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi),
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
static void
curses_util_informNeedPickTiles( XW_UtilCtxt* XP_UNUSED(uc),
XP_Bool XP_UNUSED(isInitial),
XP_U16 XP_UNUSED(player),
XP_U16 XP_UNUSED(nToPick),
XP_U16 XP_UNUSED(nFaces),
const XP_UCHAR** XP_UNUSED(faces),
const XP_U16* XP_UNUSED(counts) )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128];
XP_S16 index;
char* playerName = globals->cGlobals.gi->players[playerNum].name;
/* CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure; */
/* char query[128]; */
/* XP_S16 index; */
/* char* playerName = globals->cGlobals.gi->players[playerNum].name; */
snprintf( query, sizeof(query),
"Pick tile for %s! (Tab or type letter to select "
"then hit <cr>.)", playerName );
/* snprintf( query, sizeof(query), */
/* "Pick tile for %s! (Tab or type letter to select " */
/* "then hit <cr>.)", playerName ); */
index = curses_askLetter( globals, query, texts, nTiles );
return index;
/* index = curses_askLetter( globals, query, texts, nTiles ); */
/* return index; */
} /* util_userPickTile */
static void
@ -620,7 +624,7 @@ static XP_Bool
handleCommit( CursesAppGlobals* globals )
{
globals->doDraw = board_commitTurn( globals->cGlobals.game.board,
XP_FALSE, XP_FALSE );
XP_FALSE, XP_FALSE, NULL );
return XP_TRUE;
} /* handleCommit */
@ -1456,7 +1460,7 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyMove = curses_util_notifyMove;
util->vtable->m_util_notifyTrade = curses_util_notifyTrade;
util->vtable->m_util_notifyPickTileBlank = curses_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = curses_util_userPickTileTray;
util->vtable->m_util_informNeedPickTiles = curses_util_informNeedPickTiles;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_informMove = curses_util_informMove;
util->vtable->m_util_informUndo = curses_util_informUndo;

View file

@ -1445,7 +1445,8 @@ handle_trade_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
static void
handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) {
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE,
XP_FALSE, NULL ) ) {
board_draw( globals->cGlobals.game.board );
disenable_buttons( globals );
}
@ -1543,7 +1544,8 @@ handle_hide_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
static void
handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
{
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE, XP_FALSE ) ) {
if ( board_commitTurn( globals->cGlobals.game.board, XP_FALSE,
XP_FALSE, NULL ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* handle_commit_button */
@ -1693,7 +1695,7 @@ ask_blank( gpointer data )
XP_UCHAR* name = globals->cGlobals.gi->players[cGlobals->selPlayer].name;
XP_S16 result = gtkletterask( NULL, XP_FALSE, name,
cGlobals->nTiles, cGlobals->tiles );
cGlobals->nTiles, cGlobals->tiles, NULL );
for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) {
g_free( (gpointer)cGlobals->tiles[ii] );
@ -1725,18 +1727,78 @@ gtk_util_notifyPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum, XP_U16 col,
(void)g_idle_add( ask_blank, globals );
}
static XP_S16
gtk_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
static gint
ask_tiles( gpointer data )
{
XP_S16 chosen;
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
XP_UCHAR* name = globals->cGlobals.gi->players[playerNum].name;
GtkGameGlobals* globals = (GtkGameGlobals*)data;
CommonGlobals* cGlobals = &globals->cGlobals;
chosen = gtkletterask( pi, XP_TRUE, name, nTiles, texts );
return chosen;
} /* gtk_util_userPickTile */
TrayTileSet newTiles = {0};
XP_UCHAR* name = cGlobals->gi->players[cGlobals->selPlayer].name;
for ( XP_Bool done = XP_FALSE; !done; ) {
XP_S16 picked = gtkletterask( &newTiles, XP_TRUE, name,
cGlobals->nTiles, cGlobals->tiles,
cGlobals->tileCounts );
switch ( picked ) {
case PICKER_PICKALL:
done = XP_TRUE;
break;
case PICKER_BACKUP:
if ( newTiles.nTiles > 0 ) {
Tile backed = newTiles.tiles[--newTiles.nTiles];
++cGlobals->tileCounts[backed];
}
break;
default:
XP_ASSERT( picked >= 0 && picked < cGlobals->nTiles );
--cGlobals->tileCounts[picked];
newTiles.tiles[newTiles.nTiles++] = picked;
done = newTiles.nTiles == cGlobals->nToPick;
break;
}
}
for ( int ii = 0; ii < cGlobals->nTiles; ++ii ) {
g_free( (gpointer)cGlobals->tiles[ii] );
}
BoardCtxt* board = cGlobals->game.board;
XP_Bool draw = XP_TRUE;
if ( cGlobals->pickIsInitial ) {
server_tilesPicked( cGlobals->game.server, cGlobals->selPlayer,
&newTiles );
} else {
draw = board_commitTurn( cGlobals->game.board, XP_TRUE, XP_TRUE,
&newTiles );
}
if ( draw ) {
board_draw( board );
}
return 0;
}
static void
gtk_util_informNeedPickTiles( XW_UtilCtxt* uc, XP_Bool isInitial,
XP_U16 player, XP_U16 nToPick,
XP_U16 nFaces, const XP_UCHAR** faces,
const XP_U16* counts )
{
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
CommonGlobals* cGlobals = &globals->cGlobals;
cGlobals->selPlayer = player;
cGlobals->pickIsInitial = isInitial;
cGlobals->nToPick = nToPick;
cGlobals->nTiles = nFaces;
for ( int ii = 0; ii < nFaces; ++ii ) {
cGlobals->tiles[ii] = g_strdup( faces[ii] );
cGlobals->tileCounts[ii] = counts[ii];
}
(void)g_idle_add( ask_tiles, globals );
} /* gtk_util_informNeedPickTiles */
static gint
ask_password( gpointer data )
@ -2125,7 +2187,7 @@ ask_bad_words( gpointer data )
if ( GTK_RESPONSE_YES == gtkask( globals->window, cGlobals->question,
GTK_BUTTONS_YES_NO, NULL ) ) {
board_commitTurn( cGlobals->game.board, XP_TRUE, XP_FALSE );
board_commitTurn( cGlobals->game.board, XP_TRUE, XP_FALSE, NULL );
}
return 0;
}
@ -2275,7 +2337,7 @@ ask_move( gpointer data )
gint chosen = gtkask( globals->window, cGlobals->question, buttons, NULL );
if ( GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES ) {
BoardCtxt* board = cGlobals->game.board;
if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) {
if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
board_draw( board );
}
}
@ -2314,7 +2376,7 @@ ask_trade( gpointer data )
cGlobals->question,
GTK_BUTTONS_YES_NO, NULL ) ) {
BoardCtxt* board = cGlobals->game.board;
if ( board_commitTurn( board, XP_TRUE, XP_TRUE ) ) {
if ( board_commitTurn( board, XP_TRUE, XP_TRUE, NULL ) ) {
board_draw( board );
}
}
@ -2466,7 +2528,7 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_notifyTrade = gtk_util_notifyTrade;
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
util->vtable->m_util_notifyPickTileBlank = gtk_util_notifyPickTileBlank;
util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray;
util->vtable->m_util_informNeedPickTiles = gtk_util_informNeedPickTiles;
util->vtable->m_util_informNeedPassword = gtk_util_informNeedPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;

View file

@ -42,8 +42,8 @@ abort_button_event( GtkWidget* XP_UNUSED(widget), gpointer XP_UNUSED(closure) )
#define BUTTONS_PER_ROW 13
XP_S16
gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts )
gtkletterask( const TrayTileSet* curPick, XP_Bool forTray, const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts, const XP_U16* counts )
{
GtkWidget* dialog;
GtkWidget* label;
@ -70,7 +70,9 @@ gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
g_signal_connect( button, "clicked",
G_CALLBACK(set_bool_and_quit), &results[ii] );
gtk_widget_show( button );
/* disable if we don't have any tiles! */
gtk_widget_set_sensitive( button, !counts || counts[ii] > 0 );
if ( ii+1 == nTiles || (ii % BUTTONS_PER_ROW == 0) ) {
gtk_widget_show( hbox );
@ -118,9 +120,10 @@ gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
char curTilesBuf[64];
int len = snprintf( curTilesBuf, sizeof(curTilesBuf), "%s",
"Tiles so far: " );
for ( ii = 0; ii < pi->nCurTiles; ++ii ) {
for ( ii = 0; ii < curPick->nTiles; ++ii ) {
Tile tile = curPick->tiles[ii];
len += snprintf( &curTilesBuf[len], sizeof(curTilesBuf) - len, "%s ",
pi->curTiles[ii] );
texts[tile] );
}
GtkWidget* curTilesLabel = gtk_label_new( curTilesBuf );

View file

@ -26,9 +26,10 @@
#include "gtkboard.h"
XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray,
XP_S16 gtkletterask( const TrayTileSet* curPick, XP_Bool forTray,
const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts );
XP_U16 nTiles, const XP_UCHAR** texts,
const XP_U16* counts );
#endif /* _GTKLETTERASK_H_ */

View file

@ -220,10 +220,13 @@ struct CommonGlobals {
char question[256*4];
const XP_UCHAR* askPassName;
XP_U16 nTiles;
XP_U16 nToPick;
XP_U16 blankCol;
XP_U16 blankRow;
XP_Bool pickIsInitial;
const XP_UCHAR* tiles[MAX_UNIQUE_TILES];
XP_U16 tileCounts[MAX_UNIQUE_TILES];
#ifdef XWFEATURE_RELAY
int relaySocket; /* tcp connection to relay */