mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-29 08:34:37 +01:00
do wordlist lookups using tiles not chars
Fixing a problem with languages (like Hungarian) where it's legal to use a two-letter tile or two single-letter tiles to play the same word. When words are seen, or searched for, as char-arrays, there are duplicates. Current code crashes, but there's also risk the user gets unexpected behavior. Crash is fixed, and foundation laid for better UX, by doing all searches for tile arrays. If a search string translates to more than one tile array the user must choose. For that choice to make sense it's now possible to translate tile[] to char[] with a delimiter between the tile strings.
This commit is contained in:
parent
f8aa204842
commit
b2bc4e34cf
13 changed files with 310 additions and 170 deletions
|
@ -21,6 +21,8 @@
|
|||
package org.eehouse.android.xw4;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
@ -96,7 +98,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
{
|
||||
TextView text = (TextView)
|
||||
inflate( android.R.layout.simple_list_item_1 );
|
||||
String str = XwJNI.dict_iter_nthWord( m_dictClosure, position );
|
||||
String str = XwJNI.dict_iter_nthWord( m_dictClosure, position, null );
|
||||
if ( null != str ) {
|
||||
text.setText( str );
|
||||
text.setOnClickListener( DictBrowseDelegate.this );
|
||||
|
@ -136,6 +138,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
return section;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections()
|
||||
{
|
||||
m_prefixes = XwJNI.dict_iter_getPrefixes( m_dictClosure );
|
||||
|
@ -243,7 +246,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
setFindText( m_browseState.m_prefix );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
XwJNI.dict_iter_destroy( m_dictClosure );
|
||||
|
@ -254,6 +257,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
@Override
|
||||
public void finalize()
|
||||
{
|
||||
Assert.assertTrueNR( m_dictClosure == 0 );
|
||||
XwJNI.dict_iter_destroy( m_dictClosure );
|
||||
try {
|
||||
super.finalize();
|
||||
|
@ -262,6 +266,47 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog makeDialog( DBAlert alert, Object[] params )
|
||||
{
|
||||
Dialog dialog = null;
|
||||
DlgID dlgID = alert.getDlgID();
|
||||
switch ( dlgID ) {
|
||||
case CHOOSE_TILES:
|
||||
final byte[][] choices = (byte[][])params[0];
|
||||
final String[] strs = new String[choices.length];
|
||||
for ( int ii = 0; ii < choices.length; ++ii ) {
|
||||
strs[ii] = XwJNI.dict_iter_tilesToStr( m_dictClosure, choices[ii], "." );
|
||||
}
|
||||
final int[] chosen = {0};
|
||||
dialog = makeAlertBuilder()
|
||||
.setSingleChoiceItems( strs, chosen[0], new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick( DialogInterface dialog, int which )
|
||||
{
|
||||
chosen[0] = which;
|
||||
}
|
||||
} )
|
||||
.setPositiveButton( android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick( DialogInterface dialog, int which )
|
||||
{
|
||||
if ( 0 <= chosen[0] ) {
|
||||
showPrefix( choices[chosen[0]] );
|
||||
}
|
||||
}
|
||||
} )
|
||||
.setTitle( R.string.pick_tiles_title )
|
||||
.create();
|
||||
break;
|
||||
default:
|
||||
dialog = super.makeDialog( alert, params );
|
||||
break;
|
||||
}
|
||||
return dialog;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// View.OnClickListener interface
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -328,7 +373,16 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
String text = getFindText();
|
||||
if ( null != text && 0 < text.length() ) {
|
||||
m_browseState.m_prefix = text;
|
||||
showPrefix();
|
||||
|
||||
byte[][] choices = XwJNI.dict_iter_strToTiles( m_dictClosure, text );
|
||||
if ( null == choices || 0 == choices.length ) {
|
||||
String msg = getString( R.string.no_tiles_exist, text, m_name );
|
||||
makeOkOnlyBuilder( msg ).show();
|
||||
} else if ( 1 == choices.length ) {
|
||||
showPrefix( choices[0] );
|
||||
} else {
|
||||
showDialogFragment( DlgID.CHOOSE_TILES, (Object)choices );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,14 +398,15 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
edit.setText( text );
|
||||
}
|
||||
|
||||
private void showPrefix()
|
||||
private void showPrefix( byte[] prefix )
|
||||
{
|
||||
String text = m_browseState.m_prefix;
|
||||
if ( null != text && 0 < text.length() ) {
|
||||
int pos = XwJNI.dict_iter_getStartsWith( m_dictClosure, text );
|
||||
if ( null != prefix && 0 < prefix.length ) {
|
||||
// Here's the search
|
||||
int pos = XwJNI.dict_iter_getStartsWith( m_dictClosure, prefix );
|
||||
if ( 0 <= pos ) {
|
||||
m_list.setSelection( pos );
|
||||
} else {
|
||||
String text = XwJNI.dict_iter_tilesToStr( m_dictClosure, prefix, null );
|
||||
DbgUtils.showf( m_activity, R.string.dict_browse_nowords_fmt,
|
||||
m_name, text );
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public enum DlgID {
|
|||
, CHANGE_CONN
|
||||
, GAMES_LIST_NAME_REMATCH
|
||||
, ASK_DUP_PAUSE
|
||||
, CHOOSE_TILES
|
||||
;
|
||||
|
||||
private boolean m_addToStack;
|
||||
|
|
|
@ -524,12 +524,15 @@ public class XwJNI {
|
|||
public static native void dict_iter_destroy( long closure );
|
||||
public static native int dict_iter_wordCount( long closure );
|
||||
public static native int[] dict_iter_getCounts( long closure );
|
||||
public static native String dict_iter_nthWord( long closure, int nn );
|
||||
public static native String dict_iter_nthWord( long closure, int nn, String delim );
|
||||
public static native String[] dict_iter_getPrefixes( long closure );
|
||||
public static native int[] dict_iter_getIndices( long closure );
|
||||
public static native byte[][] dict_iter_strToTiles( long closure,
|
||||
String str );
|
||||
public static native int dict_iter_getStartsWith( long closure,
|
||||
String prefix );
|
||||
byte[] prefix );
|
||||
public static native String dict_iter_getDesc( long closure );
|
||||
public static native String dict_iter_tilesToStr( long closure, byte[] tiles, String delim );
|
||||
|
||||
// Private methods -- called only here
|
||||
private static native long initGlobals( DUtilCtxt dutil, JNIUtils jniu );
|
||||
|
|
|
@ -2548,4 +2548,7 @@
|
|||
<!-- Debug-build-only question asked to confirm deletion of saved logs -->
|
||||
<string name="logstore_clear_confirm">Are you sure you want to
|
||||
erase your debug logs? This action cannot be undone.</string>
|
||||
|
||||
<string name="pick_tiles_title">Pick a tile \"spelling\"</string>
|
||||
<string name="no_tiles_exist">\"%1$s\" cannot be spelled with tiles in %2$s.</string>
|
||||
</resources>
|
||||
|
|
|
@ -2397,7 +2397,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1sendChat
|
|||
////////////////////////////////////////////////////////////
|
||||
|
||||
typedef struct _DictIterData {
|
||||
JNIEnv* env;
|
||||
JNIEnv* env; /* GET RID OF THIS!!!! */
|
||||
JNIGlobalState* globalState;
|
||||
JNIUtilCtxt* jniutil;
|
||||
VTableMgr* vtMgr;
|
||||
|
@ -2519,7 +2519,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getPrefixes
|
|||
XP_UCHAR buf[16];
|
||||
(void)dict_tilesToString( data->dict,
|
||||
&data->idata.prefixes[depth*ii],
|
||||
depth, buf, VSIZE(buf) );
|
||||
depth, buf, VSIZE(buf), NULL );
|
||||
jstring jstr = (*env)->NewStringUTF( env, buf );
|
||||
(*env)->SetObjectArrayElement( env, result, ii, jstr );
|
||||
deleteLocalRef( env, jstr );
|
||||
|
@ -2546,33 +2546,110 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getIndices
|
|||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1nthWord
|
||||
( JNIEnv* env, jclass C, jlong closure, jint nn)
|
||||
( JNIEnv* env, jclass C, jlong closure, jint nn, jstring jdelim )
|
||||
{
|
||||
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) );
|
||||
const XP_UCHAR* delim = NULL == jdelim ? NULL
|
||||
: (*env)->GetStringUTFChars( env, jdelim, NULL );
|
||||
dict_wordToString( &data->iter, buf, VSIZE(buf), delim );
|
||||
result = (*env)->NewStringUTF( env, buf );
|
||||
if ( !!delim ) {
|
||||
(*env)->ReleaseStringUTFChars( env, jdelim, delim );
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typedef struct _FTData {
|
||||
JNIEnv* env;
|
||||
jbyteArray arrays[16];
|
||||
int nArrays;
|
||||
} FTData;
|
||||
|
||||
static XP_Bool
|
||||
onFoundTiles( void* closure, const Tile* tiles, int nTiles )
|
||||
{
|
||||
FTData* ftd = (FTData*)closure;
|
||||
ftd->arrays[ftd->nArrays++] = makeByteArray( ftd->env, nTiles,
|
||||
(const jbyte*)tiles );
|
||||
return ftd->nArrays < VSIZE(ftd->arrays); /* still have room? */
|
||||
}
|
||||
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1strToTiles
|
||||
( JNIEnv* env, jclass C, jlong closure, jstring jstr )
|
||||
{
|
||||
jobjectArray result = NULL;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
const char* str = (*env)->GetStringUTFChars( env, jstr, NULL );
|
||||
|
||||
FTData ftd = { .env = env, };
|
||||
dict_tilesForString( data->dict, str, onFoundTiles, &ftd );
|
||||
|
||||
if ( ftd.nArrays > 0 ) {
|
||||
result = makeByteArrayArray( env, ftd.nArrays );
|
||||
for ( int ii = 0; ii < ftd.nArrays; ++ii ) {
|
||||
(*env)->SetObjectArrayElement( env, result, ii, ftd.arrays[ii] );
|
||||
deleteLocalRef( env, ftd.arrays[ii] );
|
||||
}
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars( env, jstr, str );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1getStartsWith
|
||||
( JNIEnv* env, jclass C, jlong closure, jstring jprefix )
|
||||
( JNIEnv* env, jclass C, jlong closure, jbyteArray jtiles )
|
||||
{
|
||||
jint result = -1;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
if ( NULL != data ) {
|
||||
const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL );
|
||||
if ( 0 <= dict_findStartsWith( &data->iter, prefix ) ) {
|
||||
result = dict_getPosition( &data->iter );
|
||||
}
|
||||
(*env)->ReleaseStringUTFChars( env, jprefix, prefix );
|
||||
XP_U16 nTiles = (*env)->GetArrayLength( env, jtiles );
|
||||
jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL );
|
||||
|
||||
if ( 0 <= dict_findStartsWith( &data->iter, (Tile*)tiles, nTiles ) ) {
|
||||
result = dict_getPosition( &data->iter );
|
||||
}
|
||||
|
||||
(*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dict_1iter_1tilesToStr
|
||||
( JNIEnv* env, jclass C, jlong closure, jbyteArray jtiles, jstring jdelim )
|
||||
{
|
||||
jstring result = NULL;
|
||||
|
||||
XP_UCHAR buf[64];
|
||||
const XP_UCHAR* delim = NULL;
|
||||
if ( !!jdelim ) {
|
||||
delim = (*env)->GetStringUTFChars( env, jdelim, NULL );
|
||||
}
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
|
||||
XP_U16 nTiles = (*env)->GetArrayLength( env, jtiles );
|
||||
jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL );
|
||||
|
||||
XP_U16 strLen = dict_tilesToString( data->dict, (Tile*)tiles, nTiles,
|
||||
buf, VSIZE(buf), delim );
|
||||
if ( 0 < strLen ) {
|
||||
buf[strLen] = '\0';
|
||||
result = (*env)->NewStringUTF( env, buf );
|
||||
}
|
||||
|
||||
if ( !!jdelim ) {
|
||||
(*env)->ReleaseStringUTFChars( env, jdelim, delim );
|
||||
}
|
||||
(*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -3726,6 +3726,22 @@ moveTileToBoard( BoardCtxt* board, XWEnv xwe, XP_U16 col, XP_U16 row,
|
|||
} /* moveTileToBoard */
|
||||
|
||||
#ifdef KEY_SUPPORT
|
||||
|
||||
typedef struct _FTData {
|
||||
Tile tile;
|
||||
XP_Bool found;
|
||||
} FTData;
|
||||
|
||||
static XP_Bool
|
||||
foundTiles( void* closure, const Tile* tiles, int len )
|
||||
{
|
||||
XP_ASSERT( 1 == len );
|
||||
FTData* ftp = (FTData*)closure;
|
||||
ftp->tile = tiles[0];
|
||||
ftp->found = XP_TRUE;
|
||||
return XP_FALSE;
|
||||
}
|
||||
|
||||
/* Return number between 0 and MAX_TRAY_TILES-1 for valid index, < 0 otherwise */
|
||||
static XP_S16
|
||||
keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace )
|
||||
|
@ -3743,19 +3759,19 @@ keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace )
|
|||
|
||||
if ( tileIndex < 0 ) {
|
||||
DictionaryCtxt* dict = model_getDictionary( model );
|
||||
Tile tile;
|
||||
XP_UCHAR buf[2] = { key, '\0' };
|
||||
|
||||
/* Figure out if we have the tile in the tray */
|
||||
XP_U16 nTiles = 1;
|
||||
if ( dict_tilesForString( dict, buf, &tile, &nTiles ) ) { /* in dict? */
|
||||
FTData ftd = {0};
|
||||
dict_tilesForString( dict, buf, foundTiles, &ftd );
|
||||
if ( ftd.found ) {
|
||||
XP_S16 turn = board->selPlayer;
|
||||
tileIndex = model_trayContains( model, turn, tile );
|
||||
tileIndex = model_trayContains( model, turn, ftd.tile );
|
||||
if ( tileIndex < 0 ) {
|
||||
Tile blankTile = dict_getBlankTile( dict );
|
||||
tileIndex = model_trayContains( model, turn, blankTile );
|
||||
if ( tileIndex >= 0 && !!blankFace ) { /* there's a blank for it */
|
||||
*blankFace = tile;
|
||||
*blankFace = ftd.tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,57 +189,6 @@ findStartsWithTiles( DictIter* iter, const Tile* tiles, XP_U16 nTiles )
|
|||
return 0 == nTiles;
|
||||
}
|
||||
|
||||
static XP_S16
|
||||
findStartsWithChars( DictIter* iter, const XP_UCHAR* chars, XP_U16 charsOffset,
|
||||
array_edge* edge, XP_U16 nTilesUsed )
|
||||
{
|
||||
XP_S16 result = -1;
|
||||
XP_U16 charsLen = XP_STRLEN( &chars[charsOffset] );
|
||||
if ( NULL == edge ) {
|
||||
if ( 0 == charsLen ) {
|
||||
iter->nEdges = nTilesUsed;
|
||||
result = charsOffset;
|
||||
}
|
||||
} else if ( 0 == charsLen ) {
|
||||
iter->nEdges = nTilesUsed;
|
||||
result = charsOffset;
|
||||
} else {
|
||||
const DictionaryCtxt* dict = iter->dict;
|
||||
XP_U16 nodeSize = dict->nodeSize;
|
||||
for ( ; ; ) { /* for all the tiles */
|
||||
Tile tile = EDGETILE( dict, edge );
|
||||
const XP_UCHAR* facep = NULL;
|
||||
for ( ; ; ) { /* for each string that tile can be */
|
||||
facep = dict_getNextTileString( dict, tile, facep );
|
||||
if ( NULL == facep ) {
|
||||
break;
|
||||
}
|
||||
XP_U16 faceLen = XP_STRLEN( facep );
|
||||
if ( faceLen > charsLen ) {
|
||||
faceLen = charsLen;
|
||||
}
|
||||
if ( 0 == XP_STRNCMP( facep, &chars[charsOffset], faceLen ) ) {
|
||||
XP_S16 newOffset =
|
||||
findStartsWithChars( iter, chars,
|
||||
charsOffset + faceLen,
|
||||
dict_follow( dict, edge ),
|
||||
nTilesUsed + 1 );
|
||||
if ( result < newOffset ) {
|
||||
iter->edges[nTilesUsed] = edge;
|
||||
result = newOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( IS_LAST_EDGE( dict, edge ) ) {
|
||||
break;
|
||||
}
|
||||
edge += nodeSize;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static XP_Bool
|
||||
startsWith( const DictIter* iter, const Tile* tiles, XP_U16 nTiles )
|
||||
{
|
||||
|
@ -388,7 +337,8 @@ placeWordClose( DictIter* iter, const DictPosition position, XP_U16 depth,
|
|||
} /* placeWordClose */
|
||||
|
||||
static void
|
||||
iterToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
|
||||
iterToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
|
||||
const XP_UCHAR* delim )
|
||||
{
|
||||
XP_U16 ii;
|
||||
XP_U16 nEdges = iter->nEdges;
|
||||
|
@ -396,7 +346,7 @@ iterToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
|
|||
for ( ii = 0; ii < nEdges; ++ii ) {
|
||||
tiles[ii] = EDGETILE( iter->dict, iter->edges[ii] );
|
||||
}
|
||||
(void)dict_tilesToString( iter->dict, tiles, nEdges, buf, buflen );
|
||||
(void)dict_tilesToString( iter->dict, tiles, nEdges, buf, buflen, delim );
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -658,11 +608,10 @@ figurePosition( DictIter* iter )
|
|||
}
|
||||
|
||||
XP_S16
|
||||
dict_findStartsWith( DictIter* iter, const XP_UCHAR* prefix )
|
||||
dict_findStartsWith( DictIter* iter, const Tile* prefix, XP_U16 len )
|
||||
{
|
||||
ASSERT_INITED( iter );
|
||||
array_edge* edge = dict_getTopEdge( iter->dict );
|
||||
XP_S16 offset = findStartsWithChars( iter, prefix, 0, edge, 0 );
|
||||
XP_S16 offset = findStartsWithTiles( iter, prefix, len );
|
||||
if ( 0 > offset ) {
|
||||
/* not found; do nothing */
|
||||
} else if ( 0 == offset ) {
|
||||
|
@ -681,10 +630,11 @@ dict_findStartsWith( DictIter* iter, const XP_UCHAR* prefix )
|
|||
}
|
||||
|
||||
void
|
||||
dict_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
|
||||
dict_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
|
||||
const XP_UCHAR* delim )
|
||||
{
|
||||
ASSERT_INITED( iter );
|
||||
iterToString( iter, buf, buflen );
|
||||
iterToString( iter, buf, buflen, delim );
|
||||
}
|
||||
|
||||
DictPosition
|
||||
|
|
|
@ -73,8 +73,10 @@ 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_S16 dict_findStartsWith( DictIter* iter, const XP_UCHAR* prefix );
|
||||
void dict_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
|
||||
const XP_UCHAR* delim );
|
||||
XP_S16 dict_findStartsWith( DictIter* iter, const Tile* prefix, XP_U16 nTiles );
|
||||
void dict_stringToTiles( const XP_UCHAR* str, Tile out[], XP_U16* nTiles );
|
||||
DictPosition dict_getPosition( const DictIter* iter );
|
||||
#ifdef CPLUS
|
||||
}
|
||||
|
|
|
@ -200,40 +200,48 @@ dict_numTileFaces( const DictionaryCtxt* dict )
|
|||
return dict->nFaces;
|
||||
} /* dict_numTileFaces */
|
||||
|
||||
static void
|
||||
appendIfSpace( XP_UCHAR** bufp, const XP_UCHAR* end, const XP_UCHAR* newtxt )
|
||||
{
|
||||
XP_U16 len = XP_STRLEN( newtxt );
|
||||
if ( *bufp + len < end ) {
|
||||
XP_MEMCPY( *bufp, newtxt, len );
|
||||
*bufp += len;
|
||||
} else {
|
||||
*bufp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
XP_U16
|
||||
dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
|
||||
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize )
|
||||
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize,
|
||||
const XP_UCHAR* delim )
|
||||
{
|
||||
XP_UCHAR* bufp = buf;
|
||||
XP_UCHAR* end = bufp + bufSize;
|
||||
XP_U16 result = 0;
|
||||
const XP_UCHAR* end = bufp + bufSize;
|
||||
XP_U16 delimLen = NULL == delim ? 0 : XP_STRLEN(delim);
|
||||
|
||||
while ( nTiles-- ) {
|
||||
Tile tile = *tiles++;
|
||||
for ( int ii = 0; ii < nTiles && !!bufp; ++ii ) {
|
||||
|
||||
if ( 0 < delimLen && 0 < ii ) {
|
||||
appendIfSpace( &bufp, end, delim );
|
||||
}
|
||||
|
||||
Tile tile = tiles[ii];
|
||||
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
|
||||
|
||||
if ( IS_SPECIAL(*facep) ) {
|
||||
XP_UCHAR* chars = dict->chars[(XP_U16)*facep];
|
||||
XP_U16 len = XP_STRLEN( chars );
|
||||
if ( bufp + len >= end ) {
|
||||
bufp = NULL;
|
||||
break;
|
||||
}
|
||||
XP_MEMCPY( bufp, chars, len );
|
||||
bufp += len;
|
||||
appendIfSpace( &bufp, end, chars );
|
||||
} else {
|
||||
XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be
|
||||
handled by specials
|
||||
mechanism */
|
||||
if ( bufp + 1 >= end ) {
|
||||
bufp = NULL;
|
||||
break;
|
||||
}
|
||||
bufp += XP_SNPRINTF( bufp, end - bufp, XP_S, facep );
|
||||
appendIfSpace( &bufp, end, facep );
|
||||
}
|
||||
}
|
||||
|
||||
if ( bufp != NULL && bufp < end ) {
|
||||
XP_U16 result = 0;
|
||||
if ( !!bufp && bufp < end ) {
|
||||
*bufp = '\0';
|
||||
result = bufp - buf;
|
||||
}
|
||||
|
@ -244,46 +252,48 @@ dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
|
|||
* 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.
|
||||
*/
|
||||
static XP_S16
|
||||
|
||||
static XP_Bool
|
||||
tilesForStringImpl( const DictionaryCtxt* dict, const XP_UCHAR* str,
|
||||
Tile* tiles, XP_U16 nTiles, XP_U16 nFound )
|
||||
Tile* tiles, XP_U16 nTiles, XP_U16 nFound,
|
||||
OnFoundTiles proc, void* closure )
|
||||
{
|
||||
XP_S16 result = -1;
|
||||
XP_Bool goOn;
|
||||
if ( nFound == nTiles || '\0' == str[0] ) {
|
||||
result = nFound;
|
||||
/* We've recursed to the end and have found a tile! */
|
||||
goOn = (*proc)( closure, tiles, nFound );
|
||||
} else {
|
||||
goOn = XP_TRUE;
|
||||
|
||||
XP_U16 nFaces = dict_numTileFaces( dict );
|
||||
Tile tile;
|
||||
for ( tile = 0; tile < nFaces; ++tile ) {
|
||||
for ( Tile tile = 0; goOn && 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 ) ) {
|
||||
XP_S16 maxFound = tilesForStringImpl( dict, str + faceLen,
|
||||
tiles, nTiles,
|
||||
nFound + 1 );
|
||||
if ( 0 <= maxFound ) {
|
||||
tiles[nFound] = tile;
|
||||
result = maxFound;
|
||||
for ( const XP_UCHAR* facep = NULL; ; ) {
|
||||
facep = dict_getNextTileString( dict, tile, facep );
|
||||
if ( !facep ) {
|
||||
break;
|
||||
}
|
||||
XP_U16 faceLen = XP_STRLEN( facep );
|
||||
if ( 0 == XP_STRNCMP( facep, str, faceLen ) ) {
|
||||
tiles[nFound] = tile;
|
||||
goOn = tilesForStringImpl( dict, str + faceLen,
|
||||
tiles, nTiles, nFound + 1,
|
||||
proc, closure );
|
||||
break; /* impossible to have than one match per tile */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return goOn;
|
||||
} /* tilesForStringImpl */
|
||||
|
||||
XP_Bool
|
||||
void
|
||||
dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* str,
|
||||
Tile* tiles, XP_U16* nTilesP )
|
||||
OnFoundTiles proc, void* closure )
|
||||
{
|
||||
XP_S16 nFound = tilesForStringImpl( dict, str, tiles, *nTilesP, 0 );
|
||||
XP_Bool success = 0 <= nFound;
|
||||
if ( success ) {
|
||||
*nTilesP = nFound;
|
||||
}
|
||||
return success;
|
||||
Tile tiles[32];
|
||||
tilesForStringImpl( dict, str, tiles, VSIZE(tiles), 0, proc, closure );
|
||||
} /* dict_tilesForString */
|
||||
|
||||
XP_Bool
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*-mode: C; fill-column: 78; c-basic-offset: 4; -*- */
|
||||
/* -*- compile-command: "cd ../linux && make MEMDEBUG=TRUE -j3"; -*- */
|
||||
/*
|
||||
* Copyright 1997 - 2020 by Eric House (xwords@eehouse.org). All rights
|
||||
* reserved.
|
||||
|
@ -177,7 +177,8 @@ XP_U16 dict_numTiles( const DictionaryCtxt* ctxt, Tile tile );
|
|||
XP_U16 dict_numTileFaces( const DictionaryCtxt* ctxt );
|
||||
|
||||
XP_U16 dict_tilesToString( const DictionaryCtxt* ctxt, const Tile* tiles,
|
||||
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize );
|
||||
XP_U16 nTiles, XP_UCHAR* buf, XP_U16 bufSize,
|
||||
const XP_UCHAR* delim );
|
||||
const XP_UCHAR* dict_getTileString( const DictionaryCtxt* ctxt, Tile tile );
|
||||
const XP_UCHAR* dict_getNextTileString( const DictionaryCtxt* ctxt, Tile tile,
|
||||
const XP_UCHAR* cur );
|
||||
|
@ -186,8 +187,9 @@ const XP_UCHAR* dict_getLangName(const DictionaryCtxt* ctxt );
|
|||
|
||||
XP_Bool dict_isUTF8( const DictionaryCtxt* ctxt );
|
||||
|
||||
XP_Bool dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* key,
|
||||
Tile* tiles, XP_U16* nTiles );
|
||||
typedef XP_Bool (*OnFoundTiles)(void* closure, const Tile* tiles, int len);
|
||||
void dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* key,
|
||||
OnFoundTiles proc, void* closure );
|
||||
|
||||
XP_Bool dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile );
|
||||
void dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile,
|
||||
|
|
|
@ -2245,7 +2245,7 @@ formatTray( const TrayTileSet* tiles, DictionaryCtxt* dict, XP_UCHAR* buf,
|
|||
buf[ii] = '\0';
|
||||
} else {
|
||||
dict_tilesToString( dict, (Tile*)tiles->tiles, tiles->nTiles,
|
||||
buf, bufSize );
|
||||
buf, bufSize, NULL );
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
|
|
@ -753,7 +753,7 @@ scoreWord( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
|
|||
|
||||
XP_UCHAR buf[(MAX_ROWS*2)+1];
|
||||
dict_tilesToString( dict, checkWordBuf, len, buf,
|
||||
sizeof(buf) );
|
||||
sizeof(buf), NULL );
|
||||
|
||||
WNParams wnp = { .word = buf, .isLegal = legal, .dict = dict,
|
||||
#ifdef XWFEATURE_BOARDWORDS
|
||||
|
@ -889,7 +889,7 @@ wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word,
|
|||
{
|
||||
XP_UCHAR buf[(MAX_ROWS*2)+1];
|
||||
XP_U16 len = dict_tilesToString( fmtr->dict, word, fmtr->nTiles,
|
||||
buf, sizeof(buf) );
|
||||
buf, sizeof(buf), NULL );
|
||||
|
||||
if ( !!stream ) {
|
||||
stream_putBytes( stream, buf, len );
|
||||
|
|
|
@ -1912,7 +1912,7 @@ testGetNthWord( const DictionaryCtxt* dict, char** XP_UNUSED_DBG(words),
|
|||
|
||||
for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) {
|
||||
if ( dict_getNthWord( &iter, ii, depth, data ) ) {
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
dict_wordToString( &iter, buf, VSIZE(buf), "." );
|
||||
XP_ASSERT( 0 == strcmp( buf, words[ii] ) );
|
||||
# ifdef PRINT_ALL
|
||||
XP_LOGF( "%s: word[%ld]: %s", __func__, ii, buf );
|
||||
|
@ -1921,7 +1921,7 @@ testGetNthWord( const DictionaryCtxt* dict, char** XP_UNUSED_DBG(words),
|
|||
XP_ASSERT( 0 );
|
||||
}
|
||||
if ( dict_getNthWord( &iter, jj, depth, data ) ) {
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
dict_wordToString( &iter, buf, VSIZE(buf), "." );
|
||||
XP_ASSERT( 0 == strcmp( buf, words[jj] ) );
|
||||
# ifdef PRINT_ALL
|
||||
XP_LOGF( "%s: word[%ld]: %s", __func__, jj, buf );
|
||||
|
@ -1931,6 +1931,43 @@ testGetNthWord( const DictionaryCtxt* dict, char** XP_UNUSED_DBG(words),
|
|||
}
|
||||
}
|
||||
}
|
||||
typedef struct _FTData {
|
||||
DictIter* iter;
|
||||
IndexData* data;
|
||||
char** words;
|
||||
gchar* prefix;
|
||||
XP_U16 depth;
|
||||
} FTData;
|
||||
|
||||
static XP_Bool
|
||||
onFoundTiles( void* closure, const Tile* tiles, int nTiles )
|
||||
{
|
||||
FTData* ftp = (FTData*)closure;
|
||||
XP_S16 lenMatched = dict_findStartsWith( ftp->iter, tiles, nTiles );
|
||||
if ( 0 <= lenMatched ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_UCHAR bufPrev[32] = {0};
|
||||
dict_wordToString( ftp->iter, buf, VSIZE(buf), "." );
|
||||
|
||||
/* This doesn't work with synonyms like "L-L" for "L·L" */
|
||||
// XP_ASSERT( 0 == strncasecmp( buf, prefix, lenMatched ) );
|
||||
|
||||
DictPosition pos = dict_getPosition( ftp->iter );
|
||||
XP_ASSERT( 0 == strcmp( buf, ftp->words[pos] ) );
|
||||
if ( pos > 0 ) {
|
||||
if ( !dict_getNthWord( ftp->iter, pos-1, ftp->depth, ftp->data ) ) {
|
||||
XP_ASSERT( 0 );
|
||||
}
|
||||
dict_wordToString( ftp->iter, bufPrev, VSIZE(bufPrev), "." );
|
||||
XP_ASSERT( 0 == strcmp( bufPrev, ftp->words[pos-1] ) );
|
||||
}
|
||||
XP_LOGF( "dict_getStartsWith(%s) => %s (prev=%s)",
|
||||
ftp->prefix, buf, bufPrev );
|
||||
} else {
|
||||
XP_LOGFF( "nothing starts with %s", ftp->prefix );
|
||||
}
|
||||
return XP_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
||||
|
@ -1948,7 +1985,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
max = MAX_COLS_DICT;
|
||||
}
|
||||
|
||||
dict_initIter( &iter, dict, min, max );
|
||||
dict_initIter( &iter, dict, min, max );
|
||||
LengthsArray lens;
|
||||
XP_U32 count = dict_countWords( &iter, &lens );
|
||||
|
||||
|
@ -1968,7 +2005,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
gotOne = dict_getNextWord( &iter ) ) {
|
||||
XP_ASSERT( dict_getPosition( &iter ) == jj );
|
||||
XP_UCHAR buf[64];
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
dict_wordToString( &iter, buf, VSIZE(buf), "." );
|
||||
# ifdef PRINT_ALL
|
||||
fprintf( stderr, "%.6ld: %s\n", jj, buf );
|
||||
# endif
|
||||
|
@ -1984,7 +2021,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
++jj, gotOne = dict_getPrevWord( &iter ) ) {
|
||||
XP_ASSERT( dict_getPosition(&iter) == count-jj-1 );
|
||||
XP_UCHAR buf[64];
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
dict_wordToString( &iter, buf, VSIZE(buf), "." );
|
||||
# ifdef PRINT_ALL
|
||||
fprintf( stderr, "%.6ld: %s\n", jj, buf );
|
||||
# endif
|
||||
|
@ -2026,20 +2063,20 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
}
|
||||
XP_ASSERT( word.index == indices[ii] );
|
||||
XP_UCHAR buf1[64];
|
||||
dict_wordToString( dict, &word, buf1, VSIZE(buf1) );
|
||||
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) );
|
||||
dict_wordToString( dict, &word, buf2, VSIZE(buf2), "." );
|
||||
}
|
||||
char prfx[8];
|
||||
dict_tilesToString( dict, &prefixes[depth*ii], depth, prfx,
|
||||
VSIZE(prfx) );
|
||||
VSIZE(prfx), NULL );
|
||||
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" );
|
||||
XP_LOGFF( "testing getNth WITH INDEXING" );
|
||||
testGetNthWord( dict, words, depth, &data, min, max );
|
||||
|
||||
if ( !!testPrefixes ) {
|
||||
|
@ -2047,31 +2084,13 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
guint count = g_slist_length( testPrefixes );
|
||||
for ( ii = 0; ii < count; ++ii ) {
|
||||
gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii );
|
||||
XP_S16 lenMatched = dict_findStartsWith( &iter, prefix );
|
||||
if ( 0 <= lenMatched ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_UCHAR bufPrev[32] = {0};
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
XP_LOGFF( "prefix %d: %s", ii, prefix );
|
||||
|
||||
/* This doesn't work with synonyms like "L-L" for "L·L" */
|
||||
// XP_ASSERT( 0 == strncasecmp( buf, prefix, lenMatched ) );
|
||||
|
||||
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 );
|
||||
}
|
||||
FTData foundTilesData = { .iter = &iter, .words = words,
|
||||
.depth = depth, .data = &data,
|
||||
.prefix = prefix, };
|
||||
dict_tilesForString( dict, prefix, onFoundTiles, &foundTilesData );
|
||||
}
|
||||
|
||||
}
|
||||
XP_FREE( mpool, data.indices );
|
||||
XP_FREE( mpool, data.prefixes );
|
||||
|
@ -2108,7 +2127,7 @@ dumpDict( DictionaryCtxt* dict )
|
|||
result;
|
||||
result = dict_getNextWord( &iter ) ) {
|
||||
XP_UCHAR buf[32];
|
||||
dict_wordToString( &iter, buf, VSIZE(buf) );
|
||||
dict_wordToString( &iter, buf, VSIZE(buf), "." );
|
||||
fprintf( stdout, "%s\n", buf );
|
||||
}
|
||||
}
|
||||
|
@ -2594,8 +2613,10 @@ main( int argc, char** argv )
|
|||
mainParams.dbName = "xwgames.sqldb";
|
||||
mainParams.cursesListWinHt = 5;
|
||||
|
||||
trimDictPath( "./dict.xwd", dictbuf, VSIZE(dictbuf), &path, &dict );
|
||||
mainParams.pgi.dictName = copyString( mainParams.mpool, dict );
|
||||
if ( file_exists( "./dict.xwd" ) ) {
|
||||
trimDictPath( "./dict.xwd", dictbuf, VSIZE(dictbuf), &path, &dict );
|
||||
mainParams.pgi.dictName = copyString( mainParams.mpool, dict );
|
||||
}
|
||||
|
||||
char* envDictPath = getenv( "XW_DICTDIR" );
|
||||
XP_LOGFF( "envDictPath=%s", envDictPath );
|
||||
|
|
Loading…
Add table
Reference in a new issue