Merge branch 'android_branch' into send_in_background

Conflicts:
	xwords4/linux/cursesmain.c
This commit is contained in:
Andy2 2011-11-13 17:10:46 -08:00
commit 2492e7cfc5
67 changed files with 2705 additions and 742 deletions

View file

@ -22,7 +22,7 @@
to come from a domain that you own or have control over. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.eehouse.android.xw4"
android:versionCode="29"
android:versionCode="30"
android:versionName="@string/app_version"
>
@ -118,8 +118,11 @@
</activity>
<activity android:name="RelayGameActivity"/>
<activity android:name="DictBrowseActivity"
android:configChanges="keyboardHidden|orientation"
/>
<activity android:name="ChatActivity"
android:configChanges="keyboardHidden|orientation"
/>
<service android:name="RelayService"/>

View file

@ -25,6 +25,9 @@ local_DEFINES += \
-DDISABLE_EMPTYTRAY_UNDO \
-DDISABLE_TILE_SEL \
-DXWFEATURE_BOARDWORDS \
-DXWFEATURE_WALKDICT \
-DXWFEATURE_DICTSANITY \
-DFEATURE_TRAY_EDIT \
-DNODE_CAN_4 \
-DRELAY_ROOM_DEFAULT=\"\"\
-D__LITTLE_ENDIAN \
@ -47,6 +50,7 @@ common_SRC_FILES += \
$(COMMON_PATH)/pool.c \
$(COMMON_PATH)/tray.c \
$(COMMON_PATH)/dictnry.c \
$(COMMON_PATH)/dictiter.c \
$(COMMON_PATH)/mscore.c \
$(COMMON_PATH)/vtabmgr.c \
$(COMMON_PATH)/strutils.c \

View file

@ -44,6 +44,12 @@ typedef struct _AndDictionaryCtxt {
jbyteArray byteArray;
} AndDictionaryCtxt;
#define CHECK_PTR(p,c,e) \
if ( ((p)+(c)) >= (e) ) { \
XP_LOGF( "%s (line %d); out of bytes", __func__, __LINE__ ); \
goto error; \
}
static void splitFaces_via_java( JNIEnv* env, AndDictionaryCtxt* ctxt,
const XP_U8* ptr,
int nFaceBytes, int nFaces, XP_Bool isUTF8 );
@ -94,15 +100,19 @@ andCountSpecials( AndDictionaryCtxt* ctxt )
return result;
} /* andCountSpecials */
static XP_Bitmap
andMakeBitmap( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp )
static XP_Bool
andMakeBitmap( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp,
const XP_U8 const* end, XP_Bitmap* result )
{
XP_Bool success = XP_TRUE;
XP_U8 const* ptr = *ptrp;
CHECK_PTR( ptr, 1, end );
XP_U8 nCols = *ptr++;
jobject bitmap = NULL;
if ( nCols > 0 ) {
CHECK_PTR( ptr, 1, end );
XP_U8 nRows = *ptr++;
CHECK_PTR( ptr, ((nRows*nCols)+7) / 8, end );
#ifdef DROP_BITMAPS
ptr += ((nRows*nCols)+7) / 8;
#else
@ -135,14 +145,20 @@ andMakeBitmap( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp )
XP_FREE( ctxt->super.mpool, colors );
#endif
}
goto done;
error:
success = XP_FALSE;
done:
*ptrp = ptr;
return (XP_Bitmap)bitmap;
*result = bitmap;
return success;
} /* andMakeBitmap */
static void
andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp )
static XP_Bool
andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp,
const XP_U8 const* end )
{
XP_Bool success = XP_TRUE;
XP_U16 nSpecials = andCountSpecials( ctxt );
XP_U8 const* ptr = *ptrp;
Tile ii;
@ -159,23 +175,36 @@ andLoadSpecialData( AndDictionaryCtxt* ctxt, XP_U8 const** ptrp )
const XP_UCHAR* facep = ctxt->super.facePtrs[(short)ii];
if ( IS_SPECIAL(*facep) ) {
/* get the string */
CHECK_PTR( ptr, 1, end );
XP_U8 txtlen = *ptr++;
CHECK_PTR( ptr, txtlen, end );
XP_UCHAR* text = (XP_UCHAR*)XP_MALLOC(ctxt->super.mpool, txtlen+1);
texts[(int)*facep] = text;
XP_MEMCPY( text, ptr, txtlen );
ptr += txtlen;
text[txtlen] = '\0';
XP_ASSERT( *facep < nSpecials ); /* firing */
texts[(int)*facep] = text;
bitmaps[(int)*facep].largeBM = andMakeBitmap( ctxt, &ptr );
bitmaps[(int)*facep].smallBM = andMakeBitmap( ctxt, &ptr );
if ( !andMakeBitmap( ctxt, &ptr, end,
&bitmaps[(int)*facep].largeBM ) ) {
goto error;
}
if ( !andMakeBitmap( ctxt, &ptr, end,
&bitmaps[(int)*facep].smallBM ) ) {
goto error;
}
}
}
goto done;
error:
success = XP_FALSE;
done:
ctxt->super.chars = texts;
ctxt->super.bitmaps = bitmaps;
*ptrp = ptr;
return success;
} /* andLoadSpecialData */
/** Android doesn't include iconv for C code to use, so we'll have java do it.
@ -236,132 +265,151 @@ splitFaces_via_java( JNIEnv* env, AndDictionaryCtxt* ctxt, const XP_U8* ptr,
ctxt->super.facePtrs = ptrs;
} /* splitFaces_via_java */
static void
parseDict( AndDictionaryCtxt* ctxt, XP_U8 const* ptr, XP_U32 dictLength )
static XP_Bool
parseDict( AndDictionaryCtxt* ctxt, XP_U8 const* ptr, XP_U32 dictLength,
XP_U32* numEdges )
{
while( !!ptr ) { /* lets us break.... */
XP_U32 offset;
XP_U16 nFaces, numFaceBytes = 0;
XP_U16 i;
XP_U16 flags;
void* mappedBase = (void*)ptr;
XP_U8 nodeSize;
XP_Bool isUTF8 = XP_FALSE;
XP_Bool success = XP_TRUE;
XP_ASSERT( !!ptr );
const XP_U8 const* end = ptr + dictLength;
XP_U32 offset;
XP_U16 nFaces, numFaceBytes = 0;
XP_U16 i;
XP_U16 flags;
void* mappedBase = (void*)ptr;
XP_U8 nodeSize;
XP_Bool isUTF8 = XP_FALSE;
flags = n_ptr_tohs( &ptr );
if ( 0 != (DICT_HEADER_MASK & flags) ) {
flags &= ~DICT_HEADER_MASK;
XP_U16 headerLen = n_ptr_tohs( &ptr );
if ( 4 <= headerLen ) { /* have word count? */
ctxt->super.nWords = n_ptr_tohl( &ptr );
headerLen -= 4; /* don't skip it */
}
ptr += headerLen;
CHECK_PTR( ptr, sizeof(flags), end );
flags = n_ptr_tohs( &ptr );
if ( 0 != (DICT_HEADER_MASK & flags) ) {
XP_U16 headerLen;
flags &= ~DICT_HEADER_MASK;
CHECK_PTR( ptr, sizeof(headerLen), end );
headerLen = n_ptr_tohs( &ptr );
if ( 4 <= headerLen ) { /* have word count? */
CHECK_PTR( ptr, sizeof(ctxt->super.nWords), end );
ctxt->super.nWords = n_ptr_tohl( &ptr );
headerLen -= 4; /* don't skip it */
}
if ( flags == 0x0002 ) {
nodeSize = 3;
} else if ( flags == 0x0003 ) {
nodeSize = 4;
} else if ( flags == 0x0004 ) {
isUTF8 = XP_TRUE;
nodeSize = 3;
} else if ( flags == 0x0005 ) {
isUTF8 = XP_TRUE;
nodeSize = 4;
} else {
break; /* we want to return NULL */
}
if ( isUTF8 ) {
numFaceBytes = (XP_U16)(*ptr++);
}
nFaces = (XP_U16)(*ptr++);
if ( nFaces > 64 ) {
break;
}
ctxt->super.nodeSize = nodeSize;
if ( !isUTF8 ) {
numFaceBytes = nFaces * 2;
}
ctxt->super.nFaces = (XP_U8)nFaces;
ctxt->super.isUTF8 = isUTF8;
if ( isUTF8 ) {
splitFaces_via_java( ctxt->env, ctxt, ptr, numFaceBytes, nFaces,
XP_TRUE );
ptr += numFaceBytes;
} else {
XP_U8 tmp[nFaces*4]; /* should be enough... */
XP_U16 nBytes = 0;
XP_U16 ii;
/* Need to translate from iso-8859-n to utf8 */
for ( ii = 0; ii < nFaces; ++ii ) {
XP_UCHAR ch = ptr[1];
ptr += 2;
tmp[nBytes] = ch;
nBytes += 1;
}
XP_ASSERT( nFaces == nBytes );
splitFaces_via_java( ctxt->env, ctxt, tmp, nBytes, nFaces,
XP_FALSE );
}
ctxt->super.is_4_byte = (ctxt->super.nodeSize == 4);
ctxt->super.countsAndValues =
(XP_U8*)XP_MALLOC(ctxt->super.mpool, nFaces*2);
ctxt->super.langCode = ptr[0] & 0x7F;
ptr += 2; /* skip xloc header */
for ( i = 0; i < nFaces*2; i += 2 ) {
ctxt->super.countsAndValues[i] = *ptr++;
ctxt->super.countsAndValues[i+1] = *ptr++;
}
andLoadSpecialData( ctxt, &ptr );
dictLength -= ptr - (XP_U8*)mappedBase;
if ( dictLength >= sizeof(offset) ) {
offset = n_ptr_tohl( &ptr );
dictLength -= sizeof(offset);
#ifdef NODE_CAN_4
XP_ASSERT( dictLength % ctxt->super.nodeSize == 0 );
# ifdef DEBUG
ctxt->super.numEdges = dictLength / ctxt->super.nodeSize;
# endif
#else
XP_ASSERT( dictLength % 3 == 0 );
# ifdef DEBUG
ctxt->super.numEdges = dictLength / 3;
# endif
#endif
} else {
offset = 0;
}
if ( dictLength > 0 ) {
ctxt->super.base = (array_edge*)ptr;
#ifdef NODE_CAN_4
ctxt->super.topEdge = ctxt->super.base
+ (offset * ctxt->super.nodeSize);
#else
ctxt->super.topEdge = ctxt->super.base + (offset * 3);
#endif
} else {
ctxt->super.topEdge = (array_edge*)NULL;
ctxt->super.base = (array_edge*)NULL;
}
setBlankTile( &ctxt->super );
break; /* exit phony while loop */
CHECK_PTR( ptr, headerLen, end );
ptr += headerLen;
}
if ( flags == 0x0002 ) {
nodeSize = 3;
} else if ( flags == 0x0003 ) {
nodeSize = 4;
} else if ( flags == 0x0004 ) {
isUTF8 = XP_TRUE;
nodeSize = 3;
} else if ( flags == 0x0005 ) {
isUTF8 = XP_TRUE;
nodeSize = 4;
} else {
goto error;
}
if ( isUTF8 ) {
CHECK_PTR( ptr, 1, end );
numFaceBytes = (XP_U16)(*ptr++);
}
CHECK_PTR( ptr, 1, end );
nFaces = (XP_U16)(*ptr++);
if ( nFaces > 64 ) {
goto error;
}
ctxt->super.nodeSize = nodeSize;
if ( !isUTF8 ) {
numFaceBytes = nFaces * 2;
}
ctxt->super.nFaces = (XP_U8)nFaces;
ctxt->super.isUTF8 = isUTF8;
if ( isUTF8 ) {
CHECK_PTR( ptr, numFaceBytes, end );
splitFaces_via_java( ctxt->env, ctxt, ptr, numFaceBytes, nFaces,
XP_TRUE );
ptr += numFaceBytes;
} else {
XP_U8 tmp[nFaces*4]; /* should be enough... */
XP_U16 nBytes = 0;
XP_U16 ii;
/* Need to translate from iso-8859-n to utf8 */
CHECK_PTR( ptr, 2 * nFaces, end );
for ( ii = 0; ii < nFaces; ++ii ) {
XP_UCHAR ch = ptr[1];
ptr += 2;
tmp[nBytes] = ch;
nBytes += 1;
}
XP_ASSERT( nFaces == nBytes );
splitFaces_via_java( ctxt->env, ctxt, tmp, nBytes, nFaces,
XP_FALSE );
}
ctxt->super.is_4_byte = (ctxt->super.nodeSize == 4);
ctxt->super.countsAndValues =
(XP_U8*)XP_MALLOC(ctxt->super.mpool, nFaces*2);
CHECK_PTR( ptr, 2, end );
ctxt->super.langCode = ptr[0] & 0x7F;
ptr += 2; /* skip xloc header */
CHECK_PTR( ptr, 2 * nFaces, end );
for ( i = 0; i < nFaces*2; i += 2 ) {
ctxt->super.countsAndValues[i] = *ptr++;
ctxt->super.countsAndValues[i+1] = *ptr++;
}
if ( !andLoadSpecialData( ctxt, &ptr, end ) ) {
goto error;
}
dictLength -= ptr - (XP_U8*)mappedBase;
if ( dictLength >= sizeof(offset) ) {
CHECK_PTR( ptr, sizeof(offset), end );
offset = n_ptr_tohl( &ptr );
dictLength -= sizeof(offset);
#ifdef NODE_CAN_4
XP_ASSERT( dictLength % ctxt->super.nodeSize == 0 );
*numEdges = dictLength / ctxt->super.nodeSize;
#else
XP_ASSERT( dictLength % 3 == 0 );
*numEdges = dictLength / 3;
#endif
#ifdef DEBUG
ctxt->super.numEdges = *numEdges;
#endif
} else {
offset = 0;
}
if ( dictLength > 0 ) {
ctxt->super.base = (array_edge*)ptr;
#ifdef NODE_CAN_4
ctxt->super.topEdge = ctxt->super.base
+ (offset * ctxt->super.nodeSize);
#else
ctxt->super.topEdge = ctxt->super.base + (offset * 3);
#endif
} else {
ctxt->super.topEdge = (array_edge*)NULL;
ctxt->super.base = (array_edge*)NULL;
}
setBlankTile( &ctxt->super );
goto done;
error:
success = XP_FALSE;
done:
return success;
} /* parseDict */
static void
@ -391,9 +439,9 @@ and_dictionary_destroy( DictionaryCtxt* dict )
XP_FREE( ctxt->super.mpool, ctxt->super.bitmaps );
}
XP_FREE( ctxt->super.mpool, ctxt->super.faces );
XP_FREE( ctxt->super.mpool, ctxt->super.facePtrs );
XP_FREE( ctxt->super.mpool, ctxt->super.countsAndValues );
XP_FREEP( ctxt->super.mpool, &ctxt->super.faces );
XP_FREEP( ctxt->super.mpool, &ctxt->super.facePtrs );
XP_FREEP( ctxt->super.mpool, &ctxt->super.countsAndValues );
XP_FREEP( ctxt->super.mpool, &ctxt->super.name );
XP_FREEP( ctxt->super.mpool, &ctxt->super.langName );
@ -408,7 +456,7 @@ and_dictionary_destroy( DictionaryCtxt* dict )
(*env)->DeleteGlobalRef( env, ctxt->byteArray );
}
XP_FREE( ctxt->super.mpool, ctxt );
}
} /* and_dictionary_destroy */
jobject
and_dictionary_getChars( JNIEnv* env, DictionaryCtxt* dict )
@ -454,7 +502,7 @@ makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil,
if ( NULL != jdict || NULL != jpath ) {
jstring jname = (*env)->GetObjectArrayElement( env, jnames, ii );
dict = makeDict( MPPARM(mpool) env, jniutil, jname, jdict,
jpath, jlang );
jpath, jlang, false );
XP_ASSERT( !!dict );
(*env)->DeleteLocalRef( env, jdict );
(*env)->DeleteLocalRef( env, jname );
@ -474,46 +522,57 @@ makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil,
DictionaryCtxt*
makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil, jstring jname,
jbyteArray jbytes, jstring jpath, jstring jlangname )
jbyteArray jbytes, jstring jpath, jstring jlangname, jboolean check )
{
AndDictionaryCtxt* anddict = (AndDictionaryCtxt*)
and_dictionary_make_empty( MPPARM(mpool) env, jniutil );
jsize len = 0;
jbyte* bytes = NULL;
jbyteArray byteArray = NULL;
off_t bytesSize = 0;
if ( NULL == jpath ) {
len = (*env)->GetArrayLength( env, jbytes );
anddict->byteArray = (*env)->NewGlobalRef( env, jbytes );
anddict->bytes =
(*env)->GetByteArrayElements( env, anddict->byteArray, NULL );
bytesSize = (*env)->GetArrayLength( env, jbytes );
byteArray = (*env)->NewGlobalRef( env, jbytes );
bytes = (*env)->GetByteArrayElements( env, byteArray, NULL );
} else {
XP_ASSERT( NULL == anddict->byteArray );
const char* path = (*env)->GetStringUTFChars( env, jpath, NULL );
struct stat statbuf;
if ( 0 == stat( path, &statbuf ) ) {
int fd = open( path, O_RDONLY );
if ( 0 == stat( path, &statbuf ) && 0 < statbuf.st_size ) {
int fd = open( path, O_RDONLY );
if ( fd >= 0 ) {
anddict->bytes = mmap( NULL, statbuf.st_size,
PROT_READ, MAP_PRIVATE,
fd, 0 );
void* ptr = mmap( NULL, statbuf.st_size, PROT_READ,
MAP_PRIVATE, fd, 0 );
close( fd );
anddict->bytesSize = statbuf.st_size;
len = statbuf.st_size;
XP_ASSERT( MAP_FAILED != anddict->bytes );
if ( MAP_FAILED != ptr ) {
bytes = ptr;
bytesSize = statbuf.st_size;
}
}
}
(*env)->ReleaseStringUTFChars( env, jpath, path );
}
anddict->super.destructor = and_dictionary_destroy;
AndDictionaryCtxt* anddict = NULL;
if ( NULL != bytes ) {
anddict = (AndDictionaryCtxt*)
and_dictionary_make_empty( MPPARM(mpool) env, jniutil );
anddict->bytes = bytes;
anddict->byteArray = byteArray;
anddict->bytesSize = bytesSize;
parseDict( anddict, (XP_U8*)anddict->bytes, len );
anddict->super.destructor = and_dictionary_destroy;
/* copy the name */
anddict->super.name = getStringCopy( MPPARM(mpool) env, jname );
anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname );
/* copy the name */
anddict->super.name = getStringCopy( MPPARM(mpool) env, jname );
anddict->super.langName = getStringCopy( MPPARM(mpool) env, jlangname );
XP_U32 numEdges;
XP_Bool parses = parseDict( anddict, (XP_U8*)anddict->bytes,
bytesSize, &numEdges );
if ( !parses || (check && !checkSanity( &anddict->super, numEdges ) ) ) {
and_dictionary_destroy( (DictionaryCtxt*)anddict );
anddict = NULL;
}
}
return (DictionaryCtxt*)anddict;
}

View file

