mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-01-16 15:41:16 +01:00
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:
parent
98ce0e416f
commit
0e9661aa19
15 changed files with 436 additions and 229 deletions
|
@ -1,6 +1,6 @@
|
|||
/* -*- 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.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
|
@ -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;
|
||||
|
@ -47,6 +49,7 @@ import java.util.Arrays;
|
|||
public class DictBrowseDelegate extends DelegateBase
|
||||
implements View.OnClickListener, OnItemSelectedListener {
|
||||
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_LOC = "DICT_LOC";
|
||||
|
@ -96,7 +99,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
{
|
||||
TextView text = (TextView)
|
||||
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 ) {
|
||||
text.setText( str );
|
||||
text.setOnClickListener( DictBrowseDelegate.this );
|
||||
|
@ -136,6 +139,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
return section;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getSections()
|
||||
{
|
||||
m_prefixes = XwJNI.di_getPrefixes( m_dictClosure );
|
||||
|
@ -242,7 +246,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
setFindText( m_browseState.m_prefix );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
XwJNI.di_destroy( m_dictClosure );
|
||||
|
@ -253,6 +257,7 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
@Override
|
||||
public void finalize()
|
||||
{
|
||||
Assert.assertTrueNR( m_dictClosure == 0 );
|
||||
XwJNI.di_destroy( m_dictClosure );
|
||||
try {
|
||||
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
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -327,7 +374,16 @@ public class DictBrowseDelegate extends DelegateBase
|
|||
String text = getFindText();
|
||||
if ( null != text && 0 < text.length() ) {
|
||||
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 );
|
||||
}
|
||||
|
||||
private void showPrefix()
|
||||
private void showPrefix( byte[][] prefix, String delim )
|
||||
{
|
||||
String text = m_browseState.m_prefix;
|
||||
if ( null != text && 0 < text.length() ) {
|
||||
int pos = XwJNI.di_getStartsWith( m_dictClosure, text );
|
||||
if ( null != prefix && 0 < prefix.length && 0 < prefix[0].length ) {
|
||||
int pos = XwJNI.di_getStartsWith( m_dictClosure, prefix );
|
||||
if ( 0 <= pos ) {
|
||||
m_list.setSelection( pos );
|
||||
} else {
|
||||
String text = XwJNI.di_tilesToStr( m_dictClosure, prefix[0], delim );
|
||||
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;
|
||||
|
|
|
@ -514,22 +514,22 @@ public class XwJNI {
|
|||
|
||||
// Dict iterator
|
||||
public final static int MAX_COLS_DICT = 15; // from dictiter.h
|
||||
public static long di_init( byte[] dict, String name,
|
||||
String path )
|
||||
public static long di_init( byte[] dict, String name, String path )
|
||||
{
|
||||
return di_init( getJNI().m_ptrGlobals, dict, name, path );
|
||||
}
|
||||
public static native void di_setMinMax( long closure,
|
||||
int min, int max );
|
||||
public static native void di_setMinMax( long closure, int min, int max );
|
||||
public static native void di_destroy( long closure );
|
||||
public static native int di_wordCount( 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 int[] di_getIndices( long closure );
|
||||
public static native int di_getStartsWith( long closure,
|
||||
String prefix );
|
||||
public static native byte[][] di_strToTiles( long closure, String str );
|
||||
public static native int di_getStartsWith( long closure, byte[][] prefix );
|
||||
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 static native long initGlobals( DUtilCtxt dutil, JNIUtils jniu );
|
||||
|
@ -560,5 +560,6 @@ public class XwJNI {
|
|||
String fromPhone,
|
||||
int wantPort);
|
||||
|
||||
// This always returns true on release builds now.
|
||||
private static native boolean haveEnv( long jniState );
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -977,12 +977,15 @@ struct _JNIState {
|
|||
XP_U16 lastSavedSize;
|
||||
#ifdef DEBUG
|
||||
const char* envSetterFunc;
|
||||
XP_U32 guard;
|
||||
#endif
|
||||
MPSLOT
|
||||
};
|
||||
|
||||
#define GAME_GUARD 0x453627
|
||||
#define XWJNI_START() { \
|
||||
JNIState* state = getState( env, gamePtr, __func__ ); \
|
||||
XP_ASSERT( state->guard == GAME_GUARD ); \
|
||||
MPSLOT; \
|
||||
MPASSIGN( mpool, state->mpool ); \
|
||||
XP_ASSERT( !!state->globalJNI ); \
|
||||
|
@ -1003,6 +1006,9 @@ Java_org_eehouse_android_xw4_jni_XwJNI_initGameJNI
|
|||
MemPoolCtx* mpool = ((JNIGlobalState*)jniGlobalPtr)->mpool;
|
||||
#endif
|
||||
JNIState* state = (JNIState*)XP_CALLOC( mpool, sizeof(*state) );
|
||||
#ifdef DEBUG
|
||||
state->guard = GAME_GUARD;
|
||||
#endif
|
||||
state->globalJNI = (JNIGlobalState*)jniGlobalPtr;
|
||||
MAP_THREAD( &state->globalJNI->ti, env );
|
||||
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 )
|
||||
{
|
||||
JNIState* state = getState( env, gamePtr, __func__ );
|
||||
XP_ASSERT( state->guard == GAME_GUARD );
|
||||
|
||||
#ifdef MEM_DEBUG
|
||||
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 );
|
||||
|
||||
#ifdef DEBUG
|
||||
XP_MEMSET( state, -1, sizeof(*state) );
|
||||
#endif
|
||||
XP_FREE( mpool, state );
|
||||
} /* game_dispose */
|
||||
|
||||
|
@ -2398,19 +2408,18 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1sendChat
|
|||
|
||||
typedef struct _DictIterData {
|
||||
JNIGlobalState* globalState;
|
||||
JNIUtilCtxt* jniutil;
|
||||
VTableMgr* vtMgr;
|
||||
DictionaryCtxt* dict;
|
||||
DictIter iter;
|
||||
IndexData idata;
|
||||
XP_U16 depth;
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool;
|
||||
#ifdef DEBUG
|
||||
XP_U32 guard;
|
||||
#endif
|
||||
} DictIterData;
|
||||
|
||||
static void makeIndex( DictIterData* data );
|
||||
static void freeIndices( DictIterData* data );
|
||||
#define GI_GUARD 0x89ab72
|
||||
|
||||
JNIEXPORT jlong JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
|
||||
|
@ -2428,47 +2437,54 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1init
|
|||
if ( !!dict ) {
|
||||
DictIterData* data = XP_CALLOC( globalState->mpool, sizeof(*data) );
|
||||
data->globalState = globalState;
|
||||
data->vtMgr = make_vtablemgr( MPPARM_NOCOMMA(globalState->mpool) );
|
||||
data->jniutil = globalState->jniutil;
|
||||
data->dict = dict;
|
||||
data->depth = 2;
|
||||
#ifdef MEM_DEBUG
|
||||
data->mpool = globalState->mpool;
|
||||
#ifdef DEBUG
|
||||
data->guard = GI_GUARD;
|
||||
#endif
|
||||
closure = (jlong)data;
|
||||
}
|
||||
return closure;
|
||||
}
|
||||
|
||||
|
||||
#define DI_HEADER() { \
|
||||
DictIterData* data = (DictIterData*)closure; \
|
||||
XP_ASSERT( NULL == data || data->guard == GI_GUARD ); \
|
||||
|
||||
#define DI_HEADER_END() \
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_di_1setMinMax
|
||||
( JNIEnv* env, jclass C, jlong closure, jint min, jint max )
|
||||
{
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
di_initIter( &data->iter, data->dict, min, max );
|
||||
makeIndex( data );
|
||||
(void)di_firstWord( &data->iter );
|
||||
}
|
||||
DI_HEADER_END();
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_di_1destroy
|
||||
( JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = data->mpool;
|
||||
MemPoolCtx* mpool = data->globalState->mpool;
|
||||
#endif
|
||||
dict_unref( data->dict, env );
|
||||
freeIndices( data );
|
||||
|
||||
MAP_REMOVE( &data->globalState->ti, env );
|
||||
|
||||
vtmgr_destroy( MPPARM(mpool) data->vtMgr );
|
||||
XP_FREE( mpool, data );
|
||||
}
|
||||
DI_HEADER_END();
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
|
@ -2476,10 +2492,11 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1wordCount
|
|||
(JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
jint result = 0;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
result = data->iter.nWords;
|
||||
}
|
||||
DI_HEADER_END();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2488,7 +2505,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getCounts
|
|||
(JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
jintArray result = NULL;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
DictIter iter;
|
||||
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]) );
|
||||
}
|
||||
}
|
||||
DI_HEADER_END();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2508,7 +2526,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getPrefixes
|
|||
( JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
jobjectArray result = NULL;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data && NULL != data->idata.prefixes ) {
|
||||
result = makeStringArray( env, data->idata.count, NULL );
|
||||
|
||||
|
@ -2517,12 +2535,13 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_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 );
|
||||
}
|
||||
}
|
||||
DI_HEADER_END();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2531,7 +2550,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getIndices
|
|||
( JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
jintArray jindices = NULL;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
XP_ASSERT( !!data->idata.indices );
|
||||
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,
|
||||
sizeof(data->idata.indices[0]) );
|
||||
}
|
||||
DI_HEADER_END();
|
||||
return jindices;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
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;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
if ( di_getNthWord( &data->iter, nn, data->depth, &data->idata ) ) {
|
||||
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 );
|
||||
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;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
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;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
if ( NULL != data ) {
|
||||
const char* prefix = (*env)->GetStringUTFChars( env, jprefix, NULL );
|
||||
if ( 0 <= di_findStartsWith( &data->iter, prefix ) ) {
|
||||
DI_HEADER();
|
||||
|
||||
int len = (*env)->GetArrayLength( env, jtilesArr );
|
||||
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 );
|
||||
}
|
||||
(*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;
|
||||
}
|
||||
|
||||
|
@ -2579,13 +2693,14 @@ Java_org_eehouse_android_xw4_jni_XwJNI_di_1getDesc
|
|||
( JNIEnv* env, jclass C, jlong closure )
|
||||
{
|
||||
jstring result = NULL;
|
||||
DictIterData* data = (DictIterData*)closure;
|
||||
DI_HEADER();
|
||||
if ( NULL != data ) {
|
||||
const XP_UCHAR* disc = dict_getDesc( data->dict );
|
||||
if ( NULL != disc && '\0' != disc[0] ) {
|
||||
result = (*env)->NewStringUTF( env, disc );
|
||||
}
|
||||
}
|
||||
DI_HEADER_END();
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -2594,11 +2709,11 @@ freeIndices( DictIterData* data )
|
|||
{
|
||||
IndexData* idata = &data->idata;
|
||||
if ( !!idata->prefixes ) {
|
||||
XP_FREE( data->mpool, idata->prefixes );
|
||||
XP_FREE( data->globalState->mpool, idata->prefixes );
|
||||
idata->prefixes = NULL;
|
||||
}
|
||||
if( !!idata->indices ) {
|
||||
XP_FREE( data->mpool, idata->indices );
|
||||
XP_FREE( data->globalState->mpool, idata->indices );
|
||||
idata->indices = NULL;
|
||||
}
|
||||
}
|
||||
|
@ -2616,18 +2731,20 @@ makeIndex( DictIterData* data )
|
|||
freeIndices( data );
|
||||
|
||||
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) );
|
||||
idata->indices = XP_MALLOC( data->mpool,
|
||||
count * sizeof(*idata->indices) );
|
||||
idata->indices = XP_MALLOC( mpool, count * sizeof(*idata->indices) );
|
||||
idata->count = count;
|
||||
|
||||
di_makeIndex( &data->iter, data->depth, idata );
|
||||
if ( 0 < idata->count ) {
|
||||
idata->prefixes = XP_REALLOC( data->mpool, idata->prefixes,
|
||||
idata->prefixes = XP_REALLOC( mpool, idata->prefixes,
|
||||
idata->count * data->depth *
|
||||
sizeof(*idata->prefixes) );
|
||||
idata->indices = XP_REALLOC( data->mpool, idata->indices,
|
||||
idata->indices = XP_REALLOC( mpool, idata->indices,
|
||||
idata->count * sizeof(*idata->indices) );
|
||||
} else {
|
||||
freeIndices( data );
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -196,57 +196,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 )
|
||||
{
|
||||
|
@ -395,7 +344,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;
|
||||
|
@ -403,7 +353,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
|
||||
|
@ -504,7 +454,7 @@ di_makeIndex( const DictIter* iter, XP_U16 depth, IndexData* data )
|
|||
DI_ASSERT( data->indices[pos-1] < data->indices[pos] );
|
||||
}
|
||||
#endif
|
||||
} /* di_makeIndex */
|
||||
} /* dict_makeIndex */
|
||||
|
||||
static void
|
||||
initWord( DictIter* iter )
|
||||
|
@ -665,33 +615,29 @@ figurePosition( DictIter* iter )
|
|||
}
|
||||
|
||||
XP_S16
|
||||
di_findStartsWith( DictIter* iter, const XP_UCHAR* prefix )
|
||||
di_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 );
|
||||
if ( 0 > offset ) {
|
||||
/* not found; do nothing */
|
||||
} else if ( 0 == offset ) {
|
||||
if ( !firstWord( iter ) ) {
|
||||
offset = -1;
|
||||
}
|
||||
} else {
|
||||
XP_S16 result = -1;
|
||||
|
||||
if ( findStartsWithTiles( iter, prefix, len ) ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_U16 offset = dict_tilesToString( iter->dict, prefix,
|
||||
len, buf, VSIZE(buf), NULL );
|
||||
if ( ACCEPT_ITER( iter, iter->nEdges ) || nextWord( iter ) ) {
|
||||
DictPosition result = figurePosition( iter );
|
||||
iter->position = result;
|
||||
} else {
|
||||
offset = -1;
|
||||
iter->position = figurePosition( iter );
|
||||
result = offset;
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
return result;
|
||||
}
|
||||
|
||||
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 );
|
||||
iterToString( iter, buf, buflen );
|
||||
iterToString( iter, buf, buflen, delim );
|
||||
}
|
||||
|
||||
DictPosition
|
||||
|
|
|
@ -73,8 +73,10 @@ XP_Bool di_getNextWord( DictIter* iter );
|
|||
XP_Bool di_getPrevWord( DictIter* iter );
|
||||
XP_Bool di_getNthWord( DictIter* iter, DictPosition position, XP_U16 depth,
|
||||
const IndexData* data );
|
||||
void di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen );
|
||||
XP_S16 di_findStartsWith( DictIter* iter, const XP_UCHAR* prefix );
|
||||
void di_wordToString( const DictIter* iter, XP_UCHAR* buf, XP_U16 buflen,
|
||||
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 );
|
||||
#ifdef CPLUS
|
||||
}
|
||||
|
|
|
@ -232,11 +232,11 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
|
|||
XP_U16 headerLen;
|
||||
XP_U32 wordCount;
|
||||
|
||||
memcpy( &headerLen, ptr, sizeof(headerLen) );
|
||||
XP_MEMCPY( &headerLen, ptr, sizeof(headerLen) );
|
||||
ptr += sizeof(headerLen);
|
||||
headerLen = XP_NTOHS( headerLen );
|
||||
|
||||
memcpy( &wordCount, ptr, sizeof(wordCount) );
|
||||
XP_MEMCPY( &wordCount, ptr, sizeof(wordCount) );
|
||||
ptr += sizeof(wordCount);
|
||||
headerLen -= sizeof(wordCount);
|
||||
dctx->nWords = XP_NTOHL( wordCount );
|
||||
|
@ -252,6 +252,20 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
|
|||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -463,40 +477,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;
|
||||
}
|
||||
|
@ -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
|
||||
* 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
|
||||
|
@ -863,6 +887,12 @@ dict_getMd5Sum( const DictionaryCtxt* dict )
|
|||
return dict->md5Sum;
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
dict_hasDuplicates( const DictionaryCtxt* dict )
|
||||
{
|
||||
return 0 != (dict->headerFlags & HEADERFLAGS_DUPS_SUPPORTED_BIT);
|
||||
}
|
||||
|
||||
#ifdef STUBBED_DICT
|
||||
|
||||
#define BLANK_FACE '\0'
|
||||
|
|
|
@ -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.
|
||||
|
@ -58,6 +58,8 @@ typedef struct _XP_Bitmaps {
|
|||
XP_Bitmap bmps[2]; /* 2 is private, may change */
|
||||
} XP_Bitmaps;
|
||||
|
||||
#define HEADERFLAGS_DUPS_SUPPORTED_BIT 0x0001
|
||||
|
||||
struct DictionaryCtxt {
|
||||
void (*destructor)( DictionaryCtxt* dict, XWEnv xwe );
|
||||
|
||||
|
@ -94,6 +96,7 @@ struct DictionaryCtxt {
|
|||
XP_LangCode langCode;
|
||||
|
||||
XP_U16 refCount;
|
||||
XP_U16 headerFlags;
|
||||
XP_U8 nFaces;
|
||||
XP_U8 nodeSize;
|
||||
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_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 +190,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,
|
||||
|
@ -198,6 +203,7 @@ XP_U32 dict_getWordCount( const DictionaryCtxt* dict );
|
|||
|
||||
const XP_UCHAR* dict_getDesc( 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_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream );
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -23,6 +23,9 @@ TARGET_TYPE ?= WINCE
|
|||
|
||||
DICTNOTE = "Derived from szotar/alap/ in https://github.com/laszlonemeth/magyarispell.git"
|
||||
|
||||
# Needs only to be defined
|
||||
ALLOWS_DUPLICATES = TRUE
|
||||
|
||||
LANG_SPECIAL_INFO = \
|
||||
"CS Cs cS cs" /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)
|
||||
cat $< \
|
||||
| tr -d '\r' \
|
||||
| grep -v '[1-7]' \
|
||||
| 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,1,g' \
|
||||
| sed -e 's,^\(.*\)CS\(.*\)$$,\11\2\n\1CS\2,g' \
|
||||
| sed -e 's,GY,2,g' \
|
||||
| sed -e 's,LY,3,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,ZS,7,g' \
|
||||
| grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]*$$' \
|
||||
| sed -e 's,^\(.*\)ZS\(.*\)$$,\17\2\n\1ZS\2,g' \
|
||||
| grep '^[1-7AÁBCDEÉFGHIÍJKLMNOÓÖŐPRSTUÚÜŰVZ]\{2,15\}$$' \
|
||||
| tr '1234567' '\001\002\003\004\005\006\007' \
|
||||
| gzip -c > $@
|
||||
|
||||
|
|
|
@ -299,7 +299,12 @@ $(XWLANG)%_md5sum.bin:
|
|||
dawg$(XWLANG)$*_*.bin | md5sum | awk '{print $$1}' | tr -d '\n' > $@
|
||||
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; \
|
||||
for FILE in $+; do \
|
||||
SIZ=$$(($$SIZ + $$(ls -l $$FILE | awk '{print $$5}'))); \
|
||||
|
|
|
@ -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 ( 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] ) );
|
||||
# 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 ( 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] ) );
|
||||
# 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 = 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
|
||||
walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
||||
|
@ -1968,7 +2005,7 @@ walk_dict_test( MPFORMAL const DictionaryCtxt* dict,
|
|||
gotOne = di_getNextWord( &iter ) ) {
|
||||
XP_ASSERT( di_getPosition( &iter ) == jj );
|
||||
XP_UCHAR buf[64];
|
||||
di_wordToString( &iter, buf, VSIZE(buf) );
|
||||
di_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 = di_getPrevWord( &iter ) ) {
|
||||
XP_ASSERT( di_getPosition(&iter) == count-jj-1 );
|
||||
XP_UCHAR buf[64];
|
||||
di_wordToString( &iter, buf, VSIZE(buf) );
|
||||
di_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 = di_findStartsWith( &iter, prefix );
|
||||
if ( 0 <= lenMatched ) {
|
||||
XP_UCHAR buf[32];
|
||||
XP_UCHAR bufPrev[32] = {0};
|
||||
di_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 = di_getPosition( &iter );
|
||||
XP_ASSERT( 0 == strcmp( buf, words[pos] ) );
|
||||
if ( pos > 0 ) {
|
||||
if ( !di_getNthWord( &iter, pos-1, depth, &data ) ) {
|
||||
XP_ASSERT( 0 );
|
||||
FTData foundTilesData = { .iter = &iter, .words = words,
|
||||
.depth = depth, .data = &data,
|
||||
.prefix = prefix, };
|
||||
dict_tilesForString( dict, prefix, onFoundTiles, &foundTilesData );
|
||||
}
|
||||
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.prefixes );
|
||||
|
@ -2108,7 +2127,7 @@ dumpDict( DictionaryCtxt* dict )
|
|||
result;
|
||||
result = di_getNextWord( &iter ) ) {
|
||||
XP_UCHAR buf[32];
|
||||
di_wordToString( &iter, buf, VSIZE(buf) );
|
||||
di_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;
|
||||
|
||||
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…
Reference in a new issue