From cb2d847e6d84fb9cfb4f09db3cefc3b994671244 Mon Sep 17 00:00:00 2001
From: Eric House
Date: Thu, 10 Mar 2022 15:21:36 -0800
Subject: [PATCH 01/13] add bonus squares for 21x21 board (linux only)
---
xwords4/common/comtypes.h | 2 ++
xwords4/common/model.c | 2 +-
xwords4/common/mscore.c | 4 ++++
xwords4/linux/linuxutl.c | 34 ++++++++++++++++++++++++++++------
4 files changed, 35 insertions(+), 7 deletions(-)
diff --git a/xwords4/common/comtypes.h b/xwords4/common/comtypes.h
index d2c35f93b..2779b4906 100644
--- a/xwords4/common/comtypes.h
+++ b/xwords4/common/comtypes.h
@@ -212,6 +212,8 @@ typedef enum {
BONUS_DOUBLE_WORD,
BONUS_TRIPLE_LETTER,
BONUS_TRIPLE_WORD,
+ BONUS_QUAD_LETTER,
+ BONUS_QUAD_WORD,
BONUS_LAST
} XWBonusType;
diff --git a/xwords4/common/model.c b/xwords4/common/model.c
index 016355898..1381142da 100644
--- a/xwords4/common/model.c
+++ b/xwords4/common/model.c
@@ -391,7 +391,7 @@ model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses, XP_U16 nBonuses
XP_FREE( model->vol.mpool, model->vol.bonuses );
}
model->vol.bonuses = XP_MALLOC( model->vol.mpool,
- nBonuses * sizeof(model->vol.bonuses[0]) );
+ nBonuses * sizeof(model->vol.bonuses[0]) );
XP_MEMCPY( model->vol.bonuses, bonuses,
nBonuses * sizeof(model->vol.bonuses[0]) );
model->vol.nBonuses = nBonuses;
diff --git a/xwords4/common/mscore.c b/xwords4/common/mscore.c
index 2fbc18cf3..cdc54abc8 100644
--- a/xwords4/common/mscore.c
+++ b/xwords4/common/mscore.c
@@ -620,6 +620,8 @@ word_multiplier( const ModelCtxt* model, XWEnv xwe, XP_U16 col, XP_U16 row )
return 2;
case BONUS_TRIPLE_WORD:
return 3;
+ case BONUS_QUAD_WORD:
+ return 4;
default:
return 1;
}
@@ -634,6 +636,8 @@ tile_multiplier( const ModelCtxt* model, XWEnv xwe, XP_U16 col, XP_U16 row )
return 2;
case BONUS_TRIPLE_LETTER:
return 3;
+ case BONUS_QUAD_LETTER:
+ return 4;
default:
return 1;
}
diff --git a/xwords4/linux/linuxutl.c b/xwords4/linux/linuxutl.c
index 3f52ad43a..6101b820d 100644
--- a/xwords4/linux/linuxutl.c
+++ b/xwords4/linux/linuxutl.c
@@ -115,11 +115,13 @@ linux_util_makeEmptyDict( XW_UtilCtxt* XP_UNUSED_DBG(uctx), XWEnv xwe )
#define DW BONUS_DOUBLE_WORD
#define TL BONUS_TRIPLE_LETTER
#define TW BONUS_TRIPLE_WORD
+#define QL BONUS_QUAD_LETTER
+#define QW BONUS_QUAD_WORD
static XWBonusType*
bonusesFor( XP_U16 boardSize, XP_U16* len )
{
- static XWBonusType scrabbleBoard[] = {
+ static XWBonusType scrabbleBoard[] = {
TW,//EM,EM,DL,EM,EM,EM,TW,
EM,DW,//EM,EM,EM,TL,EM,EM,
@@ -133,7 +135,7 @@ bonusesFor( XP_U16 boardSize, XP_U16* len )
TW,EM,EM,DL,EM,EM,EM,DW,
}; /* scrabbleBoard */
- static XWBonusType seventeen[] = {
+ static XWBonusType seventeen[] = {
TW,//EM,EM,DL,EM,EM,EM,TW,
EM,DW,//EM,EM,EM,TL,EM,EM,
@@ -146,17 +148,37 @@ bonusesFor( XP_U16 boardSize, XP_U16* len )
EM,EM,DL,EM,EM,EM,DL,//EM,
TW,EM,EM,DL,EM,EM,EM,DW,
TW,EM,EM,DL,EM,EM,EM,DW,DW,
- }; /* scrabbleBoard */
+ }; /* seventeen */
+
+ static XWBonusType twentyOne[] = {
+ QW,
+ EM, DW,
+ EM, EM, DW,
+ DL, EM, EM, TW,
+ EM, TL, EM, EM, DW,
+ EM, EM, QL, EM, EM, DW,
+ EM, EM, EM, DL, EM, EM, DW,
+ TW, EM, EM, EM, EM, EM, EM, DW,
+ EM, DW, EM, EM, TL, EM, EM, EM, TL,
+ EM, EM, DW, EM, EM, DL, EM, EM, EM, DL,
+ DL, EM, EM, TW, EM, EM, DL, EM, EM, EM, DW,
+ }; /* twentyOne */
XWBonusType* result = NULL;
- if ( boardSize == 15 ) {
+ switch ( boardSize ) {
+ case 15:
result = scrabbleBoard;
*len = VSIZE(scrabbleBoard);
- } else if ( boardSize == 17 ) {
+ break;
+ case 17:
result = seventeen;
*len = VSIZE(seventeen);
+ break;
+ case 21:
+ result = twentyOne;
+ *len = VSIZE(twentyOne);
+ break;
}
-
return result;
}
From b34190d62d86e3f2921f3cf1a28c039675fad518 Mon Sep 17 00:00:00 2001
From: Eric House
Date: Thu, 10 Mar 2022 16:56:09 -0800
Subject: [PATCH 02/13] make 21x21 work on Android
Still needs default colors and the bonus square values working for
smaller sizes.
---
.../org/eehouse/android/xw4/BoardCanvas.java | 4 +-
.../android/xw4/GameConfigDelegate.java | 24 +++-
.../eehouse/android/xw4/jni/CommonPrefs.java | 2 +-
.../app/src/main/res/values/common_rsrc.xml | 11 ++
.../app/src/main/res/values/strings.xml | 8 ++
.../main/res/xml/prefs_appear_colors_dark.xml | 10 ++
xwords4/android/jni/utilwrapper.c | 31 +-----
xwords4/common/board.c | 2 +-
xwords4/common/boarddrw.c | 4 +-
xwords4/common/model.c | 78 +++++++++----
xwords4/common/model.h | 2 +-
xwords4/common/mscore.c | 25 ++---
xwords4/common/util.h | 6 -
xwords4/linux/linuxutl.c | 105 ------------------
14 files changed, 123 insertions(+), 189 deletions(-)
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
index a5a6cc257..a908741cf 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/BoardCanvas.java
@@ -173,7 +173,9 @@ public class BoardCanvas extends Canvas implements DrawCtx {
int[] ids = { R.string.bonus_l2x_summary,
R.string.bonus_w2x_summary ,
R.string.bonus_l3x_summary,
- R.string.bonus_w3x_summary };
+ R.string.bonus_w3x_summary,
+ R.string.bonus_l4x_summary,
+ R.string.bonus_w4x_summary };
m_bonusSummaries = new String[1 + ids.length];
for ( int ii = 0; ii < ids.length; ++ii ) {
m_bonusSummaries[ ii+1 ] = res.getString( ids[ii] );
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java
index 6d5788b91..f5a2d3699 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/GameConfigDelegate.java
@@ -1123,9 +1123,12 @@ public class GameConfigDelegate extends DelegateBase
private int positionToSize( int position ) {
switch( position ) {
- case 0: return 15;
- case 1: return 13;
- case 2: return 11;
+ case 0: return 21;
+ case 1: return 19;
+ case 2: return 17;
+ case 3: return 15;
+ case 4: return 13;
+ case 5: return 11;
default:
Assert.failDbg();
}
@@ -1137,15 +1140,24 @@ public class GameConfigDelegate extends DelegateBase
int size = m_gi.boardSize;
int selection = 0;
switch( size ) {
- case 15:
+ case 21:
selection = 0;
break;
- case 13:
+ case 19:
selection = 1;
break;
- case 11:
+ case 17:
selection = 2;
break;
+ case 15:
+ selection = 3;
+ break;
+ case 13:
+ selection = 4;
+ break;
+ case 11:
+ selection = 5;
+ break;
default:
Assert.failDbg();
break;
diff --git a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
index eec642156..a72df3583 100644
--- a/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
+++ b/xwords4/android/app/src/main/java/org/eehouse/android/xw4/jni/CommonPrefs.java
@@ -79,7 +79,7 @@ public class CommonPrefs extends XWPrefs {
private CommonPrefs()
{
playerColors = new int[4];
- bonusColors = new int[5];
+ bonusColors = new int[7];
bonusColors[0] = 0xF0F0F0F0; // garbage
otherColors = new int[COLOR_LAST];
}
diff --git a/xwords4/android/app/src/main/res/values/common_rsrc.xml b/xwords4/android/app/src/main/res/values/common_rsrc.xml
index 4d017c77b..0dae3ec8c 100644
--- a/xwords4/android/app/src/main/res/values/common_rsrc.xml
+++ b/xwords4/android/app/src/main/res/values/common_rsrc.xml
@@ -26,8 +26,10 @@
key_clr_player3
key_clr_bonus_l2x
key_clr_bonus_l3x
+ key_clr_bonus_l4x
key_clr_bonus_w2x
key_clr_bonus_w3x
+ key_clr_bonus_w4x
key_clr_tile_back
key_clr_empty
key_clr_background
@@ -42,8 +44,10 @@
key_clr_player3_dark
key_clr_bonus_l2x_dark
key_clr_bonus_l3x_dark
+ key_clr_bonus_l4x_dark
key_clr_bonus_w2x_dark
key_clr_bonus_w3x_dark
+ key_clr_bonus_w4x_dark
key_clr_tile_back_dark
key_clr_empty_dark
key_clr_background_dark
@@ -198,6 +202,9 @@
https://eehouse.org/xw4/faq.html#%1$s
+ - 21x21
+ - 19x19
+ - 17x17
- 15x15
- 13x13
- 11x11
@@ -389,6 +396,8 @@
- @string/key_bonus_w2x
- @string/key_bonus_l3x
- @string/key_bonus_w3x
+ - @string/key_bonus_l4x
+ - @string/key_bonus_w4x
- @string/key_tile_back
- @string/key_empty
@@ -408,6 +417,8 @@
- @string/key_bonus_w2x_dark
- @string/key_bonus_l3x_dark
- @string/key_bonus_w3x_dark
+ - @string/key_bonus_l4x_dark
+ - @string/key_bonus_w4x_dark
- @string/key_tile_back_dark
- @string/key_empty_dark
diff --git a/xwords4/android/app/src/main/res/values/strings.xml b/xwords4/android/app/src/main/res/values/strings.xml
index bfe88e3b5..bbec9affe 100644
--- a/xwords4/android/app/src/main/res/values/strings.xml
+++ b/xwords4/android/app/src/main/res/values/strings.xml
@@ -445,6 +445,12 @@
3W
+
+ 4L
+
+ 4W
(No moves yet)
@@ -863,10 +869,12 @@
Double letter
Triple letter
+ Quadruple letter
Double word
Triple word
+ Quadruple word
From cdc77eaf680aa7efbae5ce6f024a02d7ec515278 Mon Sep 17 00:00:00 2001
From: Eric House
Date: Mon, 14 Mar 2022 18:28:08 +0100
Subject: [PATCH 10/13] add board sizes to wordlist format (English only)
Modify language metadata to have possibly different counts of tiles
for different board sizes. Make the necessary changes for loading such
files. Works on linux version at least. Only English will build for
now thanks to changes in info.txt format.
---
xwords4/common/dictnry.c | 77 ++++++++++++++++++++++----------
xwords4/common/dictnry.h | 3 ++
xwords4/dawg/English/info.txt | 60 ++++++++++++++-----------
xwords4/dawg/Makefile.langcommon | 8 +++-
xwords4/dawg/dawg2dict.py | 30 ++++++++++---
xwords4/dawg/xloc.py | 40 +++++++++++++----
6 files changed, 150 insertions(+), 68 deletions(-)
diff --git a/xwords4/common/dictnry.c b/xwords4/common/dictnry.c
index afaad90a3..8d00473a4 100644
--- a/xwords4/common/dictnry.c
+++ b/xwords4/common/dictnry.c
@@ -250,12 +250,12 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
if ( 0 < headerLen ) {
dctx->desc = getNullTermParam( dctx, &ptr, &headerLen );
} else {
- XP_LOGF( "%s: no note", __func__ );
+ XP_LOGFF( "no note" );
}
if ( 0 < headerLen ) {
dctx->md5Sum = getNullTermParam( dctx, &ptr, &headerLen );
} else {
- XP_LOGF( "%s: no md5Sum", __func__ );
+ XP_LOGFF( "no md5Sum" );
}
XP_U16 headerFlags = 0;
@@ -268,12 +268,27 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
XP_LOGFF( "setting headerFlags: 0x%x", headerFlags );
dctx->headerFlags = headerFlags;
+ if ( 0 < headerLen ) {
+ dctx->nBoardSizes = *ptr++;
+ XP_ASSERT( dctx->nBoardSizes <= VSIZE(dctx->boardSizes) );
+ for ( int ii = 0; ii < dctx->nBoardSizes; ++ii ) {
+ dctx->boardSizes[ii] = *ptr++;
+ }
+ headerLen -= 1 + dctx->nBoardSizes;
+ XP_ASSERT( 0 <= headerLen );
+ }
+
if ( 0 < headerLen ) {
XP_LOGFF( "skipping %d bytes of header", headerLen );
}
ptr += headerLen;
}
+ if ( 0 == dctx->nBoardSizes ) { /* wasn't provided */
+ dctx->boardSizes[0] = 15;
+ dctx->nBoardSizes = 1;
+ }
+
if ( isUTF8 ) {
numFaceBytes = *ptr++;
}
@@ -301,7 +316,8 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
dctx->nFaces = numFaces;
- dctx->countsAndValues = XP_MALLOC( dctx->mpool, numFaces * 2 );
+ dctx->countsAndValues = XP_MALLOC( dctx->mpool,
+ numFaces * (1 + dctx->nBoardSizes) );
XP_U16 facesSize = numFaceBytes;
if ( !isUTF8 ) {
facesSize /= 2;
@@ -316,8 +332,9 @@ parseCommon( DictionaryCtxt* dctx, XWEnv xwe, const XP_U8** ptrp, const XP_U8* e
unsigned short xloc;
XP_MEMCPY( &xloc, ptr, sizeof(xloc) );
ptr += sizeof(xloc);
- XP_MEMCPY( dctx->countsAndValues, ptr, numFaces*2 );
- ptr += numFaces*2;
+ size_t cvSize = numFaces * (1 + dctx->nBoardSizes);
+ XP_MEMCPY( dctx->countsAndValues, ptr, cvSize );
+ ptr += cvSize;
dctx->langCode = xloc & 0x7F;
}
@@ -413,7 +430,7 @@ dict_getBlankTile( const DictionaryCtxt* dict )
} /* dict_getBlankTile */
XP_U16
-dict_getTileValue( const DictionaryCtxt* dict, Tile tile )
+dict_getTileValue( const DictionaryCtxt* dict, const Tile tile )
{
XP_ASSERT( !!dict );
if ( (tile & TILE_VALUE_MASK) != tile ) {
@@ -421,9 +438,11 @@ dict_getTileValue( const DictionaryCtxt* dict, Tile tile )
tile == dict_getBlankTile( dict ) );
}
XP_ASSERT( tile < dict->nFaces );
- tile *= 2;
+ int offset = tile * (1 + dict->nBoardSizes);
XP_ASSERT( !!dict->countsAndValues );
- return dict->countsAndValues[tile+1];
+ XP_U16 result = dict->countsAndValues[offset + dict->nBoardSizes];
+ /* XP_LOGFF( "(%d) => %d", tile, result ); */
+ return result;
} /* dict_getTileValue */
static const XP_UCHAR*
@@ -479,22 +498,32 @@ dict_getNextTileString( const DictionaryCtxt* dict, Tile tile,
XP_U16
dict_numTilesForSize( const DictionaryCtxt* dict, Tile tile, XP_U16 nCols )
{
- tile *= 2;
- XP_U16 count = dict->countsAndValues[tile];
-
- /* Wordlists are built assuming 15x15 boards. Different sized boards need
- different numbers of tiles. The wordlist might provide for the size we
- have. If not, let's adjust the count based on how many squares we have
- vs. 15x15.
- */
- XP_U16 pct = (nCols * nCols * 100) / (15 * 15);
- XP_U16 newCount = count * pct / 100;
- if ( 50 < (count * pct) % 100 ) {
- ++newCount;
+ XP_Bool matched = XP_FALSE;
+ int offset = tile * (1 + dict->nBoardSizes);
+ for ( int ii = 0; !matched && ii < dict->nBoardSizes; ++ii ) {
+ if ( nCols == dict->boardSizes[ii] ) { /* perfect match? */
+ offset += ii;
+ matched = XP_TRUE;
+ }
}
- XP_LOGFF( "adjusted count %d to %d based on pct of %d", count, newCount, pct );
- count = newCount;
+ XP_U16 count = dict->countsAndValues[offset];
+ if ( !matched ) {
+ /* Older wordlists are built assuming 15x15 boards. Different sized
+ boards need different numbers of tiles. The wordlist might provide
+ for the size we have. If not, let's adjust the count based on how
+ many squares we have vs. 15x15.
+ */
+ XP_U16 pct = (nCols * nCols * 100) / (15 * 15);
+ XP_U16 newCount = count * pct / 100;
+ if ( 50 < (count * pct) % 100 ) {
+ ++newCount;
+ }
+ // XP_LOGFF( "adjusted count %d to %d based on pct of %d", count, newCount, pct );
+ count = newCount;
+ }
+
+ // XP_LOGFF( "(tile=%d, ncols=%d) => %d", tile, nCols, count );
return count;
} /* dict_numTiles */
@@ -702,6 +731,7 @@ dict_writeToStream( const DictionaryCtxt* dict, XWStreamCtxt* stream )
stream_putBits( stream, 6, dict->nFaces );
+ XP_ASSERT(0); /* if this fires, need to fix for per-boardSize counts */
for ( ii = 0; ii < dict->nFaces*2; ii+=2 ) {
XP_U16 count, value;
@@ -789,6 +819,7 @@ common_destructor( DictionaryCtxt* dict, XWEnv XP_UNUSED(xwe) )
void
dict_loadFromStream( DictionaryCtxt* dict, XWEnv xwe, XWStreamCtxt* stream )
{
+ XP_ASSERT(0); /* if this fires, need to fix for per-boardSize counts */
XP_U8 nFaces, nFaceBytes;
XP_U16 maxCountBits, maxValueBits;
XP_U16 ii, nSpecials;
@@ -1043,7 +1074,7 @@ make_stubbed_dict( MPFORMAL_NOCOMMA )
setBlankTile( dict );
return dict;
-} /* make_subbed_dict */
+} /* make_stubbed_dict */
#endif /* STUBBED_DICT */
diff --git a/xwords4/common/dictnry.h b/xwords4/common/dictnry.h
index 7815423b1..e47e1b1b1 100644
--- a/xwords4/common/dictnry.h
+++ b/xwords4/common/dictnry.h
@@ -88,6 +88,9 @@ struct DictionaryCtxt {
const XP_UCHAR** facePtrs; /* elems point into faces, above */
XP_U8* countsAndValues;
+ XP_U8 nBoardSizes;
+ XP_U8 boardSizes[2]; /* will be [15] or [15,21] for now */
+
SpecialBitmaps* bitmaps;
XP_UCHAR** chars;
XP_UCHAR** charEnds;
diff --git a/xwords4/dawg/English/info.txt b/xwords4/dawg/English/info.txt
index 40593ae95..a99abd55e 100644
--- a/xwords4/dawg/English/info.txt
+++ b/xwords4/dawg/English/info.txt
@@ -38,34 +38,40 @@ LANGINFO: will simply be excluded from the dictionary.
# English==1. Low byte is padding
XLOC_HEADER:0x8100
+# COUNT_SIZES: Columns 2-n in the BEGIN_TILES section are for boards
+# of what sizes? 15 is the default, and COUNT_SIZES is not needed if
+# there's only one sizes column and it's for a 15x15 board. Having
+# only one column that's for other than 15 is an error.
+COUNT_SIZES: 15 21
+
-2 0 {"_"}
-9 1 'A|a'
-2 3 'B|b'
-2 3 'C|c'
-4 2 'D|d'
-12 1 'E|e'
-2 4 'F|f'
-3 2 'G|g'
-2 4 'H|h'
-9 1 'I|i'
-1 8 'J|j'
-1 5 'K|k'
-4 1 'L|l'
-2 3 'M|m'
-6 1 'N|n'
-8 1 'O|o'
-2 3 'P|p'
-1 10 'Q|q'
-6 1 'R|r'
-4 1 'S|s'
-6 1 'T|t'
-4 1 'U|u'
-2 4 'V|v'
-2 4 'W|w'
-1 8 'X|x'
-2 4 'Y|y'
-1 10 'Z|z'
+{"_"} 0 2 4
+'A|a' 1 9 16
+'B|b' 3 2 4
+'C|c' 3 2 6
+'D|d' 2 4 8
+'E|e' 1 12 24
+'F|f' 4 2 4
+'G|g' 2 3 5
+'H|h' 4 2 5
+'I|i' 1 9 13
+'J|j' 8 1 2
+'K|k' 5 1 2
+'L|l' 1 4 7
+'M|m' 3 2 6
+'N|n' 1 6 13
+'O|o' 1 8 15
+'P|p' 3 2 4
+'Q|q' 10 1 2
+'R|r' 1 6 13
+'S|s' 1 4 10
+'T|t' 1 6 15
+'U|u' 1 4 7
+'V|v' 4 2 3
+'W|w' 4 2 4
+'X|x' 8 1 2
+'Y|y' 4 2 4
+'Z|z' 10 1 2
# should ignore all after the above
diff --git a/xwords4/dawg/Makefile.langcommon b/xwords4/dawg/Makefile.langcommon
index 45d26f033..432bbf118 100644
--- a/xwords4/dawg/Makefile.langcommon
+++ b/xwords4/dawg/Makefile.langcommon
@@ -220,7 +220,8 @@ endif
frankspecials.bin: ../frank_mkspecials.py $(BMPFILES)
$< $(BLANK_INFO) $(LANG_SPECIAL_INFO) > $@
-$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin charcount.bin table.bin values.bin frankspecials.bin
+$(XWLANG)%.$(FRANK_EXT): dawg$(XWLANG)%.stamp $(XWLANG)%_flags.bin $(XWLANG)%_newheader.bin \
+ charcount.bin table.bin values.bin frankspecials.bin
cat $(XWLANG)$*_flags.bin $(XWLANG)$*_newheader.bin charcount.bin table.bin values.bin \
frankspecials.bin $(XWLANG)StartLoc.bin \
$$(ls dawg$(XWLANG)$*_*.bin) > $@
@@ -273,6 +274,9 @@ allbins:
$(MAKE) TARGET_TYPE=FRANK byodbins
rm palmspecials.bin
+boardSizes.bin: ../xloc.py info.txt
+ ../xloc.py -bs -out $@
+
table.bin: ../xloc.py
ifdef NEWDAWG
../xloc.py $(ENCP) -tn -out $@
@@ -308,7 +312,7 @@ $(XWLANG)%_headerFlags.bin:
perl -e "print pack(\"n\",$$FLAGS)" > $@
$(XWLANG)%_newheader.bin: $(XWLANG)%_wordcount.bin $(XWLANG)%_note.bin \
- $(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin
+ $(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin boardSizes.bin
SIZ=0; \
for FILE in $+; do \
SIZ=$$(($$SIZ + $$(ls -l $$FILE | awk '{print $$5}'))); \
diff --git a/xwords4/dawg/dawg2dict.py b/xwords4/dawg/dawg2dict.py
index 3c325f188..9d7ae87b1 100755
--- a/xwords4/dawg/dawg2dict.py
+++ b/xwords4/dawg/dawg2dict.py
@@ -54,12 +54,13 @@ def splitFaces( buf ):
return faces
-def loadCountsAndValues( fh, numFaces, data ):
- twoBytesFmt = struct.Struct('BB')
+def loadCountsAndValues( fh, numFaces, nSizes, data ):
for ii in range(numFaces):
- pair = twoBytesFmt.unpack(fh.read(twoBytesFmt.size))
- data[ii]['count'] = int(pair[0])
- data[ii]['val'] = int(pair[1])
+ counts = []
+ for jj in range(nSizes):
+ counts.append(int.from_bytes(fh.read(1), 'little'))
+ data[ii]['counts'] = counts
+ data[ii]['val'] = int.from_bytes(fh.read(1), 'little')
def eatBitmap( fh ):
nCols = int(oneByteFmt.unpack(fh.read(oneByteFmt.size))[0])
@@ -142,6 +143,7 @@ def process(args):
with open(args.DAWG, "rb") as dawg:
nWords = 0
+ boardSizes = [15]
headerFmt = struct.Struct('!HH')
(flags, headerLen) = headerFmt.unpack(dawg.read(headerFmt.size))
@@ -162,7 +164,21 @@ def process(args):
sys.exit(0)
md5Sum = getNullTermParam(header)
print( 'header: read sum: {}'.format(md5Sum), file=sys.stderr )
- except:
+
+ # skip header flags
+ header.read(2)
+ print( 'header: skipped flags', file=sys.stderr)
+
+ nBoardSizes = int.from_bytes(header.read(1), 'big')
+ print( 'header: nBoardSizes: {}'.format(nBoardSizes), file=sys.stderr )
+ boardSizes = []
+ for ii in range(nBoardSizes):
+ siz = int.from_bytes(header.read(1), 'big')
+ boardSizes.append(siz)
+ print( 'header: read sizes: {}'.format(boardSizes), file=sys.stderr)
+
+ except Exception as ex:
+ print( 'header: exception!! {} '.format(ex) )
md5Sum = None
if args.GET_SUM:
@@ -214,7 +230,7 @@ def process(args):
langCode = 0x7F & oneByteFmt.unpack(dawg.read(oneByteFmt.size))[0]
dawg.read( oneByteFmt.size ) # skip byte
- loadCountsAndValues( dawg, numFaces, data )
+ loadCountsAndValues( dawg, numFaces, len(boardSizes), data )
loadSpecialData( dawg, data )
offsetStruct = struct.Struct('!L')
diff --git a/xwords4/dawg/xloc.py b/xwords4/dawg/xloc.py
index c329ebd34..b3e651817 100755
--- a/xwords4/dawg/xloc.py
+++ b/xwords4/dawg/xloc.py
@@ -8,7 +8,10 @@ def errorOut(msg):
def mkParser():
parser = argparse.ArgumentParser()
parser.add_argument('-enc', dest = 'ENCODING', type = str, help = 'use this encoding' )
- parser.add_argument('-tn', dest = 'DO_TABLE', action = 'store_true', help = 'output table file' )
+ parser.add_argument('-tn', dest = 'DO_TABLE', action = 'store_true',
+ help = 'output table file' )
+ parser.add_argument('-bs', dest = 'DO_BOARDSIZE', action = 'store_true',
+ help = 'output boardSizes file' )
# parser.add_argument('-tn', dest = 'UNICODE', default = False,
# action = 'store_true', help = 'assume unicode')
@@ -53,8 +56,10 @@ def parseTileInfo(infoFile, encoding):
if sEndTiles.match(line):
break
else:
- (count, val, face) = line.split(None, 2)
- result['_TILES'].append((count, val, face))
+ (face, val, counts) = line.split(None, 2)
+ result['_TILES'].append({'counts': counts,
+ 'val': val,
+ 'face': face})
elif sBeginTiles.match(line):
inTiles = True
else:
@@ -72,11 +77,11 @@ def printLetters( letters, outfile ):
outfile.write(letters.encode('utf8'))
def writeMapFile(xlocToken, outfile):
- print('writeMapFile()')
+ print('writeMapFile(out={})'.format(outfile))
tiles = xlocToken['_TILES']
specialCount = 0
for tile in tiles:
- face = tile[2]
+ face = tile['face']
match = sSingleCharMatch.match(face)
if match:
printLetters( match.group(1), outfile )
@@ -94,13 +99,25 @@ def writeMapFile(xlocToken, outfile):
def writeValuesFile(xlocToken, outfile):
header = xlocToken.get('XLOC_HEADER') or errorOut('no XLOC_HEADER found')
- print('writing header: {}'.format(header))
+ print('writeValuesFile(out={}): writing header: {}'.format(outfile, header))
outfile.write(struct.pack('!H', int(header, 16)))
+ nCounts = 0
for tile in xlocToken['_TILES']:
- val = int(tile[0])
- count = int(tile[1])
- outfile.write(struct.pack('BB', val, count))
+ counts = tile['counts'].split()
+ assert nCounts == 0 or nCounts == len(counts)
+ nCounts = len(counts)
+ for count in counts:
+ outfile.write(struct.pack('B', int(count)))
+
+ val = int(tile['val'])
+ outfile.write(struct.pack('B', val))
+
+def writeBoardSizesFile(xlocToken, outfile):
+ cs = xlocToken.get('COUNT_SIZES', '15').split()
+ outfile.write(struct.pack('B', len(cs)))
+ for siz in cs:
+ outfile.write(struct.pack('B', int(siz)))
def main():
print('{}.main {} called'.format(sys.argv[0], sys.argv[1:]))
@@ -127,6 +144,11 @@ def main():
with open(path, 'wb') as outfile:
writeValuesFile( xlocToken, outfile )
+ if args.DO_BOARDSIZE and args.OUTFILE:
+ with open(args.OUTFILE, 'wb') as outfile:
+ writeBoardSizesFile( xlocToken, outfile )
+
+
##############################################################################
if __name__ == '__main__':
main()
From c970d397686532e982e7ab08cd79cc56add45ff1 Mon Sep 17 00:00:00 2001
From: Eric House
Date: Tue, 15 Mar 2022 10:12:31 +0100
Subject: [PATCH 11/13] move extra counts to header
Fix so old versions of the app will still be able to read the new format
---
xwords4/dawg/English/Makefile.BasEnglish | 2 +-
xwords4/dawg/English/info.txt | 3 +-
xwords4/dawg/Makefile.langcommon | 16 ++++++--
xwords4/dawg/dawg2dict.py | 34 ++++++++++-------
xwords4/dawg/xloc.py | 47 ++++++++++++++++++------
5 files changed, 72 insertions(+), 30 deletions(-)
diff --git a/xwords4/dawg/English/Makefile.BasEnglish b/xwords4/dawg/English/Makefile.BasEnglish
index fa67d38a8..0041e8dd5 100644
--- a/xwords4/dawg/English/Makefile.BasEnglish
+++ b/xwords4/dawg/English/Makefile.BasEnglish
@@ -16,7 +16,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
XWLANG=BasEnglish
-LANGCODE=en_US
+LANGCODE=en
DICT2DAWGARGS = -r -nosort
DICTNOTE = "Wordlist created in the 1930s for language learners"
diff --git a/xwords4/dawg/English/info.txt b/xwords4/dawg/English/info.txt
index a99abd55e..ca95195f9 100644
--- a/xwords4/dawg/English/info.txt
+++ b/xwords4/dawg/English/info.txt
@@ -15,7 +15,8 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-LANGCODE:en_US
+LANGCODE:en
+LANGNAME:English
# deal with DOS files
LANGFILTER: tr -d '\r'
diff --git a/xwords4/dawg/Makefile.langcommon b/xwords4/dawg/Makefile.langcommon
index 432bbf118..16c48441a 100644
--- a/xwords4/dawg/Makefile.langcommon
+++ b/xwords4/dawg/Makefile.langcommon
@@ -274,8 +274,8 @@ allbins:
$(MAKE) TARGET_TYPE=FRANK byodbins
rm palmspecials.bin
-boardSizes.bin: ../xloc.py info.txt
- ../xloc.py -bs -out $@
+extraCounts.bin: ../xloc.py info.txt
+ ../xloc.py -ec -out $@
table.bin: ../xloc.py
ifdef NEWDAWG
@@ -302,6 +302,15 @@ $(XWLANG)%_note.bin:
echo -n $(DICTNOTE) > $@
perl -e "print pack(\"c\",0)" >> $@
+langName.bin: ../xloc.py
+ ../xloc.py -info LANGNAME -out $@
+
+langCode.bin: ../xloc.py
+ ../xloc.py -info LANGCODE -out $@
+
+otherCounts.bin: ../xloc.py
+ ../xloc.py -oc -out otherCounts.bin
+
$(XWLANG)%_md5sum.bin:
cat table.bin values.bin frankspecials.bin $(XWLANG)StartLoc.bin \
dawg$(XWLANG)$*_*.bin | md5sum | awk '{print $$1}' | tr -d '\n' > $@
@@ -312,7 +321,8 @@ $(XWLANG)%_headerFlags.bin:
perl -e "print pack(\"n\",$$FLAGS)" > $@
$(XWLANG)%_newheader.bin: $(XWLANG)%_wordcount.bin $(XWLANG)%_note.bin \
- $(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin boardSizes.bin
+ $(XWLANG)%_md5sum.bin $(XWLANG)%_headerFlags.bin langName.bin \
+ langCode.bin otherCounts.bin
SIZ=0; \
for FILE in $+; do \
SIZ=$$(($$SIZ + $$(ls -l $$FILE | awk '{print $$5}'))); \
diff --git a/xwords4/dawg/dawg2dict.py b/xwords4/dawg/dawg2dict.py
index 9d7ae87b1..08248a317 100755
--- a/xwords4/dawg/dawg2dict.py
+++ b/xwords4/dawg/dawg2dict.py
@@ -54,13 +54,18 @@ def splitFaces( buf ):
return faces
-def loadCountsAndValues( fh, numFaces, nSizes, data ):
+def loadCountsAndValues( fh, numFaces, extraData, data ):
for ii in range(numFaces):
- counts = []
- for jj in range(nSizes):
- counts.append(int.from_bytes(fh.read(1), 'little'))
- data[ii]['counts'] = counts
+ data[ii]['counts'] = {15: int.from_bytes(fh.read(1), 'little')}
data[ii]['val'] = int.from_bytes(fh.read(1), 'little')
+ if extraData:
+ buf = io.BytesIO(extraData)
+ while True:
+ siz = int.from_bytes(buf.read(1), 'little')
+ if not siz: break
+ for ii in range(numFaces):
+ count = int.from_bytes(buf.read(1), 'little')
+ data[ii]['counts'][siz] = count
def eatBitmap( fh ):
nCols = int(oneByteFmt.unpack(fh.read(oneByteFmt.size))[0])
@@ -143,7 +148,7 @@ def process(args):
with open(args.DAWG, "rb") as dawg:
nWords = 0
- boardSizes = [15]
+ extraData = None
headerFmt = struct.Struct('!HH')
(flags, headerLen) = headerFmt.unpack(dawg.read(headerFmt.size))
@@ -169,13 +174,14 @@ def process(args):
header.read(2)
print( 'header: skipped flags', file=sys.stderr)
- nBoardSizes = int.from_bytes(header.read(1), 'big')
- print( 'header: nBoardSizes: {}'.format(nBoardSizes), file=sys.stderr )
- boardSizes = []
- for ii in range(nBoardSizes):
- siz = int.from_bytes(header.read(1), 'big')
- boardSizes.append(siz)
- print( 'header: read sizes: {}'.format(boardSizes), file=sys.stderr)
+ langName = getNullTermParam(header)
+ langCode = getNullTermParam(header)
+ print('header: langName: {}; langCode: {}'.format(langName, langCode),
+ file=sys.stderr)
+
+ extraSize = int.from_bytes(header.read(1), 'little')
+ print( 'header: extraSize: {}'.format(extraSize), file=sys.stderr )
+ extraData = header.read(extraSize)
except Exception as ex:
print( 'header: exception!! {} '.format(ex) )
@@ -230,7 +236,7 @@ def process(args):
langCode = 0x7F & oneByteFmt.unpack(dawg.read(oneByteFmt.size))[0]
dawg.read( oneByteFmt.size ) # skip byte
- loadCountsAndValues( dawg, numFaces, len(boardSizes), data )
+ loadCountsAndValues( dawg, numFaces, extraData, data )
loadSpecialData( dawg, data )
offsetStruct = struct.Struct('!L')
diff --git a/xwords4/dawg/xloc.py b/xwords4/dawg/xloc.py
index b3e651817..ec895ffa8 100755
--- a/xwords4/dawg/xloc.py
+++ b/xwords4/dawg/xloc.py
@@ -10,8 +10,10 @@ def mkParser():
parser.add_argument('-enc', dest = 'ENCODING', type = str, help = 'use this encoding' )
parser.add_argument('-tn', dest = 'DO_TABLE', action = 'store_true',
help = 'output table file' )
- parser.add_argument('-bs', dest = 'DO_BOARDSIZE', action = 'store_true',
- help = 'output boardSizes file' )
+ parser.add_argument('-oc', dest = 'DO_OTHERCOUNTS', action = 'store_true',
+ help = 'write extra (non-15x15 board) counts' )
+ parser.add_argument('-info', dest = 'INFO_KEY', type = str,
+ help = 'info.txt keyword to write null-terminated' )
# parser.add_argument('-tn', dest = 'UNICODE', default = False,
# action = 'store_true', help = 'assume unicode')
@@ -76,6 +78,12 @@ def printLetters( letters, outfile ):
letters = ' '.join(letters)
outfile.write(letters.encode('utf8'))
+def writeInfoFile(xlocToken, key, outfile):
+ val = xlocToken[key]
+ assert val
+ outfile.write(val.encode('utf8'))
+ outfile.write(struct.pack('B', 0 ))
+
def writeMapFile(xlocToken, outfile):
print('writeMapFile(out={})'.format(outfile))
tiles = xlocToken['_TILES']
@@ -102,22 +110,36 @@ def writeValuesFile(xlocToken, outfile):
print('writeValuesFile(out={}): writing header: {}'.format(outfile, header))
outfile.write(struct.pack('!H', int(header, 16)))
+ cs = xlocToken.get('COUNT_SIZES', '15').split()
+ useOffset = cs.index('15')
+
nCounts = 0
for tile in xlocToken['_TILES']:
counts = tile['counts'].split()
assert nCounts == 0 or nCounts == len(counts)
nCounts = len(counts)
- for count in counts:
- outfile.write(struct.pack('B', int(count)))
+ assert nCounts == len(cs)
+ outfile.write(struct.pack('B', int(counts[useOffset])))
val = int(tile['val'])
outfile.write(struct.pack('B', val))
-def writeBoardSizesFile(xlocToken, outfile):
+def writeOtherCounts(xlocToken, outfile):
cs = xlocToken.get('COUNT_SIZES', '15').split()
- outfile.write(struct.pack('B', len(cs)))
- for siz in cs:
- outfile.write(struct.pack('B', int(siz)))
+
+ tiles = xlocToken['_TILES']
+ # Write the size of the data so it can be skipped by the reader,
+ # which won't know how many faces the tile set has yet.
+ totalSiz = (len(cs) - 1) * (1 + len(tiles))
+ outfile.write(struct.pack('B', totalSiz))
+
+ for useOffset in range(len(cs)):
+ siz = int(cs[useOffset])
+ if siz == 15: continue
+ outfile.write(struct.pack('B', siz))
+ for tile in tiles:
+ count = tile['counts'].split()[useOffset]
+ outfile.write(struct.pack('B', int(count)))
def main():
print('{}.main {} called'.format(sys.argv[0], sys.argv[1:]))
@@ -128,6 +150,10 @@ def main():
errorOut('{} not found'.format(infoFile))
xlocToken = parseTileInfo(infoFile, args.ENCODING)
+ if args.INFO_KEY and args.OUTFILE:
+ with open(args.OUTFILE, 'wb') as outfile:
+ writeInfoFile(xlocToken, args.INFO_KEY, outfile);
+
if args.DO_TABLE or args.TABLE_FILE:
path = args.TABLE_FILE or args.OUTFILE
with open(path, 'wb') as outfile:
@@ -144,10 +170,9 @@ def main():
with open(path, 'wb') as outfile:
writeValuesFile( xlocToken, outfile )
- if args.DO_BOARDSIZE and args.OUTFILE:
+ if args.DO_OTHERCOUNTS and args.OUTFILE:
with open(args.OUTFILE, 'wb') as outfile:
- writeBoardSizesFile( xlocToken, outfile )
-
+ writeOtherCounts(xlocToken, outfile)
##############################################################################
if __name__ == '__main__':
From 9568e4ab0337a6644fa944215719131cf37081a8 Mon Sep 17 00:00:00 2001
From: Eric House
Date: Tue, 15 Mar 2022 23:03:03 +0100
Subject: [PATCH 12/13] move extra counts into header so new dicts backward
compatible
---
.../app/src/main/assets/BasEnglish2to8.xwd | Bin 4803 -> 4831 bytes
.../app/src/main/assets/CollegeEng_2to8.xwd | Bin 101094 -> 101206 bytes
.../android/app/src/main/assets/Top5000.xwd | Bin 19907 -> 19935 bytes
xwords4/android/jni/anddict.c | 8 +-
xwords4/common/comtypes.h | 1 +
xwords4/common/dictnry.c | 342 ++++++++++--------
xwords4/common/dictnry.h | 9 +-
xwords4/dawg/English/Makefile.top5000 | 4 +-
xwords4/linux/Makefile | 2 +-
xwords4/linux/gtknewgame.c | 7 +-
xwords4/linux/linuxdict.c | 10 +-
11 files changed, 216 insertions(+), 167 deletions(-)
diff --git a/xwords4/android/app/src/main/assets/BasEnglish2to8.xwd b/xwords4/android/app/src/main/assets/BasEnglish2to8.xwd
index 4a070bc2c94fb39688763e6d9f0f5569207ff9ec..8d4b9958d749670b259900304da3bdaa1e406367 100644
GIT binary patch
delta 62
zcmX@CdS8`=L587uB8&M%PbDn|2G_jwoXp}3hSWR;8BrDi7B&tE7FJeXCMI?^UVauP
RUM_xiW)>zErj6MVLI9&G3Z4J}
delta 34
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