mirror of
git://xwords.git.sourceforge.net/gitroot/xwords/xwords
synced 2025-02-15 20:48:00 +01:00
new feature to remember acceptable phonies
Present to user option to remember "phonies", words played but not in the current wordlist. They're stored by language (not by wordlist) and not available for hints or the robot to use (as that would require incorporating them into a wordlist, a much larger change.)
This commit is contained in:
parent
182ce9b7f0
commit
8be9d18287
30 changed files with 860 additions and 126 deletions
|
@ -39,6 +39,7 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupMenu;
|
||||
|
@ -315,15 +316,25 @@ public class BoardDelegate extends DelegateBase
|
|||
}
|
||||
break;
|
||||
|
||||
case NOTIFY_BADWORDS: {
|
||||
case ASK_BADWORDS: {
|
||||
LinearLayout rpLayout =
|
||||
(LinearLayout)inflate( R.layout.phonies_found );
|
||||
|
||||
TextView tv = (TextView)rpLayout.findViewById( R.id.message );
|
||||
tv.setText( (String)params[0] );
|
||||
|
||||
final int badWordsKey = (int)params[1];
|
||||
lstnr = new OnClickListener() {
|
||||
@Override
|
||||
public void onClick( DialogInterface dlg, int bx ) {
|
||||
handleViaThread( JNICmd.CMD_COMMIT, true, false );
|
||||
CheckBox cb = (CheckBox)rpLayout
|
||||
.findViewById(R.id.remember_phonies_check);
|
||||
handleViaThread( JNICmd.CMD_COMMIT, true, false,
|
||||
cb.isChecked() ? badWordsKey : 0 );
|
||||
}
|
||||
};
|
||||
dialog = ab.setTitle( R.string.phonies_found_title )
|
||||
.setMessage( (String)params[0] )
|
||||
.setView( rpLayout )
|
||||
.setPositiveButton( R.string.button_yes, lstnr )
|
||||
.setNegativeButton( android.R.string.cancel, null )
|
||||
.create();
|
||||
|
@ -2187,7 +2198,7 @@ public class BoardDelegate extends DelegateBase
|
|||
|
||||
@Override
|
||||
public void notifyIllegalWords( String dict, String[] words, int turn,
|
||||
boolean turnLost )
|
||||
boolean turnLost, int badWordsKey )
|
||||
{
|
||||
String wordsString = TextUtils.join( ", ", words );
|
||||
String message =
|
||||
|
@ -2198,7 +2209,7 @@ public class BoardDelegate extends DelegateBase
|
|||
message + getString( R.string.badwords_lost ) );
|
||||
} else {
|
||||
String msg = message + getString( R.string.badwords_accept );
|
||||
showDialogFragment( DlgID.NOTIFY_BADWORDS, msg );
|
||||
showDialogFragment( DlgID.ASK_BADWORDS, msg, badWordsKey );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ public enum DlgID {
|
|||
WARN_NODICT_INVITED, // when responding to invitation
|
||||
WARN_NODICT_SUBST, // when a substitution will be possible/suggested
|
||||
DLG_BADWORDS,
|
||||
NOTIFY_BADWORDS,
|
||||
ASK_BADWORDS,
|
||||
QUERY_MOVE,
|
||||
QUERY_TRADE,
|
||||
ASK_PASSWORD,
|
||||
|
|
|
@ -1817,6 +1817,9 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
enable = nothingSelected && Utils.isGooglePlayApp( m_activity );
|
||||
Utils.setItemVisible( menu, R.id.games_menu_rateme, enable );
|
||||
|
||||
enable = BuildConfig.NON_RELEASE && XwJNI.dvc_haveLegalPhonies();
|
||||
Utils.setItemVisible( menu, R.id.games_submenu_legalPhonies, enable );
|
||||
|
||||
enable = nothingSelected && XWPrefs.getStudyEnabled( m_activity );
|
||||
Utils.setItemVisible( menu, R.id.games_menu_study, enable );
|
||||
|
||||
|
@ -1906,6 +1909,15 @@ public class GamesListDelegate extends ListDelegateBase
|
|||
}
|
||||
break;
|
||||
|
||||
case R.id.games_menu_clearLPs:
|
||||
int nDeleted = XwJNI.dvc_clearLegalPhonies();
|
||||
Utils.showToast( m_activity, R.string.cleared_lps_fmt, nDeleted );
|
||||
break;
|
||||
case R.id.games_menu_listLPs:
|
||||
String txt = XwJNI.dvc_listLegalPhonies();
|
||||
makeOkOnlyBuilder( txt ).show();
|
||||
break;
|
||||
|
||||
case R.id.games_menu_study:
|
||||
StudyListDelegate.launchOrAlert( getDelegator(), this );
|
||||
break;
|
||||
|
|
|
@ -573,13 +573,22 @@ public class JNIThread extends Thread implements AutoCloseable {
|
|||
break;
|
||||
|
||||
case CMD_COMMIT:
|
||||
boolean phoniesConfirmed = args.length < 1
|
||||
? false : (Boolean)args[0];
|
||||
boolean turnConfirmed = args.length < 2
|
||||
? false : (Boolean)args[1];
|
||||
int[] newTiles = args.length < 3 ? null : (int[])args[2];
|
||||
boolean phoniesConfirmed =
|
||||
args.length >= 1 ? (Boolean)args[0] : false;
|
||||
boolean turnConfirmed =
|
||||
args.length >= 2 ? (Boolean)args[1] : false;
|
||||
int[] newTiles = null;
|
||||
int badWordsKey = 0;
|
||||
if ( args.length >= 3 ) {
|
||||
Object obj = args[2];
|
||||
if ( obj instanceof Integer ) {
|
||||
badWordsKey = (Integer)obj;
|
||||
} else if ( obj instanceof int[] ) {
|
||||
newTiles = (int[])obj;
|
||||
}
|
||||
}
|
||||
draw = XwJNI.board_commitTurn( m_jniGamePtr, phoniesConfirmed,
|
||||
turnConfirmed, newTiles );
|
||||
badWordsKey, turnConfirmed, newTiles );
|
||||
break;
|
||||
|
||||
case CMD_TILES_PICKED:
|
||||
|
|
|
@ -117,7 +117,7 @@ public interface UtilCtxt {
|
|||
//void yOffsetChange( int maxOffset, int oldOffset, int newOffset );
|
||||
|
||||
void notifyIllegalWords( String dict, String[] words, int turn,
|
||||
boolean turnLost );
|
||||
boolean turnLost, int badWordsKey );
|
||||
|
||||
void showChat( String msg, int fromPlayer, int tsSeconds );
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ public class UtilCtxtImpl implements UtilCtxt {
|
|||
|
||||
@Override
|
||||
public void notifyIllegalWords( String dict, String[] words, int turn,
|
||||
boolean turnLost )
|
||||
boolean turnLost, int badWordsKey )
|
||||
{
|
||||
subclassOverride( "notifyIllegalWords" );
|
||||
}
|
||||
|
|
|
@ -189,6 +189,21 @@ public class XwJNI {
|
|||
dvc_onWebSendResult( getJNI().m_ptrGlobals, resultKey, succeeded, result );
|
||||
}
|
||||
|
||||
public static boolean dvc_haveLegalPhonies()
|
||||
{
|
||||
return dvc_haveLegalPhonies( getJNI().m_ptrGlobals );
|
||||
}
|
||||
|
||||
public static String dvc_listLegalPhonies()
|
||||
{
|
||||
return dvc_listLegalPhonies( getJNI().m_ptrGlobals );
|
||||
}
|
||||
|
||||
public static int dvc_clearLegalPhonies()
|
||||
{
|
||||
return dvc_clearLegalPhonies( getJNI().m_ptrGlobals );
|
||||
}
|
||||
|
||||
public static boolean hasKnownPlayers()
|
||||
{
|
||||
String[] players = kplr_getPlayers();
|
||||
|
@ -476,6 +491,7 @@ public class XwJNI {
|
|||
public static native boolean board_showTray( GamePtr gamePtr );
|
||||
public static native boolean board_commitTurn( GamePtr gamePtr,
|
||||
boolean phoniesConfirmed,
|
||||
int badWordsKey,
|
||||
boolean turnConfirmed,
|
||||
int[] newTiles );
|
||||
|
||||
|
@ -802,6 +818,9 @@ public class XwJNI {
|
|||
private static native void dvc_onWebSendResult( long jniState, int resultKey,
|
||||
boolean succeeded,
|
||||
String result );
|
||||
private static native boolean dvc_haveLegalPhonies( long jniState );
|
||||
private static native String dvc_listLegalPhonies( long jniState );
|
||||
private static native int dvc_clearLegalPhonies( long jniState );
|
||||
private static native String[] kplr_getPlayers( long jniState, boolean byDate );
|
||||
private static native boolean kplr_renamePlayer( long jniState, String oldName,
|
||||
String newName );
|
||||
|
|
34
xwords4/android/app/src/main/res/layout/phonies_found.xml
Normal file
34
xwords4/android/app/src/main/res/layout/phonies_found.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2008 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="8dp"
|
||||
>
|
||||
<TextView android:id="@+id/message"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
|
||||
<CheckBox android:id="@+id/remember_phonies_check"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/remember_phonies_label"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
</LinearLayout>
|
|
@ -33,6 +33,19 @@
|
|||
android:icon="@drawable/dict__gen"
|
||||
android:showAsAction="ifRoom"
|
||||
/>
|
||||
<item android:id="@+id/games_submenu_legalPhonies"
|
||||
android:title="@string/gamel_menu_legalPhonies"
|
||||
>
|
||||
<menu>
|
||||
<item android:id="@+id/games_menu_clearLPs"
|
||||
android:title="@string/gamel_menu_clearLPs"
|
||||
/>
|
||||
<item android:id="@+id/games_menu_listLPs"
|
||||
android:title="@string/gamel_menu_listLPs"
|
||||
/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
<item android:id="@+id/games_menu_study"
|
||||
android:title="@string/gamel_menu_study"
|
||||
/>
|
||||
|
|
|
@ -15,4 +15,12 @@
|
|||
<string name="url_scheme_http">Force http</string>
|
||||
<string name="url_scheme_https">Force https</string>
|
||||
|
||||
<string name="gamel_menu_legalPhonies">Legal phonies</string>
|
||||
<string name="gamel_menu_clearLPs">Clear all</string>
|
||||
<string name="gamel_menu_listLPs">Show all</string>
|
||||
|
||||
<string name="remember_phonies_label">Accept from now on</string>
|
||||
|
||||
<string name="cleared_lps_fmt">Cleared %d legal phonies</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -555,19 +555,22 @@ and_dutil_remove( XW_DUtilCtxt* duc, const XP_UCHAR* keys[] )
|
|||
#endif
|
||||
|
||||
static void
|
||||
and_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv xwe, BadWordInfo* bwi,
|
||||
XP_U16 turn, XP_Bool turnLost )
|
||||
and_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv xwe,
|
||||
const BadWordInfo* bwi,
|
||||
const XP_UCHAR* dictName,
|
||||
XP_U16 turn, XP_Bool turnLost,
|
||||
XP_U32 badWordsKey )
|
||||
{
|
||||
UTIL_CBK_HEADER("notifyIllegalWords",
|
||||
"(Ljava/lang/String;[Ljava/lang/String;IZ)V" );
|
||||
"(Ljava/lang/String;[Ljava/lang/String;IZI)V" );
|
||||
XP_ASSERT( bwi->nWords > 0 );
|
||||
if ( bwi->nWords > 0 ) {
|
||||
jobjectArray jwords = makeStringArray( env, bwi->nWords,
|
||||
(const XP_UCHAR**)bwi->words );
|
||||
XP_ASSERT( !!bwi->dictName );
|
||||
jstring jname = (*env)->NewStringUTF( env, bwi->dictName );
|
||||
XP_ASSERT( !!dictName );
|
||||
jstring jname = (*env)->NewStringUTF( env, dictName );
|
||||
(*env)->CallVoidMethod( env, util->jutil, mid,
|
||||
jname, jwords, turn, turnLost );
|
||||
jname, jwords, turn, turnLost, badWordsKey );
|
||||
deleteLocalRefs( env, jwords, jname, DELETE_NO_REF );
|
||||
}
|
||||
UTIL_CBK_TAIL();
|
||||
|
|
|
@ -794,6 +794,50 @@ Java_org_eehouse_android_xw4_jni_XwJNI_dvc_1onWebSendResult
|
|||
DVC_HEADER_END();
|
||||
}
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dvc_1haveLegalPhonies
|
||||
( JNIEnv* env, jclass C, jlong jniGlobalPtr )
|
||||
{
|
||||
jboolean jresult;
|
||||
DVC_HEADER(jniGlobalPtr);
|
||||
jresult = dvc_haveLegalPhonies( globalState->dutil, env );
|
||||
DVC_HEADER_END();
|
||||
return jresult;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dvc_1listLegalPhonies
|
||||
( JNIEnv* env, jclass C, jlong jniGlobalPtr )
|
||||
{
|
||||
jstring jresult;
|
||||
DVC_HEADER(jniGlobalPtr);
|
||||
#ifdef MEM_DEBUG
|
||||
MemPoolCtx* mpool = GETMPOOL( globalState );
|
||||
#endif
|
||||
|
||||
XWStreamCtxt* stream = mem_stream_make( MPPARM(mpool) globalState->vtMgr,
|
||||
NULL, 0, NULL, NULL );
|
||||
dvc_listLegalPhonies( globalState->dutil, env, stream );
|
||||
jresult = streamToJString( env, stream );
|
||||
stream_destroy( stream );
|
||||
|
||||
DVC_HEADER_END();
|
||||
return jresult;
|
||||
}
|
||||
#endif
|
||||
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_dvc_1clearLegalPhonies
|
||||
( JNIEnv* env, jclass C, jlong jniGlobalPtr )
|
||||
{
|
||||
jint result;
|
||||
DVC_HEADER(jniGlobalPtr);
|
||||
result = dvc_clearLegalPhonies( globalState->dutil, env );
|
||||
DVC_HEADER_END();
|
||||
return result;
|
||||
}
|
||||
|
||||
# ifdef XWFEATURE_KNOWNPLAYERS
|
||||
JNIEXPORT jobjectArray JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_kplr_1getPlayers
|
||||
|
@ -1925,7 +1969,7 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1setBlankValue
|
|||
JNIEXPORT jboolean JNICALL
|
||||
Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn
|
||||
( JNIEnv* env, jclass C, GamePtrType gamePtr, jboolean phoniesConfirmed,
|
||||
jboolean turnConfirmed, jintArray jNewTiles )
|
||||
jint badWordsKey, jboolean turnConfirmed, jintArray jNewTiles )
|
||||
{
|
||||
jboolean result;
|
||||
XWJNI_START(gamePtr);
|
||||
|
@ -1936,8 +1980,11 @@ Java_org_eehouse_android_xw4_jni_XwJNI_board_1commitTurn
|
|||
tilesArrayToTileSet( env, jNewTiles, &newTiles );
|
||||
newTilesP = &newTiles;
|
||||
}
|
||||
|
||||
result = board_commitTurn( state->game.board, env, phoniesConfirmed,
|
||||
PhoniesConf pc = {.confirmed = phoniesConfirmed,
|
||||
.key = badWordsKey,
|
||||
};
|
||||
result = board_commitTurn( state->game.board, env,
|
||||
phoniesConfirmed ? &pc : NULL,
|
||||
turnConfirmed, newTilesP );
|
||||
XWJNI_END();
|
||||
return result;
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "comms.h" /* for CHANNEL_NONE */
|
||||
#include "dictnry.h"
|
||||
#include "draw.h"
|
||||
#include "device.h"
|
||||
#include "engine.h"
|
||||
#include "util.h"
|
||||
#include "mempool.h" /* debug only */
|
||||
|
@ -323,7 +324,6 @@ board_getDraw( const BoardCtxt* board )
|
|||
void
|
||||
board_writeToStream( const BoardCtxt* board, XWStreamCtxt* stream )
|
||||
{
|
||||
XP_U16 nPlayers, ii;
|
||||
XP_U16 nColsNBits;
|
||||
#ifdef STREAM_VERS_BIGBOARD
|
||||
nColsNBits = 16 > model_numCols(board->model) ? NUMCOLS_NBITS_4
|
||||
|
@ -347,9 +347,9 @@ board_writeToStream( const BoardCtxt* board, XWStreamCtxt* stream )
|
|||
#endif
|
||||
|
||||
XP_ASSERT( !!board->server );
|
||||
nPlayers = board->gi->nPlayers;
|
||||
XP_U16 nPlayers = board->gi->nPlayers;
|
||||
|
||||
for ( ii = 0; ii < nPlayers; ++ii ) {
|
||||
for ( int ii = 0; ii < nPlayers; ++ii ) {
|
||||
const PerTurnInfo* pti = &board->pti[ii];
|
||||
const BoardArrow* arrow = &pti->boardArrow;
|
||||
stream_putBits( stream, nColsNBits, arrow->col );
|
||||
|
@ -1043,12 +1043,6 @@ hideMiniWindow( BoardCtxt* board, XP_Bool destroy, MiniWindowType winType )
|
|||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct _BadWordList {
|
||||
BadWordInfo bwi;
|
||||
XP_UCHAR buf[256];
|
||||
XP_U16 index;
|
||||
} BadWordList;
|
||||
|
||||
static void
|
||||
saveBadWords( const WNParams* wnp, void* closure )
|
||||
{
|
||||
|
@ -1076,7 +1070,8 @@ boardNotifyTrade( BoardCtxt* board, XWEnv xwe, const TrayTileSet* tiles )
|
|||
}
|
||||
|
||||
XP_Bool
|
||||
board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
||||
board_commitTurn( BoardCtxt* board, XWEnv xwe,
|
||||
const PhoniesConf* pconf,
|
||||
XP_Bool turnConfirmed /* includes trade */,
|
||||
TrayTileSet* newTiles )
|
||||
{
|
||||
|
@ -1084,6 +1079,7 @@ board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
|||
const XP_S16 turn = server_getCurrentTurn( board->server, NULL );
|
||||
const XP_U16 selPlayer = board->selPlayer;
|
||||
ModelCtxt* model = board->model;
|
||||
const XP_Bool phoniesConfirmed = !!pconf && pconf->confirmed;
|
||||
|
||||
if ( board->gameOver || turn < 0 ) {
|
||||
/* do nothing */
|
||||
|
@ -1093,6 +1089,22 @@ board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
|||
/* game's over but still undoable so turn hasn't changed; do
|
||||
nothing */
|
||||
} else if ( phoniesConfirmed || turnConfirmed || checkRevealTray( board, xwe ) ) {
|
||||
const DictionaryCtxt* dict = model_getPlayerDict( model, selPlayer );
|
||||
BadWordList* bwl = &board->bwl;
|
||||
|
||||
if ( phoniesConfirmed && 0 != pconf->key ) {
|
||||
XP_ASSERT( bwl->key == pconf->key );
|
||||
if ( bwl->key == pconf->key ) {
|
||||
const BadWordInfo* bwi = &bwl->bwi;
|
||||
const XP_UCHAR* isoCode = dict_getISOCode( dict );
|
||||
|
||||
for ( int ii = 0; ii < bwi->nWords; ++ii ) {
|
||||
dvc_addLegalPhony( board->dutil, xwe, isoCode,
|
||||
bwi->words[ii] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PerTurnInfo* pti = &board->pti[selPlayer];
|
||||
if ( pti->tradeInProgress ) {
|
||||
TileBit traySelBits = pti->traySelBits;
|
||||
|
@ -1127,8 +1139,6 @@ board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
|||
} else {
|
||||
XWStreamCtxt* stream = NULL;
|
||||
XP_Bool legal = turnConfirmed;
|
||||
BadWordList bwl;
|
||||
XP_MEMSET( &bwl, 0, sizeof(bwl) );
|
||||
|
||||
if ( !legal ) {
|
||||
stream = mem_stream_make_raw( MPPARM(board->mpool)
|
||||
|
@ -1136,23 +1146,26 @@ board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
|||
|
||||
XP_U16 stringCode = board->gi->inDuplicateMode
|
||||
? STR_SUBMIT_CONFIRM : STR_COMMIT_CONFIRM;
|
||||
const XP_UCHAR* str = dutil_getUserString( board->dutil, xwe, stringCode );
|
||||
const XP_UCHAR* str = dutil_getUserString( board->dutil, xwe,
|
||||
stringCode );
|
||||
stream_catString( stream, str );
|
||||
|
||||
XP_Bool warn = board->util->gameInfo->phoniesAction == PHONIES_WARN;
|
||||
WordNotifierInfo info;
|
||||
if ( warn ) {
|
||||
XP_MEMSET( bwl, 0, sizeof(*bwl) );
|
||||
bwl->key = 0x7FFFFFFF & XP_RANDOM(); /* clear high bit so can be signed */
|
||||
info.proc = saveBadWords;
|
||||
info.closure = &bwl;
|
||||
info.closure = bwl;
|
||||
}
|
||||
legal = model_checkMoveLegal( model, xwe, selPlayer, stream,
|
||||
warn? &info:(WordNotifierInfo*)NULL);
|
||||
}
|
||||
|
||||
if ( 0 < bwl.bwi.nWords && !phoniesConfirmed ) {
|
||||
bwl.bwi.dictName =
|
||||
dict_getShortName( model_getPlayerDict( model, selPlayer ) );
|
||||
util_notifyIllegalWords( board->util, xwe, &bwl.bwi, selPlayer, XP_FALSE );
|
||||
if ( 0 < bwl->bwi.nWords && !phoniesConfirmed ) {
|
||||
const XP_UCHAR* dictName = dict_getShortName( dict );
|
||||
util_notifyIllegalWords( board->util, xwe, &bwl->bwi, dictName,
|
||||
selPlayer, XP_FALSE, bwl->key );
|
||||
} else if ( legal ) {
|
||||
/* Hide the tray so no peeking. Leave it hidden even if user
|
||||
cancels as otherwise another player could get around
|
||||
|
|
|
@ -176,7 +176,11 @@ XP_Bool board_setBlankValue( BoardCtxt* board, XP_U16 XP_UNUSED(player),
|
|||
|
||||
void board_resetEngine( BoardCtxt* board );
|
||||
|
||||
XP_Bool board_commitTurn( BoardCtxt* board, XWEnv xwe, XP_Bool phoniesConfirmed,
|
||||
typedef struct _PhoniesConf {
|
||||
XP_Bool confirmed;
|
||||
XP_U32 key;
|
||||
} PhoniesConf;
|
||||
XP_Bool board_commitTurn( BoardCtxt* board, XWEnv xwe, const PhoniesConf* pc,
|
||||
XP_Bool turnConfirmed, TrayTileSet* newTiles );
|
||||
|
||||
void board_pushTimerSave( BoardCtxt* board, XWEnv xwe );
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "board.h"
|
||||
#include "engine.h"
|
||||
#include "mempool.h" /* debug only */
|
||||
#include "util.h"
|
||||
|
||||
#ifdef CPLUS
|
||||
extern "C" {
|
||||
|
@ -133,6 +134,13 @@ typedef struct _ScrollData {
|
|||
} ScrollData;
|
||||
typedef enum { SCROLL_H, SCROLL_V, N_SCROLL_DIMS } SDIndex;
|
||||
|
||||
typedef struct _BadWordList {
|
||||
BadWordInfo bwi;
|
||||
XP_UCHAR buf[256];
|
||||
XP_U16 index;
|
||||
XP_U32 key;
|
||||
} BadWordList;
|
||||
|
||||
struct BoardCtxt {
|
||||
/* BoardVTable* vtable; */
|
||||
ModelCtxt* model;
|
||||
|
@ -144,6 +152,8 @@ struct BoardCtxt {
|
|||
struct CurGameInfo* gi;
|
||||
ScrollData sd[N_SCROLL_DIMS];
|
||||
|
||||
BadWordList bwl;
|
||||
|
||||
XP_U16 preHideYOffset;
|
||||
XP_U16 prevYScrollOffset; /* represents where the last draw took place;
|
||||
used to see if bit scrolling can be used */
|
||||
|
|
|
@ -256,6 +256,7 @@ typedef enum _TileValueType {
|
|||
#define SUFFIX_PARTIALS "partials"
|
||||
#define SUFFIX_NEXTID "nextID"
|
||||
#define SUFFIX_DEVSTATE "devState"
|
||||
#define SUFFIX_LEGAL_PHONIES "legalPhonies"
|
||||
#define SUFFIX_MQTT_DEVID "mqtt_devid_key"
|
||||
#define SUFFIX_KNOWN_PLAYERS "known_players_key_dev1"
|
||||
|
||||
|
@ -264,6 +265,7 @@ typedef enum _TileValueType {
|
|||
#define KEY_PARTIALS FULL_KEY(SUFFIX_PARTIALS)
|
||||
#define KEY_NEXTID FULL_KEY(SUFFIX_NEXTID)
|
||||
#define KEY_DEVSTATE FULL_KEY(SUFFIX_DEVSTATE)
|
||||
#define KEY_LEGAL_PHONIES FULL_KEY(SUFFIX_LEGAL_PHONIES)
|
||||
#define MQTT_DEVID_KEY FULL_KEY(SUFFIX_MQTT_DEVID)
|
||||
#define KNOWN_PLAYERS_KEY FULL_KEY(SUFFIX_KNOWN_PLAYERS)
|
||||
|
||||
|
|
|
@ -39,6 +39,8 @@
|
|||
#define LAST_REG_KEY FULL_KEY("device_last_reg")
|
||||
#define KEY_GITREV FULL_KEY("device_gitrev")
|
||||
|
||||
#define PD_VERSION_1 2
|
||||
|
||||
static XWStreamCtxt*
|
||||
mkStream( XW_DUtilCtxt* dutil )
|
||||
{
|
||||
|
@ -55,11 +57,25 @@ typedef struct WSData {
|
|||
WSR code;
|
||||
} WSData;
|
||||
|
||||
typedef struct _PhoniesDataStrs {
|
||||
DLHead links;
|
||||
XP_UCHAR* phony;
|
||||
} PhoniesDataStrs;
|
||||
|
||||
typedef struct _PhoniesDataCodes {
|
||||
DLHead links;
|
||||
XP_UCHAR* isoCode;
|
||||
PhoniesDataStrs* head;
|
||||
} PhoniesDataCodes;
|
||||
|
||||
typedef struct _DevCtxt {
|
||||
XP_U16 devCount;
|
||||
WSData* webSendData;
|
||||
XP_U32 mWebSendKey;
|
||||
pthread_mutex_t webSendMutex;
|
||||
|
||||
PhoniesDataCodes* pd;
|
||||
|
||||
#ifdef DEBUG
|
||||
XP_U32 magic;
|
||||
#endif
|
||||
|
@ -706,6 +722,295 @@ dvc_onWebSendResult( XW_DUtilCtxt* dutil, XWEnv xwe, XP_U32 resultKey,
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct _FindIsoState {
|
||||
const XP_UCHAR* isoCode;
|
||||
PhoniesDataCodes* found;
|
||||
} FindIsoState;
|
||||
|
||||
static ForEachAct
|
||||
findIsoProc( const DLHead* elem, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
PhoniesDataCodes* pdc = (PhoniesDataCodes*)elem;
|
||||
|
||||
FindIsoState* fis = (FindIsoState*)closure;
|
||||
if ( 0 == XP_STRCMP( fis->isoCode, pdc->isoCode ) ) {
|
||||
fis->found = pdc;
|
||||
result = FEA_EXIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static PhoniesDataCodes*
|
||||
findForIso( XW_DUtilCtxt* dutil, DevCtxt* dc, const XP_UCHAR* isoCode )
|
||||
{
|
||||
FindIsoState fis = {
|
||||
.isoCode = isoCode,
|
||||
};
|
||||
dll_map( &dc->pd->links, findIsoProc, NULL, &fis );
|
||||
PhoniesDataCodes* pdc = fis.found;
|
||||
|
||||
if ( !pdc ) {
|
||||
pdc = XP_CALLOC( dutil->mpool, sizeof(*pdc) );
|
||||
pdc->isoCode = copyString( dutil->mpool, isoCode );
|
||||
dc->pd = (PhoniesDataCodes*)dll_insert( &dc->pd->links, &pdc->links, NULL );
|
||||
XP_ASSERT( pdc == dc->pd );
|
||||
}
|
||||
|
||||
return pdc;
|
||||
}
|
||||
|
||||
static void
|
||||
addPhony( XW_DUtilCtxt* dutil, DevCtxt* dc, const XP_UCHAR* isoCode,
|
||||
const XP_UCHAR* phony )
|
||||
{
|
||||
PhoniesDataCodes* pdc = findForIso( dutil, dc, isoCode );
|
||||
XP_ASSERT( !!pdc );
|
||||
|
||||
PhoniesDataStrs* pd = XP_CALLOC( dutil->mpool, sizeof(*pd) );
|
||||
pd->phony = copyString( dutil->mpool, phony );
|
||||
|
||||
pdc->head = (PhoniesDataStrs*)dll_insert( &pdc->head->links, &pd->links, NULL );
|
||||
}
|
||||
|
||||
static ForEachAct
|
||||
storeStrs( const DLHead* elem, void* closure )
|
||||
{
|
||||
const PhoniesDataStrs* pds = (PhoniesDataStrs*)elem;
|
||||
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
|
||||
stringToStream( stream, pds->phony );
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
static ForEachAct
|
||||
storeIso( const DLHead* elem, void* closure )
|
||||
{
|
||||
const PhoniesDataCodes* pdc = (PhoniesDataCodes*)elem;
|
||||
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
|
||||
stringToStream( stream, pdc->isoCode );
|
||||
|
||||
PhoniesDataStrs* pds = pdc->head;
|
||||
XP_U16 numStrs = dll_length( &pds->links );
|
||||
XP_ASSERT( 0 < numStrs );
|
||||
stream_putU32VL( stream, numStrs );
|
||||
|
||||
dll_map( &pds->links, storeStrs, NULL, stream );
|
||||
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
/* Storage format for PD_VERSION_1:
|
||||
* version (byte)
|
||||
* isoCount (byte)
|
||||
** (repeats isoCount times)
|
||||
** isoCode (string)
|
||||
** strCount (var len XP_U32)
|
||||
*** (repeats strCount times)
|
||||
*** phony (string)
|
||||
*/
|
||||
static void
|
||||
storePhoniesData( XW_DUtilCtxt* dutil, XWEnv xwe, DevCtxt* dc )
|
||||
{
|
||||
XWStreamCtxt* stream = mkStream( dutil );
|
||||
if ( !!dc->pd ) {
|
||||
stream_putU8( stream, PD_VERSION_1 );
|
||||
|
||||
PhoniesDataCodes* pdc = dc->pd;
|
||||
XP_U16 numIsos = dll_length( &pdc->links );
|
||||
XP_ASSERT( 0 < numIsos );
|
||||
stream_putU8( stream, numIsos );
|
||||
|
||||
#ifdef DEBUG
|
||||
PhoniesDataCodes* pdc1 = (PhoniesDataCodes*)
|
||||
#endif
|
||||
dll_map( &pdc->links, storeIso, NULL, stream );
|
||||
XP_ASSERT( pdc1 == pdc );
|
||||
}
|
||||
|
||||
const XP_UCHAR* keys[] = { KEY_LEGAL_PHONIES, NULL };
|
||||
dutil_storeStream( dutil, xwe, keys, stream );
|
||||
stream_destroy( stream );
|
||||
}
|
||||
|
||||
static void
|
||||
loadPhoniesData( XW_DUtilCtxt* dutil, XWEnv xwe, DevCtxt* dc )
|
||||
{
|
||||
LOG_FUNC();
|
||||
XP_ASSERT ( !dc->pd );
|
||||
|
||||
XWStreamCtxt* stream = mkStream( dutil );
|
||||
const XP_UCHAR* keys[] = { KEY_LEGAL_PHONIES, NULL };
|
||||
dutil_loadStream( dutil, xwe, keys, stream );
|
||||
|
||||
XP_U8 flags;
|
||||
if ( stream_gotU8( stream, &flags ) && PD_VERSION_1 == flags ) {
|
||||
XP_U8 numIsos;
|
||||
if ( stream_gotU8( stream, &numIsos ) ) {
|
||||
for ( int ii = 0; ii < numIsos; ++ii ) {
|
||||
XP_UCHAR isoCode[32];
|
||||
stringFromStreamHere( stream, isoCode, VSIZE(isoCode) );
|
||||
XP_U32 numStrs = stream_getU32VL( stream );
|
||||
for ( int jj = 0; jj < numStrs; ++jj ) {
|
||||
XP_UCHAR phony[32];
|
||||
stringFromStreamHere( stream, phony, VSIZE(phony) );
|
||||
addPhony( dutil, dc, isoCode, phony );
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
XP_LOGFF( "nothing there???" );
|
||||
}
|
||||
|
||||
stream_destroy( stream );
|
||||
}
|
||||
|
||||
void
|
||||
dvc_addLegalPhony( XW_DUtilCtxt* dutil, XWEnv xwe,
|
||||
const XP_UCHAR* isoCode,
|
||||
const XP_UCHAR* phony )
|
||||
{
|
||||
if ( ! dvc_isLegalPhony( dutil, xwe, isoCode, phony ) ) {
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
addPhony( dutil, dc, isoCode, phony );
|
||||
storePhoniesData( dutil, xwe, dc );
|
||||
}
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
dvc_haveLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
XP_Bool result = 0 < dll_length( &dc->pd->links );
|
||||
LOG_RETURNF( "%s", boolToStr(result) );
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
freeOnePhony( DLHead* elem, void* closure )
|
||||
{
|
||||
XW_DUtilCtxt* dutil = (XW_DUtilCtxt*)closure;
|
||||
const PhoniesDataStrs* pds = (PhoniesDataStrs*)elem;
|
||||
XP_FREE( dutil->mpool, pds->phony );
|
||||
XP_FREE( dutil->mpool, elem );
|
||||
}
|
||||
|
||||
static void
|
||||
freeOneCode( DLHead* elem, void* closure)
|
||||
{
|
||||
XW_DUtilCtxt* dutil = (XW_DUtilCtxt*)closure;
|
||||
const PhoniesDataCodes* pdc = (PhoniesDataCodes*)elem;
|
||||
|
||||
dll_removeAll( &pdc->head->links, freeOnePhony, dutil );
|
||||
|
||||
XP_FREE( dutil->mpool, pdc->isoCode );
|
||||
XP_FREE( dutil->mpool, elem );
|
||||
}
|
||||
|
||||
static DevCtxt*
|
||||
freePhonyState( XW_DUtilCtxt* dutil, XWEnv xwe )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
dll_removeAll( &dc->pd->links, freeOneCode, dutil );
|
||||
dc->pd = NULL;
|
||||
return dc;
|
||||
}
|
||||
|
||||
XP_U16
|
||||
dvc_clearLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
XP_U16 len = dll_length( &dc->pd->links );
|
||||
freePhonyState( dutil, xwe );
|
||||
storePhoniesData( dutil, xwe, dc );
|
||||
return len;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static ForEachAct
|
||||
listPhoniesProc( const DLHead* elem, void* closure )
|
||||
{
|
||||
const PhoniesDataStrs* pds = (PhoniesDataStrs*)elem;
|
||||
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
|
||||
|
||||
stream_catString( stream, "- " );
|
||||
stream_catString( stream, pds->phony );
|
||||
stream_catString( stream, "\n" );
|
||||
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
static ForEachAct
|
||||
listIsosProc( const DLHead* elem, void* closure )
|
||||
{
|
||||
const PhoniesDataCodes* pdc = (PhoniesDataCodes*)elem;
|
||||
XWStreamCtxt* stream = (XWStreamCtxt*)closure;
|
||||
|
||||
stream_catString( stream, pdc->isoCode );
|
||||
stream_catString( stream, ":\n" );
|
||||
|
||||
dll_map( &pdc->head->links, listPhoniesProc, NULL, stream );
|
||||
|
||||
return FEA_OK;
|
||||
}
|
||||
|
||||
void
|
||||
dvc_listLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe, XWStreamCtxt* stream )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
dll_map( &dc->pd->links, listIsosProc, NULL, stream );
|
||||
stream_putU8( stream, '\0' );
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct _FindPhonyData {
|
||||
XP_Bool found;
|
||||
const XP_UCHAR* isoCode;
|
||||
const XP_UCHAR* phony;
|
||||
} FindPhonyData;
|
||||
|
||||
static ForEachAct
|
||||
findPhonyProc2( const DLHead* elem, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
FindPhonyData* fpd = (FindPhonyData*)closure;
|
||||
const PhoniesDataStrs* pds = (PhoniesDataStrs*)elem;
|
||||
if ( 0 == XP_STRCMP( fpd->phony, pds->phony ) ) {
|
||||
fpd->found = XP_TRUE;
|
||||
result |= FEA_EXIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ForEachAct
|
||||
findPhonyProc1( const DLHead* elem, void* closure )
|
||||
{
|
||||
ForEachAct result = FEA_OK;
|
||||
FindPhonyData* fpd = (FindPhonyData*)closure;
|
||||
const PhoniesDataCodes* pdc = (PhoniesDataCodes*)elem;
|
||||
if ( 0 == XP_STRCMP( fpd->isoCode, pdc->isoCode ) ) {
|
||||
dll_map( &pdc->head->links, findPhonyProc2, NULL, closure );
|
||||
result |= FEA_EXIT;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
XP_Bool
|
||||
dvc_isLegalPhony( XW_DUtilCtxt* dutil, XWEnv xwe,
|
||||
const XP_UCHAR* isoCode, const XP_UCHAR* phony )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
|
||||
FindPhonyData fpd = {
|
||||
.isoCode = isoCode,
|
||||
.phony = phony,
|
||||
.found = XP_FALSE,
|
||||
};
|
||||
dll_map( &dc->pd->links, findPhonyProc1, NULL, &fpd );
|
||||
|
||||
return fpd.found;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
registerIf( XW_DUtilCtxt* dutil, XWEnv xwe )
|
||||
{
|
||||
|
@ -768,6 +1073,8 @@ dvc_init( XW_DUtilCtxt* dutil, XWEnv xwe )
|
|||
dc->mWebSendKey = 0;
|
||||
pthread_mutex_init( &dc->webSendMutex, NULL );
|
||||
|
||||
loadPhoniesData( dutil, xwe, dc );
|
||||
|
||||
#ifdef DEBUG
|
||||
dutil->magic = MAGIC_INITED;
|
||||
#endif
|
||||
|
@ -777,7 +1084,10 @@ dvc_init( XW_DUtilCtxt* dutil, XWEnv xwe )
|
|||
void
|
||||
dvc_cleanup( XW_DUtilCtxt* dutil, XWEnv xwe )
|
||||
{
|
||||
DevCtxt* dc = load( dutil, xwe );
|
||||
LOG_FUNC();
|
||||
DevCtxt* dc = freePhonyState( dutil, xwe );
|
||||
|
||||
pthread_mutex_destroy( &dc->webSendMutex );
|
||||
|
||||
XP_FREEP( dutil->mpool, &dc );
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#define _DEVICE_H_
|
||||
|
||||
#include "dutil.h"
|
||||
#include "dictmgr.h"
|
||||
|
||||
// void device_load( XW_DUtilCtxt dctxt );
|
||||
# ifdef XWFEATURE_DEVICE
|
||||
|
@ -63,6 +64,16 @@ void dvc_parseMQTTPacket( XW_DUtilCtxt* dutil, XWEnv xwe, const XP_UCHAR* topic,
|
|||
void dvc_onWebSendResult( XW_DUtilCtxt* dutil, XWEnv xwe, XP_U32 resultKey,
|
||||
XP_Bool succeeded, const XP_UCHAR* result );
|
||||
|
||||
void dvc_addLegalPhony( XW_DUtilCtxt* dutil, XWEnv xwe,
|
||||
const XP_UCHAR* isoCode, const XP_UCHAR* phony );
|
||||
XP_Bool dvc_isLegalPhony( XW_DUtilCtxt* dutil, XWEnv xwe,
|
||||
const XP_UCHAR* isoCode, const XP_UCHAR* phony );
|
||||
XP_Bool dvc_haveLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe );
|
||||
XP_U16 dvc_clearLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe );
|
||||
#ifdef DEBUG
|
||||
void dvc_listLegalPhonies( XW_DUtilCtxt* dutil, XWEnv xwe, XWStreamCtxt* stream );
|
||||
#endif
|
||||
|
||||
/* All platforms need to call this shortly after setting up their XW_DUtilCtxt */
|
||||
void dvc_init( XW_DUtilCtxt* dutil, XWEnv xwe );
|
||||
void dvc_cleanup( XW_DUtilCtxt* dutil, XWEnv xwe );
|
||||
|
|
|
@ -126,6 +126,18 @@ dll_map( DLHead* list, DLMapProc mapProc, DLDisposeProc dispProc,
|
|||
return newHead;
|
||||
}
|
||||
|
||||
static ForEachAct
|
||||
removeAllProc( const DLHead* XP_UNUSED(elem), void* XP_UNUSED(closure) )
|
||||
{
|
||||
return FEA_OK | FEA_REMOVE;
|
||||
}
|
||||
|
||||
void
|
||||
dll_removeAll( DLHead* list, DLDisposeProc dispProc, void* closure )
|
||||
{
|
||||
dll_map( list, removeAllProc, dispProc, closure );
|
||||
}
|
||||
|
||||
DLHead*
|
||||
dll_sort( DLHead* list, DLCompProc proc )
|
||||
{
|
||||
|
|
|
@ -44,6 +44,7 @@ typedef ForEachAct (*DLMapProc)(const DLHead* elem, void* closure);
|
|||
typedef void (*DLDisposeProc)(DLHead* elem, void* closure);
|
||||
DLHead* dll_map( DLHead* list, DLMapProc mapProc, DLDisposeProc dispProc,
|
||||
void* closure );
|
||||
void dll_removeAll( DLHead* list, DLDisposeProc dispProc, void* closure );
|
||||
|
||||
#ifdef CPLUS
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "modelp.h"
|
||||
#include "util.h"
|
||||
#include "device.h"
|
||||
#include "engine.h"
|
||||
#include "game.h"
|
||||
#include "strutils.h"
|
||||
|
@ -42,7 +43,7 @@ static XP_U16 find_start( const ModelCtxt* model, XP_U16 col, XP_U16 row,
|
|||
static XP_S16 checkScoreMove( ModelCtxt* model, XWEnv xwe, XP_S16 turn,
|
||||
EngineCtxt* engine, XWStreamCtxt* stream,
|
||||
XP_Bool silent, WordNotifierInfo* notifyInfo );
|
||||
static XP_U16 scoreWord( const ModelCtxt* model, XP_U16 turn,
|
||||
static XP_U16 scoreWord( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
|
||||
const MoveInfo* movei, EngineCtxt* engine,
|
||||
XWStreamCtxt* stream, WordNotifierInfo* notifyInfo );
|
||||
|
||||
|
@ -571,7 +572,7 @@ figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
|
|||
word_multiplier( model, col, row );
|
||||
}
|
||||
|
||||
oneScore = scoreWord( model, turn, moveInfo, (EngineCtxt*)NULL,
|
||||
oneScore = scoreWord( model, xwe, turn, moveInfo, (EngineCtxt*)NULL,
|
||||
stream, notifyInfo );
|
||||
if ( !!stream ) {
|
||||
formatWordScore( stream, oneScore, moveMultiplier );
|
||||
|
@ -589,7 +590,7 @@ figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
|
|||
tmpMI.commonCoord = tiles->varCoord;
|
||||
tmpMI.tiles[0].tile = tiles->tile;
|
||||
|
||||
oneScore = scoreWord( model, turn, &tmpMI, engine, stream, notifyInfo );
|
||||
oneScore = scoreWord( model, xwe, turn, &tmpMI, engine, stream, notifyInfo );
|
||||
if ( !!stream ) {
|
||||
formatWordScore( stream, oneScore, multipliers[ii] );
|
||||
}
|
||||
|
@ -657,7 +658,7 @@ tile_multiplier( const ModelCtxt* model, XP_U16 col, XP_U16 row )
|
|||
} /* tile_multiplier */
|
||||
|
||||
static XP_U16
|
||||
scoreWord( const ModelCtxt* model, XP_U16 turn,
|
||||
scoreWord( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
|
||||
const MoveInfo* movei, /* new tiles */
|
||||
EngineCtxt* engine,/* for crosswise caching */
|
||||
XWStreamCtxt* stream,
|
||||
|
@ -781,7 +782,15 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
|
|||
dict_tilesToString( dict, checkWordBuf, len, buf,
|
||||
sizeof(buf), NULL );
|
||||
|
||||
WNParams wnp = { .word = buf, .isLegal = legal, .dict = dict,
|
||||
if ( !legal && PHONIES_WARN == model->vol.gi->phoniesAction ) {
|
||||
legal = dvc_isLegalPhony( model->vol.dutil, xwe,
|
||||
dict_getISOCode(dict), buf );
|
||||
}
|
||||
|
||||
WNParams wnp = {
|
||||
.word = buf,
|
||||
.isLegal = legal,
|
||||
.dict = dict,
|
||||
#ifdef XWFEATURE_BOARDWORDS
|
||||
.movei = movei, .start = start, .end = end,
|
||||
#endif
|
||||
|
|
|
@ -164,13 +164,19 @@ typedef struct _ServerNonvolatiles {
|
|||
|
||||
} ServerNonvolatiles;
|
||||
|
||||
typedef struct _BadWordsState {
|
||||
BadWordInfo bwi;
|
||||
XP_UCHAR* dictName;
|
||||
} BadWordsState;
|
||||
|
||||
struct ServerCtxt {
|
||||
ServerVolatiles vol;
|
||||
ServerNonvolatiles nv;
|
||||
|
||||
PoolContext* pool;
|
||||
|
||||
BadWordInfo illegalWordInfo;
|
||||
BadWordsState bws;
|
||||
|
||||
XP_U16 lastMoveSource;
|
||||
|
||||
ServerPlayer srvPlyrs[MAX_NUM_PLAYERS];
|
||||
|
@ -237,7 +243,7 @@ static void doEndGame( ServerCtxt* server, XWEnv xwe, XP_S16 quitter );
|
|||
static void endGameInternal( ServerCtxt* server, XWEnv xwe,
|
||||
GameEndReason why, XP_S16 quitter );
|
||||
static void badWordMoveUndoAndTellUser( ServerCtxt* server, XWEnv xwe,
|
||||
BadWordInfo* bwi );
|
||||
const BadWordsState* bws );
|
||||
static XP_Bool tileCountsOk( const ServerCtxt* server );
|
||||
static void setTurn( ServerCtxt* server, XWEnv xwe, XP_S16 turn );
|
||||
static XWStreamCtxt* mkServerStream( const ServerCtxt* server, XP_U8 version );
|
||||
|
@ -1997,7 +2003,7 @@ server_do( ServerCtxt* server, XWEnv xwe )
|
|||
|
||||
case XWSTATE_NEEDSEND_BADWORD_INFO:
|
||||
XP_ASSERT( server->vol.gi->serverRole == SERVER_ISHOST );
|
||||
badWordMoveUndoAndTellUser( server, xwe, &server->illegalWordInfo );
|
||||
badWordMoveUndoAndTellUser( server, xwe, &server->bws );
|
||||
sendBadWordMsgs( server, xwe );
|
||||
nextTurn( server, xwe, PICK_NEXT );
|
||||
//moreToDo = XP_TRUE; /* why? */
|
||||
|
@ -2449,11 +2455,12 @@ sendInitialMessage( ServerCtxt* server, XWEnv xwe )
|
|||
} /* sendInitialMessage */
|
||||
|
||||
static void
|
||||
freeBWI( MPFORMAL BadWordInfo* bwi )
|
||||
freeBWS( MPFORMAL BadWordsState* bws )
|
||||
{
|
||||
BadWordInfo* bwi = &bws->bwi;
|
||||
XP_U16 nWords = bwi->nWords;
|
||||
|
||||
XP_FREEP( mpool, &bwi->dictName );
|
||||
XP_FREEP( mpool, &bws->dictName );
|
||||
while ( nWords-- ) {
|
||||
XP_FREEP( mpool, &bwi->words[nWords] );
|
||||
}
|
||||
|
@ -2462,35 +2469,35 @@ freeBWI( MPFORMAL BadWordInfo* bwi )
|
|||
} /* freeBWI */
|
||||
|
||||
static void
|
||||
bwiToStream( XWStreamCtxt* stream, BadWordInfo* bwi )
|
||||
bwsToStream( XWStreamCtxt* stream, const BadWordsState* bws )
|
||||
{
|
||||
XP_U16 nWords = bwi->nWords;
|
||||
const XP_UCHAR** sp;
|
||||
const XP_U16 nWords = bws->bwi.nWords;
|
||||
|
||||
stream_putBits( stream, 4, nWords );
|
||||
if ( STREAM_VERS_DICTNAME <= stream_getVersion( stream ) ) {
|
||||
stringToStream( stream, bwi->dictName );
|
||||
stringToStream( stream, bws->dictName );
|
||||
}
|
||||
for ( sp = bwi->words; nWords > 0; --nWords, ++sp ) {
|
||||
stringToStream( stream, *sp );
|
||||
for ( int ii = 0; ii < nWords; ++ii ) {
|
||||
stringToStream( stream, bws->bwi.words[ii] );
|
||||
}
|
||||
|
||||
} /* bwiToStream */
|
||||
} /* bwsToStream */
|
||||
|
||||
static void
|
||||
bwiFromStream( MPFORMAL XWStreamCtxt* stream, BadWordInfo* bwi )
|
||||
bwsFromStream( MPFORMAL XWStreamCtxt* stream, BadWordsState* bws )
|
||||
{
|
||||
XP_U16 nWords = stream_getBits( stream, 4 );
|
||||
XP_ASSERT( nWords < VSIZE(bwi->words) - 1 );
|
||||
XP_ASSERT( nWords < VSIZE(bws->bwi.words) - 1 );
|
||||
|
||||
bwi->nWords = nWords;
|
||||
bwi->dictName = ( STREAM_VERS_DICTNAME <= stream_getVersion( stream ) )
|
||||
? stringFromStream( mpool, stream ) : NULL;
|
||||
for ( int ii = 0; ii < nWords; ++ii ) {
|
||||
bwi->words[ii] = (const XP_UCHAR*)stringFromStream( mpool, stream );
|
||||
bws->bwi.nWords = nWords;
|
||||
if ( STREAM_VERS_DICTNAME <= stream_getVersion( stream ) ) {
|
||||
bws->dictName = stringFromStream( mpool, stream );
|
||||
}
|
||||
bwi->words[nWords] = NULL;
|
||||
} /* bwiFromStream */
|
||||
for ( int ii = 0; ii < nWords; ++ii ) {
|
||||
bws->bwi.words[ii] = (const XP_UCHAR*)stringFromStream( mpool, stream );
|
||||
}
|
||||
bws->bwi.words[nWords] = NULL;
|
||||
} /* bwsFromStream */
|
||||
|
||||
#ifdef DEBUG
|
||||
#define caseStr(s) case s: str = #s; break;
|
||||
|
@ -2561,15 +2568,15 @@ messageStreamWithHeader( ServerCtxt* server, XWEnv xwe, XP_U16 devIndex, XW_Prot
|
|||
static void
|
||||
sendBadWordMsgs( ServerCtxt* server, XWEnv xwe )
|
||||
{
|
||||
XP_ASSERT( server->illegalWordInfo.nWords > 0 );
|
||||
XP_ASSERT( server->bws.bwi.nWords > 0 );
|
||||
|
||||
if ( server->illegalWordInfo.nWords > 0 ) { /* fail gracefully */
|
||||
if ( server->bws.bwi.nWords > 0 ) { /* fail gracefully */
|
||||
XWStreamCtxt* stream =
|
||||
messageStreamWithHeader( server, xwe, server->lastMoveSource,
|
||||
XWPROTO_BADWORD_INFO );
|
||||
stream_putBits( stream, PLAYERNUM_NBITS, server->nv.currentTurn );
|
||||
|
||||
bwiToStream( stream, &server->illegalWordInfo );
|
||||
bwsToStream( stream, &server->bws );
|
||||
|
||||
/* XP_U32 hash = model_getHash( server->vol.model ); */
|
||||
/* stream_putU32( stream, hash ); */
|
||||
|
@ -2577,13 +2584,14 @@ sendBadWordMsgs( ServerCtxt* server, XWEnv xwe )
|
|||
|
||||
stream_destroy( stream );
|
||||
|
||||
freeBWI( MPPARM(server->mpool) &server->illegalWordInfo );
|
||||
freeBWS( MPPARM(server->mpool) &server->bws );
|
||||
}
|
||||
SETSTATE( server, XWSTATE_INTURN );
|
||||
} /* sendBadWordMsgs */
|
||||
|
||||
static void
|
||||
badWordMoveUndoAndTellUser( ServerCtxt* server, XWEnv xwe, BadWordInfo* bwi )
|
||||
badWordMoveUndoAndTellUser( ServerCtxt* server, XWEnv xwe,
|
||||
const BadWordsState* bws )
|
||||
{
|
||||
XP_U16 turn;
|
||||
ModelCtxt* model = server->vol.model;
|
||||
|
@ -2593,7 +2601,8 @@ badWordMoveUndoAndTellUser( ServerCtxt* server, XWEnv xwe, BadWordInfo* bwi )
|
|||
|
||||
model_rejectPreviousMove( model, xwe, server->pool, &turn );
|
||||
|
||||
util_notifyIllegalWords( server->vol.util, xwe, bwi, turn, XP_TRUE );
|
||||
util_notifyIllegalWords( server->vol.util, xwe, &bws->bwi,
|
||||
bws->dictName, turn, XP_TRUE, 0 );
|
||||
} /* badWordMoveUndoAndTellUser */
|
||||
|
||||
EngineCtxt*
|
||||
|
@ -2978,14 +2987,15 @@ storeBadWords( const WNParams* wnp, void* closure )
|
|||
{
|
||||
if ( !wnp->isLegal ) {
|
||||
ServerCtxt* server = (ServerCtxt*)closure;
|
||||
const XP_UCHAR* name = dict_getShortName( wnp->dict );
|
||||
const XP_UCHAR* dictName = dict_getShortName( wnp->dict );
|
||||
|
||||
XP_LOGFF( "storeBadWords called with \"%s\" (name=%s)", wnp->word, name );
|
||||
if ( NULL == server->illegalWordInfo.dictName ) {
|
||||
server->illegalWordInfo.dictName = copyString( server->mpool, name );
|
||||
XP_LOGFF( "storeBadWords called with \"%s\" (name=%s)", wnp->word,
|
||||
dictName );
|
||||
if ( NULL == server->bws.dictName ) {
|
||||
server->bws.dictName = copyString( server->mpool, dictName );
|
||||
}
|
||||
server->illegalWordInfo.words[server->illegalWordInfo.nWords++]
|
||||
= copyString( server->mpool, wnp->word );
|
||||
BadWordInfo* bwi = &server->bws.bwi;
|
||||
bwi->words[bwi->nWords++] = copyString( server->mpool, wnp->word );
|
||||
}
|
||||
} /* storeBadWords */
|
||||
|
||||
|
@ -2993,7 +3003,7 @@ static XP_Bool
|
|||
checkMoveAllowed( ServerCtxt* server, XWEnv xwe, XP_U16 playerNum )
|
||||
{
|
||||
CurGameInfo* gi = server->vol.gi;
|
||||
XP_ASSERT( server->illegalWordInfo.nWords == 0 );
|
||||
XP_ASSERT( server->bws.bwi.nWords == 0 );
|
||||
|
||||
if ( gi->phoniesAction == PHONIES_DISALLOW ) {
|
||||
WordNotifierInfo info;
|
||||
|
@ -3003,7 +3013,7 @@ checkMoveAllowed( ServerCtxt* server, XWEnv xwe, XP_U16 playerNum )
|
|||
(XWStreamCtxt*)NULL, &info );
|
||||
}
|
||||
|
||||
return server->illegalWordInfo.nWords == 0;
|
||||
return server->bws.bwi.nWords == 0;
|
||||
} /* checkMoveAllowed */
|
||||
|
||||
static void
|
||||
|
@ -3052,9 +3062,9 @@ sendMoveTo( ServerCtxt* server, XWEnv xwe, XP_U16 devIndex, XP_U16 turn,
|
|||
}
|
||||
|
||||
if ( !legal ) {
|
||||
XP_ASSERT( server->illegalWordInfo.nWords > 0 );
|
||||
XP_ASSERT( server->bws.bwi.nWords > 0 );
|
||||
stream_putBits( stream, PLAYERNUM_NBITS, turn );
|
||||
bwiToStream( stream, &server->illegalWordInfo );
|
||||
bwsToStream( stream, &server->bws );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3935,11 +3945,11 @@ finishMove( ServerCtxt* server, XWEnv xwe, TrayTileSet* newTiles, XP_U16 turn )
|
|||
sortTilesIf( server, turn );
|
||||
|
||||
if ( !isLegalMove && !isClient ) {
|
||||
badWordMoveUndoAndTellUser( server, xwe, &server->illegalWordInfo );
|
||||
badWordMoveUndoAndTellUser( server, xwe, &server->bws );
|
||||
/* It's ok to free these guys. I'm the server, and the move was made
|
||||
here, so I've notified all clients already by setting the flag (and
|
||||
passing the word) in sendMoveToClientsExcept. */
|
||||
freeBWI( MPPARM(server->mpool) &server->illegalWordInfo );
|
||||
freeBWS( MPPARM(server->mpool) &server->bws );
|
||||
}
|
||||
|
||||
if (isClient && (gi->phoniesAction == PHONIES_DISALLOW)
|
||||
|
@ -4739,14 +4749,14 @@ tellMoveWasLegal( ServerCtxt* server, XWEnv xwe )
|
|||
static XP_Bool
|
||||
handleIllegalWord( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* incoming )
|
||||
{
|
||||
BadWordInfo bwi;
|
||||
BadWordsState bws = {{0}};
|
||||
|
||||
(void)stream_getBits( incoming, PLAYERNUM_NBITS );
|
||||
bwiFromStream( MPPARM(server->mpool) incoming, &bwi );
|
||||
bwsFromStream( MPPARM(server->mpool) incoming, &bws );
|
||||
|
||||
badWordMoveUndoAndTellUser( server, xwe, &bwi );
|
||||
badWordMoveUndoAndTellUser( server, xwe, &bws );
|
||||
|
||||
freeBWI( MPPARM(server->mpool) &bwi );
|
||||
freeBWS( MPPARM(server->mpool) &bws );
|
||||
|
||||
SETSTATE( server, XWSTATE_INTURN );
|
||||
return XP_TRUE;
|
||||
|
|
|
@ -458,7 +458,7 @@ handleActionInTray( BoardCtxt* board, XWEnv xwe, XP_S16 index, XP_Bool onDivider
|
|||
}
|
||||
#endif
|
||||
} else if ( index == -(board->gi->traySize) ) { /* pending score tile */
|
||||
result = board_commitTurn( board, xwe, XP_FALSE, XP_FALSE, NULL );
|
||||
result = board_commitTurn( board, xwe, NULL, XP_FALSE, NULL );
|
||||
#if defined XWFEATURE_TRAYUNDO_ALL
|
||||
} else if ( index < 0 ) { /* other empty area */
|
||||
/* it better be true */
|
||||
|
|
|
@ -73,7 +73,6 @@ typedef struct PickInfo {
|
|||
|
||||
typedef struct _BadWordInfo {
|
||||
XP_U16 nWords;
|
||||
const XP_UCHAR* dictName;
|
||||
/* Null-terminated array of ptrs */
|
||||
const XP_UCHAR* words[MAX_TRAY_TILES+2]; /* can form in both directions */
|
||||
} BadWordInfo;
|
||||
|
@ -139,8 +138,11 @@ typedef struct UtilVtable {
|
|||
XP_Bool (*m_util_altKeyDown)( XW_UtilCtxt* uc, XWEnv xwe );
|
||||
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc, XWEnv xwe );
|
||||
|
||||
void (*m_util_notifyIllegalWords)( XW_UtilCtxt* uc, XWEnv xwe, BadWordInfo* bwi,
|
||||
XP_U16 turn, XP_Bool turnLost );
|
||||
void (*m_util_notifyIllegalWords)( XW_UtilCtxt* uc, XWEnv xwe,
|
||||
const BadWordInfo* bwi,
|
||||
const XP_UCHAR* dictName,
|
||||
XP_U16 turn, XP_Bool turnLost,
|
||||
XP_U32 badWordsKey );
|
||||
|
||||
void (*m_util_remSelected)(XW_UtilCtxt* uc, XWEnv xwe);
|
||||
|
||||
|
@ -279,8 +281,8 @@ struct XW_UtilCtxt {
|
|||
#define util_makeEmptyDict( uc, e ) \
|
||||
(uc)->vtable->m_util_makeEmptyDict((uc), (e))
|
||||
|
||||
#define util_notifyIllegalWords( uc,e, w, p, b ) \
|
||||
(uc)->vtable->m_util_notifyIllegalWords((uc), (e),(w),(p),(b))
|
||||
#define util_notifyIllegalWords( uc,e, w, d, p, b, k ) \
|
||||
(uc)->vtable->m_util_notifyIllegalWords((uc), (e), (w), (d), (p), (b), (k))
|
||||
|
||||
#define util_remSelected( uc,e ) \
|
||||
(uc)->vtable->m_util_remSelected((uc), (e))
|
||||
|
|
|
@ -211,6 +211,7 @@ GTK_OBJS = \
|
|||
$(BUILD_PLAT_DIR)/gtkutils.o \
|
||||
$(BUILD_PLAT_DIR)/gtkntilesask.o \
|
||||
$(BUILD_PLAT_DIR)/gtkaskdict.o \
|
||||
$(BUILD_PLAT_DIR)/gtkaskbad.o \
|
||||
$(BUILD_PLAT_DIR)/gtkchat.o \
|
||||
$(BUILD_PLAT_DIR)/gtkkpdlg.o \
|
||||
$(BUILD_PLAT_DIR)/gtkrmtch.o \
|
||||
|
|
|
@ -825,10 +825,11 @@ ask_move( gpointer data )
|
|||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
const char* answers[] = {"Ok", "Cancel", NULL};
|
||||
|
||||
if (0 == cursesask(bGlobals->boardWin, cGlobals->question,
|
||||
VSIZE(answers)-1, answers) ) {
|
||||
if ( 0 == cursesask( bGlobals->boardWin, cGlobals->question,
|
||||
VSIZE(answers)-1, answers ) ) {
|
||||
BoardCtxt* board = cGlobals->game.board;
|
||||
if ( board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE, NULL ) ) {
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
if ( board_commitTurn( board, NULL_XWE, &pc, XP_TRUE, NULL ) ) {
|
||||
board_draw( board, NULL_XWE );
|
||||
linuxSaveGame( &bGlobals->cGlobals );
|
||||
}
|
||||
|
@ -873,12 +874,16 @@ curses_util_turnChanged( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
|
|||
#endif
|
||||
|
||||
static void
|
||||
curses_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe), BadWordInfo* bwi,
|
||||
XP_U16 player, XP_Bool turnLost )
|
||||
curses_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
|
||||
const BadWordInfo* bwi,
|
||||
const XP_UCHAR* XP_UNUSED(dictName),
|
||||
XP_U16 player, XP_Bool turnLost,
|
||||
XP_U32 bwKey )
|
||||
{
|
||||
gchar* strs = g_strjoinv( "\", \"", (gchar**)bwi->words );
|
||||
gchar* msg = g_strdup_printf( "Player %d played bad word[s]: \"%s\". "
|
||||
"Turn lost: %s", player, strs, boolToStr(turnLost) );
|
||||
"Turn lost: %s; key=%d", player, strs,
|
||||
boolToStr(turnLost), bwKey );
|
||||
|
||||
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)uc->closure;
|
||||
if ( !!bGlobals->boardWin ) {
|
||||
|
@ -913,7 +918,8 @@ ask_trade( gpointer data )
|
|||
if (0 == cursesask( bGlobals->boardWin, cGlobals->question,
|
||||
VSIZE(buttons), buttons ) ) {
|
||||
BoardCtxt* board = cGlobals->game.board;
|
||||
if ( board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE, NULL ) ) {
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
if ( board_commitTurn( board, NULL_XWE, &pc, XP_TRUE, NULL ) ) {
|
||||
board_draw( board, NULL_XWE );
|
||||
linuxSaveGame( cGlobals );
|
||||
}
|
||||
|
@ -1451,7 +1457,7 @@ handleCommit( void* closure, int XP_UNUSED(key) )
|
|||
{
|
||||
CursesBoardGlobals* bGlobals = (CursesBoardGlobals*)closure;
|
||||
CommonGlobals* cGlobals = &bGlobals->cGlobals;
|
||||
if ( board_commitTurn( cGlobals->game.board, NULL_XWE, XP_FALSE,
|
||||
if ( board_commitTurn( cGlobals->game.board, NULL_XWE, NULL,
|
||||
XP_FALSE, NULL ) ) {
|
||||
board_draw( cGlobals->game.board, NULL_XWE );
|
||||
}
|
||||
|
|
121
xwords4/linux/gtkaskbad.c
Normal file
121
xwords4/linux/gtkaskbad.c
Normal file
|
@ -0,0 +1,121 @@
|
|||
/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
|
||||
/*
|
||||
* Copyright 2001-2024 by Eric House (xwords@eehouse.org). All rights
|
||||
* reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifdef PLATFORM_GTK
|
||||
|
||||
#include "gtkaskbad.h"
|
||||
#include "gtkutils.h"
|
||||
#include "dbgutil.h"
|
||||
|
||||
typedef struct _AskBadState {
|
||||
GtkWidget* check;
|
||||
GStrv words;
|
||||
bool skipNext;
|
||||
const char* dictName;
|
||||
} AskBadState;
|
||||
|
||||
/* static void */
|
||||
/* handle_response( GtkWidget* item, AskBadState* state ) */
|
||||
/* { */
|
||||
/* LOG_FUNC(); */
|
||||
/* } */
|
||||
|
||||
static void
|
||||
handle_check_toggled( GtkWidget* item, AskBadState* state )
|
||||
{
|
||||
XP_ASSERT( item == state->check );
|
||||
state->skipNext = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(item));
|
||||
XP_LOGFF( "checked: %s", boolToStr(state->skipNext) );
|
||||
}
|
||||
|
||||
/* static void */
|
||||
/* handle_ok( GtkWidget* XP_UNUSED(item), AskBadState* state ) */
|
||||
/* { */
|
||||
/* state->confirmed = true; */
|
||||
/* gtk_main_quit(); */
|
||||
/* } */
|
||||
|
||||
/* static void */
|
||||
/* handle_cancel( GtkWidget* XP_UNUSED(item), AskBadState* state ) */
|
||||
/* { */
|
||||
/* state->confirmed = false; */
|
||||
/* gtk_main_quit(); */
|
||||
/* } */
|
||||
|
||||
static GtkWidget*
|
||||
buildDialog( AskBadState* state )
|
||||
{
|
||||
GtkWidget* dialog = gtk_dialog_new_with_buttons( NULL, NULL, //GtkWindow *parent,
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
"Ok", GTK_RESPONSE_ACCEPT,
|
||||
"Cancel", GTK_RESPONSE_REJECT,
|
||||
NULL );
|
||||
|
||||
/* GtkWidget* bc = gtk_dialog_get_action_area( GTK_DIALOG(dialog) ); */
|
||||
/* g_object_set_property( G_OBJECT(bc), "halign", GTK_ALIGN_CENTER ); */
|
||||
|
||||
GtkWidget* vbox = gtk_dialog_get_content_area( GTK_DIALOG(dialog) );
|
||||
|
||||
gchar* words = g_strjoinv( "\n", state->words );
|
||||
|
||||
gchar* msg = g_strdup_printf("The word (or words) below are not in the wordlist %s. "
|
||||
"\n\n%s\n\n"
|
||||
"Would you like to accept them anyway?\n",
|
||||
state->dictName, words );
|
||||
|
||||
GtkWidget* label = gtk_label_new ( msg );
|
||||
g_free( words );
|
||||
g_free( msg );
|
||||
gtk_widget_show( label );
|
||||
gtk_box_pack_start( GTK_BOX(vbox), label, FALSE, TRUE, 0 );
|
||||
|
||||
state->check = gtk_check_button_new_with_label( "Always accept" );
|
||||
g_signal_connect( state->check, "toggled",
|
||||
(GCallback)handle_check_toggled, state );
|
||||
gtk_widget_show( state->check );
|
||||
gtk_box_pack_start( GTK_BOX(vbox), state->check, FALSE, TRUE, 0 );
|
||||
|
||||
gtk_widget_show( vbox );
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/* return true if not cancelled */
|
||||
bool
|
||||
gtkAskBad( GtkGameGlobals* globals, GStrv words, const char* dictName,
|
||||
bool* skipNext )
|
||||
{
|
||||
XP_USE( globals );
|
||||
|
||||
AskBadState state = {
|
||||
.words = words,
|
||||
.dictName = dictName,
|
||||
};
|
||||
GtkWidget* dialog = buildDialog( &state );
|
||||
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
|
||||
|
||||
gtk_widget_show_all( dialog );
|
||||
gint response = gtk_dialog_run( GTK_DIALOG(dialog) );
|
||||
gtk_widget_destroy( dialog );
|
||||
|
||||
*skipNext = state.skipNext;
|
||||
return GTK_RESPONSE_ACCEPT == response;
|
||||
}
|
||||
|
||||
#endif
|
32
xwords4/linux/gtkaskbad.h
Normal file
32
xwords4/linux/gtkaskbad.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* -*- compile-command: "make MEMDEBUG=TRUE -j5"; -*- */
|
||||
/*
|
||||
* Copyright 2001-2024 by Eric House (xwords@eehouse.org). All rights
|
||||
* reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifdef PLATFORM_GTK
|
||||
#ifndef _GTKASKBAD_H_
|
||||
#define _GTKASKBAD_H_
|
||||
|
||||
#include "xptypes.h"
|
||||
#include "gtkboard.h"
|
||||
|
||||
/* return true if not cancelled */
|
||||
bool gtkAskBad( GtkGameGlobals* globals, GStrv words, const char* dictName,
|
||||
bool* skipNext );
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -56,6 +56,7 @@
|
|||
#include "movestak.h"
|
||||
#include "strutils.h"
|
||||
#include "dbgutil.h"
|
||||
#include "device.h"
|
||||
#include "gtkask.h"
|
||||
#include "gtkinvit.h"
|
||||
#include "gtkaskm.h"
|
||||
|
@ -65,6 +66,7 @@
|
|||
#include "gtkpasswdask.h"
|
||||
#include "gtkntilesask.h"
|
||||
#include "gtkaskdict.h"
|
||||
#include "gtkaskbad.h"
|
||||
#include "linuxdict.h"
|
||||
/* #include "undo.h" */
|
||||
#include "gtkdraw.h"
|
||||
|
@ -1240,8 +1242,8 @@ handle_trade_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
|||
static void
|
||||
handle_done_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
||||
{
|
||||
if ( board_commitTurn( globals->cGlobals.game.board, NULL_XWE, XP_FALSE,
|
||||
XP_FALSE, NULL ) ) {
|
||||
if ( board_commitTurn( globals->cGlobals.game.board, NULL_XWE,
|
||||
NULL, XP_FALSE, NULL ) ) {
|
||||
board_draw( globals->cGlobals.game.board, NULL_XWE );
|
||||
disenable_buttons( globals );
|
||||
}
|
||||
|
@ -1354,7 +1356,7 @@ static void
|
|||
handle_commit_button( GtkWidget* XP_UNUSED(widget), GtkGameGlobals* globals )
|
||||
{
|
||||
if ( board_commitTurn( globals->cGlobals.game.board, NULL_XWE,
|
||||
XP_FALSE, XP_FALSE, NULL ) ) {
|
||||
NULL, XP_FALSE, NULL ) ) {
|
||||
board_draw( globals->cGlobals.game.board, NULL_XWE );
|
||||
}
|
||||
} /* handle_commit_button */
|
||||
|
@ -1572,8 +1574,9 @@ ask_tiles( gpointer data )
|
|||
server_tilesPicked( cGlobals->game.server, NULL_XWE,
|
||||
cGlobals->selPlayer, &newTiles );
|
||||
} else {
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
draw = board_commitTurn( cGlobals->game.board, NULL_XWE,
|
||||
XP_TRUE, XP_TRUE, &newTiles );
|
||||
&pc, XP_TRUE, &newTiles );
|
||||
}
|
||||
|
||||
if ( draw ) {
|
||||
|
@ -1856,30 +1859,50 @@ gtk_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc), XWEnv XP_UNUSED(xwe
|
|||
#endif
|
||||
} /* gtk_util_engineProgressCallback */
|
||||
|
||||
typedef struct _BadWordsData {
|
||||
GtkGameGlobals* globals;
|
||||
XP_U32 bwKey;
|
||||
GStrv words;
|
||||
gchar* dictName;
|
||||
} BadWordsData;
|
||||
|
||||
static gint
|
||||
ask_bad_words( gpointer data )
|
||||
{
|
||||
GtkGameGlobals* globals = (GtkGameGlobals*)data;
|
||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||
BadWordsData* bwd = (BadWordsData*)data;
|
||||
CommonGlobals* cGlobals = &bwd->globals->cGlobals;
|
||||
|
||||
if ( GTK_RESPONSE_YES == gtkask( globals->window, cGlobals->question,
|
||||
GTK_BUTTONS_YES_NO, NULL ) ) {
|
||||
board_commitTurn( cGlobals->game.board, NULL_XWE, XP_TRUE, XP_FALSE, NULL );
|
||||
bool skipNext = false;
|
||||
if ( gtkAskBad( bwd->globals, bwd->words, bwd->dictName, &skipNext ) ) {
|
||||
PhoniesConf pc = {
|
||||
.confirmed = XP_TRUE,
|
||||
.key = skipNext ? bwd->bwKey : 0,
|
||||
};
|
||||
board_commitTurn( cGlobals->game.board, NULL_XWE,
|
||||
&pc, XP_FALSE, NULL );
|
||||
}
|
||||
|
||||
g_free( bwd->dictName );
|
||||
g_strfreev( bwd->words );
|
||||
XP_FREE( cGlobals->util->mpool, bwd );
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
|
||||
BadWordInfo* bwi, XP_U16 player,
|
||||
XP_Bool turnLost )
|
||||
const BadWordInfo* bwi,
|
||||
const XP_UCHAR* dictName,
|
||||
XP_U16 player, XP_Bool turnLost,
|
||||
XP_U32 bwKey )
|
||||
{
|
||||
GtkGameGlobals* globals = (GtkGameGlobals*)uc->closure;
|
||||
CommonGlobals* cGlobals = &globals->cGlobals;
|
||||
char buf[300];
|
||||
gchar* strs = g_strjoinv( "\", \"", (gchar**)bwi->words );
|
||||
|
||||
if ( turnLost ) {
|
||||
XP_ASSERT( 0 == bwKey );
|
||||
char buf[300];
|
||||
gchar* strs = g_strjoinv( "\", \"", (gchar**)bwi->words );
|
||||
|
||||
XP_UCHAR* name = cGlobals->gi->players[player].name;
|
||||
XP_ASSERT( !!name );
|
||||
|
||||
|
@ -1891,13 +1914,22 @@ gtk_util_notifyIllegalWords( XW_UtilCtxt* uc, XWEnv XP_UNUSED(xwe),
|
|||
} else {
|
||||
gtkUserError( globals, buf );
|
||||
}
|
||||
g_free( strs );
|
||||
} else {
|
||||
sprintf( cGlobals->question, "Word[s] \"%s\" not in the current dictionary (%s). "
|
||||
"Use anyway?", strs, bwi->dictName );
|
||||
BadWordsData* bwd = XP_MALLOC( cGlobals->util->mpool, sizeof(*bwd) );
|
||||
bwd->globals = globals;
|
||||
bwd->dictName = g_strdup( dictName );
|
||||
bwd->bwKey = bwKey;
|
||||
|
||||
(void)g_idle_add( ask_bad_words, globals );
|
||||
GStrvBuilder* builder = g_strv_builder_new();
|
||||
for ( const char* const* word = bwi->words; !!*word; ++word ) {
|
||||
g_strv_builder_add( builder, *word );
|
||||
}
|
||||
bwd->words = g_strv_builder_end( builder );
|
||||
g_strv_builder_unref( builder );
|
||||
|
||||
(void)g_idle_add( ask_bad_words, bwd );
|
||||
}
|
||||
g_free( strs );
|
||||
} /* gtk_util_notifyIllegalWords */
|
||||
|
||||
static void
|
||||
|
@ -2049,7 +2081,8 @@ ask_move( gpointer data )
|
|||
gint chosen = gtkask( globals->window, cGlobals->question, buttons, NULL );
|
||||
if ( GTK_RESPONSE_OK == chosen || chosen == GTK_RESPONSE_YES ) {
|
||||
BoardCtxt* board = cGlobals->game.board;
|
||||
if ( board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE, NULL ) ) {
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
if ( board_commitTurn( board, NULL_XWE, &pc, XP_TRUE, NULL ) ) {
|
||||
board_draw( board, NULL_XWE );
|
||||
}
|
||||
}
|
||||
|
@ -2081,7 +2114,8 @@ ask_trade( gpointer data )
|
|||
cGlobals->question,
|
||||
GTK_BUTTONS_YES_NO, NULL ) ) {
|
||||
BoardCtxt* board = cGlobals->game.board;
|
||||
if ( board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE, NULL ) ) {
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
if ( board_commitTurn( board, NULL_XWE, &pc, XP_TRUE, NULL ) ) {
|
||||
board_draw( board, NULL_XWE );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -163,8 +163,8 @@ linux_makeMoveIf( CommonGlobals* cGlobals, XP_Bool tryTrade )
|
|||
} else {
|
||||
XP_LOGFF( "unable to find hint; so PASSing" );
|
||||
}
|
||||
success = board_commitTurn( board, NULL_XWE, XP_TRUE, XP_TRUE,
|
||||
NULL );
|
||||
PhoniesConf pc = { .confirmed = XP_TRUE };
|
||||
success = board_commitTurn( board, NULL_XWE, &pc, XP_TRUE, NULL );
|
||||
}
|
||||
}
|
||||
return success;
|
||||
|
|
Loading…
Add table
Reference in a new issue