@ -31,7 +31,7 @@ dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes,
DictionaryCtxt* makeDict( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil,
jstring jname, jbyteArray bytes, jstring path,
jstring jlang );
jstring jlang, jboolean check );
void makeDicts( MPFORMAL JNIEnv *env, JNIUtilCtxt* jniutil,
DictionaryCtxt** dict, PlayerDicts* dicts, jobjectArray jnames,

View file

@ -280,7 +280,7 @@ makeStringArray( JNIEnv *env, int siz, const XP_UCHAR** vals )
(*env)->DeleteLocalRef( env, empty );
int ii;
for ( ii = 0; ii < siz; ++ii ) {
for ( ii = 0; !!vals && ii < siz; ++ii ) {
jstring jstr = (*env)->NewStringUTF( env, vals[ii] );
(*env)->SetObjectArrayElement( env, jarray, ii, jstr );
(*env)->DeleteLocalRef( env, jstr );

View file

@ -132,18 +132,26 @@ and_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
return result;
}
static XP_Bool
and_util_confirmTrade( XW_UtilCtxt* uc, const XP_UCHAR** tiles, XP_U16 nTiles )
{
XP_Bool result = XP_FALSE;
UTIL_CBK_HEADER("confirmTrade", "([Ljava/lang/String;)Z" );
jobjectArray jtiles = makeStringArray( env, nTiles, tiles );
result = (*env)->CallBooleanMethod( env, util->jutil, mid, jtiles );
(*env)->DeleteLocalRef( env, jtiles );
UTIL_CBK_TAIL();
return result;
}
static XP_S16
and_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** texts, XP_U16 nTiles )
and_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** tileFaces, XP_U16 nTiles )
{
XP_S16 result = -1;
UTIL_CBK_HEADER("userPickTile", "(I[Ljava/lang/String;)I" );
UTIL_CBK_HEADER("userPickTileBlank", "(I[Ljava/lang/String;)I" );
#ifdef FEATURE_TRAY_EDIT
++error; /* need to pass pi if this is on */
#endif
jobject jtexts = makeStringArray( env, nTiles, texts );
jobject jtexts = makeStringArray( env, nTiles, tileFaces );
result = (*env)->CallIntMethod( env, util->jutil, mid,
playerNum, jtexts );
@ -151,8 +159,27 @@ and_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
(*env)->DeleteLocalRef( env, jtexts );
UTIL_CBK_TAIL();
return result;
} /* and_util_userPickTile */
}
static XP_S16
and_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** tileFaces,
XP_U16 nTiles )
{
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 );
(*env)->DeleteLocalRef( env, jtexts );
(*env)->DeleteLocalRef( env, jcurtiles );
UTIL_CBK_TAIL();
return result;
} /* and_util_userPickTile */
static XP_Bool
and_util_askPassword( XW_UtilCtxt* uc, const XP_UCHAR* name,
@ -260,11 +287,17 @@ and_util_engineProgressCallback( XW_UtilCtxt* uc )
bool
utilTimerFired( XW_UtilCtxt* uc, XWTimerReason why, int handle )
{
bool handled;
AndUtil* util = (AndUtil*)uc;
TimerStorage* timerStorage = &util->timerStorage[why];
XP_ASSERT( handle == (int)timerStorage );
return (handle == (int)timerStorage)
&& (*timerStorage->proc)( timerStorage->closure, why );
if ( handle == (int)timerStorage ) {
handled = (*timerStorage->proc)( timerStorage->closure, why );
} else {
XP_LOGF( "%s: mismatch: handle=%d; timerStorage=%d", __func__,
handle, (int)timerStorage );
handled = false;
}
return handled;
}
static void
@ -498,7 +531,9 @@ makeUtil( MPFORMAL JNIEnv** envp, jobject jutil, CurGameInfo* gi,
SET_PROC(getSquareBonus);
SET_PROC(userError);
SET_PROC(userQuery);
SET_PROC(userPickTile);
SET_PROC(confirmTrade);
SET_PROC(userPickTileBlank);
SET_PROC(userPickTileTray);
SET_PROC(askPassword);
SET_PROC(trayHiddenChange);
SET_PROC(yOffsetChange);

View file

@ -76,6 +76,7 @@ void and_freep( void** ptrp );
#define XP_MIN(a,b) ((a)<(b)?(a):(b))
#define XP_MAX(a,b) ((a)>(b)?(a):(b))
#define XP_ABS(a) ((a)>=0?(a):-(a))
#ifdef DEBUG
void and_assert( const char* test, int line, const char* file, const char* func );
@ -117,4 +118,3 @@ extern "C" {
#endif
#endif

View file

@ -1,7 +1,7 @@
/* -*-mode: C; compile-command: "../../scripts/ndkbuild.sh"; -*- */
/*
* Copyright © 2009-2010 by Eric House (xwords@eehouse.org). All
* rights reserved.
* Copyright © 2009 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -28,6 +28,8 @@
#include "board.h"
#include "mempool.h"
#include "strutils.h"
#include "dictnry.h"
#include "dictiter.h"
#include "utilwrapper.h"
#include "drawwrapper.h"
@ -265,27 +267,32 @@ Java_org_eehouse_android_xw4_jni_XwJNI_comms_1getInitialAddr
setJAddrRec( env, jaddr, &addr );
}
JNIEXPORT void JNICALL
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1getInfo
( JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jpath,
jobject jniu, jobject jinfo )
jobject jniu, jboolean check, jobject jinfo )
{
jboolean result = false;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = mpool_make();
#endif
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &env, jniu );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, NULL,
jDictBytes, jpath, NULL );
jint code = dict_getLangCode( dict );
jint nWords = dict_getWordCount( dict );
dict_destroy( dict );
jDictBytes, jpath, NULL, check );
if ( NULL != dict ) {
if ( NULL != jinfo ) {
setInt( env, jinfo, "langCode", dict_getLangCode( dict ) );
setInt( env, jinfo, "wordCount", dict_getWordCount( dict ) );
}
dict_destroy( dict );
result = true;
}
destroyJNIUtil( &jniutil );
setInt( env, jinfo, "langCode", code );
setInt( env, jinfo, "wordCount", nWords );
#ifdef MEM_DEBUG
mpool_destroy( mpool );
#endif
return result;
}
/* Dictionary methods: don't use gamePtr */
@ -1267,3 +1274,205 @@ Java_org_eehouse_android_xw4_jni_XwJNI_server_1sendChat
(*env)->ReleaseStringUTFChars( env, jmsg, msg );
XWJNI_END();
}
#ifdef XWFEATURE_WALKDICT
////////////////////////////////////////////////////////////
// Dict iterator
////////////////////////////////////////////////////////////
typedef struct _DictIterData {
JNIEnv* env;
JNIUtilCtxt* jniutil;
VTableMgr* vtMgr;
DictionaryCtxt* dict;
DictIter iter;
IndexData idata;
XP_U16 depth;
#ifdef MEM_DEBUG
MemPoolCtx* mpool;
#endif
} DictIterData;
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1init
(JNIEnv* env, jclass C, jbyteArray jDictBytes, jstring jpath, jobject jniu )
{
jint closure = 0;
#ifdef MEM_DEBUG
MemPoolCtx* mpool = mpool_make();
#endif
DictIterData* data = XP_CALLOC( mpool, sizeof(*data) );
data->env = env;
JNIUtilCtxt* jniutil = makeJNIUtil( MPPARM(mpool) &data->env, jniu );
DictionaryCtxt* dict = makeDict( MPPARM(mpool) env, jniutil, NULL,
jDictBytes, jpath, NULL, false );
if ( !!dict ) {
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(mpool) );
data->jniutil = jniutil;
data->dict = dict;
#ifdef MEM_DEBUG
data->mpool = mpool;
#endif
closure = (int)data;
dict_initIter( data->dict, &data->iter );
(void)dict_firstWord( &data->iter );
} else {
destroyJNIUtil( &jniutil );
XP_FREE( mpool, data );
#ifdef MEM_DEBUG
mpool_destroy( mpool );
#endif
}
return closure;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1destroy
( JNIEnv* env, jclass C, jint closure )
{
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
#ifdef MEM_DEBUG
MemPoolCtx* mpool = data->mpool;
#endif
dict_destroy( data->dict );
destroyJNIUtil( &data->jniutil );
if ( !!data->idata.indices ) {
XP_FREE( mpool, data->idata.indices );
}
if ( !!data->idata.prefixes ) {
XP_FREE( mpool, data->idata.prefixes );
}
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
XP_FREE( mpool, data );
#ifdef MEM_DEBUG
mpool_destroy( mpool );
#endif
}
}
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1wordCount
(JNIEnv* env, jclass C, jint closure )
{
jint result = 0;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
result = data->iter.nWords;
}
return result;
}
JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1makeIndex
( JNIEnv* env, jclass C, jint closure )
{
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
data->depth = 2; /* for now */
XP_U16 nFaces = dict_numTileFaces( data->dict );
XP_U16 ii;
XP_U16 count;
for ( count = 1, ii = 0; ii < data->depth; ++ii ) {
count *= nFaces;
}
IndexData* idata = &data->idata;
XP_ASSERT( !idata->prefixes );
idata->prefixes = XP_MALLOC( data->mpool, count * data->depth
* sizeof(*idata->prefixes) );
XP_ASSERT( !idata->indices );
idata->indices = XP_MALLOC( data->mpool,
count * sizeof(*idata->indices) );
idata->count = count;
dict_makeIndex( &data->iter, data->depth, idata );
idata->prefixes = XP_REALLOC( data->mpool, idata->prefixes,
idata->count * data->depth *
sizeof(*idata->prefixes) );
idata->indices = XP_REALLOC( data->mpool, idata->indices,
idata->count * sizeof(*idata->indices) );
}
}
JNIEXPORT jobjectArray JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getPrefixes
( JNIEnv* env, jclass C, jint closure )
{
jobjectArray result = NULL;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data && NULL != data->idata.prefixes ) {
result = makeStringArray( env, data->idata.count, NULL );
int ii;
XP_U16 depth = data->depth;
for ( ii = 0; ii < data->idata.count; ++ii ) {
XP_UCHAR buf[16];
(void)dict_tilesToString( data->dict,
&data->idata.prefixes[depth*ii],
depth, buf, VSIZE(buf) );
jstring jstr = (*env)->NewStringUTF( env, buf );
(*env)->SetObjectArrayElement( env, result, ii, jstr );
(*env)->DeleteLocalRef( env, jstr );
}
(*env)->DeleteLocalRef( env, result );
}
return result;
}
JNIEXPORT jintArray JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getIndices
( JNIEnv* env, jclass C , jint closure )
{
jintArray jindices = NULL;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
XP_ASSERT( !!data->idata.indices );
XP_ASSERT( sizeof(jint) == sizeof(data->idata.indices[0]) );
jindices = makeIntArray( env, data->idata.count,
(jint*)data->idata.indices );
(*env)->DeleteLocalRef( env, jindices );
}
return jindices;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1nthWord
( JNIEnv* env, jclass C, jint closure, jint nn)
{
jstring result = NULL;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
if ( dict_getNthWord( &data->iter, nn, data->depth, &data->idata ) ) {
XP_UCHAR buf[64];
dict_wordToString( &data->iter, buf, VSIZE(buf) );
result = (*env)->NewStringUTF( env, buf );
(*env)->DeleteLocalRef( env, result );
}
}
return result;
}
JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getStartsWith
( JNIEnv* env, jclass C, jint closure, jstring jprefix )
{
jint result = -1;
DictIterData* data = (DictIterData*)closure;
if ( NULL != data ) {
Tile tiles[MAX_COLS];
XP_U16 nTiles = VSIZE(tiles);
const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL );
if ( dict_tilesForString( data->dict, prefix, tiles, &nTiles ) ) {
if ( dict_findStartsWith( &data->iter, NULL, tiles, nTiles ) ) {
result = dict_getPosition( &data->iter );
}
}
(*env)->ReleaseStringUTFChars( env, jprefix, prefix );
}
return result;
}
#endif /* XWFEATURE_BOARDWORDS */

View file

@ -35,38 +35,47 @@
/>
</LinearLayout>
<LinearLayout android:id="@+id/toolbar_horizontal"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ImageButton android:id="@+id/prevhint_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/prev_hint"
/>
<ImageButton android:id="@+id/nexthint_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/next_hint"
/>
<ImageButton android:id="@+id/shuffle_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/shuffle"
/>
<ImageButton android:id="@+id/flip_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/flip"
/>
<ImageButton android:id="@+id/zoom_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/zoom"
/>
<ImageButton android:id="@+id/undo_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/undo"
/>
<ImageButton android:id="@+id/chat_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/stat_notify_chat"
/>
</LinearLayout>
<HorizontalScrollView android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<LinearLayout android:id="@+id/toolbar_horizontal"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<ImageButton android:id="@+id/prevhint_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/prev_hint"
/>
<ImageButton android:id="@+id/nexthint_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/next_hint"
/>
<ImageButton android:id="@+id/shuffle_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/shuffle"
/>
<ImageButton android:id="@+id/zoom_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/zoom"
/>
<ImageButton android:id="@+id/undo_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/undo"
/>
<ImageButton android:id="@+id/flip_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/flip"
/>
<ImageButton android:id="@+id/dictlist_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/dicticon"
/>
<ImageButton android:id="@+id/chat_button_horizontal"
style="@style/toolbar_button"
android:src="@drawable/stat_notify_chat"
/>
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>

View file

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="8dp"
android:paddingRight="8dp">
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<EditText android:id="@+id/word_edit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_weight="1"
android:hint="@string/word_search_hint"
android:capitalize="characters"
/>
<Button android:id="@+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_search"
android:layout_weight="0"
/>
</LinearLayout>
<ListView android:id="@id/android:list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
android:drawSelectorOnTop="false"
/>
</LinearLayout>

View file

@ -217,6 +217,11 @@
android:entries="@array/phony_names"
/>
<CheckBox android:id="@+id/pick_faceup"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pick_faceup"
/>
</LinearLayout>

View file

@ -7,7 +7,4 @@
<item android:id="@+id/dicts_item_select"
android:title="@string/dicts_item_select"
/>
<item android:id="@+id/dicts_item_details"
android:title="@string/dicts_item_details"
/>
</menu>

View file

@ -6,17 +6,33 @@
</head>
<body>
<b>Crosswords 4.4 beta 37 release</b>
<b>Crosswords 4.4 beta 38 release</b>
<ul>
<li>Extend ability to look up words to include those already on the
board. Hold your finger on any played tile to look up any of the
words that use it. </li>
<li>Make toolbar buttons slightly wider and the whole thing
scroll</li>
<li>Added a wordlist browser, which you can get to by tapping a
wordlist in the Wordlist screen, or by using the wordlist
button on the game board. Eventually you'll be
able to filter based on length, chosen tiles, etc. Right now
you can scroll through all the words (even 2.7 million of them
if you're playing in Polish) and tap to look them up.</li>
<li>Fix password dialog crashes</li>
<li>Added the "download directory" to locations I search for
wordlists. This means that if you're using a browser like
Opera that doesn't respect apps' registering for certain types
of data you can still use a wordlist you've downloaded.</li>
<li>Show wordlist along with player name in Game Config screen, and
move below Language since it changes when language changes.</li>
<li>Added "Pick tiles face up" option, settable when you create a
game. This is primarily useful if you want to populate a board to
match a game that already exists, say in a newspaper puzzle or
online wordgame.</li>
<!--
TODO: test various bogus dicts in downloads/
test that opera-downloaded dicts openable/moveable
-->
</ul>

View file

@ -3,7 +3,7 @@
<!-- Resources in this file do not require localization -->
<resources>
<string name="app_version">4.4 beta 37</string>
<string name="app_version">4.4 beta 38</string>
<!-- prefs keys -->
<string name="key_color_tiles">key_color_tiles</string>
@ -75,6 +75,7 @@
<string name="key_notagain_newfrom">key_notagain_newfrom</string>
<string name="key_notagain_trading">key_notagain_trading</string>
<string name="key_na_lookup">key_na_lookup</string>
<string name="key_na_browse">key_na_browse</string>
<!-- Name is not localized -->
<string name="app_name">Crosswords</string>

View file

@ -242,12 +242,6 @@
key_default_dict and key_default_robodict.) -->
<string name="dicts_item_select">Make default</string>
<!-- Title of contextual menu item. If chosen it does nothing,
but will eventually bring up a dialog giving additional
information about the selected wordlist, possibly including a
scrolling list of all its words. -->
<string name="dicts_item_details">Details</string>
<!-- text of confirmation dialog posted when the delete 'X' button
beside the listing of a wordlist is tapped. The name of the
wordlist is substituted for %s. Sometimes one of the two
@ -308,8 +302,7 @@
is substituted for %1$s. The strings loc_internal and
loc_internal are substitued for %2$s and %3$s (or vice-versa,
depending on the current location of the wordlist.)-->
<string name="move_dictf">Move wordlist %1$s from %2$s to %3$s
storage?</string>
<string name="move_dictf">Location for wordlist %1$s</string>
<!-- see move_dictf above -->
<string name="loc_internal">Internal</string>
@ -1215,9 +1208,8 @@
<!-- Text of dialog asking user to confirm a move that exchanges
tiles (instead of forming a new word to earn points) -->
<string name="query_trade">Are you sure you want to exchange the
selected tiles?</string>
<string name="query_tradef">Are you sure you want to exchange the
selected tiles (%s)?</string>
<!-- ############################################################
# :Screens:
@ -1764,7 +1756,8 @@
dialog -->
<string name="changes_button">Recent changes</string>
<!-- New strings that need to be documented and found a home
above. -->
<string name="button_lookup">Look up words</string>
<string name="button_lookupf">Look up %s</string>
<string name="title_lookup">Tap to look up</string>
@ -1782,5 +1775,19 @@
words just played online. (Note that not all languages are
supported yet.)</string>
</resources>
<string name="button_move">Move</string>
<string name="button_search">Find</string>
<string name="word_search_hint">First letters</string>
<string name="tilepick_undo">Undo last</string>
<string name="tilepick_all">Pick for me</string>
<string name="cur_tilesf">Tile picker\n(so far: %s)</string>
<string name="pick_faceup">Pick tiles face-up</string>
<string name="dict_browse_titlef">%1$s (%2$d words)</string>
<string name="dict_browse_nowordsf">No word in %1$s starts with
%2$s.</string>
<string name="not_again_browse">This button opens the wordlist
browser on the current player\'s wordlist.</string>
</resources>

View file

@ -38,7 +38,7 @@
</style>
<style name="toolbar_button">
<item name="android:layout_width">48dp</item>
<item name="android:layout_width">58dp</item>
<item name="android:layout_height">48dp</item>
<item name="android:layout_weight">1</item>
</style>

View file

