diff --git a/xwords4/common/dictnry.c b/xwords4/common/dictnry.c index b699babba..3e930e9d5 100644 --- a/xwords4/common/dictnry.c +++ b/xwords4/common/dictnry.c @@ -682,27 +682,33 @@ dict_getLangName( const DictionaryCtxt* ctxt ) } #ifdef XWFEATURE_WALKDICT +typedef struct _EdgeArray { + array_edge* edges[MAX_COLS]; + XP_U16 nEdges; +} EdgeArray; + static void -edgesToIndices( const DictionaryCtxt* dict, XP_U16 nEdges, - array_edge** edges, DictWord* word ) +edgesToIndices( const DictionaryCtxt* dict, const EdgeArray* edges, + DictWord* word ) { XP_U16 ii; - word->nTiles = nEdges; - for ( ii = 0; ii < nEdges; ++ii ) { - word->indices[ii] = edges[ii] - dict->base; + word->nTiles = edges->nEdges; + for ( ii = 0; ii < edges->nEdges; ++ii ) { + word->indices[ii] = edges->edges[ii] - dict->base; } } static void indicesToEdges( const DictionaryCtxt* dict, - const DictWord* word, array_edge** edges ) + const DictWord* word, EdgeArray* edges ) { XP_U16 nEdges = word->nTiles; XP_U16 ii; for ( ii = 0; ii < nEdges; ++ii ) { - edges[ii] = &dict->base[word->indices[ii]]; + edges->edges[ii] = &dict->base[word->indices[ii]]; } + edges->nEdges = nEdges; } /* On entry and exit, edge at end of array should be ACCEPTING. The job of @@ -713,28 +719,28 @@ indicesToEdges( const DictionaryCtxt* dict, * LAST_EDGE, try with its next-letter neighbor. */ static XP_Bool -nextWord( const DictionaryCtxt* dict, array_edge** edges, XP_U16* nTilesP ) +nextWord( const DictionaryCtxt* dict, EdgeArray* edges ) { - XP_U16 nTiles = *nTilesP; + XP_U16 nTiles = edges->nEdges; XP_Bool success = XP_FALSE; while ( 0 < nTiles && ! success ) { - array_edge* next = dict_follow( dict, edges[nTiles-1] ); + array_edge* next = dict_follow( dict, edges->edges[nTiles-1] ); if ( !!next ) { - edges[nTiles++] = next; + edges->edges[nTiles++] = next; success = ISACCEPTING( dict, next ); continue; /* try with longer word */ } - while ( IS_LAST_EDGE( dict, edges[nTiles-1] ) && 0 < --nTiles ) { + while ( IS_LAST_EDGE( dict, edges->edges[nTiles-1] ) && 0 < --nTiles ) { } if ( 0 < nTiles ) { - edges[nTiles-1] += dict->nodeSize; - success = ISACCEPTING( dict, edges[nTiles-1] ); + edges->edges[nTiles-1] += dict->nodeSize; + success = ISACCEPTING( dict, edges->edges[nTiles-1] ); } } - *nTilesP = nTiles; + edges->nEdges = nTiles; return success; } @@ -749,74 +755,66 @@ isFirstEdge( const DictionaryCtxt* dict, array_edge* edge ) } static XP_Bool -lastEdges( const DictionaryCtxt* dict, array_edge** edges, XP_U16* nEdgesP ) +lastEdges( const DictionaryCtxt* dict, EdgeArray* edges ) { - XP_U16 nEdges = *nEdgesP; - array_edge* edge = edges[nEdges-1]; + array_edge* edge = edges->edges[edges->nEdges-1]; for ( ; ; ) { while ( !IS_LAST_EDGE( dict, edge ) ) { edge += dict->nodeSize; } - edges[nEdges-1] = edge; + edges->edges[edges->nEdges-1] = edge; edge = dict_follow( dict, edge ); if ( NULL == edge ) { break; } - ++nEdges; + ++edges->nEdges; } - *nEdgesP = nEdges; - return ISACCEPTING( dict, edges[nEdges-1] ); + return ISACCEPTING( dict, edges->edges[edges->nEdges-1] ); } static XP_Bool -prevWord( const DictionaryCtxt* dict, array_edge** edges, XP_U16* nEdgesP ) +prevWord( const DictionaryCtxt* dict, EdgeArray* edges ) { - XP_U16 nEdges = *nEdgesP; XP_Bool success = XP_FALSE; - while ( 0 < nEdges && ! success ) { - if ( isFirstEdge( dict, edges[nEdges-1] ) ) { - --nEdges; - success = 0 < nEdges && ISACCEPTING( dict, edges[nEdges-1] ); + while ( 0 < edges->nEdges && ! success ) { + if ( isFirstEdge( dict, edges->edges[edges->nEdges-1] ) ) { + --edges->nEdges; + success = 0 < edges->nEdges && ISACCEPTING( dict, edges->edges[edges->nEdges-1] ); continue; } - edges[nEdges-1] -= dict->nodeSize; - array_edge* next = dict_follow( dict, edges[nEdges-1] ); + edges->edges[edges->nEdges-1] -= dict->nodeSize; + array_edge* next = dict_follow( dict, edges->edges[edges->nEdges-1] ); if ( NULL != next ) { - edges[nEdges++] = next; - success = lastEdges( dict, edges, &nEdges ); + edges->edges[edges->nEdges++] = next; + success = lastEdges( dict, edges ); if ( success ) { continue; } } - success = ISACCEPTING( dict, edges[nEdges-1] ); + success = ISACCEPTING( dict, edges->edges[edges->nEdges-1] ); } - *nEdgesP = nEdges; return success; } -typedef XP_Bool (*WordFinder)(const DictionaryCtxt* dict, array_edge** edges, - XP_U16* nTilesP ); +typedef XP_Bool (*WordFinder)(const DictionaryCtxt* dict, EdgeArray* edges ); static XP_Bool dict_getWord( const DictionaryCtxt* dict, DictWord* word, WordFinder finder ) { - XP_U16 nTiles = word->nTiles; - array_edge* edges[MAX_COLS]; - indicesToEdges( dict, word, edges ); - XP_Bool success = (*finder)( dict, edges, &nTiles ); + EdgeArray edges; + indicesToEdges( dict, word, &edges ); + XP_Bool success = (*finder)( dict, &edges ); if ( success ) { - edgesToIndices( dict, nTiles, edges, word ); + edgesToIndices( dict, &edges, word ); } return success; } static XP_Bool -findStartsWith( const DictionaryCtxt* dict, Tile* tiles, XP_U16 nTiles, - array_edge** edges, XP_U16* nEdgesP ) +findStartsWith( const DictionaryCtxt* dict, Tile* tiles, XP_U16 nTiles, EdgeArray* edges ) { XP_Bool success = XP_TRUE; - XP_U16 nEdges = 0; array_edge* edge = dict_getTopEdge( dict ); while ( nTiles-- > 0 ) { @@ -826,49 +824,102 @@ findStartsWith( const DictionaryCtxt* dict, Tile* tiles, XP_U16 nTiles, success = XP_FALSE; break; } - edges[nEdges++] = edge; + edges->edges[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 ) +wordsEqual( EdgeArray* word1, EdgeArray* word2 ) { - XP_Bool success = nEdges1 == nEdges2; + XP_Bool success = word1->nEdges == word2->nEdges; if ( success ) { - success = 0 == memcmp( edges1, edges2, nEdges1 * sizeof(edges1[0]) ); + success = 0 == memcmp( word1->edges, word2->edges, + word1->nEdges * sizeof(word1->edges[0]) ); } return success; } - XP_U32 dict_countWords( const DictionaryCtxt* dict ) { - array_edge* edges[MAX_COLS]; - XP_U16 nEdges = 0; + EdgeArray edges = { .nEdges = 0 }; XP_U32 count = 0; - edges[nEdges++] = dict_getTopEdge( dict ); - if ( ISACCEPTING( dict, edges[0] ) ) { + edges.edges[edges.nEdges++] = dict_getTopEdge( dict ); + if ( ISACCEPTING( dict, edges.edges[0] ) ) { ++count; } - while ( nextWord( dict, edges, &nEdges ) ) { + while ( nextWord( dict, &edges ) ) { ++count; } return count; } -void -dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, - DictIndex* indices, XP_U16 count ) +static void +indexOne( const DictionaryCtxt* dict, XP_U16 depth, Tile* tiles, + DictIndex* indices, XP_U16* nextIndex, + XWStreamCtxt* stream, EdgeArray* prevEdges, DictIndex* prevIndex ) { - XP_USE( indices ); - XP_ASSERT( depth == 1 ); /* so far */ - XP_U16 ii, needCount; - XP_U16 nextIndex = 0; + EdgeArray curEdges = { .nEdges = 0 }; + if ( !findStartsWith( dict, tiles, depth, &curEdges ) ) { + indices[(*nextIndex)++] = NO_INDEX; + } else { + XP_ASSERT( curEdges.nEdges == depth ); + if ( ! ISACCEPTING( dict, curEdges.edges[curEdges.nEdges-1] ) ) { + if ( !nextWord( dict, &curEdges ) ) { + XP_ASSERT( 0 ); + } + } + if ( wordsEqual( &curEdges, prevEdges ) ) { + XP_ASSERT( *prevIndex == 0 ); + } else { + /* Walk the prev word forward until they're the same */ + while ( nextWord( dict, prevEdges ) ) { + ++*prevIndex; + if ( wordsEqual( &curEdges, prevEdges ) ) { + break; + } + } + } + indices[(*nextIndex)++] = *prevIndex; + if ( NULL != stream ) { + XP_UCHAR prefix[8]; + (void)dict_tilesToString( dict, tiles, depth, prefix, VSIZE(prefix) ); + stream_catString( stream, prefix ); + } + } + if ( NULL != stream ) { + stream_catString( stream, "\n" ); + } +} + +static void +doOneDepth( const DictionaryCtxt* dict, + const Tile* allTiles, XP_U16 nTiles, Tile* prefix, + XP_U16 curDepth, XP_U16 maxDepth, + DictIndex* indices, XP_U16* nextEntry, + XWStreamCtxt* stream, EdgeArray* prevEdges, DictIndex* prevIndex ) +{ + XP_U16 ii; + for ( ii = 0; ii < nTiles; ++ii ) { + prefix[curDepth] = allTiles[ii]; + if ( curDepth + 1 == maxDepth ) { + indexOne( dict, maxDepth, prefix, indices, nextEntry, + stream, prevEdges, prevIndex); + } else { + doOneDepth( dict, allTiles, nTiles, prefix, curDepth+1, maxDepth, + indices, nextEntry, stream, prevEdges, prevIndex ); + } + } +} + +XP_U16 +dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, + DictIndex* indices, XP_U16 count, XWStreamCtxt* stream ) +{ + XP_ASSERT( depth < MAX_COLS ); + XP_U16 ii, needCount, nTiles; XP_U16 nFaces = dict_numTileFaces( dict ); XP_Bool hasBlank = dict_hasBlankTile( dict ); if ( hasBlank ) { @@ -879,66 +930,46 @@ dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, } XP_ASSERT( needCount <= count ); + Tile allTiles[nFaces]; + nTiles = 0; + for ( ii = 0; ii < nFaces; ++ii ) { + if ( hasBlank && ii == dict_getBlankTile( dict ) ) { + continue; + } + allTiles[nTiles++] = (Tile)ii; + } + /* 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. */ + XP_U16 nextIndex = 0; DictWord firstWord; if ( dict_firstWord( dict, &firstWord ) ) { - array_edge* prevEdges[MAX_COLS]; - indicesToEdges( dict, &firstWord, prevEdges ); - XP_U16 prevNEdges = firstWord.nTiles; - DictIndex index = 0; + EdgeArray prevEdges; + DictIndex prevIndex = 0; + Tile prefix[depth]; + indicesToEdges( dict, &firstWord, &prevEdges ); - Tile tiles[1]; - nFaces = dict_numTileFaces( dict ); - for ( tiles[0] = 0; tiles[0] < nFaces; ++tiles[0] ) { - if ( hasBlank && tiles[0] == dict_getBlankTile( dict ) ) { - continue; - } + doOneDepth( dict, allTiles, nFaces, prefix, 0, depth, + indices, &nextIndex, stream, &prevEdges, &prevIndex ); - 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; - } - } } - + return nextIndex; } XP_Bool dict_firstWord( const DictionaryCtxt* dict, DictWord* word ) { - array_edge* edges[MAX_COLS]; - XP_U16 nEdges = 0; - edges[nEdges++] = dict_getTopEdge( dict ); + EdgeArray edges = { .nEdges = 0 }; + edges.edges[edges.nEdges++] = dict_getTopEdge( dict ); - XP_Bool success = ISACCEPTING( dict, edges[0] ) - || nextWord( dict, edges, &nEdges ); + XP_Bool success = ISACCEPTING( dict, edges.edges[0] ) + || nextWord( dict, &edges ); if ( success ) { word->wordCount = dict_getWordCount( dict ); - edgesToIndices( dict, nEdges, edges, word ); + edgesToIndices( dict, &edges, word ); word->index = 0; } @@ -958,15 +989,14 @@ dict_getNextWord( const DictionaryCtxt* dict, DictWord* word ) XP_Bool dict_lastWord( const DictionaryCtxt* dict, DictWord* word ) { - array_edge* edges[MAX_COLS]; - XP_U16 nEdges = 0; - edges[nEdges++] = dict_getTopEdge( dict ); + EdgeArray edges = { .nEdges = 0 }; + edges.edges[edges.nEdges++] = dict_getTopEdge( dict ); - XP_Bool success = lastEdges( dict, edges, &nEdges ); + XP_Bool success = lastEdges( dict, &edges ); if ( success ) { word->wordCount = dict_getWordCount( dict ); - edgesToIndices( dict, nEdges, edges, word ); + edgesToIndices( dict, &edges, word ); word->index = word->wordCount - 1; } @@ -1017,23 +1047,22 @@ dict_getNthWord( const DictionaryCtxt* dict, DictWord* word, XP_U32 nn ) } } - array_edge* edges[MAX_COLS]; - XP_U16 nEdges = word->nTiles; - indicesToEdges( dict, word, edges ); + EdgeArray edges; + indicesToEdges( dict, word, &edges ); if ( word->index < nn ) { for ( ii = nn - word->index; ii > 0; --ii ) { - if ( !nextWord( dict, edges, &nEdges ) ) { + if ( !nextWord( dict, &edges ) ) { XP_ASSERT( 0 ); } } } else if ( word->index > nn ) { for ( ii = word->index - nn; ii > 0; --ii ) { - if ( !prevWord( dict, edges, &nEdges ) ) { + if ( !prevWord( dict, &edges ) ) { XP_ASSERT( 0 ); } } } - edgesToIndices( dict, nEdges, edges, word ); + edgesToIndices( dict, &edges, word ); word->index = nn; } return success; @@ -1044,15 +1073,16 @@ dict_wordToString( const DictionaryCtxt* dict, const DictWord* word, XP_UCHAR* buf, XP_U16 buflen ) { XP_U16 ii; - array_edge* edges[MAX_COLS]; + const XP_U16 nTiles = word->nTiles; Tile tiles[MAX_COLS]; + EdgeArray edges; - indicesToEdges( dict, word, edges ); + indicesToEdges( dict, word, &edges ); - for ( ii = 0; ii < word->nTiles; ++ii ) { - tiles[ii] = EDGETILE( dict, edges[ii] ); + for ( ii = 0; ii < nTiles; ++ii ) { + tiles[ii] = EDGETILE( dict, edges.edges[ii] ); } - (void)dict_tilesToString( dict, tiles, word->nTiles, buf, buflen ); + (void)dict_tilesToString( dict, tiles, nTiles, buf, buflen ); } #endif /* XWFEATURE_WALKDICT */ diff --git a/xwords4/common/dictnry.h b/xwords4/common/dictnry.h index 4f1de065f..c6c291c36 100644 --- a/xwords4/common/dictnry.h +++ b/xwords4/common/dictnry.h @@ -219,8 +219,9 @@ typedef struct _DictWord { } DictWord; XP_U32 dict_countWords( const DictionaryCtxt* dict ); -void dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, - DictIndex* indices, XP_U16 count ); +XP_U16 dict_makeIndex( const DictionaryCtxt* dict, XP_U16 depth, + DictIndex* indices, XP_U16 count, + XWStreamCtxt* stream ); 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 ); diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index f18f4e95c..7b46ee2f9 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -893,9 +893,39 @@ tmp_noop_sigintterm( int XP_UNUSED(sig) ) } #ifdef XWFEATURE_WALKDICT -static void -walk_dict_test( const DictionaryCtxt* dict ) +#if 0 +static char* +mkPrefix( const DictionaryCtxt* dict, int index, int depth, char* buf, int len ) { + Tile tiles[depth]; + Tile blank; + XP_U16 nTiles = dict_numTileFaces( dict ); + if ( dict_hasBlankTile( dict ) ) { + blank = dict_getBlankTile( dict ); + --nTiles; + } else { + blank = -1; + } + int ii; + + for ( ii = depth-1; ii >= 0; --ii ) { + Tile tile = index % nTiles; + if ( -1 != blank && blank <= tile ) { + ++tile; + } + tiles[ii] = tile; + index /= nTiles; + } + + dict_tilesToString( dict, tiles, depth, buf, len ); + return buf; +} +#endif + +static void +walk_dict_test( const LaunchParams* params ) +{ + const DictionaryCtxt* dict = params->dict; /* 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; @@ -967,13 +997,20 @@ 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_U16 depth = 2; + DictIndex indices[26*26]; /* pow(26,depth) */ + XWStreamCtxt* stream = mem_stream_make( MPPARM(params->util->mpool) + params->vtMgr, + NULL, CHANNEL_NONE, NULL ); + XP_U16 nIndices = dict_makeIndex( dict, depth, indices, VSIZE(indices), stream ); + const char* ptr = (char*)stream_getPtr( stream ); + for ( ii = 0; ii < nIndices; ++ii ) { + char* next = strstr( (char*)ptr, "\n" ); + XP_ASSERT( !!next ); + if ( next > ptr && indices[ii] != NO_INDEX ) { + if ( !dict_getNthWord( dict, &word, indices[ii] ) ) { + XP_ASSERT( 0 ); + } XP_ASSERT( word.index == indices[ii] ); XP_UCHAR buf1[64]; dict_wordToString( dict, &word, buf1, VSIZE(buf1) ); @@ -981,10 +1018,27 @@ walk_dict_test( const DictionaryCtxt* dict ) 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 ); + char prfx[8]; + snprintf( prfx, depth+1, "%s", ptr ); + prfx[depth] = '\0'; + fprintf( stderr, + "%d: index: %ld; prefix: %s; word: %s (prev: %s)\n", + ii, indices[ii], prfx, buf1, buf2 ); + } else if ( next == ptr && indices[ii] == NO_INDEX ) { + } else { + XP_ASSERT( 0 ); } + ptr = next + 1; } + /* for ( ii = 0; ii < VSIZE(indices); ++ii ) { */ + /* DictIndex index = indices[ii]; */ + /* if ( NO_INDEX == index ) { */ + /* continue; */ + /* } */ + /* catOnClose( stream, NULL ); */ + stream_destroy( stream ); + exit( 0 ); } #else @@ -1380,7 +1434,7 @@ main( int argc, char** argv ) /* } */ /* } */ - walk_dict_test( mainParams.dict ); + walk_dict_test( &mainParams ); if ( 0 ) { #ifdef XWFEATURE_RELAY