From f2c4e39cd460acb4237e42433f9eaa0ae4dcdc0b Mon Sep 17 00:00:00 2001 From: Eric House Date: Wed, 26 Oct 2011 07:10:50 -0700 Subject: [PATCH] add dict_makeIndex(). Works, but only for depth==1 so far (A, not AA or AAA). --- xwords4/common/dictnry.c | 124 ++++++++++++++++++++++++++++++++++++++ xwords4/common/dictnry.h | 11 +++- xwords4/common/engine.c | 32 +--------- xwords4/linux/linuxmain.c | 20 +++++- 4 files changed, 155 insertions(+), 32 deletions(-) diff --git a/xwords4/common/dictnry.c b/xwords4/common/dictnry.c index ccb0e6061..b699babba 100644 --- a/xwords4/common/dictnry.c +++ b/xwords4/common/dictnry.c @@ -639,6 +639,30 @@ dict_super_follow( const DictionaryCtxt* dict, array_edge* in ) return result; } /* dict_super_follow */ +static array_edge* +dict_super_edge_with_tile( const DictionaryCtxt* dict, array_edge* from, + Tile tile ) +{ + for ( ; ; ) { + Tile candidate = EDGETILE(dict,from); + if ( candidate == tile ) { + break; + } + + if ( IS_LAST_EDGE(dict, from ) ) { + from = NULL; + break; + } +#ifdef NODE_CAN_4 + from += dict->nodeSize; +#else + from += 3; +#endif + } + + return from; +} /* edge_with_tile */ + void dict_super_init( DictionaryCtxt* dict ) { @@ -647,6 +671,7 @@ dict_super_init( DictionaryCtxt* dict ) dict->func_dict_getTopEdge = dict_super_getTopEdge; dict->func_dict_index_from = dict_super_index_from; dict->func_dict_follow = dict_super_follow; + dict->func_dict_edge_with_tile = dict_super_edge_with_tile; dict->func_dict_getShortName = dict_getName; } /* dict_super_init */ @@ -786,6 +811,40 @@ dict_getWord( const DictionaryCtxt* dict, DictWord* word, WordFinder finder ) return success; } +static XP_Bool +findStartsWith( const DictionaryCtxt* dict, Tile* tiles, XP_U16 nTiles, + array_edge** edges, XP_U16* nEdgesP ) +{ + XP_Bool success = XP_TRUE; + XP_U16 nEdges = 0; + array_edge* edge = dict_getTopEdge( dict ); + + while ( nTiles-- > 0 ) { + Tile tile = *tiles++; + edge = dict_edge_with_tile( dict, edge, tile ); + if ( NULL == edge ) { + success = XP_FALSE; + break; + } + edges[nEdges++] = edge; + edge = dict_follow( dict, edge ); + } + *nEdgesP = nEdges; + return success; +} + +static XP_Bool +wordsEqual( array_edge** edges1, XP_U16 nEdges1, + array_edge** edges2, XP_U16 nEdges2 ) +{ + XP_Bool success = nEdges1 == nEdges2; + if ( success ) { + success = 0 == memcmp( edges1, edges2, nEdges1 * sizeof(edges1[0]) ); + } + return success; +} + + XP_U32 dict_countWords( const DictionaryCtxt* dict ) { @@ -802,6 +861,71 @@ dict_countWords( const DictionaryCtxt* dict ) return count; } +void +dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, + DictIndex* indices, XP_U16 count ) +{ + XP_USE( indices ); + XP_ASSERT( depth == 1 ); /* so far */ + XP_U16 ii, needCount; + XP_U16 nextIndex = 0; + XP_U16 nFaces = dict_numTileFaces( dict ); + XP_Bool hasBlank = dict_hasBlankTile( dict ); + if ( hasBlank ) { + --nFaces; + } + for ( ii = 1, needCount = nFaces; ii < depth; ++ii ) { + needCount *= nFaces; + } + XP_ASSERT( needCount <= count ); + + /* For each tile string implied by depth (A if depth == 1, AAA if == 3 ), + * find the first word starting with that IF EXISTS. If it does, find its + * index. As an optimization, find index starting with the previous word. + */ + DictWord firstWord; + if ( dict_firstWord( dict, &firstWord ) ) { + array_edge* prevEdges[MAX_COLS]; + indicesToEdges( dict, &firstWord, prevEdges ); + XP_U16 prevNEdges = firstWord.nTiles; + DictIndex index = 0; + + Tile tiles[1]; + nFaces = dict_numTileFaces( dict ); + for ( tiles[0] = 0; tiles[0] < nFaces; ++tiles[0] ) { + if ( hasBlank && tiles[0] == dict_getBlankTile( dict ) ) { + continue; + } + + array_edge* curEdges[MAX_COLS]; + XP_U16 curNEdges; + if ( !findStartsWith( dict, tiles, 1, curEdges, &curNEdges ) ) { + indices[nextIndex++] = NO_INDEX; + } else { + XP_ASSERT( curNEdges == depth ); + if ( ! ISACCEPTING( dict, curEdges[curNEdges-1] ) ) { + if ( !nextWord( dict, curEdges, &curNEdges ) ) { + XP_ASSERT( 0 ); + } + } + if ( wordsEqual( curEdges, curNEdges, prevEdges, prevNEdges ) ) { + XP_ASSERT( index == 0 ); + } else { + /* Walk the prev word forward until they're the same */ + while ( nextWord( dict, prevEdges, &prevNEdges ) ) { + ++index; + if ( wordsEqual( curEdges, curNEdges, prevEdges, prevNEdges ) ) { + break; + } + } + } + indices[nextIndex++] = index; + } + } + } + +} + XP_Bool dict_firstWord( const DictionaryCtxt* dict, DictWord* word ) { diff --git a/xwords4/common/dictnry.h b/xwords4/common/dictnry.h index 437fa4a12..4f1de065f 100644 --- a/xwords4/common/dictnry.h +++ b/xwords4/common/dictnry.h @@ -73,6 +73,8 @@ struct DictionaryCtxt { array_edge* p_edge ); array_edge* (*func_dict_follow)( const DictionaryCtxt* dict, array_edge* in ); + array_edge* (*func_dict_edge_with_tile)( const DictionaryCtxt* dict, + array_edge* from, Tile tile ); const XP_UCHAR* (*func_dict_getShortName)( const DictionaryCtxt* dict ); array_edge* topEdge; @@ -135,6 +137,7 @@ struct DictionaryCtxt { #define dict_getTopEdge(d) (*((d)->func_dict_getTopEdge))(d) #define dict_index_from(d,e) (*((d)->func_dict_index_from))(d,e) #define dict_follow(d,e) (*((d)->func_dict_follow))(d,e) +#define dict_edge_with_tile(d,e,t) (*((d)->func_dict_edge_with_tile))(d,e,t) #define dict_getShortName(d) (*((d)->func_dict_getShortName))(d) #ifdef NODE_CAN_4 @@ -206,14 +209,18 @@ void dict_splitFaces( DictionaryCtxt* dict, const XP_U8* bytes, #ifdef XWFEATURE_WALKDICT /* API for iterating over a dict */ +typedef XP_U32 DictIndex; +#define NO_INDEX 0xFFFFFFFF typedef struct _DictWord { XP_U32 wordCount; - XP_U32 index; + DictIndex index; XP_U16 nTiles; XP_U32 indices[MAX_COLS]; } DictWord; XP_U32 dict_countWords( const DictionaryCtxt* dict ); +void dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, + DictIndex* indices, XP_U16 count ); 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 ); @@ -223,7 +230,7 @@ XP_Bool dict_getNthWord( const DictionaryCtxt* dict, DictWord* word, void dict_wordToString( const DictionaryCtxt* dict, const DictWord* word, XP_UCHAR* buf, XP_U16 buflen ); -#endif +#endif /* XWFEATURE_WALKDICT */ #ifdef CPLUS } diff --git a/xwords4/common/engine.c b/xwords4/common/engine.c index 97ac9ff54..ad462b4d2 100644 --- a/xwords4/common/engine.c +++ b/xwords4/common/engine.c @@ -121,8 +121,6 @@ struct EngineCtxt { static void findMovesOneRow( EngineCtxt* engine ); static Tile localGetBoardTile( EngineCtxt* engine, XP_U16 col, XP_U16 row, XP_Bool substBlank ); -static array_edge* edge_with_tile( const DictionaryCtxt* dict, - array_edge* from, Tile tile ); static XP_Bool scoreQualifies( EngineCtxt* engine, XP_U16 score ); static void findMovesForAnchor( EngineCtxt* engine, XP_S16* prevAnchor, XP_U16 col, XP_U16 row ) ; @@ -590,7 +588,7 @@ lookup( const DictionaryCtxt* dict, array_edge* edge, Tile* buf, { while ( edge != NULL ) { Tile targetTile = buf[tileIndex]; - edge = edge_with_tile( dict, edge, targetTile ); + edge = dict_edge_with_tile( dict, edge, targetTile ); if ( edge == NULL ) { /* tile not available out of this node */ return XP_FALSE; } else { @@ -982,7 +980,7 @@ extendRight( EngineCtxt* engine, Tile* tiles, XP_U16 tileLength, } } - } else if ( (edge = edge_with_tile( dict, edge, tile ) ) != NULL ) { + } else if ( (edge = dict_edge_with_tile( dict, edge, tile ) ) != NULL ) { accepting = ISACCEPTING( dict, edge ); extendRight( engine, tiles, tileLength, dict_follow(dict, edge), accepting, firstCol, col+1, row ); @@ -1334,34 +1332,10 @@ scoreQualifies( EngineCtxt* engine, XP_U16 score ) return qualifies; } /* scoreQualifies */ -static array_edge* -edge_with_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ) -{ - for ( ; ; ) { - Tile candidate = EDGETILE(dict,from); - if ( candidate == tile ) { - break; - } - - if ( IS_LAST_EDGE(dict, from ) ) { - from = NULL; - break; - } -#ifdef NODE_CAN_4 - from += dict->nodeSize; -#else - from += 3; -#endif - - } - - return from; -} /* edge_with_tile */ - static array_edge* edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ) { - array_edge* edge = edge_with_tile( dict, from, tile ); + array_edge* edge = dict_edge_with_tile( dict, from, tile ); if ( edge != NULL ) { edge = dict_follow( dict, edge ); } diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index cb7893c9a..f18f4e95c 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -955,7 +955,7 @@ walk_dict_test( const DictionaryCtxt* dict ) fprintf( stderr, "testing getNth\n" ); int ii; - for ( ii = 0 ; ii < 1000; ++ii ) { + for ( ii = 0 ; ii < 100; ++ii ) { long index = XP_RANDOM() % count; if ( dict_getNthWord( dict, &word, index ) ) { XP_ASSERT( word.index == index ); @@ -967,6 +967,24 @@ walk_dict_test( const DictionaryCtxt* dict ) } } + DictIndex indices[26]; + dict_makeIndex( dict, 1, indices, VSIZE(indices) ); + for ( ii = 0; ii < VSIZE(indices); ++ii ) { + DictIndex index = indices[ii]; + if ( NO_INDEX == index ) { + continue; + } else if ( dict_getNthWord( dict, &word, indices[ii] ) ) { + XP_ASSERT( word.index == indices[ii] ); + XP_UCHAR buf1[64]; + dict_wordToString( dict, &word, buf1, VSIZE(buf1) ); + XP_UCHAR buf2[64] = {0}; + if ( ii > 0 && dict_getNthWord( dict, &word, indices[ii]-1 ) ) { + dict_wordToString( dict, &word, buf2, VSIZE(buf2) ); + } + fprintf( stderr, "%d: index: %ld; word: %s (prev: %s)\n", ii, indices[ii], buf1, buf2 ); + } + } + exit( 0 ); } #else