fix search of wordlists containing duplicates

Hungarian is unique (so far) in having two-letter tiles that can be
spelled with one-letter tiles AND in allowing words to be spelled both
ways. This crashed search based on strings because there were
duplicates. So now search is done by tile arrays. Strings are first
converted, and then IFF there is more than one tile array result AND the
wordlist has the new flag indicating that duplicates are possible, THEN
the user is asked to choose among the possible tile spellings of the
search string.
This commit is contained in:
Eric House 2020-05-04 08:33:15 -07:00
parent 98ce0e416f
commit 0e9661aa19
15 changed files with 436 additions and 229 deletions

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */ /* -*- compile-command: "find-and-gradle.sh inXw4dDeb"; -*- */
/* /*
* Copyright 2009 - 2011 by Eric House (xwords@eehouse.org). All * Copyright 2009 - 2020 by Eric House (xwords@eehouse.org). All
* rights reserved. * rights reserved.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -21,6 +21,8 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -47,6 +49,7 @@ import java.util.Arrays;
public class DictBrowseDelegate extends DelegateBase public class DictBrowseDelegate extends DelegateBase
implements View.OnClickListener, OnItemSelectedListener { implements View.OnClickListener, OnItemSelectedListener {
private static final String TAG = DictBrowseDelegate.class.getSimpleName(); private static final String TAG = DictBrowseDelegate.class.getSimpleName();
private static final String DELIM = ".";
private static final String DICT_NAME = "DICT_NAME"; private static final String DICT_NAME = "DICT_NAME";
private static final String DICT_LOC = "DICT_LOC"; private static final String DICT_LOC = "DICT_LOC";
@ -96,7 +99,7 @@ public class DictBrowseDelegate extends DelegateBase
{ {
TextView text = (TextView) TextView text = (TextView)
inflate( android.R.layout.simple_list_item_1 ); inflate( android.R.layout.simple_list_item_1 );
String str = XwJNI.di_nthWord( m_dictClosure, position ); String str = XwJNI.di_nthWord( m_dictClosure, position, null );
if ( null != str ) { if ( null != str ) {
text.setText( str ); text.setText( str );
text.setOnClickListener( DictBrowseDelegate.this ); text.setOnClickListener( DictBrowseDelegate.this );
@ -136,6 +139,7 @@ public class DictBrowseDelegate extends DelegateBase
return section; return section;
} }
@Override
public Object[] getSections() public Object[] getSections()
{ {
m_prefixes = XwJNI.di_getPrefixes( m_dictClosure ); m_prefixes = XwJNI.di_getPrefixes( m_dictClosure );
@ -242,7 +246,7 @@ public class DictBrowseDelegate extends DelegateBase
setFindText( m_browseState.m_prefix ); setFindText( m_browseState.m_prefix );
} }
@Override
protected void onDestroy() protected void onDestroy()
{ {
XwJNI.di_destroy( m_dictClosure ); XwJNI.di_destroy( m_dictClosure );
@ -253,6 +257,7 @@ public class DictBrowseDelegate extends DelegateBase
@Override @Override
public void finalize() public void finalize()
{ {
Assert.assertTrueNR( m_dictClosure == 0 );
XwJNI.di_destroy( m_dictClosure ); XwJNI.di_destroy( m_dictClosure );
try { try {
super.finalize(); super.finalize();
@ -261,6 +266,48 @@ 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.di_tilesToStr( m_dictClosure, choices[ii], DELIM );
}
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] ) {
byte[][] theOne = {choices[chosen[0]]};
showPrefix( theOne, DELIM );
}
}
} )
.setTitle( R.string.pick_tiles_title )
.create();
break;
default:
dialog = super.makeDialog( alert, params );
break;
}
return dialog;
}
////////////////////////////////////////////////// //////////////////////////////////////////////////
// View.OnClickListener interface // View.OnClickListener interface
////////////////////////////////////////////////// //////////////////////////////////////////////////
@ -327,7 +374,16 @@ public class DictBrowseDelegate extends DelegateBase
String text = getFindText(); String text = getFindText();
if ( null != text && 0 < text.length() ) { if ( null != text && 0 < text.length() ) {
m_browseState.m_prefix = text; m_browseState.m_prefix = text;
showPrefix();
byte[][] choices = XwJNI.di_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 || !XwJNI.di_hasDuplicates(m_dictClosure) ) {
showPrefix( choices, null );
} else {
showDialogFragment( DlgID.CHOOSE_TILES, (Object)choices );
}
} }
} }
@ -343,14 +399,14 @@ public class DictBrowseDelegate extends DelegateBase
edit.setText( text ); edit.setText( text );
} }
private void showPrefix() private void showPrefix( byte[][] prefix, String delim )
{ {
String text = m_browseState.m_prefix; if ( null != prefix && 0 < prefix.length && 0 < prefix[0].length ) {
if ( null != text && 0 < text.length() ) { int pos = XwJNI.di_getStartsWith( m_dictClosure, prefix );
int pos = XwJNI.di_getStartsWith( m_dictClosure, text );
if ( 0 <= pos ) { if ( 0 <= pos ) {
m_list.setSelection( pos ); m_list.setSelection( pos );
} else { } else {
String text = XwJNI.di_tilesToStr( m_dictClosure, prefix[0], delim );
DbgUtils.showf( m_activity, R.string.dict_browse_nowords_fmt, DbgUtils.showf( m_activity, R.string.dict_browse_nowords_fmt,
m_name, text ); m_name, text );
} }

View file

@ -68,6 +68,7 @@ public enum DlgID {
, CHANGE_CONN , CHANGE_CONN
, GAMES_LIST_NAME_REMATCH , GAMES_LIST_NAME_REMATCH
, ASK_DUP_PAUSE , ASK_DUP_PAUSE
, CHOOSE_TILES
; ;
private boolean m_addToStack; private boolean m_addToStack;

View file

@ -514,22 +514,22 @@ public class XwJNI {
// Dict iterator // Dict iterator
public final static int MAX_COLS_DICT = 15; // from dictiter.h public final static int MAX_COLS_DICT = 15; // from dictiter.h
public static long di_init( byte[] dict, String name, public static long di_init( byte[] dict, String name, String path )
String path )
{ {
return di_init( getJNI().m_ptrGlobals, dict, name, path ); return di_init( getJNI().m_ptrGlobals, dict, name, path );
} }
public static native void di_setMinMax( long closure, public static native void di_setMinMax( long closure, int min, int max );
int min, int max );
public static native void di_destroy( long closure ); public static native void di_destroy( long closure );
public static native int di_wordCount( long closure ); public static native int di_wordCount( long closure );
public static native int[] di_getCounts( long closure ); public static native int[] di_getCounts( long closure );
public static native String di_nthWord( long closure, int nn ); public static native String di_nthWord( long closure, int nn, String delim );
public static native String[] di_getPrefixes( long closure ); public static native String[] di_getPrefixes( long closure );
public static native int[] di_getIndices( long closure ); public static native int[] di_getIndices( long closure );
public static native int di_getStartsWith( long closure, public static native byte[][] di_strToTiles( long closure, String str );
String prefix ); public static native int di_getStartsWith( long closure, byte[][] prefix );
public static native String di_getDesc( long closure ); public static native String di_getDesc( long closure );
public static native String di_tilesToStr( long closure, byte[] tiles, String delim );
public static native boolean di_hasDuplicates( long closure );
// Private methods -- called only here // Private methods -- called only here
private static native long initGlobals( DUtilCtxt dutil, JNIUtils jniu ); private static native long initGlobals( DUtilCtxt dutil, JNIUtils jniu );
@ -560,5 +560,6 @@ public class XwJNI {
String fromPhone, String fromPhone,
int wantPort); int wantPort);
// This always returns true on release builds now.
private static native boolean haveEnv( long jniState ); private static native boolean haveEnv( long jniState );
} }

View file

@ -2548,4 +2548,7 @@
<!-- Debug-build-only question asked to confirm deletion of saved logs --> <!-- Debug-build-only question asked to confirm deletion of saved logs -->
<string name="logstore_clear_confirm">Are you sure you want to <string name="logstore_clear_confirm">Are you sure you want to
erase your debug logs? This action cannot be undone.</string> 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> </resources>

View file

@ -977,12 +977,15 @@ struct _JNIState {
XP_U16 lastSavedSize; XP_U16 lastSavedSize;
#ifdef DEBUG #ifdef DEBUG
const char* envSetterFunc; const char* envSetterFunc;
XP_U32 guard;
#endif #endif
MPSLOT MPSLOT
}; };
#define GAME_GUARD 0x453627
#define XWJNI_START() { \ #define XWJNI_START() { \
JNIState* state = getState( env, gamePtr, __func__ ); \ JNIState* state = getState( env, gamePtr, __func__ ); \
XP_ASSERT( state->guard == GAME_GUARD ); \
MPSLOT; \ MPSLOT; \
MPASSIGN( mpool, state->mpool ); \ MPASSIGN( mpool, state->mpool ); \
XP_ASSERT( !!state->globalJNI ); \ XP_ASSERT( !!state->globalJNI ); \
@ -1003,6 +1006,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initGameJNI
MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool; MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool;
#endif #endif
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) ); JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
#ifdef DEBUG
state->guard = GAME_GUARD;
#endif
state->globalJNI = (JNIGlobalState*)jniGlobalPtr; state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
MAP_THREAD( &state->globalJNI->ti, env ); MAP_THREAD( &state->globalJNI->ti, env );
AndGameGlobals* globals = &state->globals; AndGameGlobals* globals = &state->globals;
@ -1082,6 +1088,7 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
( JNIEnv* env, jclass claz, GamePtrType gamePtr ) ( JNIEnv* env, jclass claz, GamePtrType gamePtr )
{ {
JNIState* state = getState( env, gamePtr, __func__ ); JNIState* state = getState( env, gamePtr, __func__ );
XP_ASSERT( state->guard == GAME_GUARD );
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
MemPoolCtx* mpool = state->mpool; MemPoolCtx* mpool = state->mpool;
@ -1099,6 +1106,9 @@ JNIEXPORT void JNICALL Java_org_eehouse_android_xw4_jni_XwJNI_game_1dispose
MAP_REMOVE( &state->globalJNI->ti, env ); MAP_REMOVE( &state->globalJNI->ti, env );
#ifdef DEBUG
XP_MEMSET( state, -1, sizeof(*state) );
#endif
XP_FREE( mpool, state ); XP_FREE( mpool, state );
} /* game_dispose */ } /* game_dispose */
@ -2398,19 +2408,18 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1sendChat
typedef struct _DictIterData { typedef struct _DictIterData {
JNIGlobalState* globalState; JNIGlobalState* globalState;
JNIUtilCtxt* jniutil;
VTableMgr* vtMgr;
DictionaryCtxt* dict; DictionaryCtxt* dict;
DictIter iter; DictIter iter;
IndexData idata; IndexData idata;
XP_U16 depth; XP_U16 depth;
#ifdef MEM_DEBUG #ifdef DEBUG
MemPoolCtx* mpool; XP_U32 guard;
#endif #endif
} DictIterData; } DictIterData;
static void makeIndex( DictIterData* data ); static void makeIndex( DictIterData* data );
static void freeIndices( DictIterData* data ); static void freeIndices( DictIterData* data );
#define GI_GUARD 0x89ab72
JNIEXPORT jlong JNICALL JNIEXPORT jlong JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1init Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
@ -2428,47 +2437,54 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
if ( !!dict ) { if ( !!dict ) {
DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) ); DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
data->globalState = globalState; data->globalState = globalState;
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globalState->mpool) );
data->jniutil = globalState->jniutil;
data->dict = dict; data->dict = dict;
data->depth = 2; data->depth = 2;
#ifdef MEM_DEBUG #ifdef DEBUG
data->mpool = globalState->mpool; data->guard = GI_GUARD;
#endif #endif
closure = (jlong)data; closure = (jlong)data;
} }
return closure; return closure;
} }
#define DI_HEADER() { \
DictIterData* data = (DictIterData*)closure; \
XP_ASSERT( NULL == data || data->guard == GI_GUARD ); \
#define DI_HEADER_END() \
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1setMinMax Java_org_eehouse_android_xw4_jni_XwJNI_di_1setMinMax
( JNIEnv* env, jclass C, jlong closure, jint min, jint max ) ( JNIEnv* env, jclass C, jlong closure, jint min, jint max )
{ {
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
di_initIter( &data->iter, data->dict, min, max ); di_initIter( &data->iter, data->dict, min, max );
makeIndex( data ); makeIndex( data );
(void)di_firstWord( &data->iter ); (void)di_firstWord( &data->iter );
} }
DI_HEADER_END();
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1destroy Java_org_eehouse_android_xw4_jni_XwJNI_di_1destroy
( JNIEnv* env, jclass C, jlong closure ) ( JNIEnv* env, jclass C, jlong closure )
{ {
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
MemPoolCtx* mpool = data->mpool; MemPoolCtx* mpool = data->globalState->mpool;
#endif #endif
dict_unref( data->dict, env ); dict_unref( data->dict, env );
freeIndices( data ); freeIndices( data );
MAP_REMOVE( &data->globalState->ti, env ); MAP_REMOVE( &data->globalState->ti, env );
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
XP_FREE( mpool, data ); XP_FREE( mpool, data );
} }
DI_HEADER_END();
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
@ -2476,10 +2492,11 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1wordCount
(JNIEnv* env, jclass C, jlong closure ) (JNIEnv* env, jclass C, jlong closure )
{ {
jint result = 0; jint result = 0;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
result = data->iter.nWords; result = data->iter.nWords;
} }
DI_HEADER_END();
return result; return result;
} }
@ -2488,7 +2505,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getCounts
(JNIEnv* env, jclass C, jlong closure ) (JNIEnv* env, jclass C, jlong closure )
{ {
jintArray result = NULL; jintArray result = NULL;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
DictIter iter; DictIter iter;
di_initIter( &iter, data->dict, 0, MAX_COLS_DICT ); di_initIter( &iter, data->dict, 0, MAX_COLS_DICT );
@ -2500,6 +2517,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getCounts
sizeof(lens.lens[0]) ); sizeof(lens.lens[0]) );
} }
} }
DI_HEADER_END();
return result; return result;
} }
@ -2508,7 +2526,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getPrefixes
( JNIEnv* env, jclass C, jlong closure ) ( JNIEnv* env, jclass C, jlong closure )
{ {
jobjectArray result = NULL; jobjectArray result = NULL;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data && NULL != data->idata.prefixes ) { if ( NULL != data && NULL != data->idata.prefixes ) {
result = makeStringArray( env, data->idata.count, NULL ); result = makeStringArray( env, data->idata.count, NULL );
@ -2517,12 +2535,13 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getPrefixes
XP_UCHAR buf[16]; XP_UCHAR buf[16];
(void)dict_tilesToString( data->dict, (void)dict_tilesToString( data->dict,
&data->idata.prefixes[depth*ii], &data->idata.prefixes[depth*ii],
depth, buf, VSIZE(buf) ); depth, buf, VSIZE(buf), NULL );
jstring jstr = (*env)->NewStringUTF( env, buf ); jstring jstr = (*env)->NewStringUTF( env, buf );
(*env)->SetObjectArrayElement( env, result, ii, jstr ); (*env)->SetObjectArrayElement( env, result, ii, jstr );
deleteLocalRef( env, jstr ); deleteLocalRef( env, jstr );
} }
} }
DI_HEADER_END();
return result; return result;
} }
@ -2531,7 +2550,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getIndices
( JNIEnv* env, jclass C, jlong closure ) ( JNIEnv* env, jclass C, jlong closure )
{ {
jintArray jindices = NULL; jintArray jindices = NULL;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
XP_ASSERT( !!data->idata.indices ); XP_ASSERT( !!data->idata.indices );
XP_ASSERT( sizeof(jint) == sizeof(data->idata.indices[0]) ); XP_ASSERT( sizeof(jint) == sizeof(data->idata.indices[0]) );
@ -2539,38 +2558,133 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getIndices
(jint*)data->idata.indices, (jint*)data->idata.indices,
sizeof(data->idata.indices[0]) ); sizeof(data->idata.indices[0]) );
} }
DI_HEADER_END();
return jindices; return jindices;
} }
JNIEXPORT jstring JNICALL JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1nthWord Java_org_eehouse_android_xw4_jni_XwJNI_di_1nthWord
( JNIEnv* env, jclass C, jlong closure, jint nn) ( JNIEnv* env, jclass C, jlong closure, jint nn, jstring jdelim )
{ {
jstring result = NULL; jstring result = NULL;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
if ( di_getNthWord( &data->iter, nn, data->depth, &data->idata ) ) { if ( di_getNthWord( &data->iter, nn, data->depth, &data->idata ) ) {
XP_UCHAR buf[64]; XP_UCHAR buf[64];
di_wordToString( &data->iter, buf, VSIZE(buf) ); const XP_UCHAR* delim = NULL == jdelim ? NULL
: (*env)->GetStringUTFChars( env, jdelim, NULL );
di_wordToString( &data->iter, buf, VSIZE(buf), delim );
result = (*env)->NewStringUTF( env, buf ); result = (*env)->NewStringUTF( env, buf );
if ( !!delim ) {
(*env)->ReleaseStringUTFChars( env, jdelim, delim );
} }
} }
}
DI_HEADER_END();
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_di_1strToTiles
( JNIEnv* env, jclass C, jlong closure, jstring jstr )
{
jobjectArray result = NULL;
DI_HEADER();
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 );
DI_HEADER_END();
return result; return result;
} }
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1getStartsWith Java_org_eehouse_android_xw4_jni_XwJNI_di_1getStartsWith
( JNIEnv* env, jclass C, jlong closure, jstring jprefix ) ( JNIEnv* env, jclass C, jlong closure, jobjectArray jtilesArr )
{ {
jint result = -1; jint result = -1;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) {
const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL ); int len = (*env)->GetArrayLength( env, jtilesArr );
if ( 0 <= di_findStartsWith( &data->iter, prefix ) ) { for ( int ii = 0; ii < len && -1 == result ; ++ii ) {
jbyteArray jtiles = (*env)->GetObjectArrayElement( env, jtilesArr, ii );
XP_U16 nTiles = (*env)->GetArrayLength( env, jtiles );
jbyte* tiles = (*env)->GetByteArrayElements( env, jtiles, NULL );
if ( 0 <= di_findStartsWith( &data->iter, (Tile*)tiles, nTiles ) ) {
result = di_getPosition( &data->iter ); result = di_getPosition( &data->iter );
} }
(*env)->ReleaseStringUTFChars( env, jprefix, prefix ); (*env)->ReleaseByteArrayElements( env, jtiles, tiles, 0 );
deleteLocalRef( env, jtiles );
} }
DI_HEADER_END();
return result;
}
JNIEXPORT jstring JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1tilesToStr
( JNIEnv* env, jclass C, jlong closure, jbyteArray jtiles, jstring jdelim )
{
jstring result = NULL;
DI_HEADER();
XP_UCHAR buf[64];
const XP_UCHAR* delim = NULL;
if ( !!jdelim ) {
delim = (*env)->GetStringUTFChars( env, jdelim, NULL );
}
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 );
DI_HEADER_END();
return result;
}
JNIEXPORT jboolean JNICALL
Java_org_eehouse_android_xw4_jni_XwJNI_di_1hasDuplicates
( JNIEnv* env, jclass C, jlong closure )
{
jboolean result;
DI_HEADER();
result = dict_hasDuplicates( data->dict );
DI_HEADER_END();
return result; return result;
} }
@ -2579,13 +2693,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getDesc
( JNIEnv* env, jclass C, jlong closure ) ( JNIEnv* env, jclass C, jlong closure )
{ {
jstring result = NULL; jstring result = NULL;
DictIterData* data = (DictIterData*)closure; DI_HEADER();
if ( NULL != data ) { if ( NULL != data ) {
const XP_UCHAR* disc = dict_getDesc( data->dict ); const XP_UCHAR* disc = dict_getDesc( data->dict );
if ( NULL != disc && '\0' != disc[0] ) { if ( NULL != disc && '\0' != disc[0] ) {
result = (*env)->NewStringUTF( env, disc ); result = (*env)->NewStringUTF( env, disc );
} }
} }
DI_HEADER_END();
return result; return result;
} }
@ -2594,11 +2709,11 @@ freeIndices( DictIterData* data )
{ {
IndexData* idata = &data->idata; IndexData* idata = &data->idata;
if ( !!idata->prefixes ) { if ( !!idata->prefixes ) {
XP_FREE( data->mpool, idata->prefixes ); XP_FREE( data->globalState->mpool, idata->prefixes );
idata->prefixes = NULL; idata->prefixes = NULL;
} }
if( !!idata->indices ) { if( !!idata->indices ) {
XP_FREE( data->mpool, idata->indices ); XP_FREE( data->globalState->mpool, idata->indices );
idata->indices = NULL; idata->indices = NULL;
} }
} }
@ -2616,18 +2731,20 @@ makeIndex( DictIterData* data )
freeIndices( data ); freeIndices( data );
IndexData* idata = &data->idata; IndexData* idata = &data->idata;
idata->prefixes = XP_MALLOC( data->mpool, count * data->depth #ifdef MEM_DEBUG
MemPoolCtx* mpool = data->globalState->mpool;
#endif
idata->prefixes = XP_MALLOC( mpool, count * data->depth
* sizeof(*idata->prefixes) ); * sizeof(*idata->prefixes) );
idata->indices = XP_MALLOC( data->mpool, idata->indices = XP_MALLOC( mpool, count * sizeof(*idata->indices) );
count * sizeof(*idata->indices) );
idata->count = count; idata->count = count;
di_makeIndex( &data->iter, data->depth, idata ); di_makeIndex( &data->iter, data->depth, idata );
if ( 0 < idata->count ) { if ( 0 < idata->count ) {
idata->prefixes = XP_REALLOC( data->mpool, idata->prefixes, idata->prefixes = XP_REALLOC( mpool, idata->prefixes,
idata->count * data->depth * idata->count * data->depth *
sizeof(*idata->prefixes) ); sizeof(*idata->prefixes) );
idata->indices = XP_REALLOC( data->mpool, idata->indices, idata->indices = XP_REALLOC( mpool, idata->indices,
idata->count * sizeof(*idata->indices) ); idata->count * sizeof(*idata->indices) );
} else { } else {
freeIndices( data ); freeIndices( data );

View file

@ -3726,6 +3726,22 @@ moveTileToBoard( BoardCtxt* board, XWEnv xwe, XP_U16 col, XP_U16 row,
} /* moveTileToBoard */ } /* moveTileToBoard */
#ifdef KEY_SUPPORT #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 */ /* Return number between 0 and MAX_TRAY_TILES-1 for valid index, < 0 otherwise */
static XP_S16 static XP_S16
keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace ) keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace )
@ -3743,19 +3759,19 @@ keyToIndex( BoardCtxt* board, XP_Key key, Tile* blankFace )
if ( tileIndex < 0 ) { if ( tileIndex < 0 ) {
DictionaryCtxt* dict = model_getDictionary( model ); DictionaryCtxt* dict = model_getDictionary( model );
Tile tile;
XP_UCHAR buf[2] = { key, '\0' }; XP_UCHAR buf[2] = { key, '\0' };
/* Figure out if we have the tile in the tray */ /* Figure out if we have the tile in the tray */
XP_U16 nTiles = 1; FTData ftd = {0};
if ( dict_tilesForString( dict, buf, &tile, &nTiles ) ) { /* in dict? */ dict_tilesForString( dict, buf, foundTiles, &ftd );
if ( ftd.found ) {
XP_S16 turn = board->selPlayer; XP_S16 turn = board->selPlayer;
tileIndex = model_trayContains( model, turn, tile ); tileIndex = model_trayContains( model, turn, ftd.tile );
if ( tileIndex < 0 ) { if ( tileIndex < 0 ) {
Tile blankTile = dict_getBlankTile( dict ); Tile blankTile = dict_getBlankTile( dict );
tileIndex = model_trayContains( model, turn, blankTile ); tileIndex = model_trayContains( model, turn, blankTile );
if ( tileIndex >= 0 && !!blankFace ) { /* there's a blank for it */ if ( tileIndex >= 0 && !!blankFace ) { /* there's a blank for it */
*blankFace = tile; *blankFace = ftd.tile;
} }
} }
} }

View file

@ -196,57 +196,6 @@ findStartsWithTiles( DictIter* iter, const Tile* tiles, XP_U16 nTiles )
return 0 == 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 static XP_Bool
startsWith( const DictIter* iter, const Tile* tiles, XP_U16 nTiles ) startsWith( const DictIter* iter, const Tile* tiles, XP_U16 nTiles )
{ {
@ -395,7 +344,8 @@ placeWordClose( DictIter* iter, const DictPosition position, XP_U16 depth,
} /* placeWordClose */ } /* placeWordClose */
static void 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 ii;
XP_U16 nEdges = iter->nEdges; XP_U16 nEdges = iter->nEdges;
@ -403,7 +353,7 @@ iterToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen )
for ( ii = 0; ii < nEdges; ++ii ) { for ( ii = 0; ii < nEdges; ++ii ) {
tiles[ii] = EDGETILE( iter->dict, iter->edges[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 #if 0
@ -504,7 +454,7 @@ di_makeIndex( const DictIter* iter, XP_U16 depth, IndexData* data )
DI_ASSERT( data->indices[pos-1] < data->indices[pos] ); DI_ASSERT( data->indices[pos-1] < data->indices[pos] );
} }
#endif #endif
} /* di_makeIndex */ } /* dict_makeIndex */
static void static void
initWord( DictIter* iter ) initWord( DictIter* iter )
@ -665,33 +615,29 @@ figurePosition( DictIter* iter )
} }
XP_S16 XP_S16
di_findStartsWith( DictIter* iter, const XP_UCHAR* prefix ) di_findStartsWith( DictIter* iter, const Tile* prefix, XP_U16 len )
{ {
ASSERT_INITED( iter ); ASSERT_INITED( iter );
array_edge* edge = dict_getTopEdge( iter->dict ); XP_S16 result = -1;
XP_S16 offset = findStartsWithChars( iter, prefix, 0, edge, 0 );
if ( 0 > offset ) { if ( findStartsWithTiles( iter, prefix, len ) ) {
/* not found; do nothing */ XP_UCHAR buf[32];
} else if ( 0 == offset ) { XP_U16 offset = dict_tilesToString( iter->dict, prefix,
if ( !firstWord( iter ) ) { len, buf, VSIZE(buf), NULL );
offset = -1;
}
} else {
if ( ACCEPT_ITER( iter, iter->nEdges ) || nextWord( iter ) ) { if ( ACCEPT_ITER( iter, iter->nEdges ) || nextWord( iter ) ) {
DictPosition result = figurePosition( iter ); iter->position = figurePosition( iter );
iter->position = result; result = offset;
} else {
offset = -1;
} }
} }
return offset; return result;
} }
void void
di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen ) di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
const XP_UCHAR* delim )
{ {
ASSERT_INITED( iter ); ASSERT_INITED( iter );
iterToString( iter, buf, buflen ); iterToString( iter, buf, buflen, delim );
} }
DictPosition DictPosition

