From 7c7cd82e0a4ccbff36be635f203cdb05bdd33aab Mon Sep 17 00:00:00 2001 From: Andy2 Date: Fri, 21 Oct 2011 18:51:33 -0700 Subject: [PATCH] add code to iterate over words in a dict. Works on Linux (iterating forward only) but disabled at compile time. Idea's to have a dict browser. There was some simple refactoring in common code Android uses, and that tests fine. --- xwords4/common/comtypes.h | 7 ++ xwords4/common/dictnry.c | 159 ++++++++++++++++++++++++++++++++++++++ xwords4/common/dictnry.h | 43 ++++++++++- xwords4/common/engine.c | 63 +-------------- xwords4/common/model.h | 8 +- xwords4/linux/Makefile | 1 + xwords4/linux/linuxmain.c | 21 ++++- 7 files changed, 234 insertions(+), 68 deletions(-) diff --git a/xwords4/common/comtypes.h b/xwords4/common/comtypes.h index 47daa23d6..10e38a518 100644 --- a/xwords4/common/comtypes.h +++ b/xwords4/common/comtypes.h @@ -115,6 +115,13 @@ typedef enum { } XWTimerReason; #define MAX_NUM_PLAYERS 4 +#define MAX_ROWS 16 +#define MAX_COLS MAX_ROWS +#ifdef EIGHT_TILES +# define MAX_TRAY_TILES 8 +#else +# define MAX_TRAY_TILES 7 +#endif #define PLAYERNUM_NBITS 2 #define NDEVICES_NBITS 2 /* 1-4, but reduced by 1 fits in 2 bits */ #define NPLAYERS_NBITS 3 diff --git a/xwords4/common/dictnry.c b/xwords4/common/dictnry.c index 2e93103f7..f3261ef3c 100644 --- a/xwords4/common/dictnry.c +++ b/xwords4/common/dictnry.c @@ -597,12 +597,51 @@ dict_super_getTopEdge( const DictionaryCtxt* dict ) return dict->topEdge; } /* dict_super_getTopEdge */ +static unsigned long +dict_super_index_from( const DictionaryCtxt* dict, array_edge* p_edge ) +{ + unsigned long result; + +#ifdef NODE_CAN_4 + array_edge_new* edge = (array_edge_new*)p_edge; + result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF; + + if ( dict->is_4_byte ) { + result |= ((XP_U32)edge->moreBits) << 16; + } else { + XP_ASSERT( dict->nodeSize == 3 ); + if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) { + result |= 0x00010000; /* using | instead of + saves 4 bytes */ + } + } +#else + array_edge_old* edge = (array_edge_old*)p_edge; + result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF; + if ( (edge->bits & EXTRABITMASK_OLD) != 0 ) { + result |= 0x00010000; /* using | instead of + saves 4 bytes */ + } +#endif + + return result; +} /* dict_super_index_from */ + +static array_edge* +dict_super_follow( const DictionaryCtxt* dict, array_edge* in ) +{ + XP_U32 index = dict_index_from( dict, in ); + array_edge* result = index > 0? + dict_edge_for_index( dict, index ): (array_edge*)NULL; + return result; +} /* dict_super_follow */ + void dict_super_init( DictionaryCtxt* dict ) { /* subclass may change these later.... */ dict->func_edge_for_index = dict_super_edge_for_index; dict->func_dict_getTopEdge = dict_super_getTopEdge; + dict->func_dict_index_from = dict_super_index_from; + dict->func_dict_follow = dict_super_follow; dict->func_dict_getShortName = dict_getName; } /* dict_super_init */ @@ -612,6 +651,126 @@ dict_getLangName( const DictionaryCtxt* ctxt ) return ctxt->langName; } +#ifdef XWFEATURE_WALKDICT +static void +edgesToIndices( const DictionaryCtxt* dict, XP_U16 nEdges, + array_edge** edges, DictWord* word ) +{ + XP_U16 ii; + + word->nTiles = nEdges; + for ( ii = 0; ii < nEdges; ++ii ) { + word->indices[ii] = edges[ii] - dict->base; + } +} + +static void +indicesToEdges( const DictionaryCtxt* dict, + DictWord* word, array_edge** edges ) +{ + XP_U16 nEdges = word->nTiles; + XP_U16 ii; + for ( ii = 0; ii < nEdges; ++ii ) { + edges[ii] = &dict->base[word->indices[ii]]; + } +} + +/* On entry and exit, edge at end of array should be ACCEPTING. The job of + * this function is to iterate from one such edge to the next. Steps are: 1) + * try to follow the edge, to expand to a longer word with the last one as a + * prefix. 2) If we're at the end of the array, back off the top tile (and + * repeat while at end of array); 3) Once the current top edge is not a + * LAST_EDGE, try with its next-letter neighbor. + */ +static XP_Bool +nextWord( const DictionaryCtxt* dict, array_edge** edges, XP_U16* nTilesP ) +{ + XP_U16 nTiles = *nTilesP; + XP_Bool success = XP_FALSE; + while ( 0 < nTiles && ! success ) { + array_edge* next = dict_follow( dict, edges[nTiles-1] ); + if ( !!next ) { + edges[nTiles++] = next; + success = ISACCEPTING( dict, next ); + continue; /* try with longer word */ + } + + while ( IS_LAST_EDGE( dict, edges[nTiles-1] ) && 0 < --nTiles ) { + } + + if ( 0 < nTiles ) { + edges[nTiles-1] += dict->nodeSize; + success = ISACCEPTING( dict, edges[nTiles-1] ); + } + } + + *nTilesP = nTiles; + return success; +} + +XP_Bool +dict_firstWord( const DictionaryCtxt* dict, DictWord* word ) +{ + array_edge* edges[MAX_COLS]; + XP_U16 nEdges = 0; + edges[nEdges++] = dict_getTopEdge( dict ); + + XP_Bool success = ISACCEPTING( dict, edges[0] ) /* */ + || nextWord( dict, edges, &nEdges ); + if ( success ) { + edgesToIndices( dict, nEdges, edges, word ); + } + + return success; +} + +XP_Bool +dict_getNextWord( const DictionaryCtxt* dict, DictWord* word ) +{ + XP_U16 nTiles = word->nTiles; + array_edge* edges[MAX_COLS]; + indicesToEdges( dict, word, edges ); + XP_Bool success = nextWord( dict, edges, &nTiles ); + if ( success ) { + edgesToIndices( dict, nTiles, edges, word ); + } + return success; +} + +XP_Bool +dict_lastWord( const DictionaryCtxt* dict, DictWord* word ) +{ + XP_ASSERT( 0 ); + XP_USE( dict ); + word->nTiles = 0; + return XP_FALSE; +} + +XP_Bool +dict_getPrevWord( const DictionaryCtxt* dict, DictWord* word ) +{ + XP_USE( dict ); + XP_ASSERT( 0 ); + return word->nTiles > 0; +} + +void +dict_wordToString( const DictionaryCtxt* dict, DictWord* word, + XP_UCHAR* buf, XP_U16 buflen ) +{ + XP_U16 ii; + array_edge* edges[MAX_COLS]; + Tile tiles[MAX_COLS]; + + indicesToEdges( dict, word, edges ); + + for ( ii = 0; ii < word->nTiles; ++ii ) { + tiles[ii] = EDGETILE( dict, edges[ii] ); + } + (void)dict_tilesToString( dict, tiles, word->nTiles, buf, buflen ); +} +#endif /* XWFEATURE_WALKDICT */ + #ifdef CPLUS } #endif diff --git a/xwords4/common/dictnry.h b/xwords4/common/dictnry.h index 7064d8c7e..b1dfbf755 100644 --- a/xwords4/common/dictnry.h +++ b/xwords4/common/dictnry.h @@ -66,8 +66,13 @@ typedef struct _XP_Bitmaps { struct DictionaryCtxt { void (*destructor)( DictionaryCtxt* dict ); - array_edge* (*func_edge_for_index)( const DictionaryCtxt* dict, XP_U32 index ); + array_edge* (*func_edge_for_index)( const DictionaryCtxt* dict, + XP_U32 index ); array_edge* (*func_dict_getTopEdge)( const DictionaryCtxt* dict ); + unsigned long (*func_dict_index_from)( const DictionaryCtxt* dict, + array_edge* p_edge ); + array_edge* (*func_dict_follow)( const DictionaryCtxt* dict, + array_edge* in ); const XP_UCHAR* (*func_dict_getShortName)( const DictionaryCtxt* dict ); array_edge* topEdge; @@ -128,8 +133,26 @@ struct DictionaryCtxt { #define dict_destroy(d) (*((d)->destructor))(d) #define dict_edge_for_index(d, i) (*((d)->func_edge_for_index))((d), (i)) #define dict_getTopEdge(d) (*((d)->func_dict_getTopEdge))(d) +#define dict_index_from(d,e) (*((d)->func_dict_index_from))(d,e) +#define dict_follow(d,e) (*((d)->func_dict_follow))(d,e) #define dict_getShortName(d) (*((d)->func_dict_getShortName))(d) +#ifdef NODE_CAN_4 +# define ISACCEPTING(d,e) \ + ((ACCEPTINGMASK_NEW & ((array_edge_old*)(e))->bits) != 0) +# define IS_LAST_EDGE(d,e) \ + ((LASTEDGEMASK_NEW & ((array_edge_old*)(e))->bits) != 0) +# define EDGETILE(d,edge) \ + ((Tile)(((array_edge_old*)(edge))->bits & \ + ((d)->is_4_byte?LETTERMASK_NEW_4:LETTERMASK_NEW_3))) +#else +# define ISACCEPTING(d,e) \ + ((ACCEPTINGMASK_OLD & ((array_edge_old*)(e))->bits) != 0) +# define IS_LAST_EDGE(d,e) \ + ((LASTEDGEMASK_OLD & ((array_edge_old*)(e))->bits) != 0) +# define EDGETILE(d,edge) \ + ((Tile)(((array_edge_old*)(edge))->bits & LETTERMASK_OLD)) +#endif XP_Bool dict_tilesAreSame( const DictionaryCtxt* dict1, const DictionaryCtxt* dict2 ); @@ -180,6 +203,24 @@ void dict_super_init( DictionaryCtxt* ctxt ); void dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes, XP_U16 nBytes, XP_U16 nFaces ); +#ifdef XWFEATURE_WALKDICT + +/* API for iterating over a dict */ +typedef struct _DictWord { + XP_U32 index; + XP_U16 nTiles; + XP_U32 indices[MAX_COLS]; +} DictWord; + +XP_Bool dict_firstWord( const DictionaryCtxt* dict, DictWord* word ); +XP_Bool dict_lastWord( const DictionaryCtxt* dict, DictWord* word ); +XP_Bool dict_getNextWord( const DictionaryCtxt* dict, DictWord* word ); +XP_Bool dict_getPrevWord( const DictionaryCtxt* dict, DictWord* word ); +void dict_wordToString( const DictionaryCtxt* dict, DictWord* word, + XP_UCHAR* buf, XP_U16 buflen ); + +#endif + #ifdef CPLUS } #endif diff --git a/xwords4/common/engine.c b/xwords4/common/engine.c index c8f270468..97ac9ff54 100644 --- a/xwords4/common/engine.c +++ b/xwords4/common/engine.c @@ -130,7 +130,6 @@ static void figureCrosschecks( EngineCtxt* engine, XP_U16 col, XP_U16 row, XP_U16* scoreP, Crosscheck* check ); static XP_Bool isAnchorSquare( EngineCtxt* engine, XP_U16 col, XP_U16 row ); -static array_edge* follow( const DictionaryCtxt* dict, array_edge* in ); static array_edge* edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ); static void leftPart( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength, @@ -166,23 +165,6 @@ static XP_S16 cmpMoves( PossibleMove* m1, PossibleMove* m2 ); error: need to pick one!!! #endif -#ifdef NODE_CAN_4 -# define ISACCEPTING(d,e) \ - ((ACCEPTINGMASK_NEW & ((array_edge_old*)(e))->bits) != 0) -# define IS_LAST_EDGE(d,e) \ - ((LASTEDGEMASK_NEW & ((array_edge_old*)(e))->bits) != 0) -# define EDGETILE(e,edge) \ - ((Tile)(((array_edge_old*)(edge))->bits & \ - ((e)->is_4_byte?LETTERMASK_NEW_4:LETTERMASK_NEW_3))) -#else -# define ISACCEPTING(d,e) \ - ((ACCEPTINGMASK_OLD & ((array_edge_old*)(e))->bits) != 0) -# define IS_LAST_EDGE(d,e) \ - ((LASTEDGEMASK_OLD & ((array_edge_old*)(e))->bits) != 0) -# define EDGETILE(d,edge) \ - ((Tile)(((array_edge_old*)(edge))->bits & LETTERMASK_OLD)) -#endif - /* #define CROSSCHECK_CONTAINS(chk,tile) (((chk) & (1L<<(tile))) != 0) */ #define CROSSCHECK_CONTAINS(chk,tile) checkIsSet( (chk), (tile) ) @@ -615,7 +597,7 @@ lookup( const DictionaryCtxt* dict, array_edge* edge, Tile* buf, if ( ++tileIndex == length ) { /* is this the last tile? */ return ISACCEPTING(dict, edge); } else { - edge = follow( dict, edge ); + edge = dict_follow( dict, edge ); continue; } } @@ -915,7 +897,7 @@ leftPart( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength, if ( rack_remove( engine, tile, &isBlank ) ) { tiles[tileLength] = tile; leftPart( engine, tiles, tileLength+1, - follow( engine->dict, edge ), + dict_follow( engine->dict, edge ), limit-1, firstCol-1, anchorCol, row ); rack_replace( engine, tile, isBlank ); } @@ -1002,7 +984,7 @@ extendRight( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength, } else if ( (edge = edge_with_tile( dict, edge, tile ) ) != NULL ) { accepting = ISACCEPTING( dict, edge ); - extendRight( engine, tiles, tileLength, follow(dict, edge), + extendRight( engine, tiles, tileLength, dict_follow(dict, edge), accepting, firstCol, col+1, row ); goto no_check; /* don't do the check at the end */ } else { @@ -1376,49 +1358,12 @@ edge_with_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ) return from; } /* edge_with_tile */ -static unsigned long -index_from( const DictionaryCtxt* dict, array_edge* p_edge ) -{ - unsigned long result; - -#ifdef NODE_CAN_4 - array_edge_new* edge = (array_edge_new*)p_edge; - result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF; - - if ( dict->is_4_byte ) { - result |= ((XP_U32)edge->moreBits) << 16; - } else { - XP_ASSERT( dict->nodeSize == 3 ); - if ( (edge->bits & EXTRABITMASK_NEW) != 0 ) { - result |= 0x00010000; /* using | instead of + saves 4 bytes */ - } - } -#else - array_edge_old* edge = (array_edge_old*)p_edge; - result = ((edge->highByte << 8) | edge->lowByte) & 0x0000FFFF; - if ( (edge->bits & EXTRABITMASK_OLD) != 0 ) { - result |= 0x00010000; /* using | instead of + saves 4 bytes */ - } -#endif - - return result; -} /* index_from */ - -static array_edge* -follow( const DictionaryCtxt* dict, array_edge* in ) -{ - XP_U32 index = index_from( dict, in ); - array_edge* result = index > 0? - dict_edge_for_index( dict, index ): (array_edge*)NULL; - return result; -} /* follow */ - static array_edge* edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ) { array_edge* edge = edge_with_tile( dict, from, tile ); if ( edge != NULL ) { - edge = follow( dict, edge ); + edge = dict_follow( dict, edge ); } return edge; } /* edge_from_tile */ diff --git a/xwords4/common/model.h b/xwords4/common/model.h index aa5b8b170..caf945174 100644 --- a/xwords4/common/model.h +++ b/xwords4/common/model.h @@ -29,16 +29,12 @@ extern "C" { #endif -#define MAX_ROWS 16 -#define MAX_COLS 16 #define NUMCOLS_NBITS 4 #ifdef EIGHT_TILES -#define MAX_TRAY_TILES 8 -#define NTILES_NBITS 4 +# define NTILES_NBITS 4 #else -#define MAX_TRAY_TILES 7 -#define NTILES_NBITS 3 +# define NTILES_NBITS 3 #endif /* Try making this 0, as some local rules, e.g. Spanish, allow. Will need to diff --git a/xwords4/linux/Makefile b/xwords4/linux/Makefile index b3dbcbcbd..564b38571 100644 --- a/xwords4/linux/Makefile +++ b/xwords4/linux/Makefile @@ -91,6 +91,7 @@ DEFINES += -DXWFEATURE_CHAT DEFINES += -DDISABLE_TILE_SEL DEFINES += -DSET_GAMESEED DEFINES += -DTEXT_MODEL +# DEFINES += -DXWFEATURE_WALKDICT ifdef CURSES_CELL_HT DEFINES += -DCURSES_CELL_HT=$(CURSES_CELL_HT) diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 46fc72319..bc3c92673 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -1,6 +1,6 @@ -/* -*-mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ +/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */ /* - * Copyright 2000-2009 by Eric House (xwords@eehouse.org). All rights + * Copyright 2000 - 2011 by Eric House (xwords@eehouse.org). All rights * reserved. * * This program is free software; you can redistribute it and/or @@ -1281,6 +1281,23 @@ main( int argc, char** argv ) /* } */ /* } */ +#ifdef XWFEATURE_WALKDICT + /* This is just to test that the dict-iterating code works. The words are + meant to be printed e.g. in a scrolling dialog on Android. */ + DictWord word; + int jj; + XP_Bool gotOne; + + for ( jj = 0, gotOne = dict_firstWord( mainParams.dict, &word ); + gotOne; + ++jj, gotOne = dict_getNextWord( mainParams.dict, &word ) ) { + XP_UCHAR buf[64]; + dict_wordToString( mainParams.dict, &word, buf, VSIZE(buf) ); + fprintf( stderr, "%.6d: %s\n", jj, buf ); + } + exit( 0 ); +#endif + if ( 0 ) { #ifdef XWFEATURE_RELAY } else if ( conType == COMMS_CONN_RELAY ) {