Merge branch 'android_branch' into android_translate

This commit is contained in:
Eric House 2018-03-14 19:32:25 -07:00
commit 36c9e8c93b
31 changed files with 385 additions and 443 deletions

View file

@ -1,6 +1,6 @@
def INITIAL_CLIENT_VERS = 8 def INITIAL_CLIENT_VERS = 8
def VERSION_CODE_BASE = 129 def VERSION_CODE_BASE = 130
def VERSION_NAME = '4.4.133' def VERSION_NAME = '4.4.134'
def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY") def FABRIC_API_KEY = System.getenv("FABRIC_API_KEY")
def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID") def GCM_SENDER_ID = System.getenv("GCM_SENDER_ID")
def BUILD_INFO_NAME = "build-info.txt" def BUILD_INFO_NAME = "build-info.txt"

View file

@ -13,9 +13,9 @@
</style> </style>
</head> </head>
<body> <body>
<h2>CrossWords 4.4.133 release</h2> <h2>CrossWords 4.4.134 release</h2>
<p>This release fixes a crash closing the wordlist browser.</p> <p>This release makes a few UI tweaks.</p>
<div id="survey"> <div id="survey">
<p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take <p>Please <a href="https://www.surveymonkey.com/s/GX3XLHR">take
@ -25,12 +25,12 @@
<h3>New with this release</h3> <h3>New with this release</h3>
<ul> <ul>
<li>Don't crash (even occasionally) when closing the wordlist <li>For better navigation with lots of saved games, make the
browser. (Reported via Google; thanks!)</li> scrollbar thumb draggable (and move it to the left side
<li>Prevent setting wordlist browser min word length higher than where it doesn't obscure anything)</li>
max</li> <li>When space is limited in Games List display, truncate player
<li>Don&apos;t include current player&apos;s tiles in remaining name rather than score</li>
tiles display</li> <li>Include latest Catalan translations (from Weblate)</li>
</ul> </ul>
<p>(The full changelog <p>(The full changelog

View file

@ -1095,60 +1095,29 @@ public class BTService extends XWService {
m_sender = null; m_sender = null;
} }
@Override
void postNotification( String device, int gameID, long rowid )
{
String body = LocUtils.getString( this, R.string.new_bt_body_fmt,
device );
GameUtils.postInvitedNotification( this, gameID, body, rowid );
postEvent( MultiEvent.BT_GAME_CREATED, rowid );
}
private BTCmd makeOrNotify( NetLaunchInfo nli, String btName, private BTCmd makeOrNotify( NetLaunchInfo nli, String btName,
String btAddr ) String btAddr )
{ {
BTCmd result; BTCmd result;
if ( checkNotDupe( nli ) ) { if ( handleInvitation( nli, btName, DictFetchOwner.OWNER_BT ) ) {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) { result = BTCmd.INVITE_ACCPT;
result = makeGame( nli, btName, btAddr );
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_BT );
// NetLaunchInfo.putExtras( intent, gameID, btName, btAddr );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
result = BTCmd.INVITE_ACCPT; // ???
}
} else { } else {
result = BTCmd.INVITE_DUP_INVITE; // dupe of rematch result = BTCmd.INVITE_DUP_INVITE; // dupe of rematch
} }
return result; return result;
} }
private BTCmd makeGame( NetLaunchInfo nli, String sender,
String senderAddress )
{
BTCmd result;
long[] rowids = DBUtils.getRowIDsFor( BTService.this, nli.gameID() );
if ( null == rowids || 0 == rowids.length ) {
CommsAddrRec addr = nli.makeAddrRec( BTService.this );
long rowid = GameUtils.makeNewMultiGame( BTService.this, nli,
m_btMsgSink,
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND == rowid ) {
result = BTCmd.INVITE_FAILED;
} else {
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( BTService.this, rowid, nli.gameName );
}
result = BTCmd.INVITE_ACCPT;
String body = LocUtils.getString( BTService.this,
R.string.new_bt_body_fmt,
sender );
GameUtils.postInvitedNotification( this, nli.gameID(), body,
rowid );
postEvent( MultiEvent.BT_GAME_CREATED, rowid );
}
} else {
result = BTCmd.INVITE_DUPID;
}
return result;
}
private DataOutputStream connect( BluetoothSocket socket, BTCmd cmd ) private DataOutputStream connect( BluetoothSocket socket, BTCmd cmd )
{ {
String name = socket.getRemoteDevice().getName(); String name = socket.getRemoteDevice().getName();

View file

@ -510,7 +510,9 @@ public class BoardCanvas extends Canvas implements DrawCtx {
public void score_pendingScore( Rect rect, int score, int playerNum, public void score_pendingScore( Rect rect, int score, int playerNum,
int curTurn, int flags ) int curTurn, int flags )
{ {
String text = score >= 0? String.format( "%d", score ) : "??"; Log.d( TAG, "pendingScore(playerNum=%d, curTurn=%d)",
playerNum, curTurn );
int otherIndx = (0 == (flags & CELL_ISCURSOR)) int otherIndx = (0 == (flags & CELL_ISCURSOR))
? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS; ? CommonPrefs.COLOR_BACKGRND : CommonPrefs.COLOR_FOCUS;
++rect.top; ++rect.top;
@ -522,6 +524,7 @@ public class BoardCanvas extends Canvas implements DrawCtx {
} }
m_fillPaint.setColor( playerColor ); m_fillPaint.setColor( playerColor );
String text = score >= 0? String.format( "%d", score ) : "??";
rect.bottom -= rect.height() / 2; rect.bottom -= rect.height() / 2;
drawCentered( text, rect, null ); drawCentered( text, rect, null );

View file

@ -20,18 +20,24 @@
package org.eehouse.android.xw4; package org.eehouse.android.xw4;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.database.Cursor; import android.database.Cursor;
import android.database.DatabaseUtils; import android.database.DatabaseUtils;
import android.os.Bundle;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils;
import android.text.format.Time; import android.text.format.Time;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.Iterator;
import java.util.Set;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.loc.LocUtils; import org.eehouse.android.xw4.loc.LocUtils;
import java.util.Formatter;
public class DbgUtils { public class DbgUtils {
private static final String TAG = DbgUtils.class.getSimpleName(); private static final String TAG = DbgUtils.class.getSimpleName();
@ -117,21 +123,17 @@ public class DbgUtils {
} }
} }
// public static void printIntent( Intent intent ) static String extrasToString( Intent intent )
// { {
// if ( s_doLog ) { Bundle bundle = intent.getExtras();
// Bundle bundle = intent.getExtras(); ArrayList<String> al = new ArrayList<String>();
// ArrayList<String> al = new ArrayList<String>(); if ( null != bundle ) {
// if ( null != bundle ) { for ( String key : bundle.keySet() ) {
// Set<String> keys = bundle.keySet(); al.add( key + ":" + bundle.get(key) );
// Iterator<String> iter = keys.iterator(); }
// while ( iter.hasNext() ) { }
// al.add( iter.next() ); return TextUtils.join( ", ", al );
// } }
// }
// DbgUtils.logf( "intent extras: %s", TextUtils.join( ", ", al ) );
// }
// }
public static void dumpCursor( Cursor cursor ) public static void dumpCursor( Cursor cursor )
{ {

View file

@ -1257,7 +1257,6 @@ public class GameUtils {
private static class ResendTask extends AsyncTask<Void, Void, Void> { private static class ResendTask extends AsyncTask<Void, Void, Void> {
private Context m_context; private Context m_context;
private HashMap<Long,CommsConnTypeSet> m_games;
private ResendDoneProc m_doneProc; private ResendDoneProc m_doneProc;
private CommsConnType m_filter; private CommsConnType m_filter;
private int m_nSent = 0; private int m_nSent = 0;
@ -1273,15 +1272,16 @@ public class GameUtils {
@Override @Override
protected Void doInBackground( Void... unused ) protected Void doInBackground( Void... unused )
{ {
m_games = DBUtils.getGamesWithSendsPending( m_context ); HashMap<Long,CommsConnTypeSet> games
= DBUtils.getGamesWithSendsPending( m_context );
Iterator<Long> iter = m_games.keySet().iterator(); Iterator<Long> iter = games.keySet().iterator();
while ( iter.hasNext() ) { while ( iter.hasNext() ) {
long rowid = iter.next(); long rowid = iter.next();
// If we're looking for a specific type, check // If we're looking for a specific type, check
if ( null != m_filter ) { if ( null != m_filter ) {
CommsConnTypeSet gameSet = m_games.get( rowid ); CommsConnTypeSet gameSet = games.get( rowid );
if ( gameSet != null && ! gameSet.contains( m_filter ) ) { if ( gameSet != null && ! gameSet.contains( m_filter ) ) {
continue; continue;
} }

View file

@ -2476,6 +2476,7 @@ public class GamesListDelegate extends ListDelegateBase
private void tryStartsFromIntent( Intent intent ) private void tryStartsFromIntent( Intent intent )
{ {
Log.d( TAG, "tryStartsFromIntent(extras={%s})", DbgUtils.extrasToString( intent ) );
startFirstHasDict( intent ); startFirstHasDict( intent );
startNewNetGame( intent ); startNewNetGame( intent );
startHasGameID( intent ); startHasGameID( intent );

View file

@ -23,7 +23,6 @@ package org.eehouse.android.xw4;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.text.TextUtils; import android.text.TextUtils;
@ -267,51 +266,16 @@ public class RelayService extends XWService
{ {
Log.d( TAG, "receiveInvitation: got nli from %d: %s", srcDevID, Log.d( TAG, "receiveInvitation: got nli from %d: %s", srcDevID,
nli.toString() ); nli.toString() );
if ( checkNotDupe( nli ) ) { if ( !handleInvitation( nli, null, DictFetchOwner.OWNER_RELAY ) ) {
makeOrNotify( nli ); Log.d( TAG, "handleInvitation() failed" );
} }
} }
private void makeOrNotify( NetLaunchInfo nli ) @Override
void postNotification( String device, int gameID, long rowid )
{ {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) { String body = LocUtils.getString( this, R.string.new_relay_body );
makeGame( nli ); GameUtils.postInvitedNotification( this, gameID, body, rowid );
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_RELAY );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
}
private void makeGame( NetLaunchInfo nli )
{
long[] rowids = DBUtils.getRowIDsFor( this, nli.gameID() );
if ( (null == rowids || 0 == rowids.length)
|| XWPrefs.getRelayInviteToSelfEnabled( this )) {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
long rowid = GameUtils.makeNewMultiGame( this, nli,
new RelayMsgSink(),
getUtilCtxt() );
if ( DBUtils.ROWID_NOTFOUND != rowid ) {
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( this, rowid, nli.gameName );
}
String body = LocUtils.getString( this,
R.string.new_relay_body );
GameUtils.postInvitedNotification( this, nli.gameID(), body,
rowid );
}
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_RELAY );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
}
} }
// Exists to get incoming data onto the main thread // Exists to get incoming data onto the main thread
@ -1268,19 +1232,18 @@ public class RelayService extends XWService
} }
private static class AsyncSender extends AsyncTask<Void, Void, Void> { private static class AsyncSender extends Thread {
private Context m_context; private Context m_context;
private HashMap<String,ArrayList<byte[]>> m_msgHash; private HashMap<String,ArrayList<byte[]>> m_msgHash;
public AsyncSender( Context context, AsyncSender( Context context, HashMap<String, ArrayList<byte[]>> msgHash )
HashMap<String,ArrayList<byte[]>> msgHash )
{ {
m_context = context; m_context = context;
m_msgHash = msgHash; m_msgHash = msgHash;
} }
@Override @Override
protected Void doInBackground( Void... ignored ) public void run()
{ {
// format: total msg lenth: 2 // format: total msg lenth: 2
// number-of-relayIDs: 2 // number-of-relayIDs: 2
@ -1327,9 +1290,9 @@ public class RelayService extends XWService
} }
msgLen += thisLen; msgLen += thisLen;
} }
// Now open a real socket, write size and proto, and // Now open a real socket, write size and proto, and
// copy in the formatted buffer // copy in the formatted buffer
Socket socket = NetUtils.makeProxySocket( m_context, 8000 ); Socket socket = NetUtils.makeProxySocket( m_context, 8000 );
if ( null != socket ) { if ( null != socket ) {
DataOutputStream outStream = DataOutputStream outStream =
@ -1345,15 +1308,14 @@ public class RelayService extends XWService
} catch ( java.io.IOException ioe ) { } catch ( java.io.IOException ioe ) {
Log.ex( TAG, ioe ); Log.ex( TAG, ioe );
} }
return null; } // run
} // doInBackground
} }
private static void sendToRelay( Context context, private static void sendToRelay( Context context,
HashMap<String,ArrayList<byte[]>> msgHash ) HashMap<String,ArrayList<byte[]>> msgHash )
{ {
if ( null != msgHash ) { if ( null != msgHash ) {
new AsyncSender( context, msgHash ).execute(); new AsyncSender( context, msgHash ).start();
} else { } else {
Log.w( TAG, "sendToRelay: null msgs" ); Log.w( TAG, "sendToRelay: null msgs" );
} }

View file

@ -504,20 +504,7 @@ public class SMSService extends XWService {
switch( cmd ) { switch( cmd ) {
case INVITE: case INVITE:
String nliData = dis.readUTF(); String nliData = dis.readUTF();
NetLaunchInfo nli = new NetLaunchInfo( this, nliData ); makeForInvite( phone, new NetLaunchInfo( this, nliData ) );
if ( nli.isValid() && checkNotDupe( nli ) ) {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
makeForInvite( phone, nli );
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_SMS );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
} else {
Log.w( TAG, "invalid nli from: %s", nliData );
}
break; break;
case DATA: case DATA:
int gameID = dis.readInt(); int gameID = dis.readInt();
@ -632,7 +619,8 @@ public class SMSService extends XWService {
return success; return success;
} }
private void postNotification( String phone, int gameID, long rowid ) @Override
protected void postNotification( String phone, int gameID, long rowid )
{ {
String owner = Utils.phoneToContact( this, phone, true ); String owner = Utils.phoneToContact( this, phone, true );
String body = LocUtils.getString( this, R.string.new_name_body_fmt, String body = LocUtils.getString( this, R.string.new_name_body_fmt,
@ -642,11 +630,9 @@ public class SMSService extends XWService {
private void makeForInvite( String phone, NetLaunchInfo nli ) private void makeForInvite( String phone, NetLaunchInfo nli )
{ {
long rowid = GameUtils.makeNewMultiGame( this, nli, if ( handleInvitation( nli, phone, DictFetchOwner.OWNER_SMS ) ) {
new SMSMsgSink( this ), ackInvite( phone, nli.gameID() );
getUtilCtxt() ); }
postNotification( phone, nli.gameID(), rowid );
ackInvite( phone, nli.gameID() );
} }
private PendingIntent makeStatusIntent( String msg ) private PendingIntent makeStatusIntent( String msg )

View file

@ -762,19 +762,18 @@ public class WiDirService extends XWService {
String nliData = intent.getStringExtra( KEY_NLI ); String nliData = intent.getStringExtra( KEY_NLI );
NetLaunchInfo nli = new NetLaunchInfo( this, nliData ); NetLaunchInfo nli = new NetLaunchInfo( this, nliData );
String returnMac = intent.getStringExtra( KEY_SRC ); String returnMac = intent.getStringExtra( KEY_SRC );
if ( checkNotDupe( nli ) ) {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) { if ( !handleInvitation( nli, returnMac, DictFetchOwner.OWNER_P2P ) ) {
makeGame( nli, returnMac ); Log.d( TAG, "handleInvitation() failed" );
} else {
Intent dictIntent = MultiService
.makeMissingDictIntent( this, nli,
DictFetchOwner.OWNER_P2P );
MultiService.postMissingDictNotification( this, dictIntent,
nli.gameID() );
}
} }
} }
@Override
void postNotification( String device, int gameID, long rowid )
{
Log.e( TAG, "postNotification() doing nothing" );
}
private void handleGameGone( Intent intent ) private void handleGameGone( Intent intent )
{ {
int gameID = intent.getIntExtra( KEY_GAMEID, 0 ); int gameID = intent.getIntExtra( KEY_GAMEID, 0 );

View file

@ -77,11 +77,6 @@ public class XWPrefs {
return getPrefsBoolean( context, R.string.key_show_gcm, false ); return getPrefsBoolean( context, R.string.key_show_gcm, false );
} }
public static boolean getRelayInviteToSelfEnabled( Context context )
{
return getPrefsBoolean( context, R.string.key_enable_relay_toself, false );
}
public static boolean getSMSToSelfEnabled( Context context ) public static boolean getSMSToSelfEnabled( Context context )
{ {
return getPrefsBoolean( context, R.string.key_enable_sms_toself, false ); return getPrefsBoolean( context, R.string.key_enable_sms_toself, false );

View file

@ -27,6 +27,7 @@ import android.os.IBinder;
import junit.framework.Assert; import junit.framework.Assert;
import org.eehouse.android.xw4.MultiService.DictFetchOwner;
import org.eehouse.android.xw4.MultiService.MultiEvent; import org.eehouse.android.xw4.MultiService.MultiEvent;
import org.eehouse.android.xw4.jni.CommsAddrRec; import org.eehouse.android.xw4.jni.CommsAddrRec;
import org.eehouse.android.xw4.jni.JNIThread; import org.eehouse.android.xw4.jni.JNIThread;
@ -36,7 +37,7 @@ import org.eehouse.android.xw4.jni.UtilCtxtImpl;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
class XWService extends Service { abstract class XWService extends Service {
private static final String TAG = XWService.class.getSimpleName(); private static final String TAG = XWService.class.getSimpleName();
public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED }; public static enum ReceiveResult { OK, GAME_GONE, UNCONSUMED };
@ -72,7 +73,7 @@ class XWService extends Service {
// Check that we aren't already processing an invitation with this // Check that we aren't already processing an invitation with this
// inviteID. // inviteID.
protected boolean checkNotDupe( NetLaunchInfo nli ) private boolean checkNotDupe( NetLaunchInfo nli )
{ {
String inviteID = nli.inviteID(); String inviteID = nli.inviteID();
boolean isDupe; boolean isDupe;
@ -86,6 +87,40 @@ class XWService extends Service {
return !isDupe; return !isDupe;
} }
abstract void postNotification( String device, int gameID, long rowid );
protected boolean handleInvitation( NetLaunchInfo nli, String device,
DictFetchOwner dfo )
{
boolean success = false;
long[] rowids = DBUtils.getRowIDsFor( this, nli.gameID() );
if ( 0 == rowids.length
|| ( rowids.length < nli.nPlayersT // will break for two-per-device game
&& XWPrefs.getSecondInviteAllowed( this ) ) ) {
if ( nli.isValid() && checkNotDupe( nli ) ) {
if ( DictLangCache.haveDict( this, nli.lang, nli.dict ) ) {
long rowid = GameUtils.makeNewMultiGame( this, nli,
getSink( 0 ),
getUtilCtxt() );
if ( null != nli.gameName && 0 < nli.gameName.length() ) {
DBUtils.setName( this, rowid, nli.gameName );
}
postNotification( device, nli.gameID(), rowid );
} else {
Intent intent = MultiService
.makeMissingDictIntent( this, nli, dfo );
MultiService.postMissingDictNotification( this, intent,
nli.gameID() );
}
}
}
return success;
}
protected UtilCtxt getUtilCtxt() protected UtilCtxt getUtilCtxt()
{ {
if ( null == m_utilCtxt ) { if ( null == m_utilCtxt ) {
@ -128,8 +163,7 @@ class XWService extends Service {
consumed = true; consumed = true;
jniThread.receive( msg, addr ).release(); jniThread.receive( msg, addr ).release();
} else { } else {
GameUtils.BackMoveResult bmr = GameUtils.BackMoveResult bmr = new GameUtils.BackMoveResult();
new GameUtils.BackMoveResult();
if ( null == sink ) { if ( null == sink ) {
sink = getSink( rowid ); sink = getSink( rowid );
} }

View file

@ -125,7 +125,6 @@
<string name="key_enable_dup_invite">key_enable_dup_invite</string> <string name="key_enable_dup_invite">key_enable_dup_invite</string>
<string name="key_enable_nfc_toself">key_enable_nfc_toself</string> <string name="key_enable_nfc_toself">key_enable_nfc_toself</string>
<string name="key_enable_sms_toself">key_enable_sms_toself</string> <string name="key_enable_sms_toself">key_enable_sms_toself</string>
<string name="key_enable_relay_toself">key_enable_relay_toself</string>
<string name="key_ignore_gcm">key_ignore_gcm</string> <string name="key_ignore_gcm">key_ignore_gcm</string>
<string name="key_show_gcm">key_show_gcm</string> <string name="key_show_gcm">key_show_gcm</string>
<string name="key_nag_intervals">key_nag_intervals</string> <string name="key_nag_intervals">key_nag_intervals</string>

View file

@ -2600,9 +2600,6 @@
devices and I think it\'s rare that people play with more than devices and I think it\'s rare that people play with more than
two. Let me know if I\'m wrong and I\'ll up the priority.</string> two. Let me know if I\'m wrong and I\'ll up the priority.</string>
<string name="enable_relay_toself_title">Enable relay invites to self</string>
<string name="enable_relay_toself_summary">(To aid testing and debugging)</string>
<string name="ignore_gcm_title">Ignore incoming GCM messages</string> <string name="ignore_gcm_title">Ignore incoming GCM messages</string>
<string name="ignore_gcm_summary">Mimic life without a google account</string> <string name="ignore_gcm_summary">Mimic life without a google account</string>

View file

@ -393,11 +393,6 @@
<PreferenceScreen android:title="@string/pref_group_relay_title" <PreferenceScreen android:title="@string/pref_group_relay_title"
android:summary="@string/pref_group_relay_summary" android:summary="@string/pref_group_relay_summary"
> >
<CheckBoxPreference android:key="@string/key_enable_relay_toself"
android:title="@string/enable_relay_toself_title"
android:summary="@string/enable_relay_toself_summary"
android:defaultValue="false"
/>
<CheckBoxPreference android:key="@string/key_ignore_gcm" <CheckBoxPreference android:key="@string/key_ignore_gcm"
android:title="@string/ignore_gcm_title" android:title="@string/ignore_gcm_title"
android:summary="@string/ignore_gcm_summary" android:summary="@string/ignore_gcm_summary"

View file

@ -2177,6 +2177,7 @@ board_requestHint( BoardCtxt* board,
board_popTimerSave( board ); board_popTimerSave( board );
if ( searchComplete && canMove ) { if ( searchComplete && canMove ) {
juggleMoveIfDebug( &newMove );
model_makeTurnFromMoveInfo( model, selPlayer, &newMove); model_makeTurnFromMoveInfo( model, selPlayer, &newMove);
} else { } else {
result = XP_FALSE; result = XP_FALSE;

View file

@ -2704,7 +2704,7 @@ logAddrs( const CommsCtxt* comms, const char* caller )
#endif #endif
static void static void
augmentChannelAddr( CommsCtxt* comms, AddressRecord * const rec, augmentChannelAddr( CommsCtxt* XP_UNUSED_DBG(comms), AddressRecord * const rec,
const CommsAddrRec* addr, XWHostID hostID ) const CommsAddrRec* addr, XWHostID hostID )
{ {
if ( !!addr ) { if ( !!addr ) {

View file

@ -123,7 +123,6 @@ struct EngineCtxt {
static void findMovesOneRow( EngineCtxt* engine ); static void findMovesOneRow( EngineCtxt* engine );
static Tile localGetBoardTile( EngineCtxt* engine, XP_U16 col, static Tile localGetBoardTile( EngineCtxt* engine, XP_U16 col,
XP_U16 row, XP_Bool substBlank ); XP_U16 row, XP_Bool substBlank );
static XP_Bool scoreQualifies( EngineCtxt* engine, XP_U16 score );
static void findMovesForAnchor( EngineCtxt* engine, XP_S16* prevAnchor, static void findMovesForAnchor( EngineCtxt* engine, XP_S16* prevAnchor,
XP_U16 col, XP_U16 row ) ; XP_U16 col, XP_U16 row ) ;
static void figureCrosschecks( EngineCtxt* engine, XP_U16 col, static void figureCrosschecks( EngineCtxt* engine, XP_U16 col,
@ -1115,33 +1114,37 @@ considerScoreWordHasBlanks( EngineCtxt* engine, XP_U16 blanksLeft,
XP_U16 ii; XP_U16 ii;
if ( blanksLeft == 0 ) { if ( blanksLeft == 0 ) {
XP_U16 score; /* Hack: When a single-tile move involves two words it'll be found by
XP_U16 nTiles = posmove->moveInfo.nTiles; both the horizontal and vertical passes. Since it's really the same
move both times we don't want both. It'd be better I think to
change the move comparison code to detect it as a duplicate, but
that's a lot of work. Instead, add a callback in the single-tile
vertical case to count words, and when the count it > 1 drop the
move.*/
WordNotifierInfo* wiip = NULL; WordNotifierInfo* wiip = NULL;
WordNotifierInfo wii; WordNotifierInfo wii;
XP_U16 wordCount = 0; XP_U16 singleTileWordCount = 0;
if ( 1 == nTiles ) { if ( !engine->searchHorizontal && 1 == posmove->moveInfo.nTiles ) {
wii.proc = countWords; wii.proc = countWords;
wii.closure = &wordCount; wii.closure = &singleTileWordCount;
wiip = &wii; wiip = &wii;
} }
score = figureMoveScore( engine->model, engine->turn, XP_U16 score = figureMoveScore( engine->model, engine->turn,
&posmove->moveInfo, &posmove->moveInfo,
engine, (XWStreamCtxt*)NULL, wiip ); engine, (XWStreamCtxt*)NULL, wiip );
#ifdef XWFEATURE_BONUSALL
if ( 0 != engine->allTilesBonus && 0 == engine->nTilesMax ) { if ( singleTileWordCount > 1 ) { /* only set by special-case code above */
XP_LOGF( "%s: adding bonus: %d becoming %d", __func__, score , XP_ASSERT( singleTileWordCount == 2 ); /* I think this is the limit */
score + engine->allTilesBonus );
score += engine->allTilesBonus;
}
#endif
/* First, check that the score is even what we're interested in. If
it is, then go to the expense of filling in a PossibleMove to be
compared in full */
if ( 1 == nTiles && 1 < wordCount && !engine->searchHorizontal ) {
// XP_LOGF( "%s(): dropping", __func__ ); // XP_LOGF( "%s(): dropping", __func__ );
} else if ( scoreQualifies( engine, score ) ) { } else {
#ifdef XWFEATURE_BONUSALL
if ( 0 != engine->allTilesBonus && 0 == engine->nTilesMax ) {
XP_LOGF( "%s: adding bonus: %d becoming %d", __func__, score ,
score + engine->allTilesBonus );
score += engine->allTilesBonus;
}
#endif
posmove->score = score; posmove->score = score;
XP_MEMSET( &posmove->blankVals, 0, sizeof(posmove->blankVals) ); XP_MEMSET( &posmove->blankVals, 0, sizeof(posmove->blankVals) );
for ( ii = 0; ii < usedBlanksCount; ++ii ) { for ( ii = 0; ii < usedBlanksCount; ++ii ) {
@ -1342,43 +1345,6 @@ move_cache_empty( const EngineCtxt* engine )
return empty; return empty;
} }
static XP_Bool
scoreQualifies( EngineCtxt* engine, XP_U16 score )
{
XP_Bool qualifies = XP_FALSE;
XP_Bool usePrev = engine->usePrev;
MoveIterationData* miData = &engine->miData;
if ( usePrev && score < miData->lastSeenMove.score ) {
/* drop it */
} else if ( !usePrev && score > miData->lastSeenMove.score
/* || (score < miData->lowestSavedScore) */ ) {
/* drop it */
} else {
XP_S16 ii;
PossibleMove* savedMoves = miData->savedMoves;
/* Look at each saved score, and return true as soon as one's found
with a lower or equal score to this. <eeh> As an optimization,
consider remembering what the lowest score is *once there are
NUM_SAVED_ENGINE_MOVES moves in here* and doing a quick test on
that. Or better, keeping the list in sorted order. */
for ( ii = 0, savedMoves = miData->savedMoves;
ii < engine->nMovesToSave; ++ii, ++savedMoves ) {
if ( savedMoves->score == 0 ) { /* empty slot */
qualifies = XP_TRUE;
} else if ( usePrev && score <= savedMoves->score ) {
qualifies = XP_TRUE;
break;
} else if ( !usePrev && score >= savedMoves->score ) {
qualifies = XP_TRUE;
break;
}
}
}
//XP_LOGF( "%s(%d)->%d", __func__, score, qualifies );
return qualifies;
} /* scoreQualifies */
static array_edge* static array_edge*
edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile ) edge_from_tile( const DictionaryCtxt* dict, array_edge* from, Tile tile )
{ {

View file

@ -1120,6 +1120,28 @@ model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
return success; return success;
} /* model_makeTurnFromStream */ } /* model_makeTurnFromStream */
#ifdef DEBUG
void
juggleMoveIfDebug( MoveInfo* move )
{
XP_U16 nTiles = move->nTiles;
// XP_LOGF( "%s(): move len: %d", __func__, nTiles );
MoveInfoTile tiles[MAX_TRAY_TILES];
XP_MEMCPY( tiles, move->tiles, sizeof(tiles) );
for ( int ii = 0; ii < nTiles; ++ii ) {
int last = nTiles - ii;
int choice = XP_RANDOM() % last;
move->tiles[ii] = tiles[choice];
// XP_LOGF( "%s(): setting %d to %d", __func__, ii, choice );
if ( choice != --last ) {
tiles[choice] = tiles[last];
// XP_LOGF( "%s(): replacing %d with %d", __func__, choice, last );
}
}
}
#endif
void void
model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum, model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
const MoveInfo* newMove ) const MoveInfo* newMove )
@ -1127,11 +1149,8 @@ model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
XP_U16 col, row, ii; XP_U16 col, row, ii;
XP_U16* other; XP_U16* other;
const MoveInfoTile* tinfo; const MoveInfoTile* tinfo;
Tile blank; Tile blank = dict_getBlankTile( model_getDictionary( model ) );
XP_U16 numTiles; XP_U16 numTiles = newMove->nTiles;
blank = dict_getBlankTile( model_getDictionary( model ) );
numTiles = newMove->nTiles;
col = row = newMove->commonCoord; /* just assign both */ col = row = newMove->commonCoord; /* just assign both */
other = newMove->isHorizontal? &col: &row; other = newMove->isHorizontal? &col: &row;
@ -1667,15 +1686,7 @@ static XP_S16
commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles, commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
XWStreamCtxt* stream, WordNotifierInfo* wni, XP_Bool useStack ) XWStreamCtxt* stream, WordNotifierInfo* wni, XP_Bool useStack )
{ {
XP_U16 ii;
PlayerCtxt* player;
PendingTile* pt;
XP_S16 score = -1; XP_S16 score = -1;
XP_Bool isHorizontal;
const Tile* newTilesP;
XP_U16 nTiles;
nTiles = newTiles->nTiles;
#ifdef DEBUG #ifdef DEBUG
XP_ASSERT( getCurrentMoveScoreIfLegal( model, turn, (XWStreamCtxt*)NULL, XP_ASSERT( getCurrentMoveScoreIfLegal( model, turn, (XWStreamCtxt*)NULL,
@ -1687,10 +1698,11 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
clearLastMoveInfo( model ); clearLastMoveInfo( model );
player = &model->players[turn]; PlayerCtxt* player = &model->players[turn];
if ( useStack ) { if ( useStack ) {
MoveInfo moveInfo = {0}; MoveInfo moveInfo = {0};
XP_Bool isHorizontal;
#ifdef DEBUG #ifdef DEBUG
XP_Bool inLine = XP_Bool inLine =
#endif #endif
@ -1701,19 +1713,16 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
stack_addMove( model->vol.stack, turn, &moveInfo, newTiles ); stack_addMove( model->vol.stack, turn, &moveInfo, newTiles );
} }
for ( ii = 0, pt=player->pendingTiles; ii < player->nPending; /* Where's it removed from tray? Need to assert there! */
++ii, ++pt ) { for ( int ii = 0; ii < player->nPending; ++ii ) {
XP_U16 col, row; const PendingTile* pt = &player->pendingTiles[ii];
CellTile tile; XP_U16 col = pt->col;
XP_U16 val; XP_U16 row = pt->row;
CellTile tile = getModelTileRaw( model, col, row );
col = pt->col;
row = pt->row;
tile = getModelTileRaw( model, col, row );
XP_ASSERT( (tile & TILE_PENDING_BIT) != 0 ); XP_ASSERT( (tile & TILE_PENDING_BIT) != 0 );
val = tile & TILE_VALUE_MASK; XP_U16 val = tile & TILE_VALUE_MASK;
if ( val > 1 ) { /* somebody else is using this square too! */ if ( val > 1 ) { /* somebody else is using this square too! */
putBackOtherPlayersTiles( model, turn, col, row ); putBackOtherPlayersTiles( model, turn, col, row );
} }
@ -1734,17 +1743,18 @@ commitTurn( ModelCtxt* model, XP_S16 turn, const TrayTileSet* newTiles,
player->score += score; player->score += score;
/* Why is this next loop necessary? */ /* Why is this next loop necessary? */
for ( ii = 0; ii < model->nPlayers; ++ii ) { for ( int ii = 0; ii < model->nPlayers; ++ii ) {
invalidateScore( model, ii ); invalidateScore( model, ii );
} }
player->nPending = 0; player->nPending = 0;
player->nUndone = 0; player->nUndone = 0;
newTilesP = newTiles->tiles; /* Move new tiles into tray */
while ( nTiles-- ) { for ( int ii = newTiles->nTiles - 1; ii >= 0; --ii ) {
model_addPlayerTile( model, turn, -1, *newTilesP++ ); model_addPlayerTile( model, turn, -1, newTiles->tiles[ii] );
} }
return score; return score;
} /* commitTurn */ } /* commitTurn */
@ -2045,72 +2055,67 @@ static void
printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry, printMovePre( ModelCtxt* model, XP_U16 XP_UNUSED(moveN), const StackEntry* entry,
void* p_closure ) void* p_closure )
{ {
XWStreamCtxt* stream; if ( entry->moveType != ASSIGN_TYPE ) {
const XP_UCHAR* format; const XP_UCHAR* format;
XP_UCHAR buf[64]; XP_UCHAR buf[64];
XP_UCHAR traybuf[MAX_TRAY_TILES+1]; XP_UCHAR traybuf[MAX_TRAY_TILES+1];
MovePrintClosure* closure = (MovePrintClosure*)p_closure; MovePrintClosure* closure = (MovePrintClosure*)p_closure;
XWStreamCtxt* stream = closure->stream;
if ( entry->moveType == ASSIGN_TYPE ) { XP_SNPRINTF( buf, sizeof(buf), (XP_UCHAR*)"%d:%d ", ++closure->nPrinted,
return; entry->playerNum+1 );
}
stream = closure->stream;
XP_SNPRINTF( buf, sizeof(buf), (XP_UCHAR*)"%d:%d ", ++closure->nPrinted,
entry->playerNum+1 );
printString( stream, (XP_UCHAR*)buf );
if ( entry->moveType == TRADE_TYPE ) {
} else {
XP_UCHAR letter[2] = {'\0','\0'};
XP_Bool isHorizontal = entry->u.move.moveInfo.isHorizontal;
XP_U16 col, row;
const MoveInfo* mi;
XP_Bool isPass = XP_FALSE;
if ( entry->moveType == PHONY_TYPE ) {
mi = &entry->u.phony.moveInfo;
} else {
mi = &entry->u.move.moveInfo;
if ( mi->nTiles == 0 ) {
isPass = XP_TRUE;
}
}
if ( isPass ) {
format = util_getUserString( model->vol.util, STR_PASS );
XP_SNPRINTF( buf, VSIZE(buf), "%s", format );
} else {
if ( isHorizontal ) {
format = util_getUserString( model->vol.util, STRS_MOVE_ACROSS );
} else {
format = util_getUserString( model->vol.util, STRS_MOVE_DOWN );
}
row = mi->commonCoord;
col = mi->tiles[0].varCoord;
if ( !isHorizontal ) {
XP_U16 tmp = col; col = row; row = tmp;
}
letter[0] = 'A' + col;
XP_SNPRINTF( traybuf, sizeof(traybuf), (XP_UCHAR *)"%s%d",
letter, row + 1 );
XP_SNPRINTF( buf, sizeof(buf), format, traybuf );
}
printString( stream, (XP_UCHAR*)buf ); printString( stream, (XP_UCHAR*)buf );
}
if ( !closure->keepHidden ) { if ( entry->moveType == TRADE_TYPE ) {
format = util_getUserString( model->vol.util, STRS_TRAY_AT_START ); } else {
formatTray( model_getPlayerTiles( model, entry->playerNum ), XP_UCHAR letter[2] = {'\0','\0'};
closure->dict, (XP_UCHAR*)traybuf, sizeof(traybuf), XP_Bool isHorizontal = entry->u.move.moveInfo.isHorizontal;
XP_FALSE ); XP_U16 col, row;
XP_SNPRINTF( buf, sizeof(buf), format, traybuf ); const MoveInfo* mi;
printString( stream, buf ); XP_Bool isPass = XP_FALSE;
}
if ( entry->moveType == PHONY_TYPE ) {
mi = &entry->u.phony.moveInfo;
} else {
mi = &entry->u.move.moveInfo;
if ( mi->nTiles == 0 ) {
isPass = XP_TRUE;
}
}
if ( isPass ) {
format = util_getUserString( model->vol.util, STR_PASS );
XP_SNPRINTF( buf, VSIZE(buf), "%s", format );
} else {
if ( isHorizontal ) {
format = util_getUserString( model->vol.util, STRS_MOVE_ACROSS );
} else {
format = util_getUserString( model->vol.util, STRS_MOVE_DOWN );
}
row = mi->commonCoord;
col = mi->tiles[0].varCoord;
if ( !isHorizontal ) {
XP_U16 tmp = col; col = row; row = tmp;
}
letter[0] = 'A' + col;
XP_SNPRINTF( traybuf, sizeof(traybuf), (XP_UCHAR *)"%s%d",
letter, row + 1 );
XP_SNPRINTF( buf, sizeof(buf), format, traybuf );
}
printString( stream, (XP_UCHAR*)buf );
}
if ( !closure->keepHidden ) {
format = util_getUserString( model->vol.util, STRS_TRAY_AT_START );
formatTray( model_getPlayerTiles( model, entry->playerNum ),
closure->dict, (XP_UCHAR*)traybuf, sizeof(traybuf),
XP_FALSE );
XP_SNPRINTF( buf, sizeof(buf), format, traybuf );
printString( stream, buf );
}
}
} /* printMovePre */ } /* printMovePre */
static void static void
@ -2118,69 +2123,66 @@ printMovePost( ModelCtxt* model, XP_U16 XP_UNUSED(moveN),
const StackEntry* entry, XP_S16 XP_UNUSED(score), const StackEntry* entry, XP_S16 XP_UNUSED(score),
void* p_closure ) void* p_closure )
{ {
MovePrintClosure* closure = (MovePrintClosure*)p_closure; if ( entry->moveType != ASSIGN_TYPE ) {
XWStreamCtxt* stream = closure->stream; MovePrintClosure* closure = (MovePrintClosure*)p_closure;
DictionaryCtxt* dict = closure->dict; XWStreamCtxt* stream = closure->stream;
const XP_UCHAR* format; DictionaryCtxt* dict = closure->dict;
XP_U16 nTiles; const XP_UCHAR* format;
XP_S16 totalScore; XP_U16 nTiles;
XP_UCHAR buf[100];
XP_UCHAR traybuf1[MAX_TRAY_TILES+1];
XP_UCHAR traybuf2[MAX_TRAY_TILES+1];
const MoveInfo* mi;
if ( entry->moveType == ASSIGN_TYPE ) { XP_UCHAR buf[100];
return; XP_UCHAR traybuf1[MAX_TRAY_TILES+1];
} XP_UCHAR traybuf2[MAX_TRAY_TILES+1];
const MoveInfo* mi;
XP_S16 totalScore = model_getPlayerScore( model, entry->playerNum );
totalScore = model_getPlayerScore( model, entry->playerNum ); switch( entry->moveType ) {
case TRADE_TYPE:
formatTray( (const TrayTileSet*)&entry->u.trade.oldTiles,
dict, traybuf1, sizeof(traybuf1), closure->keepHidden );
formatTray( (const TrayTileSet*) &entry->u.trade.newTiles,
dict, traybuf2, sizeof(traybuf2), closure->keepHidden );
switch( entry->moveType ) { format = util_getUserString( model->vol.util, STRSS_TRADED_FOR );
case TRADE_TYPE: XP_SNPRINTF( buf, sizeof(buf), format, traybuf1, traybuf2 );
formatTray( (const TrayTileSet*)&entry->u.trade.oldTiles, printString( stream, buf );
dict, traybuf1, sizeof(traybuf1), closure->keepHidden ); printString( stream, (XP_UCHAR*)XP_CR );
formatTray( (const TrayTileSet*) &entry->u.trade.newTiles, break;
dict, traybuf2, sizeof(traybuf2), closure->keepHidden );
format = util_getUserString( model->vol.util, STRSS_TRADED_FOR ); case PHONY_TYPE:
XP_SNPRINTF( buf, sizeof(buf), format, traybuf1, traybuf2 ); format = util_getUserString( model->vol.util, STR_PHONY_REJECTED );
printString( stream, buf ); printString( stream, format );
printString( stream, (XP_UCHAR*)XP_CR ); case MOVE_TYPE:
break; format = util_getUserString( model->vol.util, STRD_CUMULATIVE_SCORE );
XP_SNPRINTF( buf, sizeof(buf), format, totalScore );
case PHONY_TYPE: printString( stream, buf );
format = util_getUserString( model->vol.util, STR_PHONY_REJECTED );
printString( stream, format );
case MOVE_TYPE:
format = util_getUserString( model->vol.util, STRD_CUMULATIVE_SCORE );
XP_SNPRINTF( buf, sizeof(buf), format, totalScore );
printString( stream, buf );
if ( entry->moveType == PHONY_TYPE ) {
mi = &entry->u.phony.moveInfo;
} else {
mi = &entry->u.move.moveInfo;
}
nTiles = mi->nTiles;
if ( nTiles > 0 ) {
if ( entry->moveType == PHONY_TYPE ) { if ( entry->moveType == PHONY_TYPE ) {
/* printString( stream, (XP_UCHAR*)"phony rejected " ); */ mi = &entry->u.phony.moveInfo;
} else if ( !closure->keepHidden ) { } else {
format = util_getUserString(model->vol.util, STRS_NEW_TILES); mi = &entry->u.move.moveInfo;
XP_SNPRINTF( buf, sizeof(buf), format,
formatTray( &entry->u.move.newTiles, dict,
traybuf1, sizeof(traybuf1),
XP_FALSE ) );
printString( stream, buf );
stream_catString( stream, (XP_UCHAR*)XP_CR );
} }
nTiles = mi->nTiles;
if ( nTiles > 0 ) {
if ( entry->moveType == PHONY_TYPE ) {
/* printString( stream, (XP_UCHAR*)"phony rejected " ); */
} else if ( !closure->keepHidden ) {
format = util_getUserString(model->vol.util, STRS_NEW_TILES);
XP_SNPRINTF( buf, sizeof(buf), format,
formatTray( &entry->u.move.newTiles, dict,
traybuf1, sizeof(traybuf1),
XP_FALSE ) );
printString( stream, buf );
stream_catString( stream, (XP_UCHAR*)XP_CR );
}
}
break;
} }
break; printString( stream, (XP_UCHAR*)XP_CR );
} }
printString( stream, (XP_UCHAR*)XP_CR );
} /* printMovePost */ } /* printMovePost */
static void static void

View file

@ -221,6 +221,12 @@ XP_Bool model_makeTurnFromStream( ModelCtxt* model, XP_U16 playerNum,
void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum, void model_makeTurnFromMoveInfo( ModelCtxt* model, XP_U16 playerNum,
const MoveInfo* newMove ); const MoveInfo* newMove );
#ifdef DEBUG
void juggleMoveIfDebug( MoveInfo* move );
#else
# define juggleMoveIfDebug(newMove)
#endif
void model_resetCurrentTurn( ModelCtxt* model, XP_S16 turn ); void model_resetCurrentTurn( ModelCtxt* model, XP_S16 turn );
XP_S16 model_getNMoves( const ModelCtxt* model ); XP_S16 model_getNMoves( const ModelCtxt* model );

View file

@ -316,10 +316,9 @@ void
stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo, stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo,
const TrayTileSet* newTiles ) const TrayTileSet* newTiles )
{ {
StackEntry move; StackEntry move = {.playerNum = (XP_U8)turn,
.moveType = MOVE_TYPE,
move.playerNum = (XP_U8)turn; };
move.moveType = MOVE_TYPE;
XP_MEMCPY( &move.u.move.moveInfo, moveInfo, sizeof(move.u.move.moveInfo)); XP_MEMCPY( &move.u.move.moveInfo, moveInfo, sizeof(move.u.move.moveInfo));
move.u.move.newTiles = *newTiles; move.u.move.newTiles = *newTiles;
@ -330,10 +329,9 @@ stack_addMove( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo,
void void
stack_addPhony( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo ) stack_addPhony( StackCtxt* stack, XP_U16 turn, const MoveInfo* moveInfo )
{ {
StackEntry move; StackEntry move = {.playerNum = (XP_U8)turn,
.moveType = PHONY_TYPE,
move.playerNum = (XP_U8)turn; };
move.moveType = PHONY_TYPE;
XP_MEMCPY( &move.u.phony.moveInfo, moveInfo, XP_MEMCPY( &move.u.phony.moveInfo, moveInfo,
sizeof(move.u.phony.moveInfo)); sizeof(move.u.phony.moveInfo));
@ -345,10 +343,9 @@ void
stack_addTrade( StackCtxt* stack, XP_U16 turn, stack_addTrade( StackCtxt* stack, XP_U16 turn,
const TrayTileSet* oldTiles, const TrayTileSet* newTiles ) const TrayTileSet* oldTiles, const TrayTileSet* newTiles )
{ {
StackEntry move; StackEntry move = { .playerNum = (XP_U8)turn,
.moveType = TRADE_TYPE,
move.playerNum = (XP_U8)turn; };
move.moveType = TRADE_TYPE;
move.u.trade.oldTiles = *oldTiles; move.u.trade.oldTiles = *oldTiles;
move.u.trade.newTiles = *newTiles; move.u.trade.newTiles = *newTiles;
@ -359,10 +356,9 @@ stack_addTrade( StackCtxt* stack, XP_U16 turn,
void void
stack_addAssign( StackCtxt* stack, XP_U16 turn, const TrayTileSet* tiles ) stack_addAssign( StackCtxt* stack, XP_U16 turn, const TrayTileSet* tiles )
{ {
StackEntry move; StackEntry move = { .playerNum = (XP_U8)turn,
.moveType = ASSIGN_TYPE,
move.playerNum = (XP_U8)turn; };
move.moveType = ASSIGN_TYPE;
move.u.assign.tiles = *tiles; move.u.assign.tiles = *tiles;

View file

@ -615,9 +615,13 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
* each time through in the debug case */ * each time through in the debug case */
if ( 0 ) { /* makes keeping parens balanced easier */ if ( 0 ) { /* makes keeping parens balanced easier */
#ifdef DEBUG #ifdef DEBUG
/* Always run in DEBUG case */
} else if ( 1 ) { } else if ( 1 ) {
#else #else
} else if ( engine == NULL ) { /* If notifyInfo is set, we're counting on the side-effect of its
proc getting called. So skip caching in that case even on
release builds. */
} else if ( engine == NULL || notifyInfo != NULL ) {
#endif #endif
Tile checkWordBuf[MAX_ROWS]; Tile checkWordBuf[MAX_ROWS];
Tile* curTile = checkWordBuf; Tile* curTile = checkWordBuf;
@ -688,7 +692,8 @@ scoreWord( const ModelCtxt* model, XP_U16 turn,
#else #else
} else { /* non-debug case we know it's non-null */ } else { /* non-debug case we know it's non-null */
#endif #endif
XP_ASSERT( nTiles==1 ); XP_ASSERT( nTiles == 1 );
XP_ASSERT( notifyInfo == NULL );
XP_ASSERT( engine_getScoreCache( engine, movei->commonCoord ) XP_ASSERT( engine_getScoreCache( engine, movei->commonCoord )
== restScore ); == restScore );
restScore = engine_getScoreCache( engine, movei->commonCoord ); restScore = engine_getScoreCache( engine, movei->commonCoord );

View file

@ -929,6 +929,7 @@ makeRobotMove( ServerCtxt* server )
/* if canMove is false, this is a fake move, a pass */ /* if canMove is false, this is a fake move, a pass */
if ( canMove || NPASSES_OK(server) ) { if ( canMove || NPASSES_OK(server) ) {
juggleMoveIfDebug( &newMove );
model_makeTurnFromMoveInfo( model, turn, &newMove ); model_makeTurnFromMoveInfo( model, turn, &newMove );
XP_LOGF( "%s: robot making %d tile move", __func__, newMove.nTiles ); XP_LOGF( "%s: robot making %d tile move", __func__, newMove.nTiles );
@ -1832,10 +1833,7 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
{ {
XP_Bool ask; XP_Bool ask;
XP_U16 nSoFar = resultTiles->nTiles; XP_U16 nSoFar = resultTiles->nTiles;
XP_U16 nLeft;
PoolContext* pool = server->pool; PoolContext* pool = server->pool;
TrayTileSet oneTile;
PickInfo pi;
const XP_UCHAR* curTray[MAX_TRAY_TILES]; const XP_UCHAR* curTray[MAX_TRAY_TILES];
#ifdef FEATURE_TRAY_EDIT #ifdef FEATURE_TRAY_EDIT
DictionaryCtxt* dict = model_getDictionary( server->vol.model ); DictionaryCtxt* dict = model_getDictionary( server->vol.model );
@ -1849,22 +1847,22 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
ask = XP_FALSE; ask = XP_FALSE;
#endif #endif
nLeft = pool_getNTilesLeft( pool ); XP_U16 nLeft = pool_getNTilesLeft( pool );
if ( nLeft < nToFetch ) { if ( nLeft < nToFetch ) {
nToFetch = nLeft; nToFetch = nLeft;
} }
oneTile.nTiles = 1; TrayTileSet oneTile = {.nTiles = 1};
PickInfo pi = { .nTotal = nToFetch,
pi.nTotal = nToFetch; .thisPick = 0,
pi.thisPick = 0; .curTiles = curTray,
pi.curTiles = curTray; };
curTrayAsTexts( server, playerNum, tradedTiles, &pi.nCurTiles, curTray ); curTrayAsTexts( server, playerNum, tradedTiles, &pi.nCurTiles, curTray );
#ifdef FEATURE_TRAY_EDIT /* good compiler would note ask==0, but... */ #ifdef FEATURE_TRAY_EDIT /* good compiler would note ask==0, but... */
/* First ask until cancelled */ /* First ask until cancelled */
for ( ; ask && nSoFar < nToFetch; ) { while ( ask && nSoFar < nToFetch ) {
const XP_UCHAR* texts[MAX_UNIQUE_TILES]; const XP_UCHAR* texts[MAX_UNIQUE_TILES];
Tile tiles[MAX_UNIQUE_TILES]; Tile tiles[MAX_UNIQUE_TILES];
XP_S16 chosen; XP_S16 chosen;
@ -1901,12 +1899,7 @@ fetchTiles( ServerCtxt* server, XP_U16 playerNum, XP_U16 nToFetch,
/* Then fetch the rest without asking */ /* Then fetch the rest without asking */
if ( nSoFar < nToFetch ) { if ( nSoFar < nToFetch ) {
XP_U8 nLeft = nToFetch - nSoFar; XP_U8 nLeft = nToFetch - nSoFar;
Tile tiles[MAX_TRAY_TILES]; pool_requestTiles( pool, &resultTiles->tiles[nSoFar], &nLeft );
pool_requestTiles( pool, tiles, &nLeft );
XP_MEMCPY( &resultTiles->tiles[nSoFar], tiles,
nLeft * sizeof(resultTiles->tiles[0]) );
nSoFar += nLeft; nSoFar += nLeft;
} }
@ -2462,8 +2455,9 @@ server_commitMove( ServerCtxt* server, TrayTileSet* newTilesP )
if client, send to server. */ if client, send to server. */
XP_ASSERT( turn >= 0 ); XP_ASSERT( turn >= 0 );
nTilesMoved = model_getCurrentMoveCount( model, turn );
pool_removeTiles( server->pool, &newTiles ); pool_removeTiles( server->pool, &newTiles );
nTilesMoved = model_getCurrentMoveCount( model, turn );
fetchTiles( server, turn, nTilesMoved, NULL, &newTiles ); fetchTiles( server, turn, nTilesMoved, NULL, &newTiles );
#ifndef XWFEATURE_STANDALONE_ONLY #ifndef XWFEATURE_STANDALONE_ONLY

View file

@ -1372,8 +1372,8 @@ curses_util_getVTManager(XW_UtilCtxt* uc)
static void static void
curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc), curses_util_informNeedPassword( XW_UtilCtxt* XP_UNUSED(uc),
XP_U16 playerNum, XP_U16 XP_UNUSED_DBG(playerNum),
const XP_UCHAR* name ) const XP_UCHAR* XP_UNUSED_DBG(name) )
{ {
XP_WARNF( "curses_util_informNeedPassword(num=%d, name=%s", playerNum, name ); XP_WARNF( "curses_util_informNeedPassword(num=%d, name=%s", playerNum, name );
} /* curses_util_askPassword */ } /* curses_util_askPassword */
@ -1391,7 +1391,7 @@ curses_util_yOffsetChange( XW_UtilCtxt* XP_UNUSED(uc),
#ifdef XWFEATURE_TURNCHANGENOTIFY #ifdef XWFEATURE_TURNCHANGENOTIFY
static void static void
curses_util_turnChanged( XW_UtilCtxt* XP_UNUSED(uc), XP_S16 newTurn ) curses_util_turnChanged( XW_UtilCtxt* XP_UNUSED(uc), XP_S16 XP_UNUSED_DBG(newTurn) )
{ {
XP_LOGF( "%s(turn=%d)", __func__, newTurn ); XP_LOGF( "%s(turn=%d)", __func__, newTurn );
} }
@ -1725,7 +1725,7 @@ cursesGotBuf( void* closure, const CommsAddrRec* addr,
static void static void
cursesGotForRow( void* closure, const CommsAddrRec* from, cursesGotForRow( void* closure, const CommsAddrRec* from,
sqlite3_int64 rowid, const XP_U8* buf, sqlite3_int64 XP_UNUSED_DBG(rowid), const XP_U8* buf,
XP_U16 len ) XP_U16 len )
{ {
LOG_FUNC(); LOG_FUNC();

View file

@ -31,8 +31,8 @@ static void getColumnText( sqlite3_stmt *ppStmt, int iCol, XP_UCHAR* buf,
int len ); int len );
#ifdef DEBUG #ifdef DEBUG
static char* sqliteErr2str( int err ); static char* sqliteErr2str( int err );
static void assertPrintResult( sqlite3* pDb, int result, int expect );
#endif #endif
static void assertPrintResult( sqlite3* pDb, int result, int expect );
sqlite3* sqlite3*
openGamesDB( const char* dbName ) openGamesDB( const char* dbName )
@ -590,17 +590,15 @@ sqliteErr2str( int err )
} }
return "<unknown>"; return "<unknown>";
} }
#endif
static void static void
assertPrintResult( sqlite3* pDb, int result, int expect ) assertPrintResult( sqlite3* pDb, int XP_UNUSED_DBG(result), int expect )
{ {
int code = sqlite3_errcode( pDb ); int code = sqlite3_errcode( pDb );
XP_ASSERT( code == result ); /* do I need to pass it? */ XP_ASSERT( code == result ); /* do I need to pass it? */
if ( code != expect ) { if ( code != expect ) {
const char* msg = sqlite3_errmsg( pDb ); XP_LOGF( "sqlite3 error: %s", sqlite3_errmsg( pDb ) );
XP_LOGF( "sqlite3 error: %s", msg );
XP_ASSERT(0); XP_ASSERT(0);
} }
} }
#endif

View file

@ -435,7 +435,7 @@ tryConnectToServer(CommonGlobals* cGlobals)
{ {
LaunchParams* params = cGlobals->params; LaunchParams* params = cGlobals->params;
XWStreamCtxt* stream = XWStreamCtxt* stream =
mem_stream_make( cGlobals->util->mpool, params->vtMgr, mem_stream_make( MPPARM(cGlobals->util->mpool) params->vtMgr,
cGlobals, CHANNEL_NONE, cGlobals, CHANNEL_NONE,
sendOnClose ); sendOnClose );
(void)server_initClientConnection( cGlobals->game.server, (void)server_initClientConnection( cGlobals->game.server,

View file

@ -1460,8 +1460,7 @@ gtkDrawCtxtMake( GtkWidget* drawing_area, GtkGameGlobals* globals )
dctx->drawing_area = drawing_area; dctx->drawing_area = drawing_area;
dctx->globals = globals; dctx->globals = globals;
GdkWindow* window = gtk_widget_get_window(drawing_area); XP_ASSERT( !!gtk_widget_get_window(drawing_area) );
XP_ASSERT( !!window );
#ifdef USE_CAIRO #ifdef USE_CAIRO
/* dctx->cairo = gdk_cairo_create( window ); */ /* dctx->cairo = gdk_cairo_create( window ); */
/* XP_LOGF( "dctx->cairo=%p", dctx->cairo ); */ /* XP_LOGF( "dctx->cairo=%p", dctx->cairo ); */
@ -1514,6 +1513,7 @@ removeSurface( GtkDrawCtx* dctx )
dctx->surface = NULL; dctx->surface = NULL;
} }
#ifdef DEBUG
static cairo_status_t static cairo_status_t
write_func( void *closure, const unsigned char *data, write_func( void *closure, const unsigned char *data,
unsigned int length ) unsigned int length )
@ -1522,16 +1522,19 @@ write_func( void *closure, const unsigned char *data,
stream_putBytes( stream, data, length ); stream_putBytes( stream, data, length );
return CAIRO_STATUS_SUCCESS; return CAIRO_STATUS_SUCCESS;
} }
#endif
void void
getImage( GtkDrawCtx* dctx, XWStreamCtxt* stream ) getImage( GtkDrawCtx* XP_UNUSED_DBG(dctx), XWStreamCtxt* XP_UNUSED_DBG(stream) )
{ {
LOG_FUNC(); LOG_FUNC();
XP_ASSERT( !!dctx->surface ); XP_ASSERT( !!dctx->surface );
#ifdef DEBUG
cairo_status_t status = cairo_status_t status =
cairo_surface_write_to_png_stream( dctx->surface, cairo_surface_write_to_png_stream( dctx->surface,
write_func, stream ); write_func, stream );
XP_ASSERT( CAIRO_STATUS_SUCCESS == status ); XP_ASSERT( CAIRO_STATUS_SUCCESS == status );
#endif
} }
void void

View file

@ -1242,7 +1242,7 @@ linux_reset( void* closure )
#endif #endif
XP_S16 XP_S16
linux_send( const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* msgNo, linux_send( const XP_U8* buf, XP_U16 buflen, const XP_UCHAR* XP_UNUSED_DBG(msgNo),
const CommsAddrRec* addrRec, CommsConnType conType, XP_U32 gameID, const CommsAddrRec* addrRec, CommsConnType conType, XP_U32 gameID,
void* closure ) void* closure )
{ {

View file

@ -480,8 +480,9 @@ relayThread( void* arg )
while ( !storage->relayTaskList ) { while ( !storage->relayTaskList ) {
pthread_cond_wait( &storage->relayCondVar, &storage->relayMutex ); pthread_cond_wait( &storage->relayCondVar, &storage->relayMutex );
} }
#ifdef DEBUG
int len = g_slist_length( storage->relayTaskList ); int len = g_slist_length( storage->relayTaskList );
#endif
gchar* strs = listTasks( storage->relayTaskList ); gchar* strs = listTasks( storage->relayTaskList );
GSList* head = storage->relayTaskList; GSList* head = storage->relayTaskList;
storage->relayTaskList = g_slist_remove_link( storage->relayTaskList, storage->relayTaskList = g_slist_remove_link( storage->relayTaskList,
@ -740,12 +741,16 @@ relaycon_cleanup( LaunchParams* params )
RelayConStorage* storage = (RelayConStorage*)params->relayConStorage; RelayConStorage* storage = (RelayConStorage*)params->relayConStorage;
if ( storage->params->useHTTP ) { if ( storage->params->useHTTP ) {
pthread_mutex_lock( &storage->relayMutex ); pthread_mutex_lock( &storage->relayMutex );
#ifdef DEBUG
int nRelayTasks = g_slist_length( storage->relayTaskList ); int nRelayTasks = g_slist_length( storage->relayTaskList );
#endif
gchar* taskStrs = listTasks( storage->relayTaskList ); gchar* taskStrs = listTasks( storage->relayTaskList );
pthread_mutex_unlock( &storage->relayMutex ); pthread_mutex_unlock( &storage->relayMutex );
pthread_mutex_lock( &storage->gotDataMutex ); pthread_mutex_lock( &storage->gotDataMutex );
#ifdef DEBUG
int nDataTasks = g_slist_length( storage->gotDataTaskList ); int nDataTasks = g_slist_length( storage->gotDataTaskList );
#endif
gchar* gotStrs = listTasks( storage->gotDataTaskList ); gchar* gotStrs = listTasks( storage->gotDataTaskList );
pthread_mutex_unlock( &storage->gotDataMutex ); pthread_mutex_unlock( &storage->gotDataMutex );
@ -905,17 +910,16 @@ onGotQueryData( RelayTask* task )
if ( !!reply ) { if ( !!reply ) {
CommsAddrRec addr = {0}; CommsAddrRec addr = {0};
addr_addType( &addr, COMMS_CONN_RELAY ); addr_addType( &addr, COMMS_CONN_RELAY );
#ifdef DEBUG
GList* ids = g_hash_table_get_keys( task->u.query.map ); GList* ids = g_hash_table_get_keys( task->u.query.map );
const char* xxx = ids->data; #endif
json_object* jMsgs; json_object* jMsgs;
if ( json_object_object_get_ex( reply, "msgs", &jMsgs ) ) { if ( json_object_object_get_ex( reply, "msgs", &jMsgs ) ) {
/* Currently there's an array of arrays for each relayID (value) */ /* Currently there's an array of arrays for each relayID (value) */
XP_LOGF( "%s: got result of len %d", __func__, json_object_object_length(jMsgs) ); XP_LOGF( "%s: got result of len %d", __func__, json_object_object_length(jMsgs) );
XP_ASSERT( json_object_object_length(jMsgs) <= 1 ); XP_ASSERT( json_object_object_length(jMsgs) <= 1 );
json_object_object_foreach(jMsgs, relayID, arrOfArrOfMoves) { json_object_object_foreach(jMsgs, relayID, arrOfArrOfMoves) {
XP_ASSERT( 0 == strcmp( relayID, xxx ) ); XP_ASSERT( 0 == strcmp( relayID, ids->data ) );
int len1 = json_object_array_length( arrOfArrOfMoves ); int len1 = json_object_array_length( arrOfArrOfMoves );
if ( len1 > 0 ) { if ( len1 > 0 ) {
sqlite3_int64 rowid = *(sqlite3_int64*)g_hash_table_lookup( task->u.query.map, relayID ); sqlite3_int64 rowid = *(sqlite3_int64*)g_hash_table_lookup( task->u.query.map, relayID );

View file

@ -220,7 +220,7 @@ CRefMgr::getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersT,
int langCode, int seed, int clientIndx, int langCode, int seed, int clientIndx,
bool wantsPublic, bool makePublic, bool* seenSeed ) bool wantsPublic, bool makePublic, bool* seenSeed )
{ {
CidInfo* cinfo; CidInfo* cinfo = NULL;
/* We have a cookie from a new connection or from a reconnect. This may /* We have a cookie from a new connection or from a reconnect. This may
be the first time it's been seen, or there may be a game currently in be the first time it's been seen, or there may be a game currently in
@ -229,8 +229,8 @@ CRefMgr::getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersT,
a new one. Pass the connName which will be used if set, but if not set a new one. Pass the connName which will be used if set, but if not set
we'll be generating another later when the game is complete. we'll be generating another later when the game is complete.
*/ */
for ( ; ; ) { for ( int ii = 0; ; ++ii ) {
/* What's this for loop thing. It's to fix a race condition. One /* What's this for loop thing? It's to fix a race condition. One
thread has "claim" on cid <N>, which is in the DB. Another comes thread has "claim" on cid <N>, which is in the DB. Another comes
into this function and looks it up in the DB, retrieving <N>, but into this function and looks it up in the DB, retrieving <N>, but
progress is blocked inside getCookieRef_impl which calls Claim(). progress is blocked inside getCookieRef_impl which calls Claim().
@ -238,6 +238,13 @@ CRefMgr::getMakeCookieRef( const char* cookie, int nPlayersH, int nPlayersT,
cref before calling Relinquish so that when Claim() returns there's cref before calling Relinquish so that when Claim() returns there's
no cref. So we test for that case and retry. */ no cref. So we test for that case and retry. */
/* I'm now seeing an infinte loop here. Until it's tracked down, let's
assert out. Note that I've seen it here, not at any of the other
places where I'm replacing FOREVER loops with this test*/
if ( ii > 5 ) {
assert(0);
break;
}
CookieID cid; CookieID cid;
char connNameBuf[MAX_CONNNAME_LEN+1] = {0}; char connNameBuf[MAX_CONNNAME_LEN+1] = {0};
@ -295,7 +302,13 @@ CRefMgr::getMakeCookieRef( const char* connName, const char* cookie,
CookieRef* cref = NULL; CookieRef* cref = NULL;
CidInfo* cinfo = NULL; CidInfo* cinfo = NULL;
for ( ; ; ) { /* for: see comment above */ for ( int ii = 0; ; ++ii ) { /* for: see comment above */
if ( ii > 5 ) {
assert(0);
break;
}
/* fetch these from DB */ /* fetch these from DB */
char curCookie[MAX_INVITE_LEN+1]; char curCookie[MAX_INVITE_LEN+1];
int curLangCode; int curLangCode;
@ -346,7 +359,12 @@ CRefMgr::getMakeCookieRef( const char* const connName, HostID hid, bool* isDead
int nPlayersT = 0; int nPlayersT = 0;
int nAlreadyHere = 0; int nAlreadyHere = 0;
for ( ; ; ) { /* for: see comment above */ for ( int ii = 0; ; ++ii ) { /* for: see comment above */
if ( ii > 5 ) {
assert(0);
break;
}
CookieID cid = m_db->FindGame( connName, hid, curCookie, sizeof(curCookie), CookieID cid = m_db->FindGame( connName, hid, curCookie, sizeof(curCookie),
&curLangCode, &nPlayersT, &nAlreadyHere, &curLangCode, &nPlayersT, &nAlreadyHere,
isDead ); isDead );
@ -385,7 +403,12 @@ CRefMgr::getMakeCookieRef( const AddrInfo::ClientToken clientToken, HostID srcID
int nPlayersT = 0; int nPlayersT = 0;
int nAlreadyHere = 0; int nAlreadyHere = 0;
for ( ; ; ) { /* for: see comment above */ for ( int ii = 0; ; ++ii ) { /* for: see comment above */
if ( ii > 5 ) {
assert(0);
break;
}
char connName[MAX_CONNNAME_LEN+1] = {0}; char connName[MAX_CONNNAME_LEN+1] = {0};
CookieID cid = m_db->FindGame( clientToken, srcID, CookieID cid = m_db->FindGame( clientToken, srcID,
connName, sizeof(connName), connName, sizeof(connName),

View file

@ -691,12 +691,14 @@ send_msg_via_udp( const AddrInfo* addr, AddrInfo::ClientToken clientToken,
uint32_t asNetTok = htonl(clientToken); uint32_t asNetTok = htonl(clientToken);
ssize_t nSent = send_via_udp( addr, packetIDP, XWPDEV_MSG, &asNetTok, ssize_t nSent = send_via_udp( addr, packetIDP, XWPDEV_MSG, &asNetTok,
sizeof(asNetTok), buf, bufLen, NULL ); sizeof(asNetTok), buf, bufLen, NULL );
logf( XW_LOGINFO, "%s: sent %d bytes (plus header) on UDP socket, "
"token=%x(%d)", __func__, bufLen, clientToken,
clientToken );
result = 0 < nSent; result = 0 < nSent;
logf( XW_LOGINFO, "%s()=>%d", __func__, result ); if (result) {
logf( XW_LOGINFO, "%s: sent %d bytes (plus header) on UDP socket, "
"token=%x(%d)", __func__, bufLen, clientToken,
clientToken );
}
} }
// logf( XW_LOGINFO, "%s()=>%d", __func__, result );
return result; return result;
} }
@ -1802,8 +1804,8 @@ handle_udp_packet( PacketThreadClosure* ptc )
handlePutMessage( scr, hid, &addr, end - ptr, &ptr, end ); handlePutMessage( scr, hid, &addr, end - ptr, &ptr, end );
assert( ptr == end ); // DON'T CHECK THIS IN!!! assert( ptr == end ); // DON'T CHECK THIS IN!!!
} else { } else {
logf( XW_LOGERROR, "%s: invalid scr for %s", __func__, logf( XW_LOGERROR, "%s: invalid scr for %s/%d", __func__,
connName ); connName, hid );
} }
} else { } else {
logf( XW_LOGERROR, "no clientToken found!!!" ); logf( XW_LOGERROR, "no clientToken found!!!" );