@ -65,7 +65,7 @@ public class BoardActivity extends XWActivity
private static final int DLG_BADWORDS = DLG_OKONLY + 1;
private static final int QUERY_REQUEST_BLK = DLG_OKONLY + 2;
private static final int QUERY_INFORM_BLK = DLG_OKONLY + 3;
private static final int PICK_TILE_REQUEST_BLK = DLG_OKONLY + 4;
private static final int PICK_TILE_REQUESTBLANK_BLK = DLG_OKONLY + 4;
private static final int ASK_PASSWORD_BLK = DLG_OKONLY + 5;
private static final int DLG_RETRY = DLG_OKONLY + 6;
private static final int QUERY_ENDGAME = DLG_OKONLY + 7;
@ -73,6 +73,7 @@ public class BoardActivity extends XWActivity
private static final int DLG_INVITE = DLG_OKONLY + 9;
private static final int DLG_SCORES_BLK = DLG_OKONLY + 10;
private static final int DLG_LOOKUP = DLG_OKONLY + 11;
private static final int PICK_TILE_REQUESTTRAY_BLK = DLG_OKONLY + 12;
private static final int CHAT_REQUEST = 1;
private static final int SCREEN_ON_TIME = 10 * 60 * 1000; // 10 mins
@ -91,6 +92,7 @@ public class BoardActivity extends XWActivity
private static final int CHAT_ACTION = 12;
private static final int START_TRADE_ACTION = 13;
private static final int LOOKUP_ACTION = 14;
private static final int BUTTON_BROWSE_ACTION = 15;
private static final String DLG_TITLE = "DLG_TITLE";
private static final String DLG_TITLESTR = "DLG_TITLESTR";
@ -122,6 +124,8 @@ public class BoardActivity extends XWActivity
private int m_dlgTitle;
private String m_dlgTitleStr;
private String[] m_texts;
private String m_curTiles;
private boolean m_canUndoTiles;
private boolean m_firingPrefs;
private JNIUtils m_jniu;
private boolean m_volKeysZoom;
@ -164,7 +168,7 @@ public class BoardActivity extends XWActivity
}
@Override
protected Dialog onCreateDialog( int id )
protected Dialog onCreateDialog( final int id )
{
Dialog dialog = super.onCreateDialog( id );
if ( null == dialog ) {
@ -264,9 +268,9 @@ public class BoardActivity extends XWActivity
dialog.setOnDismissListener( makeODLforBlocking( id ) );
break;
case PICK_TILE_REQUEST_BLK:
ab = new AlertDialog.Builder( this )
.setTitle( R.string.title_tile_picker );
case PICK_TILE_REQUESTBLANK_BLK:
case PICK_TILE_REQUESTTRAY_BLK:
ab = new AlertDialog.Builder( this );
lstnr = new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int item ) {
@ -275,6 +279,34 @@ public class BoardActivity extends XWActivity
};
ab.setItems( m_texts, lstnr );
if ( PICK_TILE_REQUESTBLANK_BLK == id ) {
ab.setTitle( R.string.title_tile_picker );
} else {
ab.setTitle( Utils.format( this, R.string.cur_tilesf,
m_curTiles ) );
if ( m_canUndoTiles ) {
DialogInterface.OnClickListener undoClicked =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_BACKUP;
removeDialog( id );
}
};
ab.setPositiveButton( R.string.tilepick_undo,
undoClicked );
}
DialogInterface.OnClickListener doAllClicked =
new DialogInterface.OnClickListener() {
public void onClick( DialogInterface dialog,
int whichButton ) {
m_resultCode = UtilCtxt.PICKER_PICKALL;
removeDialog( id );
}
};
ab.setNegativeButton( R.string.tilepick_all, doAllClicked );
}
dialog = ab.create();
dialog.setOnDismissListener( makeODLforBlocking( id ) );
break;
@ -332,15 +364,6 @@ public class BoardActivity extends XWActivity
}
break;
case DLG_LOOKUP:
LookupView view = (LookupView)Utils.inflate( this, R.layout.lookup );
dialog = new AlertDialog.Builder( this )
.setView( view )
.create();
view.setDialog( dialog, DLG_LOOKUP );
view.setWords( m_words, m_gi.dictLang );
break;
default:
// just drop it; super.onCreateDialog likely failed
break;
@ -598,6 +621,7 @@ public class BoardActivity extends XWActivity
m_jniThread.handle( JNIThread.JNICmd.CMD_REMAINING,
R.string.tiles_left_title );
break;
case R.id.board_menu_game_history:
m_jniThread.handle( JNIThread.JNICmd.CMD_HISTORY,
R.string.history_title );
@ -669,6 +693,10 @@ public class BoardActivity extends XWActivity
Toast.LENGTH_SHORT).show();
m_toastStr = null;
break;
case BUTTON_BROWSE_ACTION:
String dictName = m_gi.dictName( m_view.getCurPlayer() );
DictBrowseActivity.launch( this, dictName );
break;
case PREV_HINT_ACTION:
cmd = JNIThread.JNICmd.CMD_PREV_HINT;
break;
@ -696,7 +724,7 @@ public class BoardActivity extends XWActivity
cmd = JNIThread.JNICmd.CMD_TRADE;
break;
case LOOKUP_ACTION:
launchLookup( m_words );
launchLookup( m_words, m_gi.dictLang );
break;
default:
Assert.fail();
@ -925,6 +953,7 @@ public class BoardActivity extends XWActivity
super( BoardActivity.this );
}
@Override
public void requestTime()
{
post( new Runnable() {
@ -936,12 +965,14 @@ public class BoardActivity extends XWActivity
} );
}
@Override
public void remSelected()
{
m_jniThread.handle( JNIThread.JNICmd.CMD_REMAINING,
R.string.tiles_left_title );
}
@Override
public void setIsServer( boolean isServer )
{
DeviceRole newRole = isServer? DeviceRole.SERVER_ISSERVER
@ -1011,11 +1042,12 @@ public class BoardActivity extends XWActivity
{
post( new Runnable() {
public void run() {
launchLookup( wordsToArray( words ) );
launchLookup( wordsToArray( words ), m_gi.dictLang );
}
} );
}
@Override
public void setTimer( int why, int when, int handle )
{
if ( null != m_timers[why] ) {
@ -1038,6 +1070,7 @@ public class BoardActivity extends XWActivity
postDelayed( m_timers[why], inHowLong );
}
@Override
public void clearTimer( int why )
{
if ( null != m_timers[why] ) {
@ -1047,13 +1080,27 @@ public class BoardActivity extends XWActivity
}
// This is supposed to be called from the jni thread
public int userPickTile( int playerNum, String[] texts )
@Override
public int userPickTileBlank( int playerNum, String[] texts)
{
m_texts = texts;
waitBlockingDialog( PICK_TILE_REQUEST_BLK, 0 );
waitBlockingDialog( PICK_TILE_REQUESTBLANK_BLK, 0 );
return m_resultCode;
}
@Override
public int userPickTileTray( int playerNum, String[] texts,
String[] curTiles, int nPicked )
{
m_texts = texts;
m_curTiles = TextUtils.join( ", ", curTiles );
m_canUndoTiles = 0 < nPicked;
waitBlockingDialog( PICK_TILE_REQUESTTRAY_BLK,
UtilCtxt.PICKER_PICKALL );
return m_resultCode;
}
@Override
public String askPassword( String name )
{
// call this each time dlg created or will get exception
@ -1070,6 +1117,7 @@ public class BoardActivity extends XWActivity
return result;
}
@Override
public void turnChanged()
{
post( new Runnable() {
@ -1081,11 +1129,13 @@ public class BoardActivity extends XWActivity
m_jniThread.handle( JNIThread.JNICmd. CMD_ZOOM, -8 );
}
@Override
public boolean engineProgressCallback()
{
return ! m_jniThread.busy();
}
@Override
public boolean userQuery( int id, String query )
{
boolean result;
@ -1103,13 +1153,8 @@ public class BoardActivity extends XWActivity
break;
// These *are* blocking dialogs
case UtilCtxt.QUERY_COMMIT_TRADE:
case UtilCtxt.QUERY_COMMIT_TURN:
if ( UtilCtxt.QUERY_COMMIT_TRADE == id ) {
m_dlgBytes = getString( R.string.query_trade );
} else {
m_dlgBytes = query;
}
m_dlgBytes = query;
m_dlgTitle = R.string.query_title;
result = 0 != waitBlockingDialog( QUERY_REQUEST_BLK, 0 );
break;
@ -1121,6 +1166,17 @@ public class BoardActivity extends XWActivity
return result;
}
@Override
public boolean confirmTrade( String[] tiles )
{
m_dlgTitle = R.string.info_title;
m_dlgBytes =
Utils.format( BoardActivity.this, R.string.query_tradef,
TextUtils.join( ", ", tiles ) );
return 0 != waitBlockingDialog( QUERY_REQUEST_BLK, 0 );
}
@Override
public void userError( int code )
{
int resid = 0;
@ -1176,6 +1232,7 @@ public class BoardActivity extends XWActivity
}
} // userError
@Override
public void informMove( String expl, String words )
{
m_dlgBytes = expl;
@ -1184,6 +1241,7 @@ public class BoardActivity extends XWActivity
waitBlockingDialog( DLG_SCORES_BLK, 0 );
}
@Override
public void notifyGameOver()
{
m_jniThread.handle( JNIThread.JNICmd.CMD_POST_OVER );
@ -1194,7 +1252,7 @@ public class BoardActivity extends XWActivity
// Utils.logf( "yOffsetChange(maxOffset=%d)", maxOffset );
// m_view.setVerticalScrollBarEnabled( maxOffset > 0 );
// }
@Override
public boolean warnIllegalWord( String[] words, int turn,
boolean turnLost )
{
@ -1230,6 +1288,7 @@ public class BoardActivity extends XWActivity
// we don't block the jni thread will continue processing messages
// and may stack dialogs on top of this one. Including later
// chat-messages.
@Override
public void showChat( final String msg )
{
post( new Runnable() {
@ -1305,7 +1364,8 @@ public class BoardActivity extends XWActivity
}
break;
case JNIThread.GOT_WORDS:
launchLookup( wordsToArray((String)msg.obj) );
launchLookup( wordsToArray((String)msg.obj),
m_gi.dictLang );
break;
}
}
@ -1356,6 +1416,10 @@ public class BoardActivity extends XWActivity
private void populateToolbar()
{
m_toolbar.setListener( Toolbar.BUTTON_BROWSE_DICT,
R.string.not_again_browse,
R.string.key_na_browse,
BUTTON_BROWSE_ACTION );
m_toolbar.setListener( Toolbar.BUTTON_HINT_PREV,
R.string.not_again_hintprev,
R.string.key_notagain_hintprev,
@ -1589,12 +1653,6 @@ public class BoardActivity extends XWActivity
return wordsArray;
}
private void launchLookup( String[] words )
{
m_words = words;
showDialog( DLG_LOOKUP );
}
private void setupPasswdVars()
{
String fmt = getString( R.string.msg_ask_password );

View file

@ -338,6 +338,11 @@ public class BoardView extends View implements DrawCtx, BoardHandler,
m_jniThread.handle( JNIThread.JNICmd.CMD_INVALALL );
}
public int getCurPlayer()
{
return m_trayOwner;
}
public int curPending()
{
return m_pendingScore;

View file

@ -0,0 +1,211 @@
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
/*
* Copyright 2009 - 2011 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package org.eehouse.android.xw4;
import android.content.Context;
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListAdapter;
import android.widget.SectionIndexer;
import android.widget.TextView;
import java.util.Arrays;
import junit.framework.Assert;
import org.eehouse.android.xw4.jni.JNIUtilsImpl;
import org.eehouse.android.xw4.jni.XwJNI;
public class DictBrowseActivity extends XWListActivity
implements View.OnClickListener {
public static final String DICT_NAME = "DICT_NAME";
private int m_dictClosure = 0;
private int m_lang;
private String m_name;
private int m_nWords;
private float m_textSize;
// - Steps to reproduce the problem:
// Create ListView, set custom adapter which implements ListAdapter and
// SectionIndexer but do not extends BaseAdapter. Enable fast scroll in
// layout. This will effect in ClassCastException.
private class DictListAdapter extends BaseAdapter
implements SectionIndexer {
private String[] m_prefixes;
private int[] m_indices;
public Object getItem( int position )
{
TextView text = new TextView( DictBrowseActivity.this );
String str = XwJNI.dict_iter_nthWord( m_dictClosure, position );
if ( null != str ) {
text.setText( str );
text.setOnClickListener( DictBrowseActivity.this );
text.setTextSize( m_textSize );
}
return text;
}
public View getView( int position, View convertView, ViewGroup parent ) {
return (View)getItem( position );
}
public long getItemId( int position ) { return position; }
public int getCount() {
Assert.assertTrue( 0 != m_dictClosure );
return m_nWords;
}
// SectionIndexer
public int getPositionForSection( int section )
{
return m_indices[section];
}
public int getSectionForPosition( int position )
{
int section = Arrays.binarySearch( m_indices, position );
if ( section < 0 ) {
section *= -1;
}
if ( section >= m_indices.length ) {
section = m_indices.length - 1;
}
return section;
}
public Object[] getSections()
{
m_prefixes = XwJNI.dict_iter_getPrefixes( m_dictClosure );
m_indices = XwJNI.dict_iter_getIndices( m_dictClosure );
return m_prefixes;
}
}
@Override
protected void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
Intent intent = getIntent();
String name = null == intent? null:intent.getStringExtra( DICT_NAME );
if ( null == name ) {
finish();
} else {
m_name = name;
m_lang = DictLangCache.getDictLangCode( this, name );
m_textSize = 2.0f + new TextView( this ).getTextSize();
String[] names = { name };
DictUtils.DictPairs pairs = DictUtils.openDicts( this, names );
m_dictClosure = XwJNI.dict_iter_init( pairs.m_bytes[0],
pairs.m_paths[0],
JNIUtilsImpl.get() );
m_nWords = XwJNI.dict_iter_wordCount( m_dictClosure );
setTitle( Utils.format( this, R.string.dict_browse_titlef,
name, m_nWords ) );
Utils.logf( "calling makeIndex" );
XwJNI.dict_iter_makeIndex( m_dictClosure );
Utils.logf( "makeIndex done" );
setContentView( R.layout.dict_browser );
setListAdapter( new DictListAdapter() );
getListView().setFastScrollEnabled( true );
Button button = (Button)findViewById( R.id.search_button );
button.setOnClickListener( new View.OnClickListener() {
public void onClick( View view )
{
findButtonClicked();
}
} );
}
}
@Override
protected void onDestroy()
{
XwJNI.dict_iter_destroy( m_dictClosure );
m_dictClosure = 0;
super.onDestroy();
}
// Just in case onDestroy didn't get called....
@Override
public void finalize()
{
XwJNI.dict_iter_destroy( m_dictClosure );
try {
super.finalize();
} catch ( java.lang.Throwable err ){
Utils.logf( "%s", err.toString() );
}
}
//////////////////////////////////////////////////
// View.OnClickListener interface
//////////////////////////////////////////////////
@Override
public void onClick( View view )
{
TextView text = (TextView)view;
String[] words = { text.getText().toString() };
launchLookup( words, m_lang, true );
}
private void findButtonClicked()
{
EditText edit = (EditText)findViewById( R.id.word_edit );
String text = edit.getText().toString();
if ( null != text && 0 < text.length() ) {
int pos = XwJNI.dict_iter_getStartsWith( m_dictClosure, text );
if ( 0 <= pos ) {
getListView().setSelection( pos );
} else {
Utils.showf( this, R.string.dict_browse_nowordsf,
m_name, text );
}
}
}
public static void launch( Context caller, String name )
{
Intent intent = new Intent( caller, DictBrowseActivity.class );
intent.putExtra( DICT_NAME, name );
caller.startActivity( intent );
}
}

View file

@ -64,18 +64,19 @@ public class DictLangCache {
public static String annotatedDictName( Context context, DictAndLoc dal )
{
String result = null;
DictInfo info = getInfo( context, dal );
int wordCount = info.wordCount;
if ( null != info ) {
int wordCount = info.wordCount;
String langName = getLangName( context, dal.name );
String result;
if ( 0 == wordCount ) {
result = String.format( "%s (%s)", dal.name, langName );
} else {
result = String.format( "%s (%s/%d)", dal.name, langName,
wordCount );
String langName = getLangName( context, dal.name );
if ( 0 == wordCount ) {
result = String.format( "%s (%s)", dal.name, langName );
} else {
result = String.format( "%s (%s/%d)", dal.name, langName,
wordCount );
}
}
return result;
}
@ -114,7 +115,7 @@ public class DictLangCache {
DictAndLoc[] dals = DictUtils.dictList( context );
for ( DictAndLoc dal : dals ) {
DictInfo info = getInfo( context, dal );
if ( code == info.langCode ) {
if ( null != info && code == info.langCode ) {
al.add( info );
}
}
@ -159,7 +160,7 @@ public class DictLangCache {
DictAndLoc[] dals = DictUtils.dictList( context );
for ( DictAndLoc dal : dals ) {
DictInfo info = getInfo( context, dal );
if ( code == info.langCode ) {
if ( null != info && code == info.langCode ) {
al.add( dal );
}
}
@ -373,11 +374,23 @@ public class DictLangCache {
info = new DictInfo();
XwJNI.dict_getInfo( pairs.m_bytes[0], pairs.m_paths[0],
JNIUtilsImpl.get(), info );
// It should not be possible for dict_getInfo to fail
// unless DictUtils.dictList() isn't doing its job and
// puts unchecked dicts on the list. Should probably
// assert that this returns true. Open question: do I
// always trust dicts in the BUILTIN and INTERNAL
// locations? Files can get damaged....
if ( XwJNI.dict_getInfo( pairs.m_bytes[0], pairs.m_paths[0],
JNIUtilsImpl.get(),
DictUtils.DictLoc.DOWNLOAD == dal.loc,
info ) ) {
info.name = dal.name;
s_nameToLang.put( dal, info );
info.name = dal.name;
s_nameToLang.put( dal, info );
} else {
info = null;
Utils.logf( "getInfo(): unable to open dict %s", dal.name );
}
}
return info;
}

View file

@ -1,7 +1,7 @@
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
/*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
* rights reserved.
* Copyright 2009-2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -114,34 +114,42 @@ public class DictUtils {
// changes?
}
private static void tryDir( File dir, boolean strict, DictLoc loc,
ArrayList<DictAndLoc> al )
{
if ( null != dir ) {
String[] list = dir.list();
if ( null != list ) {
for ( String file : list ) {
if ( isDict( file, strict? dir : null ) ) {
al.add( new DictAndLoc( removeDictExtn( file ), loc ) );
}
}
}
}
}
public static DictAndLoc[] dictList( Context context )
{
if ( null == s_dictListCache ) {
ArrayList<DictAndLoc> al = new ArrayList<DictAndLoc>();
for ( String file : getAssets( context ) ) {
if ( isDict( file ) ) {
if ( isDict( file, null ) ) {
al.add( new DictAndLoc( removeDictExtn( file ),
DictLoc.BUILT_IN ) );
}
}
for ( String file : context.fileList() ) {
if ( isDict( file ) ) {
if ( isDict( file, null ) ) {
al.add( new DictAndLoc( removeDictExtn( file ),
DictLoc.INTERNAL ) );
}
}
File sdDir = getSDDir( context );
if ( null != sdDir ) {
for ( String file : sdDir.list() ) {
if ( isDict( file ) ) {
al.add( new DictAndLoc( removeDictExtn( file ),
DictLoc.EXTERNAL ) );
}
}
}
tryDir( getSDDir( context ), false, DictLoc.EXTERNAL, al );
tryDir( getDownloadDir(), true, DictLoc.DOWNLOAD, al );
s_dictListCache =
al.toArray( new DictUtils.DictAndLoc[al.size()] );
@ -213,43 +221,38 @@ public class DictUtils {
private static boolean copyDict( Context context, String name,
DictLoc from, DictLoc to )
{
Assert.assertFalse( from.equals(to) );
boolean success = false;
File file = getSDPathFor( context, name );
if ( null != file ) {
FileChannel channelIn = null;
FileChannel channelOut = null;
FileChannel channelIn = null;
FileChannel channelOut = null;
try {
FileInputStream fis = DictLoc.INTERNAL == from
? context.openFileInput( name )
: new FileInputStream( getDictFile( context, name, from ) );
FileOutputStream fos = DictLoc.INTERNAL == to
? context.openFileOutput( name, Context.MODE_PRIVATE )
: new FileOutputStream( getDictFile( context, name, to ) );
channelIn = fis.getChannel();
channelOut = fos.getChannel();
channelIn.transferTo( 0, channelIn.size(), channelOut );
success = true;
} catch ( java.io.FileNotFoundException fnfe ) {
Utils.logf( "%s", fnfe.toString() );
} catch ( java.io.IOException ioe ) {
Utils.logf( "%s", ioe.toString() );
} finally {
try {
FileInputStream fis;
FileOutputStream fos;
if ( DictLoc.INTERNAL == from ) {
fis = context.openFileInput( name );
fos = new FileOutputStream( file );
} else {
fis = new FileInputStream( file );
fos = context.openFileOutput( name, Context.MODE_PRIVATE );
}
channelIn = fis.getChannel();
channelOut = fos.getChannel();
channelIn.transferTo( 0, channelIn.size(), channelOut );
success = true;
} catch ( java.io.FileNotFoundException fnfe ) {
Utils.logf( "%s", fnfe.toString() );
} catch ( java.io.IOException ioe ) {
Utils.logf( "%s", ioe.toString() );
} finally {
try {
// Order should match assignment order to above in
// case one or both null
channelIn.close();
channelOut.close();
} catch ( Exception e ) {
Utils.logf( "%s", e.toString() );
}
// Order should match assignment order to above in
// case one or both null
channelIn.close();
channelOut.close();
} catch ( Exception e ) {
Utils.logf( "%s", e.toString() );
}
}
return success;
@ -258,15 +261,25 @@ public class DictUtils {
public static void deleteDict( Context context, String name, DictLoc loc )
{
name = addDictExtn( name );
if ( DictLoc.EXTERNAL == loc ) {
File onSD = getSDPathFor( context, name );
if ( null != onSD ) {
onSD.delete();
} // otherwise what?
} else {
Assert.assertTrue( DictLoc.INTERNAL == loc );
File path = null;
switch( loc ) {
case DOWNLOAD:
path = getDownloadsPathFor( name );
break;
case EXTERNAL:
path = getSDPathFor( context, name );
break;
case INTERNAL:
context.deleteFile( name );
break;
default:
Assert.fail();
}
if ( null != path ) {
path.delete();
}
invalDictList();
}
@ -306,6 +319,15 @@ public class DictUtils {
if ( null == bytes ) {
try {
FileInputStream fis = null;
if ( null == fis ) {
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.DOWNLOAD ) {
File path = getDownloadsPathFor( name );
if ( null != path && path.exists() ) {
Utils.logf( "loading %s from Download", name );
fis = new FileInputStream( path );
}
}
}
if ( loc == DictLoc.UNKNOWN || loc == DictLoc.EXTERNAL ) {
File sdFile = getSDPathFor( context, name );
if ( null != sdFile && sdFile.exists() ) {
@ -358,6 +380,9 @@ public class DictUtils {
{
File path;
switch ( to ) {
case DOWNLOAD:
path = getDownloadsPathFor( name );
break;
case EXTERNAL:
path = getSDPathFor( context, name );
break;
@ -439,9 +464,15 @@ public class DictUtils {
return file.endsWith( XWConstants.GAME_EXTN );
}
private static boolean isDict( String file )
private static boolean isDict( String file, File dir )
{
return file.endsWith( XWConstants.DICT_EXTN );
boolean ok = file.endsWith( XWConstants.DICT_EXTN );
if ( ok && null != dir ) {
String fullPath = new File( dir, file ).getPath();
ok = XwJNI.dict_getInfo( null, fullPath, JNIUtilsImpl.get(),
true, null );
}
return ok;
}
public static String removeDictExtn( String str )
@ -510,4 +541,29 @@ public class DictUtils {
}
return result;
}
private static File getDownloadDir()
{
File result = null;
if ( haveWriteableSD() ) {
File storage = Environment.getExternalStorageDirectory();
if ( null != storage ) {
result = new File( storage.getPath(), "download/" );
if ( !result.exists() ) {
result = null;
}
}
}
return result;
}
private static File getDownloadsPathFor( String name )
{
File result = null;
File dir = getDownloadDir();
if ( dir != null ) {
result = new File( dir, name );
}
return result;
}
}

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "cd ../../../../../; ant install"; -*- */
/*
* Copyright 2009-2010 by Eric House (xwords@eehouse.org). All
* Copyright 2009 - 2011 by Eric House (xwords@eehouse.org). All
* rights reserved.
*
* This program is free software; you can redistribute it and/or
@ -31,6 +31,7 @@ import android.widget.Button;
import android.widget.TextView;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.res.Resources;
import android.content.SharedPreferences;
@ -47,6 +48,7 @@ import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
import android.widget.Toast;
import android.preference.PreferenceManager;
import android.net.Uri;
import java.util.Arrays;
import java.util.HashMap;
import junit.framework.Assert;
@ -67,7 +69,6 @@ public class DictsActivity extends ExpandableListActivity
private static final String NAME = "name";
private static final String LANG = "lang";
private static final String MOVEFROMLOC = "movefromloc";
private static final String MOVETOLOC = "movetoloc";
private static HashMap<String,Boolean> s_openStates =
new HashMap<String,Boolean>();
@ -90,7 +91,8 @@ public class DictsActivity extends ExpandableListActivity
private long m_packedPosition;
private DictUtils.DictLoc m_moveFromLoc;
private DictUtils.DictLoc m_moveToLoc;
private int m_moveFromItem;
private int m_moveToItm;
private LayoutInflater m_factory;
@ -152,6 +154,7 @@ public class DictsActivity extends ExpandableListActivity
}
addToCache( groupPosition, childPosition, view );
view.setOnClickListener( DictsActivity.this );
}
return view;
}
@ -244,7 +247,7 @@ public class DictsActivity extends ExpandableListActivity
@Override
protected Dialog onCreateDialog( int id )
{
DialogInterface.OnClickListener lstnr;
OnClickListener lstnr;
Dialog dialog;
String format;
String message;
@ -252,7 +255,7 @@ public class DictsActivity extends ExpandableListActivity
switch( id ) {
case PICK_STORAGE:
lstnr = new DialogInterface.OnClickListener() {
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
startDownload( m_lang, m_name, item !=
DialogInterface.BUTTON_POSITIVE );
@ -267,36 +270,58 @@ public class DictsActivity extends ExpandableListActivity
.create();
break;
case MOVE_DICT:
lstnr = new DialogInterface.OnClickListener() {
message = Utils.format( this, R.string.move_dictf,
m_adapter.getSelChildView().getText() );
String[] items = new String[3];
for ( int ii = 0; ii < 3; ++ii ) {
DictUtils.DictLoc loc = itemToRealLoc(ii);
if ( loc.equals( m_moveFromLoc ) ) {
m_moveFromItem = ii;
}
items[ii] = m_locNames[loc.ordinal()];
}
OnClickListener newSelLstnr =
new OnClickListener() {
public void onClick( DialogInterface dlgi, int item ) {
m_moveToItm = item;
AlertDialog dlg = (AlertDialog)dlgi;
Button btn =
dlg.getButton( AlertDialog.BUTTON_POSITIVE );
btn.setEnabled( m_moveToItm != m_moveFromItem );
}
};
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
XWListItem rowView = m_adapter.getSelChildView();
Assert.assertTrue( m_moveToItm != m_moveFromItem );
DictUtils.DictLoc toLoc = itemToRealLoc( m_moveToItm );
if ( DictUtils.moveDict( DictsActivity.this,
rowView.getText(),
m_moveFromLoc,
m_moveToLoc ) ) {
rowView.
setComment( m_locNames[m_moveToLoc.ordinal()]);
rowView.cache( m_moveToLoc );
toLoc ) ) {
rowView.setComment( m_locNames[toLoc.ordinal()] );
rowView.cache( toLoc );
rowView.invalidate();
} else {
Utils.logf( "moveDict failed" );
Utils.logf( "moveDict(%s) failed",
rowView.getText() );
}
}
};
format = getString( R.string.move_dictf );
message = String.format( format,
m_adapter.getSelChildView().getText(),
m_locNames[ m_moveFromLoc.ordinal() ],
m_locNames[ m_moveToLoc.ordinal() ] );
dialog = new AlertDialog.Builder( this )
.setMessage( message )
.setPositiveButton( R.string.button_ok, lstnr )
.setTitle( message )
.setSingleChoiceItems( items, m_moveFromItem, newSelLstnr )
.setPositiveButton( R.string.button_move, lstnr )
.setNegativeButton( R.string.button_cancel, null )
.create();
break;
case SET_DEFAULT:
lstnr = new DialogInterface.OnClickListener() {
lstnr = new OnClickListener() {
public void onClick( DialogInterface dlg, int item ) {
if ( DialogInterface.BUTTON_NEGATIVE == item
|| DialogInterface.BUTTON_POSITIVE == item ) {
@ -339,6 +364,14 @@ public class DictsActivity extends ExpandableListActivity
{
super.onPrepareDialog( id, dialog );
m_delegate.onPrepareDialog( id, dialog );
if ( MOVE_DICT == id ) {
// The move button should always start out disabled
// because the selected location should be where it
// currently is.
((AlertDialog)dialog).getButton( AlertDialog.BUTTON_POSITIVE )
.setEnabled( false );
}
}
@Override
@ -401,9 +434,6 @@ public class DictsActivity extends ExpandableListActivity
if ( null != m_moveFromLoc ) {
outState.putInt( MOVEFROMLOC, m_moveFromLoc.ordinal() );
}
if ( null != m_moveToLoc ) {
outState.putInt( MOVETOLOC, m_moveToLoc.ordinal() );
}
}
private void getBundledData( Bundle savedInstanceState )
@ -418,10 +448,6 @@ public class DictsActivity extends ExpandableListActivity
if ( -1 != tmp ) {
m_moveFromLoc = DictUtils.DictLoc.values()[tmp];
}
tmp = savedInstanceState.getInt( MOVETOLOC, -1 );
if ( -1 != tmp ) {
m_moveToLoc = DictUtils.DictLoc.values()[tmp];
}
}
}
@ -431,9 +457,14 @@ public class DictsActivity extends ExpandableListActivity
super.onStop();
}
public void onClick( View v )
public void onClick( View view )
{
askStartDownload( 0, null );
if ( view instanceof Button ) {
askStartDownload( 0, null );
} else {
XWListItem item = (XWListItem)view;
DictBrowseActivity.launch( this, item.getText() );
}
}
@Override
@ -492,9 +523,6 @@ public class DictsActivity extends ExpandableListActivity
case R.id.dicts_item_select:
showDialog( SET_DEFAULT );
break;
case R.id.dicts_item_details:
Utils.notImpl( this );
break;
}
return handled;
@ -518,12 +546,6 @@ public class DictsActivity extends ExpandableListActivity
private void askMoveDict( XWListItem item )
{
m_moveFromLoc = (DictUtils.DictLoc)item.getCached();
if ( m_moveFromLoc == DictUtils.DictLoc.INTERNAL ) {
m_moveToLoc = DictUtils.DictLoc.EXTERNAL;
} else {
m_moveToLoc = DictUtils.DictLoc.INTERNAL;
}
showDialog( MOVE_DICT );
}
@ -599,6 +621,12 @@ public class DictsActivity extends ExpandableListActivity
}
}
private DictUtils.DictLoc itemToRealLoc( int item )
{
item += DictUtils.DictLoc.INTERNAL.ordinal();
return DictUtils.DictLoc.values()[item];
}
private void deleteDict( String dict, DictUtils.DictLoc loc )
{
DictUtils.deleteDict( this, dict, loc );
@ -632,6 +660,7 @@ public class DictsActivity extends ExpandableListActivity
private void mkListAdapter()
{
m_langs = DictLangCache.listLangs( this );
Arrays.sort( m_langs );
m_adapter = new DictListAdapter( this );
setListAdapter( m_adapter );
}

View file

@ -43,7 +43,8 @@ public class DlgDelegate {
public static final int CONFIRM_THEN = 4;
public static final int TEXT_OR_HTML_THEN = 5;
public static final int DLG_DICTGONE = 6;
public static final int DIALOG_LAST = DLG_DICTGONE;
public static final int DLG_LOOKUP = 7;
public static final int DIALOG_LAST = DLG_LOOKUP;
public static final int TEXT_BTN = AlertDialog.BUTTON_POSITIVE;
public static final int HTML_BTN = AlertDialog.BUTTON_NEGATIVE;
@ -55,6 +56,9 @@ public class DlgDelegate {
private static final String MSGID = "msgid";
private static final String PREFSKEY = "prefskey";
private static final String POSBUTTON = "posbutton";
private static final String WORDS = "words";
private static final String LANG = "lang";
private static final String FORCELIST = "forcelist";
// Cache a couple of callback implementations that never change:
private DialogInterface.OnClickListener m_cbkOnClickLstnr = null;
@ -73,6 +77,9 @@ public class DlgDelegate {
private Activity m_activity;
private DlgClickNotify m_clickCallback;
private String m_dictName = null;
private String[] m_words = null;
private int m_wordsLang = -1;
private boolean m_forceList = false;
public DlgDelegate( Activity activity, DlgClickNotify callback,
Bundle bundle )
@ -86,6 +93,9 @@ public class DlgDelegate {
m_msgID = bundle.getInt( MSGID );
m_posButton = bundle.getInt( POSBUTTON );
m_prefsKey = bundle.getInt( PREFSKEY );
m_words = bundle.getStringArray( WORDS );
m_wordsLang = bundle.getInt( LANG );
m_forceList = bundle.getBoolean( FORCELIST );
}
}
@ -96,6 +106,9 @@ public class DlgDelegate {
outState.putInt( MSGID, m_msgID );
outState.putInt( POSBUTTON, m_posButton );
outState.putInt( PREFSKEY, m_prefsKey );
outState.putStringArray( WORDS, m_words );
outState.putInt( LANG, m_wordsLang );
outState.putBoolean( FORCELIST, m_forceList );
}
public Dialog onCreateDialog( int id )
@ -120,6 +133,15 @@ public class DlgDelegate {
case DLG_DICTGONE:
dialog = createDictGoneDialog();
break;
case DLG_LOOKUP:
LookupView view = (LookupView)Utils.inflate( m_activity,
R.layout.lookup );
dialog = new AlertDialog.Builder( m_activity )
.setView( view )
.create();
view.setDialog( dialog, DLG_LOOKUP );
view.setWords( m_words, m_wordsLang, m_forceList );
break;
}
return dialog;
}
@ -225,6 +247,14 @@ public class DlgDelegate {
}
}
public void launchLookup( String[] words, int lang, boolean forceList )
{
m_words = words;
m_wordsLang = lang;
m_forceList = forceList;
m_activity.showDialog( DLG_LOOKUP );
}
private Dialog createAboutDialog()
{
final View view = Utils.inflate( m_activity, R.layout.about_dlg );

View file

@ -114,6 +114,7 @@ public class GameConfig extends XWActivity
,R.id.room_spinner
,R.id.refresh_button
,R.id.hints_allowed
,R.id.pick_faceup
,R.id.use_timer
,R.id.timer_minutes_edit
,R.id.smart_robot
@ -121,7 +122,7 @@ public class GameConfig extends XWActivity
};
class RemoteChoices extends XWListAdapter {
public RemoteChoices() { super( GameConfig.this, m_gi.nPlayers ); }
public RemoteChoices() { super( m_gi.nPlayers ); }
public Object getItem( int position) { return m_gi.players[position]; }
public View getView( final int position, View convertView,
@ -362,7 +363,10 @@ public class GameConfig extends XWActivity
(ArrayAdapter<String>)spinner.getAdapter();
if ( position < adapter.getCount() ) {
lp.dictName = adapter.getItem(position);
String name = adapter.getItem(position);
if ( ! name.equals( m_browseText ) ) {
lp.dictName = name;
}
}
lp.setIsRobot( Utils.getChecked( dialog, R.id.robot_check ) );
@ -514,7 +518,10 @@ public class GameConfig extends XWActivity
setSmartnessSpinner();
Utils.setChecked( this, R.id.hints_allowed, !m_gi.hintsNotAllowed );
Utils.setChecked( this, R.id.hints_allowed,
!m_gi.hintsNotAllowed );
Utils.setChecked( this, R.id.pick_faceup,
m_gi.allowPickTiles );
Utils.setInt( this, R.id.timer_minutes_edit,
m_gi.gameSeconds/60/m_gi.nPlayers );
@ -950,6 +957,7 @@ public class GameConfig extends XWActivity
private void saveChanges()
{
m_gi.hintsNotAllowed = !Utils.getChecked( this, R.id.hints_allowed );
m_gi.allowPickTiles = Utils.getChecked( this, R.id.pick_faceup );
m_gi.timerEnabled = Utils.getChecked( this, R.id.use_timer );
m_gi.gameSeconds = 60 * m_gi.nPlayers *
Utils.getInt( this, R.id.timer_minutes_edit );

View file

@ -229,7 +229,7 @@ public class GameListAdapter extends XWListAdapter {
} // class LoadItemTask
public GameListAdapter( Context context, LoadItemCB cb ) {
super( context, DBUtils.gamesList(context).length );
super( DBUtils.gamesList(context).length );
m_context = context;
m_cb = cb;
m_factory = LayoutInflater.from( context );

View file

@ -62,6 +62,7 @@ public class LookupView extends LinearLayout
private static int s_lang = -1;
private String[] m_words;
private boolean m_forceList;
private static int m_lang;
private int m_wordIndex = 0;
private int m_urlIndex = 0;
@ -79,9 +80,10 @@ public class LookupView extends LinearLayout
m_context = cx;
}
public void setWords( String[] words, int lang )
public void setWords( String[] words, int lang, boolean forceList )
{
m_words = words;
m_forceList = forceList;
setLang( lang );
m_state = STATE_DONE;
@ -140,7 +142,8 @@ public class LookupView extends LinearLayout
if ( STATE_WORDS == m_state && 1 >= m_words.length ) {
m_state += incr;
}
if ( STATE_URLS == m_state && 1 >= s_lookupUrls.length ) {
if ( STATE_URLS == m_state &&
( 1 >= s_lookupUrls.length && !m_forceList ) ) {
m_state += incr;
}
if ( m_state == curState ) {

View file

@ -39,15 +39,18 @@ public class Toolbar {
public int m_id;
}
public static final int BUTTON_HINT_PREV = 0;
public static final int BUTTON_HINT_NEXT = 1;
public static final int BUTTON_FLIP = 2;
public static final int BUTTON_JUGGLE = 3;
public static final int BUTTON_ZOOM = 4;
public static final int BUTTON_UNDO = 5;
public static final int BUTTON_CHAT = 6;
public static final int BUTTON_BROWSE_DICT = 0;
public static final int BUTTON_HINT_PREV = 1;
public static final int BUTTON_HINT_NEXT = 2;
public static final int BUTTON_FLIP = 3;
public static final int BUTTON_JUGGLE = 4;
public static final int BUTTON_ZOOM = 5;
public static final int BUTTON_UNDO = 6;
public static final int BUTTON_CHAT = 7;
private static TBButtonInfo[] s_buttonInfo = {
// BUTTON_BROWSE_DICT
new TBButtonInfo(R.id.dictlist_button_horizontal ),
// BUTTON_HINT_PREV
new TBButtonInfo(R.id.prevhint_button_horizontal ),
// BUTTON_HINT_NEXT

View file

@ -91,6 +91,11 @@ public class Utils {
Toast.makeText( context, msg, Toast.LENGTH_SHORT ).show();
} // showf
public static void showf( Context context, int formatid, Object... args )
{
showf( context, context.getString( formatid ), args );
} // showf
public static void printStack( StackTraceElement[] trace )
{
if ( s_doLog ) {

View file

@ -151,6 +151,11 @@ public class XWActivity extends Activity
m_delegate.doSyncMenuitem();
}
protected void launchLookup( String[] words, int lang )
{
m_delegate.launchLookup( words, lang, false );
}
// DlgDelegate.DlgClickNotify interface
public void dlgButtonClicked( int id, int which )
{

View file

@ -153,4 +153,14 @@ public class XWListActivity extends ListActivity
Assert.fail();
}
protected void launchLookup( String[] words, int lang )
{
m_delegate.launchLookup( words, lang, false );
}
protected void launchLookup( String[] words, int lang, boolean forceList )
{
m_delegate.launchLookup( words, lang, forceList );
}
}

View file

@ -31,7 +31,10 @@ import android.database.DataSetObserver;
public abstract class XWListAdapter implements ListAdapter {
private int m_count;
public XWListAdapter( Context context, int count ) {
public XWListAdapter( ) {
this( 0 );
}
public XWListAdapter( int count ) {
m_count = count;
}

View file

@ -27,8 +27,13 @@ public interface UtilCtxt {
static final int BONUS_TRIPLE_LETTER = 3;
static final int BONUS_TRIPLE_WORD = 4;
int userPickTile( /* PickInfo* pi, add once tile-picking is enabled */
int playerNum, String[] texts );
// must match defns in util.h
public static final int PICKER_PICKALL = -1;
public static final int PICKER_BACKUP = -2;
int userPickTileBlank( int playerNum, String[] texts );
int userPickTileTray( int playerNum, String[] tiles,
String[] curTiles, int nPicked );
String askPassword( String name );
void turnChanged();
@ -76,10 +81,10 @@ public interface UtilCtxt {
String getUserString( int stringCode );
static final int QUERY_COMMIT_TURN = 0;
static final int QUERY_COMMIT_TRADE = 1;
static final int QUERY_ROBOT_TRADE = 2;
static final int QUERY_ROBOT_TRADE = 1;
boolean userQuery( int id, String query );
boolean confirmTrade( String[] tiles );
// These oughtto be an enum but then I'd have to cons one up in C.
static final int ERR_NONE = 0;

View file

@ -40,9 +40,16 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "requestTime" );
}
public int userPickTile( int playerNum, String[] texts )
public int userPickTileBlank( int playerNum, String[] texts )
{
subclassOverride( "userPickTile" );
subclassOverride( "userPickTileBlank" );
return 0;
}
public int userPickTileTray( int playerNum, String[] texts,
String[] curTiles, int nPicked )
{
subclassOverride( "userPickTileTray" );
return 0;
}
@ -184,6 +191,12 @@ public class UtilCtxtImpl implements UtilCtxt {
return false;
}
public boolean confirmTrade( String[] tiles )
{
subclassOverride( "confirmTrade" );
return false;
}
public void userError( int id )
{
subclassOverride( "userError" );

View file

@ -238,7 +238,20 @@ public class XwJNI {
// Dicts
public static native boolean dict_tilesAreSame( int dictPtr1, int dictPtr2 );
public static native String[] dict_getChars( int dictPtr );
public static native void dict_getInfo( byte[] dict, String path,
JNIUtils jniu, DictInfo info );
public static native boolean dict_getInfo( byte[] dict, String path,
JNIUtils jniu, boolean check,
DictInfo info );
public static native int dict_getTileValue( int dictPtr, int tile );
// Dict iterator
public static native int dict_iter_init( byte[] dict, String path,
JNIUtils jniu );
public static native void dict_iter_destroy( int closure );
public static native int dict_iter_wordCount( int closure );
public static native String dict_iter_nthWord( int closure, int nn );
public static native void dict_iter_makeIndex( int closure );
public static native String[] dict_iter_getPrefixes( int closure );
public static native int[] dict_iter_getIndices( int closure );
public static native int dict_iter_getStartsWith( int closure,
String prefix );
}

View file

@ -742,6 +742,20 @@ warnBadWords( const XP_UCHAR* word, XP_Bool isLegal,
return ok;
} /* warnBadWords */
static XP_Bool
boardConfirmTrade( BoardCtxt* board, const TrayTileSet* tiles )
{
const XP_UCHAR* tfaces[MAX_TRAY_TILES];
XP_U16 ii;
DictionaryCtxt* dict = model_getDictionary( board->model );
for ( ii = 0; ii < tiles->nTiles; ++ii ) {
tfaces[ii] = dict_getTileString( dict, tiles->tiles[ii] );
}
return util_confirmTrade( board->util, tfaces, tiles->nTiles );
}
XP_Bool
board_commitTurn( BoardCtxt* board )
{
@ -764,12 +778,16 @@ board_commitTurn( BoardCtxt* board )
if ( NO_TILES == traySelBits ) {
util_userError( board->util, ERR_NO_EMPTY_TRADE );
} else if ( util_userQuery( board->util, QUERY_COMMIT_TRADE,
(XWStreamCtxt*)NULL ) ) {
/* server_commitTrade() changes selPlayer, so board_endTrade
must be called first() */
(void)board_endTrade( board );
(void)server_commitTrade( board->server, traySelBits );
} else {
TrayTileSet selTiles;
getSelTiles( board, traySelBits, &selTiles );
if ( boardConfirmTrade( board, &selTiles ) ) {
/* server_commitTrade() changes selPlayer, so board_endTrade
must be called first() */
(void)board_endTrade( board );
(void)server_commitTrade( board->server, &selTiles );
}
}
} else {
XP_Bool warn, legal;
@ -3224,8 +3242,8 @@ keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace )
XP_UCHAR buf[2] = { key, '\0' };
/* Figure out if we have the tile in the tray */
tile = dict_tileForString( dict, buf );
if ( tile != EMPTY_TILE ) { /* in dict? */
XP_U16 nTiles = 1;
if ( dict_tilesForString( dict, buf, &tile, &nTiles ) ) { /* in dict? */
XP_S16 turn = board->selPlayer;
tileIndex = model_trayContains( model, turn, tile );
if ( tileIndex < 0 ) {

View file

@ -303,6 +303,7 @@ void invalCurHintRect( BoardCtxt* board, XP_U16 player );
void moveTileInTray( BoardCtxt* board, XP_U16 moveTo, XP_U16 moveFrom );
XP_Bool handleTrayDuringTrade( BoardCtxt* board, XP_S16 index );
void getSelTiles( const BoardCtxt* board, TileBit selBits, TrayTileSet* selTiles );
const XP_UCHAR* getTileDrawInfo( const BoardCtxt* board, Tile tile,
XP_Bool isBlank, XP_Bitmaps* bitmaps,

View file

@ -1649,7 +1649,7 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
__func__, channelNo & CHANNEL_MASK, channelNo,
msgID, lastMsgRcd );
payloadSize = stream_getSize( stream ) > 0; /* anything left? */
payloadSize = stream_getSize( stream ); /* anything left? */
if ( connID == CONN_ID_NONE ) {
/* special case: initial message from client or server */
@ -1663,8 +1663,8 @@ comms_checkIncomingStream( CommsCtxt* comms, XWStreamCtxt* stream,
messageValid = (NULL != rec)
&& (0 == rec->lastMsgRcd || rec->lastMsgRcd <= msgID);
if ( messageValid ) {
XP_LOGF( "got channelNo=%d;msgID=%ld",
channelNo & CHANNEL_MASK, msgID );
XP_LOGF( "%s: got channelNo=%d;msgID=%ld;len=%d", __func__,
channelNo & CHANNEL_MASK, msgID, payloadSize );
rec->lastMsgRcd = msgID;
stream_setAddress( stream, channelNo );
messageValid = payloadSize > 0;

View file

@ -115,6 +115,13 @@ typedef enum {
} XWTimerReason;
#define MAX_NUM_PLAYERS 4
#define MAX_ROWS 16
#define MAX_COLS MAX_ROWS
#ifdef EIGHT_TILES
# define MAX_TRAY_TILES 8
#else
# define MAX_TRAY_TILES 7
#endif
#define PLAYERNUM_NBITS 2
#define NDEVICES_NBITS 2 /* 1-4, but reduced by 1 fits in 2 bits */
#define NPLAYERS_NBITS 3

View file

@ -36,6 +36,7 @@ COMMONSRC = \
$(COMMONDIR)/game.c \
$(COMMONDIR)/nwgamest.c \
$(COMMONDIR)/dictnry.c \
$(COMMONDIR)/dictiter.c \
$(COMMONDIR)/engine.c \
$(COMMONDIR)/memstream.c \
$(COMMONDIR)/comms.c \
@ -65,6 +66,7 @@ COMMON3 = \
$(COMMONOBJDIR)/game.o \
$(COMMONOBJDIR)/nwgamest.o \
$(COMMONOBJDIR)/dictnry.o \
$(COMMONOBJDIR)/dictiter.o \
$(COMMONOBJDIR)/engine.o \
COMMON4 = \

559
xwords4/common/dictiter.c Normal file
View file

@ -0,0 +1,559 @@
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 1997-2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef XWFEATURE_WALKDICT
#ifdef USE_STDIO
# include <stdio.h>
# include <stdlib.h>
#endif
#include "comtypes.h"
#include "dictnryp.h"
#include "xwstream.h"
#include "strutils.h"
#include "dictnry.h"
#include "dictiter.h"
#include "game.h"
#ifdef CPLUS
extern "C" {
#endif
typedef struct _EdgeArray {
array_edge* edges[MAX_COLS];
XP_U16 nEdges;
} EdgeArray;
/* On entry and exit, edge at end of array should be ACCEPTING. The job of
* this function is to iterate from one such edge to the next. Steps are: 1)
* try to follow the edge, to expand to a longer word with the last one as a
* prefix. 2) If we're at the end of the array, back off the top tile (and
* repeat while at end of array); 3) Once the current top edge is not a
* LAST_EDGE, try with its next-letter neighbor.
*/
static XP_Bool
nextWord( DictIter* iter )
{
const DictionaryCtxt* dict = iter->dict;
XP_U16 nEdges = iter->nEdges;
XP_Bool success = XP_FALSE;
while ( 0 < nEdges && ! success ) {
array_edge* next = dict_follow( dict, iter->edges[nEdges-1] );
if ( !!next ) {
iter->edges[nEdges++] = next;
success = ISACCEPTING( dict, next );
continue; /* try with longer word */
}
while ( IS_LAST_EDGE( dict, iter->edges[nEdges-1] )
&& 0 < --nEdges ) {
}
if ( 0 < nEdges ) {
iter->edges[nEdges-1] += dict->nodeSize;
success = ISACCEPTING( dict, iter->edges[nEdges-1] );
}
}
iter->nEdges = nEdges;
return success;
}
static XP_Bool
isFirstEdge( const DictionaryCtxt* dict, array_edge* edge )
{
XP_Bool result = edge == dict->base; /* can't back up from first node */
if ( !result ) {
result = IS_LAST_EDGE( dict, edge - dict->nodeSize );
}
return result;
}
static XP_Bool
lastEdges( DictIter* iter )
{
const DictionaryCtxt* dict = iter->dict;
array_edge* edge = iter->edges[iter->nEdges-1];
for ( ; ; ) {
while ( !IS_LAST_EDGE( dict, edge ) ) {
edge += dict->nodeSize;
}
iter->edges[iter->nEdges-1] = edge;
edge = dict_follow( dict, edge );
if ( NULL == edge ) {
break;
}
++iter->nEdges;
}
return ISACCEPTING( dict, iter->edges[iter->nEdges-1] );
}
static XP_Bool
prevWord( DictIter* iter )
{
const DictionaryCtxt* dict = iter->dict;
XP_Bool success = XP_FALSE;
while ( 0 < iter->nEdges && ! success ) {
if ( isFirstEdge( dict, iter->edges[iter->nEdges-1] ) ) {
--iter->nEdges;
success = 0 < iter->nEdges
&& ISACCEPTING( dict, iter->edges[iter->nEdges-1] );
continue;
}
iter->edges[iter->nEdges-1] -= dict->nodeSize;
array_edge* next = dict_follow( dict, iter->edges[iter->nEdges-1] );
if ( NULL != next ) {
iter->edges[iter->nEdges++] = next;
success = lastEdges( iter );
if ( success ) {
continue;
}
}
success = ISACCEPTING( dict, iter->edges[iter->nEdges-1] );
}
return success;
}
static XP_Bool
findStartsWith( DictIter* iter, const Tile* tiles, XP_U16 nTiles )
{
const DictionaryCtxt* dict = iter->dict;
XP_Bool success = XP_TRUE;
array_edge* edge = dict_getTopEdge( dict );
iter->nEdges = 0;
while ( nTiles-- > 0 ) {
Tile tile = *tiles++;
edge = dict_edge_with_tile( dict, edge, tile );
if ( NULL == edge ) {
success = XP_FALSE;
break;
}
iter->edges[iter->nEdges++] = edge;
edge = dict_follow( dict, edge );
}
return success;
}
static XP_Bool
findWordStartsWith( DictIter* iter, const Tile* tiles, XP_U16 nTiles )
{
XP_Bool found = XP_FALSE;
if ( findStartsWith( iter, tiles, nTiles ) ) {
found = ISACCEPTING( iter->dict, iter->edges[iter->nEdges-1] );
if ( !found ) {
found = nextWord( iter );
}
}
return found;
}
static XP_Bool
wordsEqual( const DictIter* word1, const DictIter* word2 )
{
XP_Bool success = word1->nEdges == word2->nEdges;
if ( success ) {
success = 0 == memcmp( word1->edges, word2->edges,
word1->nEdges * sizeof(word1->edges[0]) );
}
return success;
}
static XP_Bool
firstWord( DictIter* iter )
{
iter->nEdges = 1;
iter->edges[0] = dict_getTopEdge( iter->dict );
return ISACCEPTING( iter->dict, iter->edges[0] ) || nextWord( iter );
}
XP_U32
dict_countWords( const DictionaryCtxt* dict )
{
XP_U32 count = 0;
DictIter iter;
dict_initIter( dict, &iter );
XP_Bool ok;
for ( ok = firstWord( &iter ); ok; ok = nextWord( &iter ) ) {
++count;
}
return count;
}
#define GUARD_VALUE 0x12345678
#define ASSERT_INITED( iter ) XP_ASSERT( (iter)->guard == GUARD_VALUE )
void
dict_initIter( const DictionaryCtxt* dict, DictIter* iter )
{
XP_MEMSET( iter, 0, sizeof(*iter) );
iter->dict = dict;
#ifdef DEBUG
iter->guard = GUARD_VALUE;
#endif
}
static void
copyIter( DictIter* dest, const DictIter* src )
{
XP_U16 nEdges = src->nEdges;
dest->nEdges = nEdges;
XP_MEMCPY( dest->edges, src->edges, nEdges * sizeof(dest->edges[0]) );
}
static DictPosition
placeWordClose( DictIter* iter, DictPosition position, XP_U16 depth,
const IndexData* data )
{
XP_S16 low = 0;
XP_S16 high = data->count - 1;
XP_S16 index = -1;
for ( ; ; ) {
if ( low > high ) {
break;
}
index = low + ( (high - low) / 2);
if ( position < data->indices[index] ) {
high = index - 1;
} else if ( data->indices[index+1] <= position) {
low = index + 1;
} else {
break;
}
}
/* Now we have the index immediately below the position we want. But we
may be better off starting with the next if it's closer. The last
index is a special case since we use lastWord rather than a prefix to
init */
if ( ( index + 1 < data->count )
&& (data->indices[index + 1] - position)
< (position - data->indices[index]) ) {
++index;
}
if ( !findWordStartsWith( iter, &data->prefixes[depth*index], depth ) ) {
XP_ASSERT(0);
}
return data->indices[index];
} /* placeWordClose */
static void
iterToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
{
XP_U16 ii;
XP_U16 nEdges = iter->nEdges;
Tile tiles[nEdges];
for ( ii = 0; ii < nEdges; ++ii ) {
tiles[ii] = EDGETILE( iter->dict, iter->edges[ii] );
}
(void)dict_tilesToString( iter->dict, tiles, nEdges, buf, buflen );
}
#if 0
static void
printEdges( DictIter* iter, char* comment )
{
XP_UCHAR buf[32];
iterToString( dict, edges, buf, VSIZE(buf) );
XP_LOGF( "%s: %s", comment, buf );
}
#endif
static void
indexOne( XP_U16 depth, Tile* tiles, IndexData* data, DictIter* prevIter,
DictPosition* prevIndex )
{
DictIter curIter;
dict_initIter( prevIter->dict, &curIter );
if ( findWordStartsWith( &curIter, tiles, depth ) ) {
while ( !wordsEqual( &curIter, prevIter ) ) {
++*prevIndex;
if ( !nextWord( prevIter ) ) {
XP_ASSERT( 0 );
}
}
XP_ASSERT( data->count == 0 ||
data->indices[data->count-1] < *prevIndex );
data->indices[data->count] = *prevIndex;
if ( NULL != data->prefixes ) {
XP_MEMCPY( data->prefixes + (data->count * depth), tiles, depth );
}
++data->count;
}
}
static void
doOneDepth( const Tile* allTiles, XP_U16 nTiles, Tile* prefix,
XP_U16 curDepth, XP_U16 maxDepth, IndexData* data,
DictIter* prevIter, DictPosition* prevIndex )
{
XP_U16 ii;
for ( ii = 0; ii < nTiles; ++ii ) {
prefix[curDepth] = allTiles[ii];
if ( curDepth + 1 == maxDepth ) {
indexOne( maxDepth, prefix, data, prevIter, prevIndex );
} else {
doOneDepth( allTiles, nTiles, prefix, curDepth+1, maxDepth,
data, prevIter, prevIndex );
}
}
}
void
dict_makeIndex( const DictIter* iter, XP_U16 depth, IndexData* data )
{
ASSERT_INITED( iter );
const DictionaryCtxt* dict = iter->dict;
XP_ASSERT( depth < MAX_COLS );
XP_U16 ii, needCount;
const XP_U16 nFaces = dict_numTileFaces( dict );
XP_U16 nNonBlankFaces = nFaces;
XP_Bool hasBlank = dict_hasBlankTile( dict );
if ( hasBlank ) {
--nNonBlankFaces;
}
for ( ii = 1, needCount = nNonBlankFaces; ii < depth; ++ii ) {
needCount *= nNonBlankFaces;
}
XP_ASSERT( needCount <= data->count );
Tile allTiles[nNonBlankFaces];
XP_U16 nTiles = 0;
for ( ii = 0; ii < nFaces; ++ii ) {
if ( hasBlank && ii == dict_getBlankTile( dict ) ) {
continue;
}
allTiles[nTiles++] = (Tile)ii;
}
/* For each tile string implied by depth (A if depth == 1, AAA if == 3 ),
* find the first word starting with that IF EXISTS. If it does, find its
* index. As an optimization, find index starting with the previous word.
*/
data->count = 0;
DictIter prevIter;
dict_initIter( dict, &prevIter );
if ( firstWord( &prevIter ) ) {
DictPosition prevIndex = 0;
Tile prefix[depth];
doOneDepth( allTiles, nNonBlankFaces, prefix, 0, depth,
data, &prevIter, &prevIndex );
}
#ifdef DEBUG
DictPosition pos;
for ( pos = 1; pos < data->count; ++pos ) {
XP_ASSERT( data->indices[pos-1] < data->indices[pos] );
}
#endif
} /* dict_makeIndex */
static void
initWord( DictIter* iter )
{
iter->nWords = dict_getWordCount( iter->dict );
}
XP_Bool
dict_firstWord( DictIter* iter )
{
ASSERT_INITED( iter );
XP_Bool success = firstWord( iter );
if ( success ) {
initWord( iter );
iter->position = 0;
}
return success;
}
XP_Bool
dict_getNextWord( DictIter* iter )
{
ASSERT_INITED( iter );
XP_Bool success = nextWord( iter );
if ( success ) {
++iter->position;
}
return success;
}
XP_Bool
dict_lastWord( DictIter* iter )
{
ASSERT_INITED( iter );
iter->nEdges = 1;
iter->edges[0] = dict_getTopEdge( iter->dict );
XP_Bool success = lastEdges( iter );
if ( success ) {
initWord( iter );
iter->position = iter->nWords - 1;
}
return success;
}
XP_Bool
dict_getPrevWord( DictIter* iter )
{
ASSERT_INITED( iter );
XP_Bool success = prevWord( iter );
if ( success ) {
--iter->position;
}
return success;
}
/* If we start without an initialized word, init it to be closer to what's
sought. OR if we're father than necessary from what's sought, start over
at the closer end. Then move as many steps as necessary to reach it. */
XP_Bool
dict_getNthWord( DictIter* iter, DictPosition position, XP_U16 depth,
const IndexData* data )
{
ASSERT_INITED( iter );
const DictionaryCtxt* dict = iter->dict;
XP_U32 wordCount;
XP_Bool validWord = 0 < iter->nEdges;
if ( validWord ) { /* uninitialized */
wordCount = iter->nWords;
XP_ASSERT( wordCount == dict_getWordCount( dict ) );
} else {
wordCount = dict_getWordCount( dict );
}
XP_Bool success = position < wordCount;
if ( success ) {
/* super common cases first */
success = XP_FALSE;
if ( validWord ) {
if ( iter->position == position ) {
success = XP_TRUE;
/* do nothing; we're done */
} else if ( iter->position == position - 1 ) {
success = dict_getNextWord( iter );
} else if ( iter->position == position + 1 ) {
success = dict_getPrevWord( iter );
}
}
if ( !success ) {
XP_U32 wordIndex;
if ( !!data && !!data->prefixes && !!data->indices ) {
wordIndex = placeWordClose( iter, position, depth, data );
if ( !validWord ) {
initWord( iter );
}
} else {
wordCount /= 2; /* mid-point */
/* If word's inited but farther from target than either
endpoint, better to start with an endpoint */
if ( validWord &&
XP_ABS( position - iter->position ) > wordCount ) {
validWord = XP_FALSE;
}
if ( !validWord ) {
if ( position >= wordCount ) {
dict_lastWord( iter );
} else {
dict_firstWord( iter );
}
}
wordIndex = iter->position;
}
XP_Bool (*finder)( DictIter* iter ) = NULL;/* stupid compiler */
XP_U32 repeats = 0;
if ( wordIndex < position ) {
finder = nextWord;
repeats = position - wordIndex;
} else if ( wordIndex > position ) {
finder = prevWord;
repeats = wordIndex - position;
}
while ( repeats-- ) {
if ( !(*finder)( iter ) ) {
XP_ASSERT(0);
}
}
iter->position = position;
success = XP_TRUE;
}
}
return success;
} /* dict_getNthWord */
XP_Bool
dict_findStartsWith( DictIter* iter, const IndexData* data,
const Tile* prefix, XP_U16 len )
{
XP_Bool success = XP_FALSE;
ASSERT_INITED( iter );
XP_USE(data);
XP_LOGF( "%s: not using data", __func__ );
DictIter targetIter;
dict_initIter( iter->dict, &targetIter );
if ( findWordStartsWith( &targetIter, prefix, len ) ) {
DictPosition result = 0;
DictIter iterZero;
dict_initIter( iter->dict, &iterZero );
if ( !firstWord( &iterZero ) ) {
XP_ASSERT( 0 );
}
while ( ! wordsEqual( &iterZero, &targetIter ) ) {
++result;
if ( !nextWord( &iterZero ) ) {
XP_ASSERT( 0 );
}
}
copyIter( iter, &iterZero );
iter->position = result;
success = XP_TRUE;
}
return success;
}
void
dict_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
{
ASSERT_INITED( iter );
iterToString( iter, buf, buflen );
}
DictPosition
dict_getPosition( const DictIter* iter )
{
ASSERT_INITED( iter );
return iter->position;
}
#ifdef CPLUS
}
#endif
#endif /* XWFEATURE_WALKDICT */

75
xwords4/common/dictiter.h Normal file
View file

@ -0,0 +1,75 @@
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef __DICTITER_H__
#define __DICTITER_H__
#ifdef XWFEATURE_WALKDICT
#include "comtypes.h"
#include "dawg.h"
#include "model.h"
#include "mempool.h"
#ifdef CPLUS
extern "C" {
#endif
/* API for iterating over a dict */
typedef XP_S32 DictPosition;
typedef struct _DictIter {
#ifdef DEBUG
XP_U32 guard;
#endif
const DictionaryCtxt* dict;
XP_U32 nWords;
DictPosition position;
XP_U16 nEdges;
array_edge* edges[MAX_COLS];
} DictIter;
typedef struct _IndexData {
DictPosition* indices;
Tile* prefixes;
XP_U16 count; /* in-out: must indicate others are large enough */
} IndexData;
XP_U32 dict_countWords( const DictionaryCtxt* dict );
void dict_initIter( const DictionaryCtxt* dict, DictIter* iter );
void dict_makeIndex( const DictIter* iter, XP_U16 depth, IndexData* data );
XP_Bool dict_firstWord( DictIter* iter );
XP_Bool dict_lastWord( DictIter* iter );
XP_Bool dict_getNextWord( DictIter* iter );
XP_Bool dict_getPrevWord( DictIter* iter );
XP_Bool dict_getNthWord( DictIter* iter, DictPosition position, XP_U16 depth,
const IndexData* data );
void dict_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen );
XP_Bool dict_findStartsWith( DictIter* iter, const IndexData* data,
const Tile* prefix, XP_U16 len );
DictPosition dict_getPosition( const DictIter* iter );
#ifdef CPLUS
}
#endif
#endif /* XWFEATURE_WALKDICT */
#endif

View file

@ -1,6 +1,7 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 1997-2000 by Eric House (xwords@eehouse.org). All rights reserved.
* Copyright 1997-2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -24,8 +25,10 @@
#include "comtypes.h"
#include "dictnryp.h"
#include "dictiter.h"
#include "xwstream.h"
#include "strutils.h"
#include "dictiter.h"
#include "game.h"
#ifdef CPLUS
@ -150,27 +153,41 @@ dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
return result;
} /* dict_tilesToString */
/* dict_tileForString: used to map user keys to tiles in the tray. Returns
* EMPTY_TILE if no match found.
/* Convert str to an array of tiles, continuing until we fail to match or we
* run out of room in which to return tiles. Failure to match means return of
* XP_FALSE, but if we run out of room before failing we return XP_TRUE.
*/
Tile
dict_tileForString( const DictionaryCtxt* dict, const XP_UCHAR* key )
XP_Bool
dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* str,
Tile* tiles, XP_U16* nTilesP )
{
XP_U16 nFaces = dict_numTileFaces( dict );
Tile tile = EMPTY_TILE;
XP_U16 ii;
XP_U16 nTiles = 0;
XP_Bool success = XP_TRUE;
XP_ASSERT( 0 < *nTilesP );
for ( ii = 0; ii < nFaces; ++ii ) {
if ( ii != dict->blankTile ) {
const XP_UCHAR* facep = dict_getTileString( dict, ii );
if ( 0 == XP_STRCMP( facep, key ) ) {
tile = (Tile)ii;
break;
while ( str[0] != '\0' && success && nTiles < *nTilesP ) {
Tile tile;
const XP_UCHAR* prevstr = str;
for ( tile = 0; tile < nFaces; ++tile ) {
if ( tile != dict->blankTile ) {
const XP_UCHAR* facep = dict_getTileString( dict, tile );
XP_U16 faceLen = XP_STRLEN( facep );
if ( 0 == XP_STRNCMP( facep, str, faceLen ) ) {
tiles[nTiles++] = tile;
str += faceLen;
if ( nTiles == *nTilesP ) {
break;
}
}
}
}
success = str > prevstr;
}
return tile;
} /* dict_tileForChar */
XP_ASSERT( nTiles <= *nTilesP );
*nTilesP = nTiles;
return success;
} /* dict_tilesForString */
XP_Bool
dict_tilesAreSame( const DictionaryCtxt* dict1, const DictionaryCtxt* dict2 )
@ -461,7 +478,13 @@ dict_getLangCode( const DictionaryCtxt* dict )
XP_U32
dict_getWordCount( const DictionaryCtxt* dict )
{
return dict->nWords;
XP_U32 nWords = dict->nWords;
#ifdef XWFEATURE_WALKDICT
if ( 0 == nWords ) {
nWords = dict_countWords( dict );
}
#endif
return nWords;
}
#ifdef STUBBED_DICT
@ -597,12 +620,76 @@ dict_super_getTopEdge( const DictionaryCtxt* dict )
return dict->topEdge;
} /* dict_super_getTopEdge */
static unsigned long
dict_super_index_from( const DictionaryCtxt* dict, array_edge* p_edge )
{
unsigned long result;
#ifdef NODE_CAN_4
array_edge_new* edge = (array_edge_new*)p_edge;
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
if ( dict->is_4_byte ) {
result |= ((XP_U32)edge->moreBits) << 16;
} else {
XP_ASSERT( dict->nodeSize == 3 );
if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) {
result |= 0x00010000; /* using | instead of + saves 4 bytes */
}
}
#else
array_edge_old* edge = (array_edge_old*)p_edge;
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
if ( (edge->bits & EXTRABITMASK_OLD) != 0 ) {
result |= 0x00010000; /* using | instead of + saves 4 bytes */
}
#endif
return result;
} /* dict_super_index_from */
static array_edge*
dict_super_follow( const DictionaryCtxt* dict, array_edge* in )
{
XP_U32 index = dict_index_from( dict, in );
array_edge* result = index > 0?
dict_edge_for_index( dict, index ): (array_edge*)NULL;
return result;
} /* dict_super_follow */
static array_edge*
dict_super_edge_with_tile( const DictionaryCtxt* dict, array_edge* from,
Tile tile )
{
for ( ; ; ) {
Tile candidate = EDGETILE(dict,from);
if ( candidate == tile ) {
break;
}
if ( IS_LAST_EDGE(dict, from ) ) {
from = NULL;
break;
}
#ifdef NODE_CAN_4
from += dict->nodeSize;
#else
from += 3;
#endif
}
return from;
} /* edge_with_tile */
void
dict_super_init( DictionaryCtxt* dict )
{
/* subclass may change these later.... */
dict->func_edge_for_index = dict_super_edge_for_index;
dict->func_dict_getTopEdge = dict_super_getTopEdge;
dict->func_dict_index_from = dict_super_index_from;
dict->func_dict_follow = dict_super_follow;
dict->func_dict_edge_with_tile = dict_super_edge_with_tile;
dict->func_dict_getShortName = dict_getName;
} /* dict_super_init */
@ -612,6 +699,49 @@ dict_getLangName( const DictionaryCtxt* ctxt )
return ctxt->langName;
}
#ifdef XWFEATURE_DICTSANITY
XP_Bool
checkSanity( DictionaryCtxt* dict, const XP_U32 numEdges )
{
XP_U32 ii;
XP_Bool passed = XP_TRUE;
XP_U16 nFaces = dict_numTileFaces( dict );
array_edge* edge = dict->base;
Tile prevTile = 0;
for ( ii = 0; ii < numEdges && passed; ++ii ) {
Tile tile = EDGETILE( dict, edge );
if ( tile < prevTile || tile >= nFaces ) {
XP_LOGF( "%s: node %ld (out of %ld) has too-large or "
"out-of-order tile", __func__, ii, numEdges );
passed = XP_FALSE;
break;
}
prevTile = tile;
unsigned long index = dict_index_from( dict, edge );
if ( index >= numEdges ) {
XP_LOGF( "%s: node %ld (out of %ld) has too-high index %ld", __func__,
ii, numEdges, index );
passed = XP_FALSE;
break;
}
if ( IS_LAST_EDGE( dict, edge ) ) {
prevTile = 0;
}
edge += dict->nodeSize;
}
if ( passed ) {
passed = 0 == prevTile; /* last edge seen was a LAST_EDGE */
}
XP_LOGF( "%s(numEdges=%ld)=>%d", __func__, numEdges, passed );
return passed;
} /* checkSanity */
#endif
#ifdef CPLUS
}
#endif

View file

@ -66,8 +66,15 @@ typedef struct _XP_Bitmaps {
struct DictionaryCtxt {
void (*destructor)( DictionaryCtxt* dict );
array_edge* (*func_edge_for_index)( const DictionaryCtxt* dict, XP_U32 index );
array_edge* (*func_edge_for_index)( const DictionaryCtxt* dict,
XP_U32 index );
array_edge* (*func_dict_getTopEdge)( const DictionaryCtxt* dict );
unsigned long (*func_dict_index_from)( const DictionaryCtxt* dict,
array_edge* p_edge );
array_edge* (*func_dict_follow)( const DictionaryCtxt* dict,
array_edge* in );
array_edge* (*func_dict_edge_with_tile)( const DictionaryCtxt* dict,
array_edge* from, Tile tile );
const XP_UCHAR* (*func_dict_getShortName)( const DictionaryCtxt* dict );
array_edge* topEdge;
@ -128,8 +135,27 @@ struct DictionaryCtxt {
#define dict_destroy(d) (*((d)->destructor))(d)
#define dict_edge_for_index(d, i) (*((d)->func_edge_for_index))((d), (i))
#define dict_getTopEdge(d) (*((d)->func_dict_getTopEdge))(d)
#define dict_index_from(d,e) (*((d)->func_dict_index_from))(d,e)
#define dict_follow(d,e) (*((d)->func_dict_follow))(d,e)
#define dict_edge_with_tile(d,e,t) (*((d)->func_dict_edge_with_tile))(d,e,t)
#define dict_getShortName(d) (*((d)->func_dict_getShortName))(d)
#ifdef NODE_CAN_4
# define ISACCEPTING(d,e) \
((ACCEPTINGMASK_NEW & ((array_edge_old*)(e))->bits) != 0)
# define IS_LAST_EDGE(d,e) \
((LASTEDGEMASK_NEW & ((array_edge_old*)(e))->bits) != 0)
# define EDGETILE(d,edge) \
((Tile)(((array_edge_old*)(edge))->bits & \
((d)->is_4_byte?LETTERMASK_NEW_4:LETTERMASK_NEW_3)))
#else
# define ISACCEPTING(d,e) \
((ACCEPTINGMASK_OLD & ((array_edge_old*)(e))->bits) != 0)
# define IS_LAST_EDGE(d,e) \
((LASTEDGEMASK_OLD & ((array_edge_old*)(e))->bits) != 0)
# define EDGETILE(d,edge) \
((Tile)(((array_edge_old*)(edge))->bits & LETTERMASK_OLD))
#endif
XP_Bool dict_tilesAreSame( const DictionaryCtxt* dict1,
const DictionaryCtxt* dict2 );
@ -148,7 +174,8 @@ const XP_UCHAR* dict_getLangName(const DictionaryCtxt* ctxt );
XP_Bool dict_isUTF8( const DictionaryCtxt* ctxt );
Tile dict_tileForString( const DictionaryCtxt* dict, const XP_UCHAR* key );
XP_Bool dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* key,
Tile* tiles, XP_U16* nTiles );
XP_Bool dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile );
void dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile,
@ -180,6 +207,8 @@ void dict_super_init( DictionaryCtxt* ctxt );
void dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes,
XP_U16 nBytes, XP_U16 nFaces );
XP_Bool checkSanity( DictionaryCtxt* dict, XP_U32 numEdges );
#ifdef CPLUS
}
#endif

View file

@ -121,8 +121,6 @@ struct EngineCtxt {
static void findMovesOneRow( EngineCtxt* engine );
static Tile localGetBoardTile( EngineCtxt* engine, XP_U16 col,
XP_U16 row, XP_Bool substBlank );
static array_edge* edge_with_tile( const DictionaryCtxt* dict,
array_edge* from, Tile tile );
static XP_Bool scoreQualifies( EngineCtxt* engine, XP_U16 score );
static void findMovesForAnchor( EngineCtxt* engine, XP_S16* prevAnchor,
XP_U16 col, XP_U16 row ) ;
@ -130,7 +128,6 @@ static void figureCrosschecks( EngineCtxt* engine, XP_U16 col,
XP_U16 row, XP_U16* scoreP,
Crosscheck* check );
static XP_Bool isAnchorSquare( EngineCtxt* engine, XP_U16 col, XP_U16 row );
static array_edge* follow( const DictionaryCtxt* dict, array_edge* in );
static array_edge* edge_from_tile( const DictionaryCtxt* dict,
array_edge* from, Tile tile );
static void leftPart( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength,
@ -166,23 +163,6 @@ static XP_S16 cmpMoves( PossibleMove* m1, PossibleMove* m2 );
error: need to pick one!!!
#endif
#ifdef NODE_CAN_4
# define ISACCEPTING(d,e) \
((ACCEPTINGMASK_NEW & ((array_edge_old*)(e))->bits) != 0)
# define IS_LAST_EDGE(d,e) \
((LASTEDGEMASK_NEW & ((array_edge_old*)(e))->bits) != 0)
# define EDGETILE(e,edge) \
((Tile)(((array_edge_old*)(edge))->bits & \
((e)->is_4_byte?LETTERMASK_NEW_4:LETTERMASK_NEW_3)))
#else
# define ISACCEPTING(d,e) \
((ACCEPTINGMASK_OLD & ((array_edge_old*)(e))->bits) != 0)
# define IS_LAST_EDGE(d,e) \
((LASTEDGEMASK_OLD & ((array_edge_old*)(e))->bits) != 0)
# define EDGETILE(d,edge) \
((Tile)(((array_edge_old*)(edge))->bits & LETTERMASK_OLD))
#endif
/* #define CROSSCHECK_CONTAINS(chk,tile) (((chk) & (1L<<(tile))) != 0) */
#define CROSSCHECK_CONTAINS(chk,tile) checkIsSet( (chk), (tile) )
@ -608,14 +588,14 @@ lookup( const DictionaryCtxt* dict, array_edge* edge, Tile* buf,
{
while ( edge != NULL ) {
Tile targetTile = buf[tileIndex];
edge = edge_with_tile( dict, edge, targetTile );
edge = dict_edge_with_tile( dict, edge, targetTile );
if ( edge == NULL ) { /* tile not available out of this node */
return XP_FALSE;
} else {
if ( ++tileIndex == length ) { /* is this the last tile? */
return ISACCEPTING(dict, edge);
} else {
edge = follow( dict, edge );
edge = dict_follow( dict, edge );
continue;
}
}
@ -915,7 +895,7 @@ leftPart( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength,
if ( rack_remove( engine, tile, &isBlank ) ) {
tiles[tileLength] = tile;
leftPart( engine, tiles, tileLength+1,
follow( engine->dict, edge ),
dict_follow( engine->dict, edge ),
limit-1, firstCol-1, anchorCol, row );
rack_replace( engine, tile, isBlank );
}
@ -1000,9 +980,9 @@ extendRight( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength,
}
}
} else if ( (edge = edge_with_tile( dict, edge, tile ) ) != NULL ) {
} else if ( (edge = dict_edge_with_tile( dict, edge, tile ) ) != NULL ) {
accepting = ISACCEPTING( dict, edge );
extendRight( engine, tiles, tileLength, follow(dict, edge),
extendRight( engine, tiles, tileLength, dict_follow(dict, edge),
accepting, firstCol, col+1, row );
goto no_check; /* don't do the check at the end */
} else {
@ -1352,73 +1332,12 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score )
return qualifies;
} /* scoreQualifies */
static array_edge*
edge_with_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile )
{
for ( ; ; ) {
Tile candidate = EDGETILE(dict,from);
if ( candidate == tile ) {
break;
}
if ( IS_LAST_EDGE(dict, from ) ) {
from = NULL;
break;
}
#ifdef NODE_CAN_4
from += dict->nodeSize;
#else
from += 3;
#endif
}
return from;
} /* edge_with_tile */
static unsigned long
index_from( const DictionaryCtxt* dict, array_edge* p_edge )
{
unsigned long result;
#ifdef NODE_CAN_4
array_edge_new* edge = (array_edge_new*)p_edge;
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
if ( dict->is_4_byte ) {
result |= ((XP_U32)edge->moreBits) << 16;
} else {
XP_ASSERT( dict->nodeSize == 3 );
if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) {
result |= 0x00010000; /* using | instead of + saves 4 bytes */
}
}
#else
array_edge_old* edge = (array_edge_old*)p_edge;
result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF;
if ( (edge->bits & EXTRABITMASK_OLD) != 0 ) {
result |= 0x00010000; /* using | instead of + saves 4 bytes */
}
#endif
return result;
} /* index_from */
static array_edge*
follow( const DictionaryCtxt* dict, array_edge* in )
{
XP_U32 index = index_from( dict, in );
array_edge* result = index > 0?
dict_edge_for_index( dict, index ): (array_edge*)NULL;
return result;
} /* follow */
static array_edge*
edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile )
{
array_edge* edge = edge_with_tile( dict, from, tile );
array_edge* edge = dict_edge_with_tile( dict, from, tile );
if ( edge != NULL ) {
edge = follow( dict, edge );
edge = dict_follow( dict, edge );
}
return edge;
} /* edge_from_tile */

View file

@ -1,4 +1,4 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2001-2009 by Eric House (xwords@eehouse.org). All rights
* reserved.
@ -206,6 +206,7 @@ mpool_realloc( MemPoolCtx* mpool, void* ptr, XP_U32 newsize, const char* file,
entry->fileName = file;
entry->func = func;
entry->lineNo = lineNo;
entry->size = newsize;
}
return entry->ptr;
} /* mpool_realloc */

View file

@ -835,7 +835,7 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
stream_putBits( stream, NUMCOLS_NBITS, row );
stream_putBits( stream, 1, isBlank );
}
} /* model_turnToStream */
} /* model_currentMoveToStream */
/* Take stream as the source of info about what tiles to move from tray to
* board. Undo any current move first -- a player on this device might be
@ -877,15 +877,15 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
foundAt = model_trayContains( model, playerNum, moveTile );
if ( foundAt == -1 ) {
XP_ASSERT( EMPTY_TILE==model_getPlayerTile(model, playerNum, 0));
XP_ASSERT( EMPTY_TILE == model_getPlayerTile(model, playerNum, 0));
(void)model_removePlayerTile( model, playerNum, -1 );
model_addPlayerTile( model, playerNum, -1, moveTile );
}
model_moveTrayToBoard( model, playerNum, col, row, foundAt, tileFace);
model_moveTrayToBoard( model, playerNum, col, row, foundAt, tileFace );
}
} /* model_makeMoveFromStream */
} /* model_makeTurnFromStream */
void
model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
@ -1095,17 +1095,11 @@ askBlankTile( ModelCtxt* model, XP_U16 turn )
XP_S16 chosen;
const XP_UCHAR* tfaces[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES];
PickInfo pi;
pi.why = PICK_FOR_BLANK;
pi.nTotal = 1;
pi.thisPick = 1;
model_packTilesUtil( model, NULL, XP_FALSE,
&nUsed, tfaces, tiles );
chosen = util_userPickTile( model->vol.util, &pi,
turn, tfaces, nUsed );
chosen = util_userPickTileBlank( model->vol.util, turn, tfaces, nUsed );
if ( chosen < 0 ) {
chosen = 0;
@ -1501,24 +1495,25 @@ static void
makeTileTrade( ModelCtxt* model, XP_S16 player, const TrayTileSet* oldTiles,
const TrayTileSet* newTiles )
{
XP_U16 i;
XP_U16 ii;
XP_U16 nTiles;
XP_ASSERT( newTiles->nTiles == oldTiles->nTiles );
XP_ASSERT( oldTiles != &model->players[player].trayTiles );
for ( nTiles = newTiles->nTiles, i = 0; i < nTiles; ++i ) {
Tile oldTile = oldTiles->tiles[i];
for ( nTiles = newTiles->nTiles, ii = 0; ii < nTiles; ++ii ) {
Tile oldTile = oldTiles->tiles[ii];
XP_S16 tileIndex = model_trayContains( model, player, oldTile );
XP_ASSERT( tileIndex >= 0 );
model_removePlayerTile( model, player, tileIndex );
model_addPlayerTile( model, player, tileIndex, newTiles->tiles[i] );
model_addPlayerTile( model, player, tileIndex, newTiles->tiles[ii] );
}
} /* makeTileTrade */
void
model_makeTileTrade( ModelCtxt* model, XP_S16 player,
TrayTileSet* oldTiles, TrayTileSet* newTiles )
const TrayTileSet* oldTiles, const TrayTileSet* newTiles )
{
stack_addTrade( model->vol.stack, player, oldTiles, newTiles );
@ -1526,9 +1521,9 @@ model_makeTileTrade( ModelCtxt* model, XP_S16 player,
} /* model_makeTileTrade */
Tile
model_getPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index )
model_getPlayerTile( const ModelCtxt* model, XP_S16 turn, XP_S16 index )
{
PlayerCtxt* player;
const PlayerCtxt* player;
XP_ASSERT( turn >= 0 );
player = &model->players[turn];
@ -1551,8 +1546,23 @@ model_getPlayerTiles( const ModelCtxt* model, XP_S16 turn )
return (const TrayTileSet*)&player->trayTiles;
} /* model_getPlayerTile */
#ifdef DEBUG
XP_UCHAR*
formatTileSet( const TrayTileSet* tiles, XP_UCHAR* buf, XP_U16 len )
{
XP_U16 ii, used;
for ( ii = 0, used = 0; ii < tiles->nTiles && used < len; ++ii ) {
used += XP_SNPRINTF( &buf[used], len - used, "%d,", tiles->tiles[ii] );
}
if ( used > len ) {
buf[len-1] = '\0';
}
return buf;
}
#endif
static void
addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index, Tile tile )
addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index, const Tile tile )
{
PlayerCtxt* player;
short ii;
@ -1901,7 +1911,7 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
static void
copyStack( ModelCtxt* model, StackCtxt* destStack, const StackCtxt* srcStack )
{
XWStreamCtxt* stream = mem_stream_make( MPPARM(model->vol.mpool)
XWStreamCtxt* stream = mem_stream_make( MPPARM(model->vol.mpool)
util_getVTManager(model->vol.util),
NULL, 0, NULL );
@ -1958,7 +1968,8 @@ typedef struct _FirstWordData {
static XP_Bool
getFirstWord( const XP_UCHAR* word, XP_Bool isLegal,
#ifdef XWFEATURE_BOARDWORDS
const MoveInfo* XP_UNUSED(movei), XP_U16 XP_UNUSED(start), XP_U16 XP_UNUSED(end),
const MoveInfo* XP_UNUSED(movei), XP_U16 XP_UNUSED(start),
XP_U16 XP_UNUSED(end),
#endif
void* closure )
{
@ -1990,10 +2001,10 @@ scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany,
ModelCtxt* tmpModel = makeTmpModel( model, NULL, NULL, NULL, NULL );
XP_U16 turn;
XP_S16 moveNum = -1;
copyStack( model, tmpModel->vol.stack, model->vol.stack );
if ( !model_undoLatestMoves( tmpModel, NULL, howMany, &turn,
if ( !model_undoLatestMoves( tmpModel, NULL, howMany, &turn,
&moveNum ) ) {
XP_ASSERT( 0 );
}
@ -2001,7 +2012,7 @@ scoreLastMove( ModelCtxt* model, MoveInfo* moveInfo, XP_U16 howMany,
data.word[0] = '\0';
notifyInfo.proc = getFirstWord;
notifyInfo.closure = &data;
score = figureMoveScore( tmpModel, turn, moveInfo, (EngineCtxt*)NULL,
score = figureMoveScore( tmpModel, turn, moveInfo, (EngineCtxt*)NULL,
(XWStreamCtxt*)NULL, &notifyInfo );
model_destroy( tmpModel );

View file

@ -29,16 +29,12 @@
extern "C" {
#endif
#define MAX_ROWS 16
#define MAX_COLS 16
#define NUMCOLS_NBITS 4
#ifdef EIGHT_TILES
#define MAX_TRAY_TILES 8
#define NTILES_NBITS 4
# define NTILES_NBITS 4
#else
#define MAX_TRAY_TILES 7
#define NTILES_NBITS 3
# define NTILES_NBITS 3
#endif
/* Try making this 0, as some local rules, e.g. Spanish, allow. Will need to
@ -137,7 +133,7 @@ XP_U16 model_getCellOwner( ModelCtxt* model, XP_U16 col, XP_U16 row );
void model_assignPlayerTiles( ModelCtxt* model, XP_S16 turn,
const TrayTileSet* tiles );
Tile model_getPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
Tile model_getPlayerTile( const ModelCtxt* model, XP_S16 turn, XP_S16 index );
Tile model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index,
@ -149,6 +145,10 @@ void model_moveTileOnTray( ModelCtxt* model, XP_S16 turn, XP_S16 indexCur,
player. Don't even think about modifying the array!!!! */
const TrayTileSet* model_getPlayerTiles( const ModelCtxt* model, XP_S16 turn );
#ifdef DEBUG
XP_UCHAR* formatTileSet( const TrayTileSet* tiles, XP_UCHAR* buf, XP_U16 len );
#endif
void model_sortTiles( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn );
@ -182,7 +182,8 @@ void 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,
TrayTileSet* oldTiles, TrayTileSet* newTiles );
const TrayTileSet* oldTiles,
const TrayTileSet* newTiles );
XP_Bool model_undoLatestMoves( ModelCtxt* model, PoolContext* pool,
XP_U16 nMovesSought, XP_U16* turn,

View file

@ -295,7 +295,7 @@ stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo )
void
stack_addTrade( StackCtxt* stack, XP_U16 turn,
TrayTileSet* oldTiles, TrayTileSet* newTiles )
const TrayTileSet* oldTiles, const TrayTileSet* newTiles )
{
StackEntry move;

View file

@ -79,7 +79,8 @@ void stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo,
const TrayTileSet* newTiles );
void stack_addPhony( StackCtxt* stack, XP_U16 turn, MoveInfo* moveInfo );
void stack_addTrade( StackCtxt* stack, XP_U16 turn,
TrayTileSet* oldTiles, TrayTileSet* newTiles );
const TrayTileSet* oldTiles,
const TrayTileSet* newTiles );
void stack_addAssign( StackCtxt* stack, XP_U16 turn,
const TrayTileSet* tiles );

View file

@ -1,6 +1,7 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* -*- compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */
/*
* Copyright 1998-2001 by Eric House (xwords@eehouse.org). All rights reserved.
* Copyright 1998 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License

View file

@ -171,10 +171,10 @@ pool_requestTiles( PoolContext* pool, Tile* tiles, XP_U8* maxNum )
} /* pool_requestTiles */
void
pool_replaceTiles( PoolContext* pool, TrayTileSet* tiles )
pool_replaceTiles( PoolContext* pool, const TrayTileSet* tiles )
{
XP_U16 nTiles = tiles->nTiles;
Tile* tilesP = tiles->tiles;
const Tile* tilesP = tiles->tiles;
while ( nTiles-- ) {
Tile tile = *tilesP++; /* do I need to filter off high bits? */
@ -188,10 +188,10 @@ pool_replaceTiles( PoolContext* pool, TrayTileSet* tiles )
} /* pool_replaceTiles */
void
pool_removeTiles( PoolContext* pool, TrayTileSet* tiles )
pool_removeTiles( PoolContext* pool, const TrayTileSet* tiles )
{
XP_U16 nTiles = tiles->nTiles;
Tile* tilesP = tiles->tiles;
const Tile* tilesP = tiles->tiles;
XP_ASSERT( nTiles <= MAX_TRAY_TILES );

View file

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

View file

@ -727,7 +727,9 @@ makeRobotMove( ServerCtxt* server )
/* trade if unable to find a move */
if ( trade ) {
result = server_commitTrade( server, ALLTILES );
TrayTileSet oldTiles = *model_getPlayerTiles( model, turn );
XP_LOGF( "%s: robot trading %d tiles", __func__, oldTiles.nTiles );
result = server_commitTrade( server, &oldTiles );
/* Quick hack to fix gremlin bug where all-robot game seen none
able to trade for tiles to move and blowing the undo stack.
@ -748,6 +750,7 @@ makeRobotMove( ServerCtxt* server )
if ( canMove || NPASSES_OK(server) ) {
model_makeTurnFromMoveInfo( model, turn, &newMove );
XP_LOGF( "%s: robot making %d tile move", __func__, newMove.nTiles );
if ( !!stream ) {
XWStreamCtxt* wordsStream = mkServerStream( server );
@ -1552,7 +1555,6 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
oneTile.nTiles = 1;
pi.why = PICK_FOR_CHEAT;
pi.nTotal = nToFetch;
pi.thisPick = 0;
pi.curTiles = curTray;
@ -1570,8 +1572,8 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
model_packTilesUtil( server->vol.model, pool,
XP_TRUE, &nUsed, texts, tiles );
chosen = util_userPickTile( server->vol.util, &pi, playerNum,
texts, nUsed );
chosen = util_userPickTileTray( server->vol.util, &pi, playerNum,
texts, nUsed );
if ( chosen == PICKER_PICKALL ) {
ask = XP_FALSE;
@ -1778,7 +1780,7 @@ checkMoveAllowed( ServerCtxt* server, XP_U16 playerNum )
static void
sendMoveTo( ServerCtxt* server, XP_U16 devIndex, XP_U16 turn,
XP_Bool legal, TrayTileSet* newTiles,
TrayTileSet* tradedTiles ) /* null if a move, set if a trade */
const TrayTileSet* tradedTiles ) /* null if a move, set if a trade */
{
XWStreamCtxt* stream;
XP_Bool isTrade = !!tradedTiles;
@ -1835,6 +1837,7 @@ readMoveInfo( ServerCtxt* server, XWStreamCtxt* stream,
if ( isTrade ) {
traySetFromStream( stream, tradedTiles );
XP_LOGF( "%s: got trade of %d tiles", __func__, tradedTiles->nTiles );
} else {
legalMove = stream_getBits( stream, 1 );
model_makeTurnFromStream( server->vol.model, whoMoved, stream );
@ -1851,7 +1854,7 @@ readMoveInfo( ServerCtxt* server, XWStreamCtxt* stream,
static void
sendMoveToClientsExcept( ServerCtxt* server, XP_U16 whoMoved, XP_Bool legal,
TrayTileSet* newTiles, TrayTileSet* tradedTiles,
TrayTileSet* newTiles, const TrayTileSet* tradedTiles,
XP_U16 skip )
{
XP_U16 devIndex;
@ -2123,49 +2126,27 @@ server_commitMove( ServerCtxt* server )
return XP_TRUE;
} /* server_commitMove */
static void
removeTradedTiles( ServerCtxt* server, TileBit selBits, TrayTileSet* tiles )
{
XP_U8 nTiles = 0;
XP_S16 index;
XP_S16 turn = server->nv.currentTurn;
/* selBits: It's gross that server knows this much about tray's
implementation. PENDING(ehouse) */
for ( index = 0; selBits != 0; selBits >>= 1, ++index ) {
if ( (selBits & 0x01) != 0 ) {
Tile tile = model_getPlayerTile( server->vol.model, turn, index );
tiles->tiles[nTiles++] = tile;
}
}
tiles->nTiles = nTiles;
} /* saveTradedTiles */
XP_Bool
server_commitTrade( ServerCtxt* server, TileBit selBits )
server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles )
{
TrayTileSet oldTiles;
TrayTileSet newTiles;
XP_U16 turn = server->nv.currentTurn;
removeTradedTiles( server, selBits, &oldTiles );
fetchTiles( server, turn, oldTiles.nTiles, &oldTiles, &newTiles );
fetchTiles( server, turn, oldTiles->nTiles, oldTiles, &newTiles );
#ifndef XWFEATURE_STANDALONE_ONLY
if ( server->vol.gi->serverRole == SERVER_ISCLIENT ) {
/* just send to server */
sendMoveTo(server, SERVER_DEVICE, turn, XP_TRUE, &newTiles, &oldTiles);
sendMoveTo(server, SERVER_DEVICE, turn, XP_TRUE, &newTiles, oldTiles);
} else {
sendMoveToClientsExcept( server, turn, XP_TRUE, &newTiles, &oldTiles,
sendMoveToClientsExcept( server, turn, XP_TRUE, &newTiles, oldTiles,
SERVER_DEVICE );
}
#endif
pool_replaceTiles( server->pool, &oldTiles );
model_makeTileTrade( server->vol.model, server->nv.currentTurn,
&oldTiles, &newTiles );
pool_replaceTiles( server->pool, oldTiles );
XP_ASSERT( turn == server->nv.currentTurn );
model_makeTileTrade( server->vol.model, turn, oldTiles, &newTiles );
sortTilesIf( server, turn );
nextTurn( server, PICK_NEXT );
@ -2527,6 +2508,7 @@ server_receiveMessage( ServerCtxt* server, XWStreamCtxt* incoming )
break;
case XWPROTO_MOVEMADE_INFO_SERVER: /* server telling me about a move */
XP_ASSERT( SERVER_ISCLIENT == server->vol.gi->serverRole );
accepted = reflectMove( server, incoming );
if ( accepted ) {
nextTurn( server, PICK_NEXT );

View file

@ -101,7 +101,7 @@ XP_S16 server_countTilesInPool( ServerCtxt* server );
XP_Bool server_do( ServerCtxt* server, XP_Bool* moreToDoP );
XP_Bool server_commitMove( ServerCtxt* server );
XP_Bool server_commitTrade( ServerCtxt* server, TileBit bits );
XP_Bool server_commitTrade( ServerCtxt* server, const TrayTileSet* oldTiles );
/* call this when user wants to end the game */
void server_endGame( ServerCtxt* server );

View file

@ -379,6 +379,24 @@ handleTrayDuringTrade( BoardCtxt* board, XP_S16 index )
return XP_TRUE;
} /* handleTrayDuringTrade */
void
getSelTiles( const BoardCtxt* board, TileBit selBits, TrayTileSet* selTiles )
{
XP_U16 nTiles = 0;
XP_S16 index;
XP_S16 turn = board->selPlayer;
const ModelCtxt* model = board->model;
for ( index = 0; selBits != 0; selBits >>= 1, ++index ) {
if ( 0 != (selBits & 0x01) ) {
Tile tile = model_getPlayerTile( model, turn, index );
XP_ASSERT( nTiles < VSIZE(selTiles->tiles) );
selTiles->tiles[nTiles++] = tile;
}
}
selTiles->nTiles = nTiles;
}
static XP_Bool
handleActionInTray( BoardCtxt* board, XP_S16 index, XP_Bool onDivider )
{

View file

@ -65,17 +65,11 @@ typedef enum {
typedef enum {
QUERY_COMMIT_TURN, /* 0 means cancel; 1 means commit */
QUERY_COMMIT_TRADE,
QUERY_ROBOT_TRADE,
QUERY_LAST_COMMON
} UtilQueryID;
typedef enum {
PICK_FOR_BLANK
, PICK_FOR_CHEAT
} PICK_WHY;
#define PICKER_PICKALL -1
#define PICKER_BACKUP -2
@ -84,7 +78,6 @@ typedef struct PickInfo {
XP_U16 nCurTiles;
XP_U16 nTotal; /* count to fetch for turn, <= MAX_TRAY_TILES */
XP_U16 thisPick; /* <= nTotal */
PICK_WHY why;
} PickInfo;
typedef struct BadWordInfo {
@ -113,11 +106,15 @@ typedef struct UtilVtable {
XP_Bool (*m_util_userQuery)( XW_UtilCtxt* uc, UtilQueryID id,
XWStreamCtxt* stream );
XP_Bool (*m_util_confirmTrade)( XW_UtilCtxt* uc, const XP_UCHAR** tiles,
XP_U16 nTiles );
/* return of < 0 means computer should pick */
XP_S16 (*m_util_userPickTile)( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles );
XP_S16 (*m_util_userPickTileBlank)( XW_UtilCtxt* uc, XP_U16 playerNum,
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 );
XP_Bool (*m_util_askPassword)( XW_UtilCtxt* uc, const XP_UCHAR* name,
XP_UCHAR* buf, XP_U16* len );
@ -213,8 +210,13 @@ struct XW_UtilCtxt {
#define util_userQuery(uc,qcode,str) \
(uc)->vtable->m_util_userQuery((uc),(qcode),(str))
#define util_userPickTile( uc, w, n, tx, nt ) \
(uc)->vtable->m_util_userPickTile( (uc), (w), (n), (tx), (nt) )
#define util_confirmTrade( uc, tx, nt ) \
(uc)->vtable->m_util_confirmTrade((uc),(tx),(nt))
#define util_userPickTileBlank( uc, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileBlank( (uc), (n), (tx), (nt) )
#define util_userPickTileTray( uc, w, n, tx, nt ) \
(uc)->vtable->m_util_userPickTileTray( (uc), (w), (n), (tx), (nt) )
#define util_askPassword( uc, n, b, lp ) \
(uc)->vtable->m_util_askPassword( (uc), (n), (b), (lp) )

View file

@ -91,6 +91,8 @@ DEFINES += -DXWFEATURE_CHAT
DEFINES += -DDISABLE_TILE_SEL
DEFINES += -DSET_GAMESEED
DEFINES += -DTEXT_MODEL
DEFINES += -DXWFEATURE_WALKDICT
DEFINES += -DXWFEATURE_DICTSANITY
ifdef CURSES_CELL_HT
DEFINES += -DCURSES_CELL_HT=$(CURSES_CELL_HT)

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000-2011 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -229,9 +229,26 @@ cursesUserError( CursesAppGlobals* globals, const char* format, ... )
} /* cursesUserError */
static XP_S16
curses_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* XP_UNUSED(pi),
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
curses_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128];
XP_S16 index;
char* playerName = globals->cGlobals.params->gi.players[playerNum].name;
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;
} /* 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 )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char query[128];
@ -278,11 +295,6 @@ curses_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_COMMIT_TRADE:
question = "Commit trade?";
answers[numAnswers++] = "Cancel";
answers[numAnswers++] = "Ok";
break;
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
@ -305,6 +317,16 @@ curses_util_userQuery( XW_UtilCtxt* uc, UtilQueryID id, XWStreamCtxt* stream )
return result;
} /* curses_util_userQuery */
static XP_Bool
curses_util_confirmTrade( XW_UtilCtxt* uc, const XP_UCHAR** tiles,
XP_U16 nTiles )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
char question[256];
formatConfirmTrade( tiles, nTiles, question, sizeof(question) );
return 1 == cursesask( globals, question, 2, "Cancel", "Ok" );
}
static void
curses_util_trayHiddenChange( XW_UtilCtxt* XP_UNUSED(uc),
XW_TrayVisState XP_UNUSED(state),
@ -427,7 +449,6 @@ curses_util_clearTimer( XW_UtilCtxt* uc, XWTimerReason why )
static gboolean
onetime_idle( gpointer data )
{
LOG_FUNC();
CursesAppGlobals* globals = (CursesAppGlobals*)data;
if ( server_do( globals->cGlobals.game.server, NULL ) ) {
if ( !!globals->cGlobals.game.board ) {
@ -443,7 +464,12 @@ curses_util_requestTime( XW_UtilCtxt* uc )
{
CursesAppGlobals* globals = (CursesAppGlobals*)uc->closure;
#ifdef USE_GLIBLOOP
# if 0
(void)g_idle_add( onetime_idle, globals );
# else
(void)g_timeout_add( 1,// interval,
onetime_idle, globals );
# endif
#else
/* I've created a pipe whose read-only end is plugged into the array of
fds that my event loop polls so that I can write to it to simulate
@ -1040,7 +1066,7 @@ data_socket_proc( GIOChannel* source, GIOCondition condition, gpointer data )
}
}
return TRUE;
}
} /* data_socket_proc */
#endif
static void
@ -1473,7 +1499,9 @@ setupCursesUtilCallbacks( CursesAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_makeStreamFromAddr = curses_util_makeStreamFromAddr;
#endif
util->vtable->m_util_userQuery = curses_util_userQuery;
util->vtable->m_util_userPickTile = curses_util_userPickTile;
util->vtable->m_util_confirmTrade = curses_util_confirmTrade;
util->vtable->m_util_userPickTileBlank = curses_util_userPickTileBlank;
util->vtable->m_util_userPickTileTray = curses_util_userPickTileTray;
util->vtable->m_util_trayHiddenChange = curses_util_trayHiddenChange;
util->vtable->m_util_informMove = curses_util_informMove;
util->vtable->m_util_notifyGameOver = curses_util_notifyGameOver;

View file

@ -1,4 +1,4 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
/* -*- compile-command: "make -j3 MEMDEBUG=TRUE"; -*- */
/*
* Copyright 2000-2009 by Eric House (xwords@eehouse.org). All rights
* reserved.
@ -24,11 +24,10 @@
#include "gtkask.h"
static void
button_event( GtkWidget* XP_UNUSED(widget), gpointer closure )
set_bool_and_quit( GtkWidget* XP_UNUSED(widget), gpointer closure )
{
XP_Bool* whichSet = (XP_Bool*)closure;
*whichSet = 1;
*whichSet = XP_TRUE;
gtk_main_quit();
} /* button_event */
@ -43,7 +42,7 @@ abort_button_event( GtkWidget* XP_UNUSED(widget), gpointer XP_UNUSED(closure) )
#define BUTTONS_PER_ROW 13
XP_S16
gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
gtkletterask( const PickInfo* pi, XP_Bool forTray, const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts )
{
GtkWidget* dialog;
@ -55,8 +54,9 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
XP_S16 ii;
GtkWidget* button;
XP_UCHAR buf[64];
XP_Bool backedUp = XP_FALSE;
XP_MEMSET( results, 0, sizeof(results) );
XP_MEMSET( results, XP_FALSE, sizeof(results) );
vbox = gtk_vbox_new( FALSE, 0 );
@ -69,7 +69,7 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
g_signal_connect( GTK_OBJECT(button), "clicked",
G_CALLBACK(button_event), &results[ii] );
G_CALLBACK(set_bool_and_quit), &results[ii] );
gtk_widget_show( button );
if ( ii+1 == nTiles || (ii % BUTTONS_PER_ROW == 0) ) {
@ -79,12 +79,20 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
}
#ifdef FEATURE_TRAY_EDIT
button = gtk_button_new_with_label( "Just pick em!" );
hbox = gtk_hbox_new( FALSE, 0 );
button = gtk_button_new_with_label( "Just pick em!" );
g_signal_connect( GTK_OBJECT(button), "clicked",
G_CALLBACK(abort_button_event), NULL );
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
gtk_widget_show( button );
button = gtk_button_new_with_label( "Back up" );
g_signal_connect( GTK_OBJECT(button), "clicked",
G_CALLBACK(set_bool_and_quit), &backedUp );
gtk_box_pack_start( GTK_BOX(hbox), button, FALSE, TRUE, 0 );
gtk_widget_show( button );
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
#endif
@ -95,19 +103,18 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
XP_Bool forBlank = PICK_FOR_BLANK == pi->why;
if ( forBlank ) {
txt = "Choose a letter for your blank.";
} else {
if ( forTray ) {
char* fmt = "Choose a tile for %s.";
XP_SNPRINTF( buf, sizeof(buf), fmt, name );
txt = buf;
} else {
txt = "Choose a letter for your blank.";
}
label = gtk_label_new( txt );
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox),
label);
if ( !forBlank ) {
if ( forTray ) {
char curTilesBuf[64];
int len = snprintf( curTilesBuf, sizeof(curTilesBuf), "%s",
"Tiles so far: " );
@ -128,13 +135,17 @@ gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
gtk_widget_destroy( dialog );
for ( ii = 0; ii < nTiles; ++ii ) {
if ( results[ii] ) {
break;
if ( backedUp ) {
ii = PICKER_BACKUP;
} else {
for ( ii = 0; ii < nTiles; ++ii ) {
if ( results[ii] ) {
break;
}
}
if ( ii == nTiles ) {
ii = PICKER_PICKALL;
}
}
if ( ii == nTiles ) {
ii = -1;
}
return ii;

View file

@ -26,7 +26,8 @@
#include "gtkmain.h"
XP_S16 gtkletterask( const PickInfo* pi, const XP_UCHAR* name,
XP_S16 gtkletterask( const PickInfo* pi, XP_Bool forTray,
const XP_UCHAR* name,
XP_U16 nTiles, const XP_UCHAR** texts );

View file

@ -1300,15 +1300,27 @@ gtk_util_getVTManager(XW_UtilCtxt* uc)
} /* linux_util_getVTManager */
static XP_S16
gtk_util_userPickTile( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
gtk_util_userPickTileBlank( XW_UtilCtxt* uc, XP_U16 playerNum,
const XP_UCHAR** texts, XP_U16 nTiles )
{
XP_S16 chosen;
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
XP_UCHAR* name = globals->cGlobals.params->gi.players[playerNum].name;
chosen = gtkletterask( pi, name, nTiles, texts );
chosen = gtkletterask( NULL, XP_FALSE, name, nTiles, texts );
return chosen;
}
static XP_S16
gtk_util_userPickTileTray( XW_UtilCtxt* uc, const PickInfo* pi,
XP_U16 playerNum, const XP_UCHAR** texts,
XP_U16 nTiles )
{
XP_S16 chosen;
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
XP_UCHAR* name = globals->cGlobals.params->gi.players[playerNum].name;
chosen = gtkletterask( pi, XP_TRUE, name, nTiles, texts );
return chosen;
} /* gtk_util_userPickTile */
@ -1761,9 +1773,6 @@ gtk_util_userQuery( XW_UtilCtxt* XP_UNUSED(uc), UtilQueryID id,
question = strFromStream( stream );
freeMe = XP_TRUE;
break;
case QUERY_COMMIT_TRADE:
question = "Are you sure you want to trade the selected tiles?";
break;
case QUERY_ROBOT_TRADE:
question = strFromStream( stream );
freeMe = XP_TRUE;
@ -1784,6 +1793,15 @@ gtk_util_userQuery( XW_UtilCtxt* XP_UNUSED(uc), UtilQueryID id,
return result;
} /* gtk_util_userQuery */
static XP_Bool
gtk_util_confirmTrade( XW_UtilCtxt* XP_UNUSED(uc),
const XP_UCHAR** tiles, XP_U16 nTiles )
{
char question[256];
formatConfirmTrade( tiles, nTiles, question, sizeof(question) );
return gtkask( question, GTK_BUTTONS_YES_NO );
}
static GtkWidget*
makeShowButtonFromBitmap( void* closure, const gchar* filename,
const gchar* alt, GCallback func )
@ -1922,8 +1940,10 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
{
util->vtable->m_util_userError = gtk_util_userError;
util->vtable->m_util_userQuery = gtk_util_userQuery;
util->vtable->m_util_confirmTrade = gtk_util_confirmTrade;
util->vtable->m_util_getVTManager = gtk_util_getVTManager;
util->vtable->m_util_userPickTile = gtk_util_userPickTile;
util->vtable->m_util_userPickTileBlank = gtk_util_userPickTileBlank;
util->vtable->m_util_userPickTileTray = gtk_util_userPickTileTray;
util->vtable->m_util_askPassword = gtk_util_askPassword;
util->vtable->m_util_trayHiddenChange = gtk_util_trayHiddenChange;
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 1997-2009 by Eric House (xwords@eehouse.org). All rights
* Copyright 1997 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -219,23 +219,25 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const char* fileName )
const XP_U8* ptr;
struct stat statbuf;
if ( 0 != stat( fileName, &statbuf ) ) {
if ( 0 != stat( fileName, &statbuf ) || 0 == statbuf.st_size ) {
goto closeAndExit;
}
dctx->dictLength = statbuf.st_size;
{
FILE* dictF = fopen( fileName, "r" );
XP_ASSERT( !!dictF );
if ( dctx->useMMap ) {
dctx->dictBase = mmap( NULL, dctx->dictLength, PROT_READ, MAP_PRIVATE, fileno(dictF), 0 );
} else {
dctx->dictBase = XP_MALLOC( dctx->super.mpool, dctx->dictLength );
if ( dctx->dictLength != fread( dctx->dictBase, 1, dctx->dictLength, dictF ) ) {
XP_ASSERT( 0 );
}
}
fclose( dictF );
FILE* dictF = fopen( fileName, "r" );
XP_ASSERT( !!dictF );
if ( dctx->useMMap ) {
dctx->dictBase = mmap( NULL, dctx->dictLength, PROT_READ,
MAP_PRIVATE, fileno(dictF), 0 );
} else {
dctx->dictBase = XP_MALLOC( dctx->super.mpool, dctx->dictLength );
if ( dctx->dictLength != fread( dctx->dictBase, 1,
dctx->dictLength, dictF ) ) {
XP_ASSERT( 0 );
}
}
fclose( dictF );
}
ptr = dctx->dictBase;
@ -247,65 +249,65 @@ initFromDictFile( LinuxDictionaryCtxt* dctx, const char* fileName )
XP_DEBUGF( "flags=0X%X", flags );
hasHeader = 0 != (DICT_HEADER_MASK & flags);
if ( hasHeader ) {
flags &= ~DICT_HEADER_MASK;
XP_DEBUGF( "has header!" );
flags &= ~DICT_HEADER_MASK;
XP_DEBUGF( "has header!" );
}
#ifdef NODE_CAN_4
if ( flags == 0x0001 ) {
dctx->super.nodeSize = 3;
charSize = 1;
dctx->super.is_4_byte = XP_FALSE;
dctx->super.nodeSize = 3;
charSize = 1;
dctx->super.is_4_byte = XP_FALSE;
} else if ( flags == 0x0002 ) {
dctx->super.nodeSize = 3;
charSize = 2;
dctx->super.is_4_byte = XP_FALSE;
dctx->super.nodeSize = 3;
charSize = 2;
dctx->super.is_4_byte = XP_FALSE;
} else if ( flags == 0x0003 ) {
dctx->super.nodeSize = 4;
charSize = 2;
dctx->super.is_4_byte = XP_TRUE;
dctx->super.nodeSize = 4;
charSize = 2;
dctx->super.is_4_byte = XP_TRUE;
} else if ( flags == 0x0004 ) {
dctx->super.nodeSize = 3;
dctx->super.isUTF8 = XP_TRUE;
isUTF8 = XP_TRUE;
dctx->super.is_4_byte = XP_FALSE;
dctx->super.nodeSize = 3;
dctx->super.isUTF8 = XP_TRUE;
isUTF8 = XP_TRUE;
dctx->super.is_4_byte = XP_FALSE;
} else if ( flags == 0x0005 ) {
dctx->super.nodeSize = 4;
dctx->super.isUTF8 = XP_TRUE;
isUTF8 = XP_TRUE;
dctx->super.is_4_byte = XP_TRUE;
dctx->super.nodeSize = 4;
dctx->super.isUTF8 = XP_TRUE;
isUTF8 = XP_TRUE;
dctx->super.is_4_byte = XP_TRUE;
} else {
/* case I don't know how to deal with */
formatOk = XP_FALSE;
XP_ASSERT(0);
/* case I don't know how to deal with */
formatOk = XP_FALSE;
XP_ASSERT(0);
}
#else
XP_ASSERT( flags == 0x0001 );
XP_ASSERT( flags == 0x0001 );
#endif
if ( formatOk ) {
XP_U8 numFaceBytes, numFaces;
XP_U8 numFaceBytes, numFaces;
if ( hasHeader ) {
XP_U16 headerLen;
XP_U32 wordCount;
memcpy( &headerLen, ptr, sizeof(headerLen) );
ptr += sizeof(headerLen);
memcpy( &headerLen, ptr, sizeof(headerLen) );
ptr += sizeof(headerLen);
headerLen = ntohs( headerLen );
if ( headerLen != sizeof(wordCount) ) { /* the only case we know right now */
goto closeAndExit;
}
memcpy( &wordCount, ptr, sizeof(wordCount) );
ptr += sizeof(wordCount);
memcpy( &wordCount, ptr, sizeof(wordCount) );
ptr += sizeof(wordCount);
dctx->super.nWords = ntohl( wordCount );
XP_DEBUGF( "dict contains %ld words", dctx->super.nWords );
}
if ( isUTF8 ) {
numFaceBytes = *ptr++;
numFaceBytes = *ptr++;
}
numFaces = *ptr++;
numFaces = *ptr++;
if ( !isUTF8 ) {
numFaceBytes = numFaces * charSize;
}
@ -320,42 +322,47 @@ XP_ASSERT( flags == 0x0001 );
}
XP_U8 tmp[numFaceBytes];
memcpy( tmp, ptr, numFaceBytes );
ptr += numFaceBytes;
memcpy( tmp, ptr, numFaceBytes );
ptr += numFaceBytes;
dict_splitFaces( &dctx->super, tmp, numFaceBytes, numFaces );
memcpy( &xloc, ptr, sizeof(xloc) );
ptr += sizeof(xloc);
memcpy( dctx->super.countsAndValues, ptr, numFaces*2 );
ptr += numFaces*2;
memcpy( &xloc, ptr, sizeof(xloc) );
ptr += sizeof(xloc);
memcpy( dctx->super.countsAndValues, ptr, numFaces*2 );
ptr += numFaces*2;
}
dctx->super.langCode = xloc & 0x7F;
if ( formatOk ) {
XP_U32 numEdges;
skipBitmaps( dctx, &ptr );
curPos = ptr - dctx->dictBase;
dictLength = dctx->dictLength - curPos;
if ( dictLength > 0 ) {
memcpy( &topOffset, ptr, sizeof(topOffset) );
memcpy( &topOffset, ptr, sizeof(topOffset) );
/* it's in big-endian order */
topOffset = ntohl(topOffset);
dictLength -= sizeof(topOffset); /* first four bytes are offset */
ptr += sizeof(topOffset);
ptr += sizeof(topOffset);
}
if ( dictLength > 0 ) {
# ifdef NODE_CAN_4
numEdges = dictLength / dctx->super.nodeSize;
# else
numEdges = dictLength / 3;
# endif
#ifdef DEBUG
# ifdef NODE_CAN_4
dctx->super.numEdges = dictLength / dctx->super.nodeSize;
XP_ASSERT( (dictLength % dctx->super.nodeSize) == 0 );
# else
dctx->super.numEdges = dictLength / 3;
XP_ASSERT( (dictLength % 3) == 0 );
# endif
dctx->super.numEdges = numEdges;
#endif
dctx->super.base = (array_edge*)ptr;
@ -366,6 +373,10 @@ XP_ASSERT( flags == 0x0001 );
}
dctx->super.name = copyString( dctx->super.mpool, fileName );
if ( ! checkSanity( &dctx->super, numEdges ) ) {
goto closeAndExit;
}
}
goto ok;

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000-2009 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000 - 2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -50,6 +50,7 @@
#include "linuxbt.h"
#include "linuxsms.h"
#include "linuxudp.h"
#include "dictiter.h"
#include "main.h"
#ifdef PLATFORM_NCURSES
# include "cursesmain.h"
@ -60,6 +61,7 @@
#include "model.h"
#include "util.h"
#include "strutils.h"
#include "dictiter.h"
/* #include "commgr.h" */
/* #include "compipe.h" */
#include "memstream.h"
@ -374,11 +376,15 @@ typedef enum {
,CMD_SHOW_OTHERSCORES
,CMD_HOSTIP
,CMD_DICT
#ifdef XWFEATURE_WALKDICT
,CMD_TESTDICT
,CMD_TESTPRFX
#endif
,CMD_PLAYERDICT
,CMD_SEED
,CMD_GAMESEED
,CMD_GAMEFILE
,CMD_MMAP
,CMD_NOMMAP
,CMD_PRINTHISORY
,CMD_SKIPWARNINGS
,CMD_LOCALPWD
@ -449,11 +455,15 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_SHOW_OTHERSCORES, false, "show-other", "show robot/remote scores" }
,{ CMD_HOSTIP, true, "hostip", "remote host ip address (for direct connect)" }
,{ CMD_DICT, true, "game-dict", "dictionary name for game" }
#ifdef XWFEATURE_WALKDICT
,{ CMD_TESTDICT, true, "test-dict", "dictionary to be used for iterator test" }
,{ CMD_TESTPRFX, true, "test-prefix", "list first word starting with this" }
#endif
,{ CMD_PLAYERDICT, true, "player-dict", "dictionary name for player (in sequence)" }
,{ CMD_SEED, true, "seed", "random seed" }
,{ CMD_GAMESEED, true, "game-seed", "game seed (for relay play)" }
,{ CMD_GAMEFILE, true, "file", "file to save to/read from" }
,{ CMD_MMAP, false, "use-mmap", "mmap dicts rather than copy them to memory" }
,{ CMD_NOMMAP, false, "no-mmap", "copy dicts to memory rather than mmap them" }
,{ CMD_PRINTHISORY, false, "print-history", "print history on game over" }
,{ CMD_SKIPWARNINGS, false, "skip-warnings", "no modals on phonies" }
,{ CMD_LOCALPWD, true, "password", "password for user (in sequence)" }
@ -1022,6 +1032,198 @@ tmp_noop_sigintterm( int XP_UNUSED(sig) )
LOG_FUNC();
}
#ifdef XWFEATURE_WALKDICT
//# define PRINT_ALL
static void
testGetNthWord( const DictionaryCtxt* dict, char** words,
XP_U16 depth, IndexData* data )
{
XP_U32 half = dict_getWordCount( dict ) / 2;
XP_UCHAR buf[64];
XP_U32 ii, jj;
DictIter iter;
XP_U32 interval = 1000;
dict_initIter( dict, &iter );
for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) {
if ( dict_getNthWord( &iter, ii, depth, data ) ) {
dict_wordToString( &iter, buf, VSIZE(buf) );
XP_ASSERT( 0 == strcmp( buf, words[ii] ) );
// XP_LOGF( "%s: word[%ld]: %s", __func__, ii, buf );
} else {
XP_ASSERT( 0 );
}
if ( dict_getNthWord( &iter, jj, depth, data ) ) {
dict_wordToString( &iter, buf, VSIZE(buf) );
XP_ASSERT( 0 == strcmp( buf, words[jj] ) );
// XP_LOGF( "%s: word[%ld]: %s", __func__, jj, buf );
} else {
XP_ASSERT( 0 );
}
}
}
static void
walk_dict_test( const LaunchParams* params, const DictionaryCtxt* dict,
GSList* testPrefixes )
{
/* This is just to test that the dict-iterating code works. The words are
meant to be printed e.g. in a scrolling dialog on Android. */
DictIter iter;
long jj;
XP_Bool gotOne;
XP_U32 count = dict_getWordCount( dict );
XP_ASSERT( count > 0 );
XP_ASSERT( count == dict_countWords( dict ) );
char** words = g_malloc( count * sizeof(char*) );
XP_ASSERT( !!words );
/* if ( dict_firstWord( dict, &word ) */
/* && dict_getNextWord( dict, &word ) */
/* && dict_getPrevWord( dict, &word ) ) { */
/* fprintf( stderr, "yay!: dict_getPrevWord returned\n" ); */
/* } */
/* exit( 0 ); */
dict_initIter( dict, &iter );
for ( jj = 0, gotOne = dict_firstWord( &iter );
gotOne;
++jj, gotOne = dict_getNextWord( &iter ) ) {
XP_ASSERT( dict_getPosition( &iter ) == jj );
XP_UCHAR buf[64];
dict_wordToString( &iter, buf, VSIZE(buf) );
# ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif
if ( !!words ) {
words[jj] = g_strdup( buf );
}
}
XP_ASSERT( count == jj );
for ( jj = 0, gotOne = dict_lastWord( &iter );
gotOne;
++jj, gotOne = dict_getPrevWord( &iter ) ) {
XP_ASSERT( dict_getPosition(&iter) == count-jj-1 );
XP_UCHAR buf[64];
dict_wordToString( &iter, buf, VSIZE(buf) );
# ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif
if ( !!words ) {
if ( strcmp( buf, words[count-jj-1] ) ) {
fprintf( stderr, "failure at %ld: %s going forward; %s "
"going backward\n", jj, words[count-jj-1], buf );
break;
}
}
}
XP_ASSERT( count == jj );
XP_LOGF( "finished comparing runs in both directions" );
XP_LOGF( "testing getNth" );
testGetNthWord( dict, words, 0, NULL );
XP_U16 depth = 2;
XP_U16 maxCount = dict_numTileFaces( dict );
IndexData data;
data.count = maxCount * maxCount;
data.indices = XP_MALLOC( params->util->mpool,
data.count * depth * sizeof(data.indices[0]) );
data.prefixes = XP_MALLOC( params->util->mpool,
depth * data.count * sizeof(data.prefixes[0]) );
XP_LOGF( "making index..." );
dict_makeIndex( &iter, depth, &data );
XP_LOGF( "DONE making index" );
data.indices = XP_REALLOC( params->util->mpool, data.indices,
data.count * depth * sizeof(*data.indices) );
data.prefixes = XP_REALLOC( params->util->mpool, data.prefixes,
depth * data.count * sizeof(*data.prefixes) );
#if 0
for ( ii = 0; ii < nIndices; ++ii ) {
if ( !dict_getNthWord( dict, &word, indices[ii] ) ) {
XP_ASSERT( 0 );
}
XP_ASSERT( word.index == indices[ii] );
XP_UCHAR buf1[64];
dict_wordToString( dict, &word, buf1, VSIZE(buf1) );
XP_UCHAR buf2[64] = {0};
if ( ii > 0 && dict_getNthWord( dict, &word, indices[ii]-1 ) ) {
dict_wordToString( dict, &word, buf2, VSIZE(buf2) );
}
char prfx[8];
dict_tilesToString( dict, &prefixes[depth*ii], depth, prfx, VSIZE(prfx) );
fprintf( stderr, "%d: index: %ld; prefix: %s; word: %s (prev: %s)\n",
ii, indices[ii], prfx, buf1, buf2 );
}
#endif
XP_LOGF( "testing getNth WITH INDEXING" );
testGetNthWord( dict, words, depth, &data );
if ( !!testPrefixes ) {
int ii;
guint count = g_slist_length( testPrefixes );
for ( ii = 0; ii < count; ++ii ) {
gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii );
Tile tiles[MAX_COLS];
XP_U16 nTiles = VSIZE(tiles);
if ( dict_tilesForString( dict, prefix, tiles, &nTiles ) ) {
if ( dict_findStartsWith( &iter, NULL, tiles, nTiles ) ) {
XP_UCHAR buf[32];
XP_UCHAR bufPrev[32] = {0};
dict_wordToString( &iter, buf, VSIZE(buf) );
XP_ASSERT( 0 == strncmp( buf, prefix, strlen(prefix) ) );
DictPosition pos = dict_getPosition( &iter );
XP_ASSERT( 0 == strcmp( buf, words[pos] ) );
if ( pos > 0 ) {
if ( !dict_getNthWord( &iter, pos-1, depth, &data ) ) {
XP_ASSERT( 0 );
}
dict_wordToString( &iter, bufPrev, VSIZE(bufPrev) );
XP_ASSERT( 0 == strcmp( bufPrev, words[pos-1] ) );
}
XP_LOGF( "dict_getStartsWith(%s) => %s (prev=%s)",
prefix, buf, bufPrev );
} else {
XP_LOGF( "nothing starts with %s", prefix );
}
}
}
}
XP_LOGF( "done" );
XP_FREE( params->util->mpool, data.indices );
XP_FREE( params->util->mpool, data.prefixes );
} /* walk_dict_test */
static void
walk_dict_test_all( const LaunchParams* params, GSList* testDicts,
GSList* testPrefixes )
{
int ii;
guint count = g_slist_length( testDicts );
for ( ii = 0; ii < count; ++ii ) {
gchar* name = (gchar*)g_slist_nth_data( testDicts, ii );
DictionaryCtxt* dict =
linux_dictionary_make( MPPARM(params->util->mpool) name,
params->useMmap );
if ( NULL != dict ) {
XP_LOGF( "walk_dict_test(%s)", name );
walk_dict_test( params, dict, testPrefixes );
dict_destroy( dict );
}
}
}
#endif
int
main( int argc, char** argv )
{
@ -1037,6 +1239,10 @@ main( int argc, char** argv )
XP_U16 nPlayerDicts = 0;
XP_U16 robotCount = 0;
XP_U16 ii;
#ifdef XWFEATURE_WALKDICT
GSList* testDicts = NULL;
GSList* testPrefixes = NULL;
#endif
/* install a no-op signal handler. Later curses- or gtk-specific code
will install one that does the right thing in that context */
@ -1109,6 +1315,7 @@ main( int argc, char** argv )
mainParams.showColors = XP_TRUE;
mainParams.allowPeek = XP_TRUE;
mainParams.showRobotScores = XP_FALSE;
mainParams.useMmap = XP_TRUE;
/* serverName = mainParams.info.clientInfo.serverName = "localhost"; */
@ -1152,6 +1359,14 @@ main( int argc, char** argv )
mainParams.gi.dictName = copyString( mainParams.util->mpool,
(XP_UCHAR*)optarg );
break;
#ifdef XWFEATURE_WALKDICT
case CMD_TESTDICT:
testDicts = g_slist_prepend( testDicts, g_strdup(optarg) );
break;
case CMD_TESTPRFX:
testPrefixes = g_slist_prepend( testPrefixes, g_strdup(optarg) );
break;
#endif
case CMD_PLAYERDICT:
mainParams.gi.players[nPlayerDicts++].dictName = optarg;
break;
@ -1164,8 +1379,8 @@ main( int argc, char** argv )
case CMD_GAMEFILE:
mainParams.fileName = optarg;
break;
case CMD_MMAP:
mainParams.useMmap = true;
case CMD_NOMMAP:
mainParams.useMmap = false;
break;
case CMD_PRINTHISORY:
mainParams.printHistory = 1;
@ -1375,8 +1590,9 @@ main( int argc, char** argv )
if ( !!mainParams.gi.dictName ) {
mainParams.dict =
linux_dictionary_make(MPPARM(mainParams.util->mpool) mainParams.gi.dictName,
mainParams.useMmap );
linux_dictionary_make( MPPARM(mainParams.util->mpool)
mainParams.gi.dictName,
mainParams.useMmap );
XP_ASSERT( !!mainParams.dict );
mainParams.gi.dictLang = dict_getLangCode( mainParams.dict );
} else if ( isServer ) {
@ -1413,7 +1629,12 @@ main( int argc, char** argv )
/* mainParams.needsNewGame = XP_TRUE; */
/* } */
/* } */
#ifdef XWFEATURE_WALKDICT
if ( !!testDicts ) {
walk_dict_test_all( &mainParams, testDicts, testPrefixes );
exit( 0 );
}
#endif
if ( 0 ) {
#ifdef XWFEATURE_RELAY
} else if ( conType == COMMS_CONN_RELAY ) {

View file

@ -1,6 +1,6 @@
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2000-2009 by Eric House (xwords@eehouse.org). All rights
* Copyright 2000-2011 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -150,6 +150,8 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model,
XP_U16 col, XP_U16 row )
{
static XWBonusType* parsedFile = NULL;
XWBonusType result = EM;
CommonGlobals* cGlobals = (CommonGlobals*)uc->closure;
XP_U16 nCols = model_numCols( model );
if ( NULL == parsedFile && NULL != cGlobals->params->bonusFile ) {
@ -158,34 +160,42 @@ linux_util_getSquareBonus( XW_UtilCtxt* uc, const ModelCtxt* model,
}
}
if ( NULL != parsedFile ) {
return parsedFile[(row*nCols) + col];
result = parsedFile[(row*nCols) + col];
} else {
XP_U16 index;
XP_U16 index, ii;
/* This must be static or won't compile under multilink (for Palm).
Fix! */
static char scrabbleBoard[8*8] = {
TW,EM,EM,DL,EM,EM,EM,TW,
EM,DW,EM,EM,EM,TL,EM,EM,
TW,//EM,EM,DL,EM,EM,EM,TW,
EM,DW,//EM,EM,EM,TL,EM,EM,
EM,EM,DW,EM,EM,EM,DL,EM,
DL,EM,EM,DW,EM,EM,EM,DL,
EM,EM,DW,//EM,EM,EM,DL,EM,
DL,EM,EM,DW,//EM,EM,EM,DL,
EM,EM,EM,EM,DW,EM,EM,EM,
EM,TL,EM,EM,EM,TL,EM,EM,
EM,EM,EM,EM,DW,//EM,EM,EM,
EM,TL,EM,EM,EM,TL,//EM,EM,
EM,EM,DL,EM,EM,EM,DL,EM,
EM,EM,DL,EM,EM,EM,DL,//EM,
TW,EM,EM,DL,EM,EM,EM,DW,
}; /* scrabbleBoard */
if ( col > 7 ) col = 14 - col;
if ( row > 7 ) row = 14 - row;
index = (row*8) + col;
if ( index >= 8*8 ) {
return (XWBonusType)EM;
} else {
return (XWBonusType)scrabbleBoard[index];
if ( col > row ) {
XP_U16 tmp = col;
col = row;
row = tmp;
}
index = col;
for ( ii = 1; ii <= row; ++ii ) {
index += ii;
}
if ( index < VSIZE(scrabbleBoard) ) {
result = (XWBonusType)scrabbleBoard[index];
}
}
return result;
} /* linux_util_getSquareBonus */
static XP_U32
@ -407,6 +417,27 @@ linux_getErrString( UtilErrID id, XP_Bool* silent )
return (XP_UCHAR*)message;
} /* linux_getErrString */
void
formatConfirmTrade( const XP_UCHAR** tiles, XP_U16 nTiles,
char* buf, XP_U16 buflen )
{
char tileBuf[128];
int offset = 0;
int ii;
XP_ASSERT( nTiles > 0 );
for ( ii = 0; ii < nTiles; ++ii ) {
offset += snprintf( &tileBuf[offset], sizeof(tileBuf) - offset,
"%s, ", tiles[ii] );
XP_ASSERT( offset < sizeof(tileBuf) );
}
tileBuf[offset-2] = '\0';
snprintf( buf, buflen,
"Are you sure you want to trade the selected tiles (%s)?",
tileBuf );
}
#ifdef TEXT_MODEL
/* This is broken for UTF-8, even Spanish */
void

View file

@ -38,4 +38,7 @@ void linux_util_vt_destroy( XW_UtilCtxt* util );
const XP_UCHAR* linux_getErrString( UtilErrID id, XP_Bool* silent );
void formatConfirmTrade( const XP_UCHAR** tiles, XP_U16 nTiles, char* buf,
XP_U16 buflen );
#endif

View file

@ -139,7 +139,7 @@ build_cmds() {
CMD="./obj_linux_memdbg/xwords --room $ROOM"
CMD="$CMD --robot ${NAMES[$DEV]} --robot-iq $((1 + (RANDOM%100))) "
CMD="$CMD $OTHERS --game-dict $DICT --port $PORT --host $HOST "
CMD="$CMD --file $FILE --slow-robot 1:3 --skip-confirm --use-mmap"
CMD="$CMD --file $FILE --slow-robot 1:3 --skip-confirm"
CMD="$CMD --drop-nth-packet $DROP_N $PLAT_PARMS"
CMD="$CMD $PUBLIC"
@ -279,14 +279,14 @@ check_game() {
fi
if [ -n "$OTHERS" ]; then
echo -n "Closing $CONNNAME: "
echo -n "Closing $CONNNAME [$(date)]: "
# kill_from_logs $OTHERS $KEY
for ID in $OTHERS $KEY; do
echo -n "${LOGS[$ID]}, "
kill_from_log ${LOGS[$ID]} || true
close_device $ID $DONEDIR "game over"
done
date
echo ""
# XWRELAY_ERROR_DELETED may be old
elif grep -q 'relay_error_curses(XWRELAY_ERROR_DELETED)' $LOG; then
echo "deleting $LOG $(connName $LOG) b/c another resigned"

View file

@ -0,0 +1,25 @@
#!/bin/sh
set -u -e
PARAMS="--curses --robot Kati --remote-player --game-dict dict.xwd --quit-after 1 --sort-tiles"
run() {
SEED=$1
LOG=LOG__${SEED}.txt
ROOM=ROOM_${SEED}
> $LOG
./obj_linux_memdbg/xwords $PARAMS --room $ROOM \
--seed $SEED >/dev/null 2>>$LOG &
sleep 1
./obj_linux_memdbg/xwords $PARAMS --room $ROOM \
--seed $((SEED+1000)) >/dev/null 2>>$LOG &
}
for SEED in $(seq 1 1000); do
echo "trying seed $SEED"
run $SEED
wait
done