mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-15 15:41:24 +01:00
Merge branch 'android_branch' into send_in_background
Conflicts: xwords4/linux/cursesmain.c
This commit is contained in:
commit
2492e7cfc5
67 changed files with 2705 additions and 742 deletions
|
@ -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"/>
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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>
|
||||
|
|
38
xwords4/android/XWords4/res/layout/dict_browser.xml
Normal file
38
xwords4/android/XWords4/res/layout/dict_browser.xml
Normal 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>
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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" );
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
559
xwords4/common/dictiter.c
Normal 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
75
xwords4/common/dictiter.h
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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, ¬ifyInfo );
|
||||
|
||||
model_destroy( tmpModel );
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -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) )
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 );
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 ) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
25
xwords4/linux/scripts/game_with_seed.sh
Executable file
25
xwords4/linux/scripts/game_with_seed.sh
Executable 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
|
Loading…
Reference in a new issue