View file

@ -73,8 +73,10 @@ XP_Bool di_getNextWord( DictIter* iter );
XP_Bool di_getPrevWord( DictIter* iter ); XP_Bool di_getPrevWord( DictIter* iter );
XP_Bool di_getNthWord( DictIter* iter, DictPosition position, XP_U16 depth, XP_Bool di_getNthWord( DictIter* iter, DictPosition position, XP_U16 depth,
const IndexData* data ); const IndexData* data );
void di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen ); void di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
XP_S16 di_findStartsWith( DictIter* iter, const XP_UCHAR* prefix ); const XP_UCHAR* delim );
XP_S16 di_findStartsWith( DictIter* iter, const Tile* prefix, XP_U16 nTiles );
void di_stringToTiles( const XP_UCHAR* str, Tile out[], XP_U16* nTiles );
DictPosition di_getPosition( const DictIter* iter ); DictPosition di_getPosition( const DictIter* iter );
#ifdef CPLUS #ifdef CPLUS
} }

View file

@ -232,11 +232,11 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
XP_U16 headerLen; XP_U16 headerLen;
XP_U32 wordCount; XP_U32 wordCount;
memcpy( &headerLen, ptr, sizeof(headerLen) ); XP_MEMCPY( &headerLen, ptr, sizeof(headerLen) );
ptr += sizeof(headerLen); ptr += sizeof(headerLen);
headerLen = XP_NTOHS( headerLen ); headerLen = XP_NTOHS( headerLen );
memcpy( &wordCount, ptr, sizeof(wordCount) ); XP_MEMCPY( &wordCount, ptr, sizeof(wordCount) );
ptr += sizeof(wordCount); ptr += sizeof(wordCount);
headerLen -= sizeof(wordCount); headerLen -= sizeof(wordCount);
dctx->nWords = XP_NTOHL( wordCount ); dctx->nWords = XP_NTOHL( wordCount );
@ -252,6 +252,20 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
} else { } else {
XP_LOGF( "%s: no md5Sum", __func__ ); XP_LOGF( "%s: no md5Sum", __func__ );
} }
XP_U16 headerFlags = 0;
if ( sizeof(headerFlags) <= headerLen ) {
XP_MEMCPY( &headerFlags, ptr, sizeof(headerFlags) );
headerFlags = XP_NTOHS( headerFlags );
ptr += sizeof(headerFlags);
headerLen -= sizeof(headerFlags);
}
XP_LOGFF( "setting headerFlags: 0x%x", headerFlags );
dctx->headerFlags = headerFlags;
if ( 0 < headerLen ) {
XP_LOGFF( "skipping %d bytes of header", headerLen );
}
ptr += headerLen; ptr += headerLen;
} }
@ -463,40 +477,48 @@ dict_numTileFaces( const DictionaryCtxt* dict )
return dict->nFaces; return dict->nFaces;
} /* dict_numTileFaces */ } /* 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 XP_U16
dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles, 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* bufp = buf;
XP_UCHAR* end = bufp + bufSize; const XP_UCHAR* end = bufp + bufSize;
XP_U16 result = 0; XP_U16 delimLen = NULL == delim ? 0 : XP_STRLEN(delim);
while ( nTiles-- ) { for ( int ii = 0; ii < nTiles && !!bufp; ++ii ) {
Tile tile = *tiles++;
if ( 0 < delimLen && 0 < ii ) {
appendIfSpace( &bufp, end, delim );
}
Tile tile = tiles[ii];
const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile ); const XP_UCHAR* facep = dict_getTileStringRaw( dict, tile );
if ( IS_SPECIAL(*facep) ) { if ( IS_SPECIAL(*facep) ) {
XP_UCHAR* chars = dict->chars[(XP_U16)*facep]; XP_UCHAR* chars = dict->chars[(XP_U16)*facep];
XP_U16 len = XP_STRLEN( chars ); appendIfSpace( &bufp, end, chars );
if ( bufp + len >= end ) {
bufp = NULL;
break;
}
XP_MEMCPY( bufp, chars, len );
bufp += len;
} else { } else {
XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be XP_ASSERT ( tile != dict->blankTile ); /* printing blank should be
handled by specials handled by specials
mechanism */ mechanism */
if ( bufp + 1 >= end ) { appendIfSpace( &bufp, end, facep );
bufp = NULL;
break;
}
bufp += XP_SNPRINTF( bufp, end - bufp, XP_S, facep );
} }
} }
if ( bufp != NULL && bufp < end ) { XP_U16 result = 0;
if ( !!bufp && bufp < end ) {
*bufp = '\0'; *bufp = '\0';
result = bufp - buf; result = bufp - buf;
} }
@ -507,46 +529,48 @@ dict_tilesToString( const DictionaryCtxt* dict, const Tile* tiles,
* run out of room in which to return tiles. Failure to match means return of * 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. * 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, 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] ) { 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 { } else {
goOn = XP_TRUE;
XP_U16 nFaces = dict_numTileFaces( dict ); XP_U16 nFaces = dict_numTileFaces( dict );
Tile tile; for ( Tile tile = 0; goOn && tile < nFaces; ++tile ) {
for ( tile = 0; tile < nFaces; ++tile ) {
if ( tile != dict->blankTile ) { if ( tile != dict->blankTile ) {
const XP_UCHAR* facep = dict_getTileString( dict, tile ); for ( const XP_UCHAR* facep = NULL; ; ) {
XP_U16 faceLen = XP_STRLEN( facep ); facep = dict_getNextTileString( dict, tile, facep );
if ( 0 == XP_STRNCMP( facep, str, faceLen ) ) { if ( !facep ) {
XP_S16 maxFound = tilesForStringImpl( dict, str + faceLen,
tiles, nTiles,
nFound + 1 );
if ( 0 <= maxFound ) {
tiles[nFound] = tile;
result = maxFound;
break; 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 */ } /* tilesForStringImpl */
XP_Bool void
dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* str, 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 ); Tile tiles[32];
XP_Bool success = 0 <= nFound; tilesForStringImpl( dict, str, tiles, VSIZE(tiles), 0, proc, closure );
if ( success ) {
*nTilesP = nFound;
}
return success;
} /* dict_tilesForString */ } /* dict_tilesForString */
XP_Bool XP_Bool
@ -863,6 +887,12 @@ dict_getMd5Sum( const DictionaryCtxt* dict )
return dict->md5Sum; return dict->md5Sum;
} }
XP_Bool
dict_hasDuplicates( const DictionaryCtxt* dict )
{
return 0 != (dict->headerFlags & HEADERFLAGS_DUPS_SUPPORTED_BIT);
}
#ifdef STUBBED_DICT #ifdef STUBBED_DICT
#define BLANK_FACE '\0' #define BLANK_FACE '\0'

View file

@ -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 * Copyright 1997 - 2020 by Eric House (xwords@eehouse.org). All rights
* reserved. * reserved.
@ -58,6 +58,8 @@ typedef struct _XP_Bitmaps {
XP_Bitmap bmps[2]; /* 2 is private, may change */ XP_Bitmap bmps[2]; /* 2 is private, may change */
} XP_Bitmaps; } XP_Bitmaps;
#define HEADERFLAGS_DUPS_SUPPORTED_BIT 0x0001
struct DictionaryCtxt { struct DictionaryCtxt {
void (*destructor)( DictionaryCtxt* dict, XWEnv xwe ); void (*destructor)( DictionaryCtxt* dict, XWEnv xwe );
@ -94,6 +96,7 @@ struct DictionaryCtxt {
XP_LangCode langCode; XP_LangCode langCode;
XP_U16 refCount; XP_U16 refCount;
XP_U16 headerFlags;
XP_U8 nFaces; XP_U8 nFaces;
XP_U8 nodeSize; XP_U8 nodeSize;
XP_Bool is_4_byte; XP_Bool is_4_byte;
@ -177,7 +180,8 @@ XP_U16 dict_numTiles( const DictionaryCtxt* ctxt, Tile tile );
XP_U16 dict_numTileFaces( const DictionaryCtxt* ctxt ); XP_U16 dict_numTileFaces( const DictionaryCtxt* ctxt );
XP_U16 dict_tilesToString( const DictionaryCtxt* ctxt, const Tile* tiles, 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_getTileString( const DictionaryCtxt* ctxt, Tile tile );
const XP_UCHAR* dict_getNextTileString( const DictionaryCtxt* ctxt, Tile tile, const XP_UCHAR* dict_getNextTileString( const DictionaryCtxt* ctxt, Tile tile,
const XP_UCHAR* cur ); const XP_UCHAR* cur );
@ -186,8 +190,9 @@ const XP_UCHAR* dict_getLangName(const DictionaryCtxt* ctxt );
XP_Bool dict_isUTF8( const DictionaryCtxt* ctxt ); XP_Bool dict_isUTF8( const DictionaryCtxt* ctxt );
XP_Bool dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* key, typedef XP_Bool (*OnFoundTiles)(void* closure, const Tile* tiles, int len);
Tile* tiles, XP_U16* nTiles ); void dict_tilesForString( const DictionaryCtxt* dict, const XP_UCHAR* key,
OnFoundTiles proc, void* closure );
XP_Bool dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile ); XP_Bool dict_faceIsBitmap( const DictionaryCtxt* dict, Tile tile );
void dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile, void dict_getFaceBitmaps( const DictionaryCtxt* dict, Tile tile,
@ -198,6 +203,7 @@ XP_U32 dict_getWordCount( const DictionaryCtxt* dict );
const XP_UCHAR* dict_getDesc( const DictionaryCtxt* dict ); const XP_UCHAR* dict_getDesc( const DictionaryCtxt* dict );
const XP_UCHAR* dict_getMd5Sum( const DictionaryCtxt* dict ); const XP_UCHAR* dict_getMd5Sum( const DictionaryCtxt* dict );
XP_Bool dict_hasDuplicates( const DictionaryCtxt* dict );
void dict_writeToStream( const DictionaryCtxt* ctxt, XWStreamCtxt* stream ); void dict_writeToStream( const DictionaryCtxt* ctxt, XWStreamCtxt* stream );
void dict_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream ); void dict_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream );

View file

@ -2245,7 +2245,7 @@ formatTray( const TrayTileSet* tiles, DictionaryCtxt* dict, XP_UCHAR* buf,
buf[ii] = '\0'; buf[ii] = '\0';
} else { } else {
dict_tilesToString( dict, (Tile*)tiles->tiles, tiles->nTiles, dict_tilesToString( dict, (Tile*)tiles->tiles, tiles->nTiles,
buf, bufSize ); buf, bufSize, NULL );
} }
return buf; return buf;

View file

@ -753,7 +753,7 @@ scoreWord( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
XP_UCHAR buf[(MAX_ROWS*2)+1]; XP_UCHAR buf[(MAX_ROWS*2)+1];
dict_tilesToString( dict, checkWordBuf, len, buf, dict_tilesToString( dict, checkWordBuf, len, buf,
sizeof(buf) ); sizeof(buf), NULL );
WNParams wnp = { .word = buf, .isLegal = legal, .dict = dict, WNParams wnp = { .word = buf, .isLegal = legal, .dict = dict,
#ifdef XWFEATURE_BOARDWORDS #ifdef XWFEATURE_BOARDWORDS
@ -889,7 +889,7 @@ wordScoreFormatterFinish( WordScoreFormatter* fmtr, Tile* word,
{ {
XP_UCHAR buf[(MAX_ROWS*2)+1]; XP_UCHAR buf[(MAX_ROWS*2)+1];
XP_U16 len = dict_tilesToString( fmtr->dict, word, fmtr->nTiles, XP_U16 len = dict_tilesToString( fmtr->dict, word, fmtr->nTiles,
buf, sizeof(buf) ); buf, sizeof(buf), NULL );
if ( !!stream ) { if ( !!stream ) {
stream_putBytes( stream, buf, len ); stream_putBytes( stream, buf, len );

View file

@ -23,6 +23,9 @@ TARGET_TYPE ?= WINCE
DICTNOTE = "Derived from szotar/alap/ in https://github.com/laszlonemeth/magyarispell.git" DICTNOTE = "Derived from szotar/alap/ in https://github.com/laszlonemeth/magyarispell.git"
# Needs only to be defined
ALLOWS_DUPLICATES = TRUE
LANG_SPECIAL_INFO = \ LANG_SPECIAL_INFO = \
"CS Cs cS cs" /dev/null /dev/null \ "CS Cs cS cs" /dev/null /dev/null \
"GY Gy gY gy" /dev/null /dev/null \ "GY Gy gY gy" /dev/null /dev/null \
@ -70,16 +73,16 @@ $(TMP_LIST): $(SRC)
$(XWLANG)Main.dict.gz: $(TMP_LIST) $(XWLANG)Main.dict.gz: $(TMP_LIST)
cat $< \ cat $< \
| tr -d '\r' \ | tr -d '\r' \
| grep -v '[1-7]' \
| tr [aábcdeéfghiíjklmnnyoóöőprtuúüűvzs] [AÁBCDEÉFGHIÍJKLMNNYOÓÖŐPRTUÚÜŰVZS] \ | tr [aábcdeéfghiíjklmnnyoóöőprtuúüűvzs] [AÁBCDEÉFGHIÍJKLMNNYOÓÖŐPRTUÚÜŰVZS] \
| grep -v '1\|2\|3\|4\|5\|6\|7' \ | sed -e 's,^\(.*\)CS\(.*\)$$,\11\2\n\1CS\2,g' \
| sed -e 's,CS,1,g' \
| sed -e 's,GY,2,g' \ | sed -e 's,GY,2,g' \
| sed -e 's,LY,3,g' \ | sed -e 's,LY,3,g' \
| sed -e 's,NY,4,g' \ | sed -e 's,NY,4,g' \
| sed -e 's,SZ,5,g' \ | sed -e 's,^\(.*\)SZ\(.*\)$$,\15\2\n\1SZ\2,g' \
| sed -e 's,TY,6,g' \ | sed -e 's,TY,6,g' \
| sed -e 's,ZS,7,g' \ | sed -e 's,^\(.*\)ZS\(.*\)$$,\17\2\n\1ZS\2,g' \
| grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]*$$' \ | grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]\{2,15\}$$' \
| tr '1234567' '\001\002\003\004\005\006\007' \ | tr '1234567' '\001\002\003\004\005\006\007' \
| gzip -c > $@ | gzip -c > $@

View file

@ -299,7 +299,12 @@ $(XWLANG)%_md5sum.bin:
dawg$(XWLANG)$*_*.bin | md5sum | awk '{print $$1}' | tr -d '\n' > $@ dawg$(XWLANG)$*_*.bin | md5sum | awk '{print $$1}' | tr -d '\n' > $@
perl -e "print pack(\"c\",0)" >> $@ perl -e "print pack(\"c\",0)" >> $@
$(XWLANG)%_newheader.bin: $(XWLANG)%_wordcount.bin $(XWLANG)%_note.bin $(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin:
[ -n "$(ALLOWS_DUPLICATES)" ] && FLAGS=1 || FLAGS=0; \
perl -e "print pack(\"n\",$$FLAGS)" > $@
$(XWLANG)%_newheader.bin: $(XWLANG)%_wordcount.bin $(XWLANG)%_note.bin \
$(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin
SIZ=0; \ SIZ=0; \
for FILE in $+; do \ for FILE in $+; do \
SIZ=$$(($$SIZ + $$(ls -l $$FILE | awk '{print $$5}'))); \ SIZ=$$(($$SIZ + $$(ls -l $$FILE | awk '{print $$5}'))); \

View file

@ -1912,7 +1912,7 @@ testGetNthWord( const DictionaryCtxt* dict, char** XP_UNUSED_DBG(words),
for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) { for ( ii = 0, jj = half; ii < half; ii += interval, jj += interval ) {
if ( di_getNthWord( &iter, ii, depth, data ) ) { if ( di_getNthWord( &iter, ii, depth, data ) ) {
di_wordToString( &iter, buf, VSIZE(buf) ); di_wordToString( &iter, buf, VSIZE(buf), "." );
XP_ASSERT( 0 == strcmp( buf, words[ii] ) ); XP_ASSERT( 0 == strcmp( buf, words[ii] ) );
# ifdef PRINT_ALL # ifdef PRINT_ALL
XP_LOGF( "%s: word[%ld]: %s", __func__, ii, buf ); 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 ); XP_ASSERT( 0 );
} }
if ( di_getNthWord( &iter, jj, depth, data ) ) { if ( di_getNthWord( &iter, jj, depth, data ) ) {
di_wordToString( &iter, buf, VSIZE(buf) ); di_wordToString( &iter, buf, VSIZE(buf), "." );
XP_ASSERT( 0 == strcmp( buf, words[jj] ) ); XP_ASSERT( 0 == strcmp( buf, words[jj] ) );
# ifdef PRINT_ALL # ifdef PRINT_ALL
XP_LOGF( "%s: word[%ld]: %s", __func__, jj, buf ); 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 = di_findStartsWith( ftp->iter, tiles, nTiles );
if ( 0 <= lenMatched ) {
XP_UCHAR buf[32];
XP_UCHAR bufPrev[32] = {0};
di_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 = di_getPosition( ftp->iter );
XP_ASSERT( 0 == strcmp( buf, ftp->words[pos] ) );
if ( pos > 0 ) {
if ( !di_getNthWord( ftp->iter, pos-1, ftp->depth, ftp->data ) ) {
XP_ASSERT( 0 );
}
di_wordToString( ftp->iter, bufPrev, VSIZE(bufPrev), "." );
XP_ASSERT( 0 == strcmp( bufPrev, ftp->words[pos-1] ) );
}
XP_LOGF( "di_getStartsWith(%s) => %s (prev=%s)",
ftp->prefix, buf, bufPrev );
} else {
XP_LOGFF( "nothing starts with %s", ftp->prefix );
}
return XP_TRUE;
}
static void static void
walk_dict_test( MPFORMAL const DictionaryCtxt* dict, walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
@ -1968,7 +2005,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
gotOne = di_getNextWord( &iter ) ) { gotOne = di_getNextWord( &iter ) ) {
XP_ASSERT( di_getPosition( &iter ) == jj ); XP_ASSERT( di_getPosition( &iter ) == jj );
XP_UCHAR buf[64]; XP_UCHAR buf[64];
di_wordToString( &iter, buf, VSIZE(buf) ); di_wordToString( &iter, buf, VSIZE(buf), "." );
# ifdef PRINT_ALL # ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf ); fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif # endif
@ -1984,7 +2021,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
++jj, gotOne = di_getPrevWord( &iter ) ) { ++jj, gotOne = di_getPrevWord( &iter ) ) {
XP_ASSERT( di_getPosition(&iter) == count-jj-1 ); XP_ASSERT( di_getPosition(&iter) == count-jj-1 );
XP_UCHAR buf[64]; XP_UCHAR buf[64];
di_wordToString( &iter, buf, VSIZE(buf) ); di_wordToString( &iter, buf, VSIZE(buf), "." );
# ifdef PRINT_ALL # ifdef PRINT_ALL
fprintf( stderr, "%.6ld: %s\n", jj, buf ); fprintf( stderr, "%.6ld: %s\n", jj, buf );
# endif # endif
@ -2026,20 +2063,20 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
} }
XP_ASSERT( word.index == indices[ii] ); XP_ASSERT( word.index == indices[ii] );
XP_UCHAR buf1[64]; XP_UCHAR buf1[64];
dict_wordToString( dict, &word, buf1, VSIZE(buf1) ); dict_wordToString( dict, &word, buf1, VSIZE(buf1), "." );
XP_UCHAR buf2[64] = {0}; XP_UCHAR buf2[64] = {0};
if ( ii > 0 && dict_getNthWord( dict, &word, indices[ii]-1 ) ) { 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]; char prfx[8];
dict_tilesToString( dict, &prefixes[depth*ii], depth, prfx, 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", fprintf( stderr, "%d: index: %ld; prefix: %s; word: %s (prev: %s)\n",
ii, indices[ii], prfx, buf1, buf2 ); ii, indices[ii], prfx, buf1, buf2 );
} }
#endif #endif
XP_LOGF( "testing getNth WITH INDEXING" ); XP_LOGFF( "testing getNth WITH INDEXING" );
testGetNthWord( dict, words, depth, &data, min, max ); testGetNthWord( dict, words, depth, &data, min, max );
if ( !!testPrefixes ) { if ( !!testPrefixes ) {
@ -2047,31 +2084,13 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
guint count = g_slist_length( testPrefixes ); guint count = g_slist_length( testPrefixes );
for ( ii = 0; ii < count; ++ii ) { for ( ii = 0; ii < count; ++ii ) {
gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii ); gchar* prefix = (gchar*)g_slist_nth_data( testPrefixes, ii );
XP_S16 lenMatched = di_findStartsWith( &iter, prefix ); XP_LOGFF( "prefix %d: %s", ii, prefix );
if ( 0 <= lenMatched ) {
XP_UCHAR buf[32];
XP_UCHAR bufPrev[32] = {0};
di_wordToString( &iter, buf, VSIZE(buf) );
/* This doesn't work with synonyms like "L-L" for "L·L" */ FTData foundTilesData = { .iter = &iter, .words = words,
// XP_ASSERT( 0 == strncasecmp( buf, prefix, lenMatched ) ); .depth = depth, .data = &data,
.prefix = prefix, };
DictPosition pos = di_getPosition( &iter ); dict_tilesForString( dict, prefix, onFoundTiles, &foundTilesData );
XP_ASSERT( 0 == strcmp( buf, words[pos] ) );
if ( pos > 0 ) {
if ( !di_getNthWord( &iter, pos-1, depth, &data ) ) {
XP_ASSERT( 0 );
} }
di_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_FREE( mpool, data.indices ); XP_FREE( mpool, data.indices );
XP_FREE( mpool, data.prefixes ); XP_FREE( mpool, data.prefixes );
@ -2108,7 +2127,7 @@ dumpDict( DictionaryCtxt* dict )
result; result;
result = di_getNextWord( &iter ) ) { result = di_getNextWord( &iter ) ) {
XP_UCHAR buf[32]; XP_UCHAR buf[32];
di_wordToString( &iter, buf, VSIZE(buf) ); di_wordToString( &iter, buf, VSIZE(buf), "." );
fprintf( stdout, "%s\n", buf ); fprintf( stdout, "%s\n", buf );
} }
} }
@ -2594,8 +2613,10 @@ main( int argc, char** argv )
mainParams.dbName = "xwgames.sqldb"; mainParams.dbName = "xwgames.sqldb";
mainParams.cursesListWinHt = 5; mainParams.cursesListWinHt = 5;
if ( file_exists( "./dict.xwd" ) ) {
trimDictPath( "./dict.xwd", dictbuf, VSIZE(dictbuf), &path, &dict ); trimDictPath( "./dict.xwd", dictbuf, VSIZE(dictbuf), &path, &dict );
mainParams.pgi.dictName = copyString( mainParams.mpool, dict ); mainParams.pgi.dictName = copyString( mainParams.mpool, dict );
}
char* envDictPath = getenv( "XW_DICTDIR" ); char* envDictPath = getenv( "XW_DICTDIR" );
XP_LOGFF( "envDictPath=%s", envDictPath ); XP_LOGFF( "envDictPath=%s", envDictPath );