support new phony to block words not in dict

Currently detects the same as tiles not in a line and calls out to a new
util method that's currently parameter-less. On Android the option only
appears in d variants.
This commit is contained in:
Eric House 2020-04-19 13:03:19 -07:00
parent 988facccd1
commit c0f074c1bf
19 changed files with 138 additions and 29 deletions

View file

@ -1861,6 +1861,18 @@ public class BoardDelegate extends DelegateBase
}
}
@Override
public void informWordBlocked()
{
runOnUiThread( new Runnable() {
@Override
public void run() {
makeOkOnlyBuilder( "Word blocked" )
.show();
}
} );
}
@Override
public void playerScoreHeld( int player )
{

View file

@ -51,7 +51,7 @@ public class CurGameInfo implements Serializable {
private static final String PHONIES = "PHONIES";
private static final String DUP = "DUP";
public enum XWPhoniesChoice { PHONIES_IGNORE, PHONIES_WARN, PHONIES_DISALLOW };
public enum XWPhoniesChoice { PHONIES_IGNORE, PHONIES_WARN, PHONIES_DISALLOW, PHONIES_BLOCK, };
public enum DeviceRole { SERVER_STANDALONE, SERVER_ISSERVER, SERVER_ISCLIENT };
public String dictName;

View file

@ -62,6 +62,7 @@ public interface UtilCtxt {
void remSelected();
void timerSelected( boolean inDuplicateMode, boolean canPause );
void setIsServer( boolean isServer );
void informWordBlocked();
void bonusSquareHeld( int bonus );
void playerScoreHeld( int player );

View file

@ -106,6 +106,12 @@ public class UtilCtxtImpl implements UtilCtxt {
subclassOverride( "setIsServer" );
}
@Override
public void informWordBlocked()
{
subclassOverride( "informWordBlocked" );
}
@Override
public void bonusSquareHeld( int bonus )
{

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation"
>
<!-- These are temporary, until the new feature's released -->
<string name="phonies_block">Block phonies</string>
<string-array name="phony_names">
<item>@string/phonies_ignore</item>
<item>@string/phonies_warn</item>
<item>@string/phonies_disallow</item>
<item>@string/phonies_block</item>
</string-array>
</resources>

View file

@ -0,0 +1 @@
../../../xw4d/res/values/tmp_for_phony.xml

View file

@ -706,6 +706,14 @@ and_util_setIsServer( XW_UtilCtxt* uc, XP_Bool isServer )
UTIL_CBK_TAIL();
}
static void
and_util_informWordBlocked( XW_UtilCtxt* uc )
{
UTIL_CBK_HEADER( "informWordBlocked", "()V" );
(*env)->CallVoidMethod( env, util->jutil, mid );
UTIL_CBK_TAIL();
}
#ifdef XWFEATURE_DEVID
static const XP_UCHAR*
and_dutil_getDevID( XW_DUtilCtxt* duc, DevIDType* typ )
@ -908,6 +916,7 @@ makeUtil( MPFORMAL EnvThreadInfo* ti, jobject jutil, CurGameInfo* gi,
#endif
SET_PROC(getDevUtilCtxt);
SET_PROC(informWordBlocked);
#undef SET_PROC
assertTableFull( vtable, sizeof(*vtable), "util" );

View file

@ -1040,10 +1040,10 @@ typedef struct _BadWordList {
} BadWordList;
static void
saveBadWords( const WNParams* wnp )
saveBadWords( const WNParams* wnp, void* closure )
{
if ( !wnp->isLegal ) {
BadWordList* bwlp = (BadWordList*)wnp->closure;
BadWordList* bwlp = (BadWordList*)closure;
bwlp->bwi.words[bwlp->bwi.nWords] = &bwlp->buf[bwlp->index];
XP_STRCAT( &bwlp->buf[bwlp->index], wnp->word );
bwlp->index += XP_STRLEN(wnp->word) + 1;

View file

@ -140,8 +140,12 @@ typedef XP_U8 DeviceRole;
enum {
PHONIES_IGNORE,
/* You can commit a phony after viewing a warning */
PHONIES_WARN,
PHONIES_DISALLOW
/* You can commit a phony, but you'll lose your turn */
PHONIES_DISALLOW,
/* a phony is an illegal move, like tiles out-of-line */
PHONIES_BLOCK,
};
typedef XP_U8 XWPhoniesChoice;

View file

@ -1104,9 +1104,9 @@ considerMove( EngineCtxt* engine, Tile* tiles, XP_S16 tileLength,
} /* considerMove */
static void
countWords( const WNParams* wnp )
countWords( const WNParams* wnp, void* closure )
{
XP_U16* wcp = (XP_U16*)wnp->closure;
XP_U16* wcp = (XP_U16*)closure;
if ( wnp->isLegal ) {
++*wcp;
}

View file

@ -77,7 +77,7 @@ static void loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
static void writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
const PlayerCtxt* pc );
static XP_U16 model_getRecentPassCount( ModelCtxt* model );
static void recordWord( const WNParams* wnp );
static void recordWord( const WNParams* wnp, void *closure );
#ifdef DEBUG
typedef struct _DiffTurnState {
XP_S16 lastPlayerNum;
@ -2459,9 +2459,9 @@ typedef struct _FirstWordData {
} FirstWordData;
static void
getFirstWord( const WNParams* wnp )
getFirstWord( const WNParams* wnp, void* closure )
{
FirstWordData* data = (FirstWordData*)wnp->closure;
FirstWordData* data = (FirstWordData*)closure;
if ( '\0' == data->word[0] && '\0' != wnp->word[0] ) {
XP_STRCAT( data->word, wnp->word );
}
@ -2548,9 +2548,9 @@ appendWithCR( XWStreamCtxt* stream, const XP_UCHAR* word, XP_U16* counter )
}
static void
recordWord( const WNParams* wnp )
recordWord( const WNParams* wnp, void* closure )
{
RecordWordsInfo* info = (RecordWordsInfo*)wnp->closure;
RecordWordsInfo* info = (RecordWordsInfo*)closure;
appendWithCR( info->stream, wnp->word, &info->nWords );
}
@ -2572,9 +2572,9 @@ typedef struct _ListWordsThroughInfo {
} ListWordsThroughInfo;
static void
listWordsThrough( const WNParams* wnp )
listWordsThrough( const WNParams* wnp, void* closure )
{
ListWordsThroughInfo* info = (ListWordsThroughInfo*)wnp->closure;
ListWordsThroughInfo* info = (ListWordsThroughInfo*)closure;
const MoveInfo* movei = wnp->movei;
XP_Bool contained = XP_FALSE;

View file

@ -291,10 +291,9 @@ typedef struct _WNParams {
XP_U16 start;
XP_U16 end;
#endif
void* closure;
} WNParams;
typedef void (*WordNotifierProc)( const WNParams* wnp );
typedef void (*WordNotifierProc)( const WNParams* wnp, void* closure );
typedef struct WordNotifierInfo {
WordNotifierProc proc;
void* closure;

View file

@ -217,6 +217,24 @@ model_figureFinalScores( ModelCtxt* model, ScoresArray* finalScoresP,
}
} /* model_figureFinalScores */
typedef struct _BlockCheckState {
WordNotifierInfo* chainNI;
XP_Bool allLegal;
} BlockCheckState;
static void
blockCheck( const WNParams* wnp, void* closure )
{
BlockCheckState* bcs = (BlockCheckState*)closure;
if ( !!bcs->chainNI ) {
(bcs->chainNI->proc)( wnp, bcs->chainNI->closure );
}
if ( !wnp->isLegal ) {
bcs->allLegal = XP_FALSE;
}
}
/* checkScoreMove.
* Negative score means illegal.
*/
@ -238,17 +256,38 @@ checkScoreMove( ModelCtxt* model, XP_S16 turn, EngineCtxt* engine,
formatSummary( stream, model, 0 );
}
} else if ( tilesInLine( model, turn, &isHorizontal ) ) {
} else if ( !tilesInLine( model, turn, &isHorizontal ) ) {
if ( !silent ) { /* tiles out of line */
util_userError( model->vol.util, ERR_TILES_NOT_IN_LINE );
}
} else {
MoveInfo moveInfo;
normalizeMoves( model, turn, isHorizontal, &moveInfo );
if ( isLegalMove( model, &moveInfo, silent ) ) {
score = figureMoveScore( model, turn, &moveInfo, engine, stream,
notifyInfo );
/* If I'm testing for blocking, I need to chain my test onto any
existing WordNotifierInfo. blockCheck() does that. */
XP_Bool checkDict = PHONIES_BLOCK == model->vol.gi->phoniesAction;
WordNotifierInfo blockWNI;
BlockCheckState bcs;
if ( checkDict ) {
bcs.allLegal = XP_TRUE;
bcs.chainNI = notifyInfo;
blockWNI.proc = blockCheck;
blockWNI.closure = &bcs;
notifyInfo = &blockWNI;
}
XP_S16 tmpScore = figureMoveScore( model, turn, &moveInfo,
engine, stream, notifyInfo );
if ( checkDict && !bcs.allLegal ) {
if ( !silent ) {
util_informWordBlocked( model->vol.util );
}
} else {
score = tmpScore;
}
}
} else if ( !silent ) { /* tiles out of line */
util_userError( model->vol.util, ERR_TILES_NOT_IN_LINE );
}
return score;
} /* checkScoreMove */
@ -700,9 +739,8 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
#ifdef XWFEATURE_BOARDWORDS
.movei = movei, .start = start, .end = end,
#endif
.closure = notifyInfo->closure,
};
(void)(*notifyInfo->proc)( &wnp );
(void)(*notifyInfo->proc)( &wnp, notifyInfo->closure );
}
if ( !!stream ) {

View file

@ -2613,10 +2613,10 @@ server_setGameOverListener( ServerCtxt* server, GameOverListener gol,
} /* server_setGameOverListener */
static void
storeBadWords( const WNParams* wnp )
storeBadWords( const WNParams* wnp, void* closure )
{
if ( !wnp->isLegal ) {
ServerCtxt* server = (ServerCtxt*)wnp->closure;
ServerCtxt* server = (ServerCtxt*)closure;
const XP_UCHAR* name = dict_getShortName( wnp->dict );
XP_LOGF( "storeBadWords called with \"%s\" (name=%s)", wnp->word, name );

View file

@ -173,6 +173,8 @@ typedef struct UtilVtable {
void (*m_util_setIsServer)(XW_UtilCtxt* uc, XP_Bool isServer );
#endif
void (*m_util_informWordBlocked)( XW_UtilCtxt* uc );
#ifdef XWFEATURE_SEARCHLIMIT
XP_Bool (*m_util_getTraySearchLimits)(XW_UtilCtxt* uc,
XP_U16* min, XP_U16* max );
@ -308,6 +310,8 @@ struct XW_UtilCtxt {
# define util_addrChange( uc, addro, addrn )
#endif
#define util_informWordBlocked(uc) (uc)->vtable->m_util_informWordBlocked( uc )
#ifdef XWFEATURE_SEARCHLIMIT
#define util_getTraySearchLimits(uc,min,max) \
(uc)->vtable->m_util_getTraySearchLimits((uc), (min), (max))

View file

@ -1049,6 +1049,11 @@ curses_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words )
}
#endif
static void
curses_util_informWordBlocked( XW_UtilCtxt* XP_UNUSED(uc) )
{
LOG_FUNC();
}
#ifndef XWFEATURE_STANDALONE_ONLY
static XWStreamCtxt*
@ -1120,6 +1125,7 @@ setupCursesUtilCallbacks( CursesBoardGlobals* bGlobals, XW_UtilCtxt* util )
#ifdef XWFEATURE_BOARDWORDS
SET_PROC(cellSquareHeld);
#endif
SET_PROC(informWordBlocked);
#ifdef XWFEATURE_SEARCHLIMIT
SET_PROC(getTraySearchLimits);

View file

@ -1997,6 +1997,13 @@ gtk_util_cellSquareHeld( XW_UtilCtxt* uc, XWStreamCtxt* words )
}
#endif
static void
gtk_util_informWordBlocked( XW_UtilCtxt* uc )
{
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
gtkUserError( globals, "Word blocked by phonies setting" );
}
static void
gtk_util_userError( XW_UtilCtxt* uc, UtilErrID id )
{
@ -2229,7 +2236,7 @@ setupGtkUtilCallbacks( GtkGameGlobals* globals, XW_UtilCtxt* util )
#ifdef XWFEATURE_BOARDWORDS
SET_PROC(cellSquareHeld);
#endif
SET_PROC(informWordBlocked);
#undef SET_PROC
assertTableFull( util->vtable, sizeof(*util->vtable), "gtk util" );

View file

@ -218,7 +218,7 @@ addPhoniesCombo( GtkNewGameState* state, GtkWidget* parent )
FALSE, TRUE, 0 );
GtkWidget* phoniesCombo = gtk_combo_box_text_new();
const char* ptxts[] = { "IGNORE", "WARN", "DISALLOW" };
const char* ptxts[] = { "IGNORE", "WARN", "LOSE TURN", "BLOCK" };
for ( int ii = 0; ii < VSIZE(ptxts); ++ii ) {
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(phoniesCombo),
@ -227,6 +227,8 @@ addPhoniesCombo( GtkNewGameState* state, GtkWidget* parent )
g_signal_connect( phoniesCombo, "changed",
G_CALLBACK(phonies_combo_changed), state );
XWPhoniesChoice startChoice = state->globals->cGlobals.params->pgi.phoniesAction;
gtk_combo_box_set_active( GTK_COMBO_BOX(phoniesCombo), startChoice );
gtk_widget_show( phoniesCombo );
gtk_box_pack_start( GTK_BOX(hbox), phoniesCombo, FALSE, TRUE, 0 );
gtk_widget_show( hbox );

View file

@ -1018,7 +1018,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_ADVERTISEROOM, false, "make-public", "make room public on relay" }
,{ CMD_JOINADVERTISED, false, "join-public", "look for a public room" }
,{ CMD_PHONIES, true, "phonies",
"ignore (0, default), warn (1) or lose turn (2)" }
"ignore (0, default), warn (1), lose turn (2), or refuse to commit (3)" }
,{ CMD_BONUSFILE, true, "bonus-file",
"provides bonus info: . + * ^ and ! are legal" }
,{ CMD_INVITEE_RELAYID, true, "invitee-relayid", "relayID to send any invitation to" }
@ -2823,8 +2823,11 @@ main( int argc, char** argv )
case 2:
mainParams.pgi.phoniesAction = PHONIES_DISALLOW;
break;
case 3:
mainParams.pgi.phoniesAction = PHONIES_BLOCK;
break;
default:
usage( argv[0], "phonies takes 0 or 1 or 2" );
usage( argv[0], "phonies takes 0 or 1 or 2 or 3" );
}
break;
case CMD_BONUSFILE: