add support for trays with up to 9 tiles

This commit is contained in:
Eric House 2021-07-16 11:50:05 -07:00
parent 8f83a390f1
commit 694953c820
39 changed files with 483 additions and 205 deletions

View file

@ -847,7 +847,7 @@ public class BoardDelegate extends DelegateBase
case R.id.board_menu_done:
int nTiles = XwJNI.model_getNumTilesInTray( m_jniGamePtr,
m_view.getCurPlayer() );
if ( XWApp.MAX_TRAY_TILES > nTiles ) {
if ( m_gi.traySize > nTiles ) {
makeNotAgainBuilder( R.string.not_again_done,
R.string.key_notagain_done,
Action.COMMIT_ACTION )

View file

@ -51,14 +51,12 @@ public class EnableSMSAlert extends DlgDelegateAlert {
View layout = LocUtils.inflate( context, R.layout.confirm_sms );
mSpinner = (Spinner)layout.findViewById( R.id.confirm_sms_reasons );
OnItemSelectedListener onItemSel = new OnItemSelectedListener() {
OnItemSelectedListener onItemSel = new Utils.OnNothingSelDoesNothing() {
@Override
public void onItemSelected( AdapterView<?> parent, View view,
int position, long id ) {
checkEnableButton( (AlertDialog)getDialog() );
}
@Override
public void onNothingSelected( AdapterView<?> parent ) {}
};
mSpinner.setOnItemSelectedListener( onItemSel );

View file

@ -100,6 +100,7 @@ public class GameConfigDelegate extends DelegateBase
// private Spinner m_connectSpinner;
private Spinner m_phoniesSpinner;
private Spinner m_boardsizeSpinner;
private Spinner m_traysizeSpinner;
private Spinner m_langSpinner;
private Spinner m_smartnessSpinner;
private TextView m_connLabel;
@ -126,6 +127,7 @@ public class GameConfigDelegate extends DelegateBase
R.id.duplicate_check,
R.id.pick_faceup,
R.id.boardsize_spinner,
R.id.traysize_spinner,
R.id.use_timer,
R.id.timer_minutes_edit,
R.id.smart_robot,
@ -524,6 +526,8 @@ public class GameConfigDelegate extends DelegateBase
.getSpinner();
m_boardsizeSpinner = ((LabeledSpinner)findViewById( R.id.boardsize_spinner ))
.getSpinner();
m_traysizeSpinner = ((LabeledSpinner)findViewById( R.id.traysize_spinner ))
.getSpinner();
m_smartnessSpinner = ((LabeledSpinner)findViewById( R.id.smart_robot ))
.getSpinner();
@ -680,6 +684,31 @@ public class GameConfigDelegate extends DelegateBase
setChecked( R.id.pick_faceup, m_gi.allowPickTiles );
setBoardsizeSpinner();
final int[] curSel = {-1};
String val = String.format( "%d", m_gi.traySize );
SpinnerAdapter adapter = m_traysizeSpinner.getAdapter();
for ( int ii = 0; ii < adapter.getCount(); ++ii ) {
if ( val.equals( adapter.getItem(ii) ) ) {
m_traysizeSpinner.setSelection( ii );
curSel[0] = ii;
break;
}
}
m_traysizeSpinner
.setOnItemSelectedListener( new Utils.OnNothingSelDoesNothing() {
@Override
public void onItemSelected( AdapterView<?> parent, View spinner,
int position, long id ) {
if ( curSel[0] != position ) {
curSel[0] = position;
makeNotAgainBuilder( R.string.not_again_traysize,
R.string.key_na_traysize )
.show();
}
}
} );
}
} // loadGame
@ -1014,8 +1043,7 @@ public class GameConfigDelegate extends DelegateBase
dictsSpinner.setPrompt( getString( R.string.dicts_list_prompt_fmt,
langName ) );
OnItemSelectedListener onSel =
new OnItemSelectedListener() {
OnItemSelectedListener onSel = new Utils.OnNothingSelDoesNothing() {
@Override
public void onItemSelected( AdapterView<?> parentView,
View selectedItemView,
@ -1029,9 +1057,6 @@ public class GameConfigDelegate extends DelegateBase
m_gi.dictLang );
}
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {}
};
ArrayAdapter<String> adapter =
@ -1048,8 +1073,7 @@ public class GameConfigDelegate extends DelegateBase
final LangsArrayAdapter adapter = DictLangCache.getLangsAdapter( m_activity );
OnItemSelectedListener onSel =
new OnItemSelectedListener() {
OnItemSelectedListener onSel = new Utils.OnNothingSelDoesNothing() {
@Override
public void onItemSelected(AdapterView<?> parentView,
View selectedItemView,
@ -1067,9 +1091,6 @@ public class GameConfigDelegate extends DelegateBase
}
}
}
@Override
public void onNothingSelected(AdapterView<?> parentView) {}
};
String lang = DictLangCache.getLangName( m_activity, m_gi.dictLang );
@ -1306,6 +1327,7 @@ public class GameConfigDelegate extends DelegateBase
position = m_boardsizeSpinner.getSelectedItemPosition();
m_gi.boardSize = positionToSize( position );
m_gi.traySize = Integer.parseInt( m_traysizeSpinner.getSelectedItem().toString() );
if ( m_conTypes.contains( CommsConnType.COMMS_CONN_RELAY ) ) {
m_car.ip_relay_seeksPublicRoom = m_joinPublicCheck.isChecked();

View file

@ -360,7 +360,8 @@ abstract class InviteDelegate extends DelegateBase
}
spinner.setAdapter( adapter );
spinner.setVisibility( View.VISIBLE );
spinner.setOnItemSelectedListener( new OnItemSelectedListener() {
spinner.setOnItemSelectedListener( new Utils.OnNothingSelDoesNothing() {
@Override
public void onItemSelected( AdapterView<?> parent,
View view, int pos,
long id )
@ -368,8 +369,6 @@ abstract class InviteDelegate extends DelegateBase
m_counts.put( item, 1 + pos );
tryEnable();
}
public void onNothingSelected( AdapterView<?> parent ) {}
} );
}

View file

@ -226,6 +226,7 @@ public class StudyListDelegate extends ListDelegateBase
//////////////////////////////////////////////////
// AdapterView.OnItemSelectedListener interface
//////////////////////////////////////////////////
@Override
public void onItemSelected( AdapterView<?> parent, View view,
int position, long id )
{
@ -234,6 +235,7 @@ public class StudyListDelegate extends ListDelegateBase
loadList(); // because language has changed
}
@Override
public void onNothingSelected( AdapterView<?> parent )
{
}

View file

@ -49,6 +49,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
@ -820,4 +821,10 @@ public class Utils {
}
}
}
static abstract class OnNothingSelDoesNothing
implements AdapterView.OnItemSelectedListener {
@Override
public void onNothingSelected(AdapterView<?> parentView) {}
}
}

View file

@ -49,7 +49,7 @@ public class XWApp extends Application
public static final boolean OFFER_DUALPANE = false;
public static final String SMS_PUBLIC_HEADER = "-XW4";
public static final int MAX_TRAY_TILES = 7; // comtypes.h
public static final int MIN_TRAY_TILES = 7; // comtypes.h
public static final int SEL_COLOR = Color.argb( 0xFF, 0x09, 0x70, 0x93 );
public static final int GREEN = 0xFF00AF00;

View file

@ -543,6 +543,11 @@ public class XWPrefs {
return result;
}
public static int getDefaultTraySize( Context context )
{
return getPrefsInt( context, R.string.key_tray_size, XWApp.MIN_TRAY_TILES );
}
public static void setAddrTypes( Context context, CommsConnTypeSet set )
{
int flags = set.toInt();

View file

@ -27,7 +27,7 @@ public class BoardDims {
public int width, height; // of the bitmap
public int scoreLeft, scoreWidth, scoreHt;
public int boardWidth, boardHt;
public int trayLeft, trayTop, trayWidth, trayHt;
public int trayLeft, trayTop, trayWidth, trayHt, traySize;
public int cellSize, maxCellSize;
public int timerWidth;

View file

@ -37,6 +37,8 @@ import org.eehouse.android.xw4.DictUtils;
import org.eehouse.android.xw4.Log;
import org.eehouse.android.xw4.R;
import org.eehouse.android.xw4.Utils;
import org.eehouse.android.xw4.XWApp;
import org.eehouse.android.xw4.XWPrefs;
import org.eehouse.android.xw4.loc.LocUtils;
public class CurGameInfo implements Serializable {
@ -45,6 +47,8 @@ public class CurGameInfo implements Serializable {
public static final int MAX_NUM_PLAYERS = 4;
private static final String BOARD_SIZE = "BOARD_SIZE";
private static final String TRAY_SIZE = "TRAY_SIZE";
private static final String BINGO_MIN = "BINGO_MIN";
private static final String NO_HINTS = "NO_HINTS";
private static final String TIMER = "TIMER";
private static final String ALLOW_PICK = "ALLOW_PICK";
@ -61,6 +65,8 @@ public class CurGameInfo implements Serializable {
public int gameSeconds;
public int nPlayers;
public int boardSize;
public int traySize;
public int bingoMin;
public int forceChannel;
public DeviceRole serverRole;
@ -89,6 +95,8 @@ public class CurGameInfo implements Serializable {
gameSeconds = inDuplicateMode ? (5 * 60)
: 60 * nPlayers * CommonPrefs.getDefaultPlayerMinutes( context );
boardSize = CommonPrefs.getDefaultBoardSize( context );
traySize = XWPrefs.getDefaultTraySize( context );
bingoMin = XWApp.MIN_TRAY_TILES;
players = new LocalPlayer[MAX_NUM_PLAYERS];
serverRole = isNetworked ? DeviceRole.SERVER_ISCLIENT
: DeviceRole.SERVER_STANDALONE;
@ -140,6 +148,8 @@ public class CurGameInfo implements Serializable {
nPlayers = src.nPlayers;
gameSeconds = src.gameSeconds;
boardSize = src.boardSize;
traySize = src.traySize;
bingoMin = src.bingoMin;
players = new LocalPlayer[MAX_NUM_PLAYERS];
serverRole = src.serverRole;
dictName = src.dictName;
@ -190,6 +200,8 @@ public class CurGameInfo implements Serializable {
try {
JSONObject obj = new JSONObject()
.put( BOARD_SIZE, boardSize )
.put( TRAY_SIZE, traySize )
.put( BINGO_MIN, bingoMin )
.put( NO_HINTS, hintsNotAllowed )
.put( DUP, inDuplicateMode )
.put( TIMER, timerEnabled )
@ -210,6 +222,8 @@ public class CurGameInfo implements Serializable {
try {
JSONObject obj = new JSONObject( jsonData );
boardSize = obj.optInt( BOARD_SIZE, boardSize );
traySize = obj.optInt( TRAY_SIZE, traySize );
bingoMin = obj.optInt( BINGO_MIN, bingoMin );
hintsNotAllowed = obj.optBoolean( NO_HINTS, hintsNotAllowed );
inDuplicateMode = obj.optBoolean( DUP, inDuplicateMode );
timerEnabled = obj.optBoolean( TIMER, timerEnabled );
@ -287,6 +301,8 @@ public class CurGameInfo implements Serializable {
|| serverRole != other.serverRole
|| dictLang != other.dictLang
|| boardSize != other.boardSize
|| traySize != other.traySize
|| bingoMin != other.bingoMin
|| hintsNotAllowed != other.hintsNotAllowed
|| inDuplicateMode != other.inDuplicateMode
|| allowPickTiles != other.allowPickTiles
@ -320,6 +336,8 @@ public class CurGameInfo implements Serializable {
&& gameSeconds == other.gameSeconds
&& nPlayers == other.nPlayers
&& boardSize == other.boardSize
&& traySize == other.traySize
&& bingoMin == other.bingoMin
&& forceChannel == other.forceChannel
&& hintsNotAllowed == other.hintsNotAllowed
&& inDuplicateMode == other.inDuplicateMode

View file

@ -128,6 +128,7 @@ public class DUtilCtxt {
private static final int STRD_DUP_TRADED = 28;
private static final int STRSD_DUP_ONESCORE = 29;
private static final int STR_PENDING_PLAYER = 30;
private static final int STR_BONUS_ALL_SUB = 31;
public String getUserString( final int stringCode )
{
@ -184,6 +185,9 @@ public class DUtilCtxt {
case STR_BONUS_ALL:
id = R.string.str_bonus_all;
break;
case STR_BONUS_ALL_SUB:
id = R.string.str_bonus_all_fmt;
break;
case STRD_TURN_SCORE:
id = R.string.strd_turn_score_fmt;
break;

View file

@ -117,6 +117,7 @@ public class LocDelegate extends ListDelegateBase
//////////////////////////////////////////////////
// AdapterView.OnItemSelectedListener interface
//////////////////////////////////////////////////
@Override
public void onItemSelected( AdapterView<?> parent, View view,
int position, long id )
{
@ -124,7 +125,6 @@ public class LocDelegate extends ListDelegateBase
makeNewAdapter();
}
public void onNothingSelected( AdapterView<?> parent )
{
}
@Override
public void onNothingSelected( AdapterView<?> parent ) {}
}

View file

@ -263,11 +263,18 @@
/>
</org.eehouse.android.xw4.LabeledSpinner>
<CheckBox android:id="@+id/pick_faceup"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pick_faceup"
/>
<org.eehouse.android.xw4.LabeledSpinner
style="@style/config_spinner_container"
android:id="@+id/traysize_spinner"
>
<TextView style="@style/config_spinner_label"
android:text="@string/tray_size"
/>
<Spinner style="@style/config_spinner_spinner"
android:prompt="@string/tray_size"
android:entries="@array/tray_sizes"
/>
</org.eehouse.android.xw4.LabeledSpinner>
<org.eehouse.android.xw4.LabeledSpinner
style="@style/config_spinner_container"
@ -282,6 +289,12 @@
/>
</org.eehouse.android.xw4.LabeledSpinner>
<CheckBox android:id="@+id/pick_faceup"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/pick_faceup"
/>
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"

View file

@ -58,6 +58,7 @@
<string name="key_init_nethintsallowed">key_init_nethintsallowed</string>
<string name="key_init_autojuggle">key_init_autojuggle</string>
<string name="key_board_size">key_board_size</string>
<string name="key_tray_size">key_tray_size</string>
<string name="key_initial_player_minutes">key_initial_player_minutes</string>
<string name="key_default_language">key_default_language</string>
<string name="key_default_dict">key_default_dict</string>
@ -157,6 +158,7 @@
<string name="key_na_perms_phonestate">key_na_perms_phonestate</string>
<string name="key_na_perms_storage_dicts">key_na_perms_storage_dicts</string>
<string name="key_na_newFeatureFilter">key_na_newFeatureFilter</string>
<string name="key_na_traysize">key_na_traysize</string>
<string name="key_na_dupstatus_host">key_na_dupstatus_host</string>
<string name="key_na_dupstatus_guest">key_na_dupstatus_guest</string>
@ -186,6 +188,12 @@
<item>11x11</item>
</string-array>
<string-array name="tray_sizes">
<item>7</item>
<item>8</item>
<item>9</item>
</string-array>
<string-array name="phony_names">
<item>@string/phonies_ignore</item>
<item>@string/phonies_warn</item>

View file

@ -702,6 +702,8 @@
<string name="vs_join">\u0020vs.\u0020</string>
<!-- Used in formatting moves and history -->
<string name="str_bonus_all">Bonus for using all tiles: 50\n</string>
<!-- Used instead of above when fewer than a full tray is required for bingo -->
<string name="str_bonus_all_fmt">Bonus for using %1$d tiles: 50\n</string>
<!-- Used in formatting moves and history. The total score for
one turn is substituted for %1$d.-->
<string name="strd_turn_score_fmt">Score for turn: %1$d\n</string>
@ -811,6 +813,7 @@
<string name="initial_player_minutes">Timer minutes per player</string>
<!-- preference for board size (15x15, 13x13 etc.) -->
<string name="board_size">Board size</string>
<string name="tray_size">Tiles in rack</string>
<!--
############################################################
# :Screens:
@ -1657,6 +1660,13 @@
by tapping the Expander Arrow at the upper-right corner of the
window.\n\nRead more in the FAQ by tapping the button
below.</string>
<!-- Shown when user changes the new traysize config -->
<string name="not_again_traysize">This new setting changes the
number of tiles in the rack.\n\nNote: if anyone in your networked
game is using an older version of CrossWords this setting will
revert to 7 for everyone in the game.</string>
<string name="board_menu_file_email">Email author…</string>
<!-- -->
<string name="email_author_subject">Comment about CrossWords</string>

View file

@ -65,6 +65,13 @@
android:numeric="decimal"
/>
<org.eehouse.android.xw4.XWListPreference
android:key="@string/key_tray_size"
android:title="@string/tray_size"
android:entries="@array/tray_sizes"
android:entryValues="@array/tray_sizes"
android:defaultValue="7"
/>
<org.eehouse.android.xw4.XWListPreference
android:key="@string/key_board_size"
android:title="@string/board_size"

View file

@ -34,5 +34,6 @@
# define STRD_DUP_TRADED 28
# define STRSD_DUP_ONESCORE 29
# define STR_PENDING_PLAYER 30
# define N_AND_USER_STRINGS 30
# define STR_BONUS_ALL_SUB 31
# define N_AND_USER_STRINGS 31
#endif

View file

@ -421,6 +421,8 @@ static const SetInfo gi_ints[] = {
ARR_MEMBER( CurGameInfo, nPlayers )
,ARR_MEMBER( CurGameInfo, gameSeconds )
,ARR_MEMBER( CurGameInfo, boardSize )
,ARR_MEMBER( CurGameInfo, traySize )
,ARR_MEMBER( CurGameInfo, bingoMin )
,ARR_MEMBER( CurGameInfo, gameID )
,ARR_MEMBER( CurGameInfo, dictLang )
,ARR_MEMBER( CurGameInfo, forceChannel )
@ -541,23 +543,23 @@ setJGI( JNIEnv* env, jobject jgi, const CurGameInfo* gi )
} /* setJGI */
#ifdef COMMON_LAYOUT
static const SetInfo bd_ints[] = {
ARR_MEMBER( BoardDims, left )
,ARR_MEMBER( BoardDims, top )
,ARR_MEMBER( BoardDims, width )
,ARR_MEMBER( BoardDims, height )
,ARR_MEMBER( BoardDims, scoreLeft )
,ARR_MEMBER( BoardDims, scoreHt )
,ARR_MEMBER( BoardDims, scoreWidth )
,ARR_MEMBER( BoardDims, boardWidth )
,ARR_MEMBER( BoardDims, boardHt )
,ARR_MEMBER( BoardDims, trayLeft )
,ARR_MEMBER( BoardDims, trayTop )
,ARR_MEMBER( BoardDims, trayWidth )
,ARR_MEMBER( BoardDims, trayHt )
,ARR_MEMBER( BoardDims, cellSize )
,ARR_MEMBER( BoardDims, maxCellSize )
,ARR_MEMBER( BoardDims, timerWidth )
static const SetInfo bd_ints[] = { ARR_MEMBER( BoardDims, left ),
ARR_MEMBER( BoardDims, top ),
ARR_MEMBER( BoardDims, width ),
ARR_MEMBER( BoardDims, height ),
ARR_MEMBER( BoardDims, scoreLeft ),
ARR_MEMBER( BoardDims, scoreHt ),
ARR_MEMBER( BoardDims, scoreWidth ),
ARR_MEMBER( BoardDims, boardWidth ),
ARR_MEMBER( BoardDims, boardHt ),
ARR_MEMBER( BoardDims, trayLeft ),
ARR_MEMBER( BoardDims, trayTop ),
ARR_MEMBER( BoardDims, trayWidth ),
ARR_MEMBER( BoardDims, trayHt ),
ARR_MEMBER( BoardDims, traySize ),
ARR_MEMBER( BoardDims, cellSize ),
ARR_MEMBER( BoardDims, maxCellSize ),
ARR_MEMBER( BoardDims, timerWidth ),
};
static void

View file

@ -258,10 +258,10 @@ board_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, ModelCtxt* model
arrow->visible = (XP_Bool)stream_getBits( stream, 1 );
if ( STREAM_VERS_MODELDIVIDER > version ) {
(void)stream_getBits( stream, NTILES_NBITS );
(void)stream_getBits( stream, NTILES_NBITS_7 );
}
pti->traySelBits = (TileBit)stream_getBits( stream,
MAX_TRAY_TILES );
XP_U16 nBits = STREAM_VERS_NINETILES <= version ? MAX_TRAY_TILES : 7;
pti->traySelBits = (TileBit)stream_getBits( stream, nBits );
pti->tradeInProgress = (XP_Bool)stream_getBits( stream, 1 );
if ( version >= STREAM_VERS_KEYNAV ) {
@ -287,7 +287,6 @@ board_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream, ModelCtxt* model
pti->limits.bottom = stream_getBits( stream, 4 );
}
#endif
}
board->selPlayer = (XP_U8)stream_getBits( stream, PLAYERNUM_NBITS );
@ -351,6 +350,7 @@ board_writeToStream( const BoardCtxt* board, XWStreamCtxt* stream )
stream_putBits( stream, 1, arrow->vert );
stream_putBits( stream, 1, arrow->visible );
XP_ASSERT( CUR_STREAM_VERS == stream_getVersion(stream) );
stream_putBits( stream, MAX_TRAY_TILES, pti->traySelBits );
stream_putBits( stream, 1, pti->tradeInProgress );
@ -464,8 +464,7 @@ board_figureLayout( BoardCtxt* board, XWEnv xwe, const CurGameInfo* gi,
XP_U16 scoreWidth, XP_U16 fontWidth, XP_U16 fontHt,
XP_Bool squareTiles, BoardDims* dimsp )
{
BoardDims ldims;
XP_MEMSET( &ldims, 0, sizeof(ldims) );
BoardDims ldims = {0};
XP_U16 nCells = gi->boardSize;
XP_U16 maxCellSize = 8 * fontHt;
@ -569,6 +568,7 @@ board_figureLayout( BoardCtxt* board, XWEnv xwe, const CurGameInfo* gi,
ldims.boardHt = cellSize * nCells;
ldims.trayTop = ldims.top + scoreHt + (cellSize * (nCells-nToScroll));
ldims.traySize = gi->traySize;
ldims.height =
#ifdef FORCE_SQUARE
ldims.width
@ -629,7 +629,7 @@ board_applyLayout( BoardCtxt* board, XWEnv xwe, const BoardDims* dims )
dims->top, dims->timerWidth, dims->scoreHt );
board_setTrayLoc( board, xwe, dims->trayLeft, dims->trayTop,
dims->trayWidth, dims->trayHt );
dims->trayWidth, dims->trayHt, dims->traySize );
}
#endif
@ -1692,7 +1692,7 @@ onBorderCanScroll( const BoardCtxt* board, SDIndex indx,
void
board_setTrayLoc( BoardCtxt* board, XWEnv xwe, XP_U16 trayLeft, XP_U16 trayTop,
XP_U16 trayWidth, XP_U16 trayHeight )
XP_U16 trayWidth, XP_U16 trayHeight, XP_U16 nTiles )
{
/* XP_LOGF( "%s(%d,%d,%d,%d)", __func__, trayLeft, trayTop, */
/* trayWidth, trayHeight ); */
@ -1709,7 +1709,7 @@ board_setTrayLoc( BoardCtxt* board, XWEnv xwe, XP_U16 trayLeft, XP_U16 trayTop,
dividerWidth = dividerWidth +
((trayWidth - dividerWidth) % MAX_TRAY_TILES);
board->trayScaleH = (trayWidth - dividerWidth) / MAX_TRAY_TILES;
board->trayScaleH = (trayWidth - dividerWidth) / nTiles;
board->trayScaleV = trayHeight;
board->dividerWidth = dividerWidth;

View file

@ -91,6 +91,7 @@ typedef struct _BoardDims {
/* tray */
XP_U16 trayLeft, trayTop, trayWidth, trayHt;
XP_U16 traySize;
/* other */
XP_U16 cellSize, maxCellSize;
@ -116,7 +117,7 @@ void board_setScoreboardLoc( BoardCtxt* board,
XP_Bool divideHorizontally );
void board_setTrayLoc( BoardCtxt* board, XWEnv xwe,
XP_U16 trayLeft, XP_U16 trayTop,
XP_U16 trayWidth, XP_U16 trayHeight );
XP_U16 trayWidth, XP_U16 trayHeight, XP_U16 nTiles );
/* Vertical scroll support; offset is in rows, not pixels */
XP_Bool board_setYOffset( BoardCtxt* board, XWEnv xwe, XP_U16 newOffset );

View file

@ -47,6 +47,7 @@
#endif
#define MAX_COLS MAX_ROWS
#define STREAM_VERS_NINETILES 0x1E
#define STREAM_VERS_NOEMPTYDICT 0x1D
#define STREAM_VERS_GICREATED 0x1C /* game struct gets created timestamp */
#define STREAM_VERS_DUPLICATE 0x1B
@ -91,7 +92,7 @@
#define STREAM_VERS_405 0x01
/* search for FIX_NEXT_VERSION_CHANGE next time this is changed */
#define CUR_STREAM_VERS STREAM_VERS_NOEMPTYDICT
#define CUR_STREAM_VERS STREAM_VERS_NINETILES
typedef struct XP_Rect {
XP_S16 left;
@ -189,15 +190,12 @@ typedef enum {
} XWTimerReason;
#define MAX_NUM_PLAYERS 4
#ifdef EIGHT_TILES
# define MAX_TRAY_TILES 8
#else
# define MAX_TRAY_TILES 7
#endif
#define MIN_TRAY_TILES 7
#define MAX_TRAY_TILES 9
#define PLAYERNUM_NBITS 2
#define NDEVICES_NBITS 2 /* 1-4, but reduced by 1 fits in 2 bits */
#define NPLAYERS_NBITS 3
#define EMPTIED_TRAY_BONUS 50
#define BINGO_BONUS 50
#if MAX_ROWS <= 16
typedef XP_U16 RowFlags;
@ -293,7 +291,7 @@ typedef struct _MoveInfoTile {
Tile tile; /* 6 bits will do */
} MoveInfoTile;
typedef struct MoveInfo {
typedef struct _MoveInfo {
XP_U8 nTiles; /* 4 bits: 0-7 */
XP_U8 commonCoord; /* 5 bits: 0-16 if 17x17 possible */
XP_Bool isHorizontal; /* 1 bit */

View file

@ -576,8 +576,7 @@ static void
findMovesOneRow( EngineCtxt* engine, XWEnv xwe )
{
XP_U16 lastCol = engine->numCols - 1;
XP_U16 col, row = engine->curRow;
XP_S16 prevAnchor;
XP_U16 row = engine->curRow;
XP_U16 firstSearchCol, lastSearchCol;
#ifdef XWFEATURE_SEARCHLIMIT
const BdHintLimits* searchLimits = engine->searchLimits;
@ -600,7 +599,7 @@ findMovesOneRow( EngineCtxt* engine, XWEnv xwe )
}
XP_MEMSET( &engine->rowChecks, 0, sizeof(engine->rowChecks) ); /* clear */
for ( col = 0; col <= lastCol; ++col ) {
for ( XP_U16 col = 0; col <= lastCol; ++col ) {
if ( col < firstSearchCol || col > lastSearchCol ) {
engine->scoreCache[col] = 0;
} else {
@ -610,8 +609,8 @@ findMovesOneRow( EngineCtxt* engine, XWEnv xwe )
}
}
prevAnchor = firstSearchCol - 1;
for ( col = firstSearchCol; col <= lastSearchCol && !engine->returnNOW;
XP_S16 prevAnchor = firstSearchCol - 1;
for ( XP_U16 col = firstSearchCol; col <= lastSearchCol && !engine->returnNOW;
++col ) {
if ( isAnchorSquare( engine, col, row ) ) {
findMovesForAnchor( engine, xwe, &prevAnchor, col, row );

View file

@ -649,6 +649,8 @@ gi_copy( MPFORMAL CurGameInfo* destGI, const CurGameInfo* srcGI )
destGI->nPlayers = (XP_U8)srcGI->nPlayers;
nPlayers = srcGI->nPlayers;
destGI->boardSize = (XP_U8)srcGI->boardSize;
destGI->traySize = srcGI->traySize;
destGI->bingoMin = srcGI->bingoMin;
destGI->serverRole = srcGI->serverRole;
destGI->hintsNotAllowed = srcGI->hintsNotAllowed;
@ -748,6 +750,12 @@ gi_readFromStream( MPFORMAL XWStreamCtxt* stream, CurGameInfo* gi )
gi->nPlayers = (XP_U8)stream_getBits( stream, NPLAYERS_NBITS );
gi->boardSize = (XP_U8)stream_getBits( stream, nColsNBits );
if ( STREAM_VERS_NINETILES <= strVersion ) {
gi->traySize = (XP_U8)stream_getBits( stream, NTILES_NBITS_9 );
gi->bingoMin = (XP_U8)stream_getBits( stream, NTILES_NBITS_9 );
} else {
gi->traySize = gi->bingoMin = 7;
}
gi->serverRole = (DeviceRole)stream_getBits( stream, 2 );
/* XP_LOGF( "%s: read serverRole of %d", __func__, gi->serverRole ); */
gi->hintsNotAllowed = stream_getBits( stream, 1 );
@ -830,6 +838,14 @@ gi_writeToStream( XWStreamCtxt* stream, const CurGameInfo* gi )
stream_putBits( stream, NPLAYERS_NBITS, gi->nPlayers );
stream_putBits( stream, nColsNBits, gi->boardSize );
if ( STREAM_VERS_NINETILES <= strVersion ) {
XP_ASSERT( 0 < gi->traySize );
stream_putBits( stream, NTILES_NBITS_9, gi->traySize );
stream_putBits( stream, NTILES_NBITS_9, gi->bingoMin );
} else {
XP_LOGFF( "strVersion: %d so not writing traySize", strVersion );
}
stream_putBits( stream, 2, gi->serverRole );
stream_putBits( stream, 1, gi->hintsNotAllowed );
stream_putBits( stream, 2, gi->phoniesAction );

View file

@ -51,6 +51,8 @@ typedef struct CurGameInfo {
XP_LangCode dictLang;
XP_U8 nPlayers;
XP_U8 boardSize;
XP_U8 traySize;
XP_U8 bingoMin;
XP_U8 forceChannel;
DeviceRole serverRole;

View file

@ -123,7 +123,6 @@ model_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
{
ModelCtxt* model;
XP_U16 nCols;
XP_U16 nPlayers;
XP_U16 version = stream_getVersion( stream );
XP_ASSERT( !!dict || !!dicts );
@ -139,7 +138,7 @@ model_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
}
XP_ASSERT( MAX_COLS >= nCols );
nPlayers = (XP_U16)stream_getBits( stream, NPLAYERS_NBITS );
XP_U16 nPlayers = (XP_U16)stream_getBits( stream, NPLAYERS_NBITS );
model = model_make( MPPARM(mpool) xwe, dict, dicts, util, nCols );
model->nPlayers = nPlayers;
@ -161,7 +160,6 @@ model_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
stack_loadFromStream( model->vol.stack, stream );
MovePrintFuncPre pre = NULL;
MovePrintFuncPost post = NULL;
void* closure = NULL;
#ifdef DEBUG
pre = assertDiffTurn;
@ -171,7 +169,7 @@ model_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
buildModelFromStack( model, xwe, model->vol.stack, XP_FALSE, 0,
(XWStreamCtxt*)NULL, (WordNotifierInfo*)NULL,
pre, post, closure );
pre, (MovePrintFuncPost)NULL, closure );
for ( int ii = 0; ii < model->nPlayers; ++ii ) {
loadPlayerCtxt( model, stream, version, &model->players[ii] );
@ -284,6 +282,12 @@ model_setSize( ModelCtxt* model, XP_U16 nCols )
}
} /* model_setSize */
void
model_forceStack7Tiles( ModelCtxt* model )
{
stack_set7Tiles( model->vol.stack );
}
void
model_destroy( ModelCtxt* model, XWEnv xwe )
{
@ -1139,7 +1143,7 @@ model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
XP_ASSERT( turn >= 0 );
XP_S16 numTiles = model->players[turn].nPending;
stream_putBits( stream, NTILES_NBITS, numTiles );
stream_putBits( stream, tilesNBits(stream), numTiles );
while ( numTiles-- ) {
Tile tile;
@ -1177,8 +1181,8 @@ model_makeTurnFromStream( ModelCtxt* model, XWEnv xwe, XP_U16 playerNum,
model_resetCurrentTurn( model, xwe, playerNum );
XP_U16 numTiles = (XP_U16)stream_getBits( stream, NTILES_NBITS );
XP_LOGF( "%s: numTiles=%d", __func__, numTiles );
XP_U16 numTiles = (XP_U16)stream_getBits( stream, tilesNBits(stream) );
XP_LOGFF( "numTiles=%d", numTiles );
Tile tileFaces[numTiles];
XP_U16 cols[numTiles];
@ -2141,17 +2145,17 @@ model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn )
XP_ASSERT( turn >= 0 );
player = &model->players[turn];
XP_U16 result = player->trayTiles.nTiles;
// XP_LOGF( "%s(turn=%d) => %d", __func__, turn, result );
// XP_LOGFF( "(turn=%d) => %d", turn, result );
return result;
} /* model_getNumTilesInTray */
XP_U16
model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn )
{
PlayerCtxt* player;
XP_ASSERT( turn >= 0 );
player = &model->players[turn];
return player->trayTiles.nTiles + player->nPending;
PlayerCtxt* player = &model->players[turn];
XP_U16 result = player->trayTiles.nTiles + player->nPending;
return result;
} /* model_getNumTilesTotal */
XP_U16
@ -2423,8 +2427,10 @@ copyStack( const ModelCtxt* model, XWEnv xwe, StackCtxt* destStack,
mem_stream_make_raw( MPPARM(model->vol.mpool)
dutil_getVTManager(model->vol.dutil) );
stack_writeToStream( (StackCtxt*)srcStack, stream );
stream_setVersion( stream, stack_getVersion(srcStack) );
stack_writeToStream( srcStack, stream );
stack_loadFromStream( destStack, stream );
XP_ASSERT( stack_getVersion(destStack) == stack_getVersion( srcStack ) );
stream_destroy( stream, xwe );
} /* copyStack */
@ -2758,8 +2764,6 @@ static void
loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream, XP_U16 version,
PlayerCtxt* pc )
{
PendingTile* pt;
XP_U16 nTiles;
XP_U16 nColsNBits;
#ifdef STREAM_VERS_BIGBOARD
nColsNBits = 16 <= model_numCols( model ) ? NUMCOLS_NBITS_5
@ -2772,25 +2776,25 @@ loadPlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream, XP_U16 version,
pc->curMoveValid = stream_getBits( stream, 1 );
traySetFromStream( stream, &pc->trayTiles );
pc->nPending = (XP_U8)stream_getBits( stream, NTILES_NBITS );
const XP_U16 nTileBits = tilesNBits(stream);
pc->nPending = (XP_U8)stream_getBits( stream, nTileBits );
if ( STREAM_VERS_NUNDONE <= version ) {
pc->nUndone = (XP_U8)stream_getBits( stream, NTILES_NBITS );
pc->nUndone = (XP_U8)stream_getBits( stream, nTileBits );
} else {
XP_ASSERT( 0 == pc->nUndone );
}
XP_ASSERT( 0 == pc->dividerLoc );
if ( STREAM_VERS_MODELDIVIDER <= version ) {
pc->dividerLoc = stream_getBits( stream, NTILES_NBITS );
pc->dividerLoc = stream_getBits( stream, nTileBits );
}
nTiles = pc->nPending + pc->nUndone;
for ( pt = pc->pendingTiles; nTiles-- > 0; ++pt ) {
XP_U16 nBits;
XP_U16 nTiles = pc->nPending + pc->nUndone;
for ( PendingTile* pt = pc->pendingTiles; nTiles-- > 0; ++pt ) {
pt->col = (XP_U8)stream_getBits( stream, nColsNBits );
pt->row = (XP_U8)stream_getBits( stream, nColsNBits );
nBits = (version <= STREAM_VERS_RELAY) ? 6 : 7;
XP_U16 nBits = (version <= STREAM_VERS_RELAY) ? 6 : 7;
pt->tile = (Tile)stream_getBits( stream, nBits );
}
@ -2814,10 +2818,11 @@ writePlayerCtxt( const ModelCtxt* model, XWStreamCtxt* stream,
stream_putBits( stream, 1, pc->curMoveValid );
traySetToStream( stream, &pc->trayTiles );
stream_putBits( stream, NTILES_NBITS, pc->nPending );
stream_putBits( stream, NTILES_NBITS, pc->nUndone );
stream_putBits( stream, NTILES_NBITS, pc->dividerLoc );
XP_U16 nBits = tilesNBits( stream );
stream_putBits( stream, nBits, pc->nPending );
stream_putBits( stream, nBits, pc->nUndone );
stream_putBits( stream, nBits, pc->dividerLoc );
nTiles = pc->nPending + pc->nUndone;
for ( pt = pc->pendingTiles; nTiles-- > 0; ++pt ) {

View file

@ -35,11 +35,8 @@ extern "C" {
# define NUMCOLS_NBITS_5 5
#endif
#ifdef EIGHT_TILES
# define NTILES_NBITS 4
#else
# define NTILES_NBITS 3
#endif
#define NTILES_NBITS_7 3
#define NTILES_NBITS_9 4
/* apply to CellTile */
#define TILE_VALUE_MASK 0x003F
@ -63,7 +60,7 @@ typedef struct BlankQueue {
XP_U8 row[MAX_NUM_BLANKS];
} BlankQueue;
typedef XP_U8 TileBit; /* bits indicating selection of tiles in tray */
typedef XP_U16 TileBit; /* bits indicating selection of tiles in tray */
#define ALLTILES ((TileBit)~(0xFF<<(MAX_TRAY_TILES)))
#define ILLEGAL_MOVE_SCORE (-1)
@ -88,6 +85,7 @@ void model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream );
#endif
void model_setSize( ModelCtxt* model, XP_U16 boardSize );
void model_forceStack7Tiles( ModelCtxt* model );
void model_destroy( ModelCtxt* model, XWEnv xwe );
XP_U32 model_getHash( const ModelCtxt* model );
XP_Bool model_hashMatches( const ModelCtxt* model, XP_U32 hash );

View file

@ -34,7 +34,7 @@ typedef struct PendingTile {
Tile tile; /* includes face and blank bit */
} PendingTile;
typedef struct PlayerCtxt {
typedef struct _PlayerCtxt {
XP_S16 score;
XP_S16 curMoveScore; /* negative means illegal */
XP_Bool curMoveValid;

View file

@ -59,6 +59,7 @@ struct StackCtxt {
};
#define HAVE_FLAGS_MASK ((XP_U16)0x8000)
#define VERS_7TILES_BIT 0x01
static XP_Bool popEntryImpl( StackCtxt* stack, StackEntry* entry );
@ -74,6 +75,21 @@ stack_init( StackCtxt* stack, XP_U16 nPlayers, XP_Bool inDuplicateMode )
shrunk to fit as soon as we serialize/deserialize anyway. */
} /* stack_init */
void
stack_set7Tiles( StackCtxt* stack )
{
XP_ASSERT( !stack->data );
stack->flags |= VERS_7TILES_BIT;
}
XP_U16
stack_getVersion( const StackCtxt* stack )
{
XP_ASSERT( !!stack->data );
return stream_getVersion( stack->data );
}
#ifdef STREAM_VERS_HASHSTREAM
XP_U32
stack_getHash( const StackCtxt* stack )
@ -143,12 +159,18 @@ stack_loadFromStream( StackCtxt* stack, XWStreamCtxt* stream )
nBytes &= ~HAVE_FLAGS_MASK;
if ( nBytes > 0 ) {
XP_U8 stackVersion = STREAM_VERS_NINETILES - 1;
if ( STREAM_VERS_NINETILES <= stream_getVersion(stream) ) {
stackVersion = stream_getU8( stream );
XP_LOGFF( "read stackVersion: %d from stream", stackVersion );
}
stack->highWaterMark = stream_getU16( stream );
stack->nEntries = stream_getU16( stream );
stack->top = stream_getU32( stream );
stack->data = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
stream_getFromStream( stack->data, stream, nBytes );
stream_setVersion( stack->data, stackVersion );
} else {
XP_ASSERT( stack->nEntries == 0 );
XP_ASSERT( stack->top == 0 );
@ -178,6 +200,9 @@ stack_writeToStream( const StackCtxt* stack, XWStreamCtxt* stream )
}
if ( nBytes > 0 ) {
if ( STREAM_VERS_NINETILES <= stream_getVersion(stream) ) {
stream_putU8( stream, stream_getVersion(data) );
}
stream_putU16( stream, stack->highWaterMark );
stream_putU16( stream, stack->nEntries );
stream_putU32( stream, stack->top );
@ -208,16 +233,18 @@ stack_copy( const StackCtxt* stack )
static void
pushEntryImpl( StackCtxt* stack, const StackEntry* entry )
{
XWStreamCtxt* stream = stack->data;
XP_LOGFF( "(typ=%s, player=%d)", StackMoveType_2str(entry->moveType),
entry->playerNum );
XWStreamCtxt* stream = stack->data;
if ( !stream ) {
stream = mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
stack->data = stream;
stream = stack->data =
mem_stream_make_raw( MPPARM(stack->mpool) stack->vtmgr );
XP_U16 version = 0 == (stack->flags & VERS_7TILES_BIT)
? CUR_STREAM_VERS : STREAM_VERS_NINETILES - 1;
stream_setVersion( stream, version );
stack->typeBits = stack->inDuplicateMode ? 3 : 2; /* the new size */
XP_ASSERT( 0 == stack->flags );
XP_ASSERT( 0 == (~VERS_7TILES_BIT & stack->flags) );
}
XWStreamPos oldLoc = stream_setPos( stream, POS_WRITE, stack->top );

View file

@ -65,7 +65,7 @@ typedef struct _PauseRec {
const XP_UCHAR* msg; /* requires stack_freeEntry() */
} PauseRec;
typedef union EntryData {
typedef union _EntryData {
AssignRec assign;
TradeRec trade;
MoveRec move;
@ -73,7 +73,7 @@ typedef union EntryData {
PauseRec pause;
} EntryData;
typedef struct StackEntry {
typedef struct _StackEntry {
StackMoveType moveType;
XP_U8 playerNum;
XP_U8 moveNum;
@ -86,6 +86,8 @@ StackCtxt* stack_make( MPFORMAL VTableMgr* vtmgr, XP_U16 nPlayers, XP_Bool inDup
void stack_destroy( StackCtxt* stack );
void stack_init( StackCtxt* stack, XP_U16 nPlayers, XP_Bool inDuplicateMode );
void stack_set7Tiles( StackCtxt* stack );
XP_U16 stack_getVersion( const StackCtxt* stack );
XP_U32 stack_getHash( const StackCtxt* stack );
void stack_setBitsPerTile( StackCtxt* stack, XP_U16 bitsPerTile );

View file

@ -527,9 +527,9 @@ isLegalMove( ModelCtxt* model, XWEnv xwe, MoveInfo* mInfo, XP_Bool silent )
} /* isLegalMove */
XP_U16
figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn, const MoveInfo* moveInfo,
EngineCtxt* engine, XWStreamCtxt* stream,
WordNotifierInfo* notifyInfo )
figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
const MoveInfo* moveInfo, EngineCtxt* engine,
XWStreamCtxt* stream, WordNotifierInfo* notifyInfo )
{
XP_U16 col, row;
XP_U16* incr;
@ -584,13 +584,22 @@ figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn, const MoveInfo*
score += oneScore;
}
const CurGameInfo* gi = model->vol.gi;
/* did he use all 7 tiles? */
if ( nTiles == MAX_TRAY_TILES ) {
score += EMPTIED_TRAY_BONUS;
if ( gi->bingoMin <= nTiles ) {
score += BINGO_BONUS;
if ( !!stream ) {
const XP_UCHAR* bstr = dutil_getUserString( model->vol.dutil,
xwe, STR_BONUS_ALL );
const XP_UCHAR* bstr;
XP_UCHAR buf[128];
if ( gi->bingoMin == gi->traySize ) {
bstr = dutil_getUserString( model->vol.dutil, xwe, STR_BONUS_ALL );
} else {
bstr = dutil_getUserString( model->vol.dutil, xwe, STR_BONUS_ALL_SUB );
XP_SNPRINTF( buf, VSIZE(buf), bstr, gi->bingoMin );
bstr = buf;
}
stream_catString( stream, bstr );
}
}

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */
/*
* Copyright 1997 - 2020 by Eric House (xwords@eehouse.org). All rights
* Copyright 1997 - 2021 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -912,6 +912,15 @@ setStreamVersion( ServerCtxt* server )
}
XP_LOGF( "%s: setting streamVersion: 0x%x", __func__, streamVersion );
server->nv.streamVersion = streamVersion;
CurGameInfo* gi = server->vol.gi;
if ( STREAM_VERS_NINETILES > streamVersion ) {
if ( 7 < gi->traySize ) {
XP_LOGFF( "reducing tray size from %d to 7", gi->traySize );
gi->traySize = gi->bingoMin = 7;
}
model_forceStack7Tiles( server->vol.model );
}
}
static void
@ -1919,6 +1928,9 @@ client_readInitialMessage( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream )
XP_U8 streamVersion = stream_getU8( stream );
XP_LOGF( "%s: set streamVersion to %d", __func__, streamVersion );
stream_setVersion( stream, streamVersion );
if ( STREAM_VERS_NINETILES > streamVersion ) {
model_forceStack7Tiles( server->vol.model );
}
// XP_ASSERT( streamVersion <= CUR_STREAM_VERS ); /* else do what? */
gameID = stream_getU32( stream );
@ -1934,8 +1946,6 @@ client_readInitialMessage( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream )
localGI.dictName = copyString( server->mpool, gi->dictName );
gi_copy( MPPARM(server->mpool) gi, &localGI );
XP_U16 nCols = localGI.boardSize;
if ( streamVersion < STREAM_VERS_NOEMPTYDICT ) {
XP_LOGFF( "loading and dropping empty dict" );
DictionaryCtxt* empty = util_makeEmptyDict( server->vol.util, xwe );
@ -1957,7 +1967,7 @@ client_readInitialMessage( ServerCtxt* server, XWEnv xwe, XWStreamCtxt* stream )
server->nv.addresses[0].channelNo = channelNo;
XP_LOGF( "%s: assigning channelNo %x for 0", __func__, channelNo );
model_setSize( model, nCols );
model_setSize( model, localGI.boardSize );
XP_U16 nPlayers = localGI.nPlayers;
XP_LOGF( "%s: reading in %d players", __func__, localGI.nPlayers );
@ -2583,8 +2593,8 @@ assignTilesToAll( ServerCtxt* server, XWEnv xwe )
model_setNPlayers( model, nPlayers );
numAssigned = pool_getNTilesLeft( server->pool ) / nPlayers;
if ( numAssigned > MAX_TRAY_TILES ) {
numAssigned = MAX_TRAY_TILES;
if ( numAssigned > gi->traySize ) {
numAssigned = gi->traySize;
}
/* Loop through all the players. If picking tiles is on, stop for each

View file

@ -53,7 +53,7 @@ void
traySetToStream( XWStreamCtxt* stream, const TrayTileSet* ts )
{
XP_U16 nTiles = ts->nTiles;
stream_putBits( stream, NTILES_NBITS, nTiles );
stream_putBits( stream, tilesNBits(stream), nTiles );
tilesToStream( stream, ts->tiles, nTiles );
} /* traySetFromStream */
@ -99,7 +99,7 @@ scoresFromStream( XWStreamCtxt* stream, XP_U16 nScores, XP_U16* scores )
void
traySetFromStream( XWStreamCtxt* stream, TrayTileSet* ts )
{
XP_U16 nTiles = (XP_U16)stream_getBits( stream, NTILES_NBITS );
XP_U16 nTiles = (XP_U16)stream_getBits( stream, tilesNBits( stream ) );
tilesFromStream( stream, ts->tiles, nTiles );
ts->nTiles = (XP_U8)nTiles;
} /* traySetFromStream */
@ -123,7 +123,7 @@ moveInfoToStream( XWStreamCtxt* stream, const MoveInfo* mi, XP_U16 bitsPerTile )
#endif
assertSorted( mi );
stream_putBits( stream, NTILES_NBITS, mi->nTiles );
stream_putBits( stream, tilesNBits( stream ), mi->nTiles );
stream_putBits( stream, NUMCOLS_NBITS_5, mi->commonCoord );
stream_putBits( stream, 1, mi->isHorizontal );
@ -148,7 +148,7 @@ moveInfoFromStream( XWStreamCtxt* stream, MoveInfo* mi, XP_U16 bitsPerTile )
/* XP_UCHAR buf[64] = {0}; */
/* XP_U16 offset = 0; */
#endif
mi->nTiles = stream_getBits( stream, NTILES_NBITS );
mi->nTiles = stream_getBits( stream, tilesNBits( stream ) );
XP_ASSERT( mi->nTiles <= MAX_TRAY_TILES );
mi->commonCoord = stream_getBits( stream, NUMCOLS_NBITS_5 );
mi->isHorizontal = stream_getBits( stream, 1 );
@ -396,6 +396,19 @@ finishHash( XP_U32 hash )
return hash;
}
XP_U16
tilesNBits( const XWStreamCtxt* stream )
{
XP_U16 version = stream_getVersion( stream );
XP_ASSERT( 0 < version );
if ( 0 == version ) {
XP_LOGFF( "version is 0" );
}
XP_U16 result = STREAM_VERS_NINETILES <= version
? NTILES_NBITS_9 : NTILES_NBITS_7;
return result;
}
const XP_UCHAR*
lcToLocale( XP_LangCode lc )
{

View file

@ -86,6 +86,8 @@ void insetRect( XP_Rect* rect, XP_S16 byWidth, XP_S16 byHeight );
XP_U32 augmentHash( XP_U32 hash, const XP_U8* ptr, XP_U16 len );
XP_U32 finishHash( XP_U32 hash );
XP_U16 tilesNBits( const XWStreamCtxt* stream );
const XP_UCHAR* lcToLocale( XP_LangCode lc );
void p_replaceStringIfDifferent( MPFORMAL XP_UCHAR** curLoc,

View file

@ -132,8 +132,6 @@ figureTrayTileRect( BoardCtxt* board, XP_U16 index, XP_Rect* rect )
void
drawTray( BoardCtxt* board, XWEnv xwe )
{
XP_Rect tileRect;
if ( (board->trayInvalBits != 0) || board->dividerInvalid ) {
const XP_S16 turn = board->selPlayer;
PerTurnInfo* pti = board->selInfo;
@ -165,9 +163,9 @@ drawTray( BoardCtxt* board, XWEnv xwe )
if ( dictionary != NULL ) {
XP_Bool showFaces = board->trayVisState == TRAY_REVEALED;
Tile blank = dict_getBlankTile( dictionary );
const XP_U16 nTrayTiles = board->gi->traySize;
if ( turn >= 0 ) {
XP_S16 ii; /* which tile slot are we drawing in */
XP_U16 ddAddedIndx, ddRmvdIndx;
XP_U16 numInTray = countTilesToShow( board );
XP_Bool isBlank;
@ -179,7 +177,7 @@ drawTray( BoardCtxt* board, XWEnv xwe )
/* draw in reverse order so drawing happens after
erasing */
for ( ii = MAX_TRAY_TILES - 1; ii >= 0; --ii ) {
for ( int ii = nTrayTiles - 1; ii >= 0; --ii ) {
CellFlags flags = baseFlags;
XP_U16 mask = 1 << ii;
@ -191,6 +189,7 @@ drawTray( BoardCtxt* board, XWEnv xwe )
flags |= CELL_ISCURSOR;
}
#endif
XP_Rect tileRect;
figureTrayTileRect( board, ii, &tileRect );
XP_Bool drew;
@ -262,7 +261,7 @@ drawTray( BoardCtxt* board, XWEnv xwe )
board->dividerInvalid = XP_FALSE;
}
drawPendingScore( board, xwe, turnScore,
(cursorBits & (1<<(MAX_TRAY_TILES-1))) != 0);
(cursorBits & (1<<(nTrayTiles - 1))) != 0);
}
draw_objFinished( board->draw, xwe, OBJ_TRAY, &board->trayBounds,
@ -336,12 +335,13 @@ static void
drawPendingScore( BoardCtxt* board, XWEnv xwe, XP_S16 turnScore, XP_Bool hasCursor )
{
/* Draw the pending score down in the last tray's rect */
if ( countTilesToShow( board ) < MAX_TRAY_TILES ) {
XP_U16 traySize = board->gi->traySize;
if ( countTilesToShow( board ) < traySize ) {
XP_U16 selPlayer = board->selPlayer;
XP_Bool curTurn = server_isPlayersTurn( board->server, selPlayer );
XP_Rect lastTileR;
figureTrayTileRect( board, MAX_TRAY_TILES-1, &lastTileR );
figureTrayTileRect( board, traySize - 1, &lastTileR );
if ( 0 < lastTileR.width && 0 < lastTileR.height ) {
draw_score_pendingScore( board->draw, xwe, &lastTileR, turnScore,
selPlayer, curTurn,
@ -368,10 +368,9 @@ invalTilesUnderRect( BoardCtxt* board, const XP_Rect* rect )
it for now. If it needs to be faster, invalCellsUnderRect is the model
to use. */
XP_U16 ii;
XP_Rect locRect;
for ( ii = 0; ii < MAX_TRAY_TILES; ++ii ) {
for ( int ii = 0; ii < board->gi->traySize; ++ii ) {
figureTrayTileRect( board, ii, &locRect );
if ( rectsIntersect( rect, &locRect ) ) {
board_invalTrayTiles( board, (TileBit)(1 << ii) );
@ -458,7 +457,7 @@ handleActionInTray( BoardCtxt* board, XWEnv xwe, XP_S16 index, XP_Bool onDivider
result = XP_TRUE;
}
#endif
} else if ( index == -(MAX_TRAY_TILES) ) { /* pending score tile */
} else if ( index == -(board->gi->traySize) ) { /* pending score tile */
result = board_commitTurn( board, xwe, XP_FALSE, XP_FALSE, NULL );
#if defined XWFEATURE_TRAYUNDO_ALL
} else if ( index < 0 ) { /* other empty area */
@ -535,7 +534,8 @@ void
invalTrayTilesAbove( BoardCtxt* board, XP_U16 tileIndex )
{
TileBit bits = 0;
while ( tileIndex < MAX_TRAY_TILES ) {
const XP_U16 traySize = board->gi->traySize;
while ( tileIndex < traySize ) {
bits |= 1 << tileIndex++;
}
board_invalTrayTiles( board, bits );
@ -626,16 +626,17 @@ tray_moveCursor( BoardCtxt* board, XP_Key cursorKey, XP_Bool preflightOnly,
PerTurnInfo* pti = board->selInfo;
XP_S16 trayCursorLoc;
XP_S16 newLoc;
const XP_U16 traySize = board->gi->traySize;
for ( ; ; ) {
trayCursorLoc = pti->trayCursorLoc;
newLoc = trayCursorLoc + delta;
if ( newLoc < 0 || newLoc > MAX_TRAY_TILES ) {
if ( newLoc < 0 || newLoc > traySize ) {
up = XP_TRUE;
} else if ( !preflightOnly ) {
XP_S16 tileLoc = trayCursorLoc;
XP_U16 nTiles = board->trayVisState == TRAY_REVEALED
? model_getNumTilesInTray( board->model, selPlayer )
: MAX_TRAY_TILES;
: traySize;
XP_U16 dividerLoc = getDividerLoc( board );
XP_Bool cursorOnDivider = trayCursorLoc == dividerLoc;
XP_Bool cursorObjSelected;
@ -683,7 +684,7 @@ tray_moveCursor( BoardCtxt* board, XP_Key cursorKey, XP_Bool preflightOnly,
if ( (newTileLoc > nTiles)
&& (newLoc != dividerLoc)
&& (newTileLoc < MAX_TRAY_TILES-1) ) {
&& (newTileLoc < traySize-1) ) {
continue;
}
}
@ -730,9 +731,10 @@ board_moveDivider( BoardCtxt* board, XP_Bool right )
XP_Bool result = board->trayVisState == TRAY_REVEALED;
if ( result ) {
XP_U8 loc = getDividerLoc( board );
loc += MAX_TRAY_TILES + 1;
const XP_U16 traySize = board->gi->traySize;
loc += traySize + 1;
loc += right? 1:-1;
loc %= MAX_TRAY_TILES + 1;
loc %= traySize + 1;
(void)dividerMoved( board, loc );
}

View file

@ -59,6 +59,8 @@ enum {
STRD_DUP_TRADED,
STRSD_DUP_ONESCORE,
STR_BONUS_ALL_SUB,
/* These three aren't in Android */
STR_LOCALPLAYERS,
STR_TOTALPLAYERS,

View file

@ -1,6 +1,6 @@
/* -*- compile-command: "make MEMDEBUG=TRUE -j3"; -*- */
/*
* Copyright 2001-2013 by Eric House (xwords@eehouse.org). All rights
* Copyright 2001 - 2021 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
@ -28,9 +28,13 @@
#include "nwgamest.h"
#include "gtkconnsdlg.h"
#include "gtkutils.h"
#include "gtkask.h"
#define MAX_SIZE_CHOICES 32
#define BINGO_THRESHOLD "Bingo threshold"
#define TRAY_SIZE "Tray size"
typedef struct GtkNewGameState {
GtkGameGlobals* globals;
CurGameInfo* gi;
@ -46,6 +50,8 @@ typedef struct GtkNewGameState {
XP_Bool fireConnDlg;
gboolean isNewGame;
short nCols; /* for board size */
int nTrayTiles;
int bingoMin;
gchar* dict;
#ifndef XWFEATURE_STANDALONE_ONLY
@ -167,6 +173,26 @@ size_combo_changed( GtkComboBox* combo, gpointer gp )
}
} /* size_combo_changed */
static void
tray_size_changed( GtkComboBox* combo, gpointer gp )
{
GtkNewGameState* state = (GtkNewGameState*)gp;
gint index = gtk_combo_box_get_active( GTK_COMBO_BOX(combo) );
if ( index >= 0 ) {
state->nTrayTiles = 7 + index;
}
}
static void
bingo_changed( GtkComboBox* combo, gpointer gp )
{
GtkNewGameState* state = (GtkNewGameState*)gp;
gint index = gtk_combo_box_get_active( GTK_COMBO_BOX(combo) );
if ( index >= 0 ) {
state->bingoMin = 7 + index;
}
}
static void
dict_combo_changed( GtkComboBox* combo, gpointer gp )
{
@ -278,6 +304,103 @@ addDuplicateCheckbox( GtkNewGameState* state, GtkWidget* parent )
gtk_box_pack_start( GTK_BOX(parent), duplicateCheck, FALSE, TRUE, 0 );
}
static void
addSizesRow( GtkNewGameState* state, GtkWidget* parent )
{
LOG_FUNC();
GtkWidget* hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
/* Tray size */
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new(TRAY_SIZE ":"), FALSE, TRUE, 0 );
GtkWidget* traySizeCombo = gtk_combo_box_text_new();
for ( int ii = MIN_TRAY_TILES; ii <= MAX_TRAY_TILES; ++ii ) {
char buf[10];
snprintf( buf, sizeof(buf), "%d", ii );
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(traySizeCombo), buf );
}
gtk_combo_box_set_active( GTK_COMBO_BOX(traySizeCombo),
state->nTrayTiles - MIN_TRAY_TILES );
g_signal_connect( traySizeCombo, "changed", G_CALLBACK(tray_size_changed), state );
gtk_widget_show( traySizeCombo );
gtk_box_pack_start( GTK_BOX(hbox), traySizeCombo, FALSE, TRUE, 0 );
/* Bingo threshold */
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new(BINGO_THRESHOLD":"), FALSE, TRUE, 0 );
GtkWidget* bingoCombo = gtk_combo_box_text_new();
for ( int ii = MIN_TRAY_TILES; ii <= MAX_TRAY_TILES; ++ii ) {
char buf[10];
snprintf( buf, sizeof(buf), "%d", ii );
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(bingoCombo), buf );
}
gtk_combo_box_set_active( GTK_COMBO_BOX(bingoCombo), state->bingoMin - MIN_TRAY_TILES );
g_signal_connect( bingoCombo, "changed", G_CALLBACK(bingo_changed), state );
gtk_widget_show( bingoCombo );
gtk_box_pack_start( GTK_BOX(hbox), bingoCombo, FALSE, TRUE, 0 );
/* board size choices */
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Board size:"),
FALSE, TRUE, 0 );
GtkWidget* boardSizeCombo = gtk_combo_box_text_new();
if ( !state->isNewGame ) {
gtk_widget_set_sensitive( boardSizeCombo, FALSE );
}
for ( int ii = 0; ii < MAX_SIZE_CHOICES; ++ii ) {
char buf[10];
XP_U16 siz = MAX_COLS - ii;
snprintf( buf, sizeof(buf), "%dx%d", siz, siz );
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(boardSizeCombo), buf );
if ( siz == state->nCols ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(boardSizeCombo), ii );
}
}
g_signal_connect( boardSizeCombo, "changed",
G_CALLBACK(size_combo_changed), state );
gtk_widget_show( boardSizeCombo );
gtk_box_pack_start( GTK_BOX(hbox), boardSizeCombo, FALSE, TRUE, 0 );
gtk_box_pack_start( GTK_BOX(parent), hbox, FALSE, TRUE, 0 );
} /* addSizesRow */
static void
addDictsRow( GtkNewGameState* state, GtkWidget* parent )
{
GtkWidget* hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
/* Dictionary combo */
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Dictionary: "),
FALSE, TRUE, 0 );
GtkWidget* dictCombo = gtk_combo_box_text_new();
g_signal_connect( dictCombo, "changed",
G_CALLBACK(dict_combo_changed), state );
gtk_widget_show( dictCombo );
gtk_box_pack_start( GTK_BOX(hbox), dictCombo, FALSE, TRUE, 0 );
GSList* dicts = listDicts( state->globals->cGlobals.params );
GSList* iter = dicts;
for ( int ii = 0; !!iter; iter = iter->next, ++ii ) {
const gchar* name = iter->data;
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(dictCombo), name );
if ( !!state->gi->dictName ) {
if ( !strcmp( name, state->gi->dictName ) ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
} else if ( 0 == ii ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
}
g_slist_free( dicts );
addPhoniesCombo( state, hbox );
gtk_box_pack_start( GTK_BOX(parent), hbox, FALSE, TRUE, 0 );
} /* addDictsRow */
static GtkWidget*
makeNewGameDialog( GtkNewGameState* state )
{
@ -288,11 +411,6 @@ makeNewGameDialog( GtkNewGameState* state )
GtkWidget* roleCombo;
char* roles[] = { "Standalone", "Host", "Guest" };
#endif
GtkWidget* nPlayersCombo;
GtkWidget* boardSizeCombo;
GtkWidget* dictCombo;
CurGameInfo* gi;
short ii;
dialog = gtk_dialog_new();
gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
@ -306,7 +424,7 @@ makeNewGameDialog( GtkNewGameState* state )
roleCombo = gtk_combo_box_text_new();
state->roleCombo = roleCombo;
for ( ii = 0; ii < VSIZE(roles); ++ii ) {
for ( int ii = 0; ii < VSIZE(roles); ++ii ) {
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(roleCombo),
roles[ii] );
}
@ -327,12 +445,10 @@ makeNewGameDialog( GtkNewGameState* state )
state->nPlayersLabel = gtk_label_new("");
gtk_box_pack_start( GTK_BOX(hbox), state->nPlayersLabel, FALSE, TRUE, 0 );
nPlayersCombo = gtk_combo_box_text_new();
GtkWidget* nPlayersCombo = gtk_combo_box_text_new();
state->nPlayersCombo = nPlayersCombo;
gi = state->gi;
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
for ( int ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
char buf[2] = { ii + '1', '\0' };
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(nPlayersCombo),
buf );
@ -351,7 +467,7 @@ makeNewGameDialog( GtkNewGameState* state )
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
for ( ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
for ( int ii = 0; ii < MAX_NUM_PLAYERS; ++ii ) {
GtkWidget* label = gtk_label_new("Name:");
#ifndef XWFEATURE_STANDALONE_ONLY
GtkWidget* remoteCheck = gtk_check_button_new_with_label( "Remote" );
@ -401,58 +517,8 @@ makeNewGameDialog( GtkNewGameState* state )
gtk_widget_show( hbox );
}
/* board size choices */
hbox = gtk_box_new( GTK_ORIENTATION_HORIZONTAL, 0 );
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Board size"),
FALSE, TRUE, 0 );
boardSizeCombo = gtk_combo_box_text_new();
if ( !state->isNewGame ) {
gtk_widget_set_sensitive( boardSizeCombo, FALSE );
}
for ( ii = 0; ii < MAX_SIZE_CHOICES; ++ii ) {
char buf[10];
XP_U16 siz = MAX_COLS - ii;
snprintf( buf, sizeof(buf), "%dx%d", siz, siz );
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(boardSizeCombo), buf );
if ( siz == state->nCols ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(boardSizeCombo), ii );
}
}
g_signal_connect( boardSizeCombo, "changed",
G_CALLBACK(size_combo_changed), state );
gtk_widget_show( boardSizeCombo );
gtk_box_pack_start( GTK_BOX(hbox), boardSizeCombo, FALSE, TRUE, 0 );
/* Dictionary combo */
gtk_box_pack_start( GTK_BOX(hbox), gtk_label_new("Dictionary: "),
FALSE, TRUE, 0 );
dictCombo = gtk_combo_box_text_new();
g_signal_connect( dictCombo, "changed",
G_CALLBACK(dict_combo_changed), state );
gtk_widget_show( dictCombo );
gtk_box_pack_start( GTK_BOX(hbox), dictCombo, FALSE, TRUE, 0 );
GSList* dicts = listDicts( state->globals->cGlobals.params );
GSList* iter;
for ( iter = dicts, ii = 0; !!iter; iter = iter->next, ++ii ) {
const gchar* name = iter->data;
gtk_combo_box_text_append_text( GTK_COMBO_BOX_TEXT(dictCombo), name );
if ( !!gi->dictName ) {
if ( !strcmp( name, gi->dictName ) ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
} else if ( 0 == ii ) {
gtk_combo_box_set_active( GTK_COMBO_BOX(dictCombo), ii );
}
}
g_slist_free( dicts );
addPhoniesCombo( state, hbox );
addSizesRow( state, vbox );
addDictsRow( state, vbox );
gtk_widget_show( hbox );
gtk_box_pack_start( GTK_BOX(vbox), hbox, FALSE, TRUE, 0 );
@ -664,6 +730,18 @@ setDefaults( CurGameInfo* gi )
}
}
static void
checkAndWarn( GtkNewGameState* state, GtkWidget* dialog )
{
if ( state->nTrayTiles < state->bingoMin ) {
gchar buf[128];
XP_SNPRINTF( buf, VSIZE(buf),"\"%s\" cannot be greater than \"%s\"",
BINGO_THRESHOLD, TRAY_SIZE );
gtktell( dialog, buf );
state->revert = XP_TRUE;
}
}
gboolean
gtkNewGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr,
XP_Bool isNewGame, XP_Bool fireConnDlg )
@ -694,6 +772,8 @@ gtkNewGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr,
state.revert = FALSE;
state.loaded = XP_FALSE;
state.nCols = gi->boardSize;
state.nTrayTiles = gi->traySize;
state.bingoMin = gi->bingoMin;
if ( 0 == state.nCols ) {
state.nCols = globals->cGlobals.params->pgi.boardSize;
}
@ -707,9 +787,13 @@ gtkNewGameDialog( GtkGameGlobals* globals, CurGameInfo* gi, CommsAddrRec* addr,
state.loaded = XP_TRUE;
gtk_main();
checkAndWarn( &state, dialog );
if ( !state.cancelled && !state.revert ) {
if ( newg_store( state.newGameCtxt, NULL_XWE, gi, XP_TRUE ) ) {
gi->boardSize = state.nCols;
gi->traySize = state.nTrayTiles;
gi->bingoMin = state.bingoMin;
replaceStringIfDifferent( globals->cGlobals.util->mpool,
&gi->dictName, state.dict );
gi->phoniesAction = state.phoniesAction;

View file

@ -222,6 +222,8 @@ linux_dutil_getUserString( XW_DUtilCtxt* XP_UNUSED(uc),
return (XP_UCHAR*)"Score for turn: %d\n";
case STR_BONUS_ALL:
return (XP_UCHAR*)"Bonus for using all tiles: 50\n";
case STR_BONUS_ALL_SUB:
return (XP_UCHAR*)"Bonus for using at least %d tiles: 50\n";
case STR_PENDING_PLAYER:
return (XP_UCHAR*)"(remote)";
case STRD_TIME_PENALTY_SUB:

View file

@ -838,6 +838,7 @@ typedef enum {
,CMD_NOCLOSESTDIN
,CMD_QUITAFTER
,CMD_BOARDSIZE
,CMD_TRAYSIZE
,CMD_DUP_MODE
,CMD_HIDEVALUES
,CMD_SKIPCONFIRM
@ -985,6 +986,7 @@ static CmdInfoRec CmdInfoRecs[] = {
,{ CMD_NOCLOSESTDIN, false, "no-close-stdin", "do not close stdin on start" }
,{ CMD_QUITAFTER, true, "quit-after", "exit <n> seconds after game's done" }
,{ CMD_BOARDSIZE, true, "board-size", "board is <n> by <n> cells" }
,{ CMD_TRAYSIZE, true, "tray-size", "<n> tiles per tray (7-9 are legal)" }
,{ CMD_DUP_MODE, false, "duplicate-mode", "play in duplicate mode" }
,{ CMD_HIDEVALUES, false, "hide-values", "show letters, not nums, on tiles" }
,{ CMD_SKIPCONFIRM, false, "skip-confirm", "don't confirm before commit" }
@ -2690,6 +2692,8 @@ main( int argc, char** argv )
mainParams.connInfo.sms.port = 1;
#endif
mainParams.pgi.boardSize = 15;
mainParams.pgi.traySize = 7;
mainParams.pgi.bingoMin = 7;
mainParams.quitAfter = -1;
mainParams.sleepOnAnchor = XP_FALSE;
mainParams.printHistory = XP_FALSE;
@ -3044,6 +3048,11 @@ main( int argc, char** argv )
case CMD_BOARDSIZE:
mainParams.pgi.boardSize = atoi(optarg);
break;
case CMD_TRAYSIZE:
mainParams.pgi.traySize = atoi(optarg);
XP_ASSERT( MIN_TRAY_TILES <= mainParams.pgi.traySize
&& mainParams.pgi.traySize <= MAX_TRAY_TILES );
break;
case CMD_DUP_MODE:
mainParams.pgi.inDuplicateMode = XP_TRUE;
break;

View file

@ -510,6 +510,7 @@ def build_cmds(args):
if DEV == 1 or usePublic: PARAMS += ['--force-game']
if DEV == 1:
PARAMS += ['--server', '--phonies', phonies ]
PARAMS += ['--tray-size', random.randint(7, 9)] # randint() is *inclusive*
# IFF there are any non-1 player counts, tell inviter which
if sum(LOCALS) > NDEVS:
PARAMS += ['--invitee-counts', ":".join(str(n) for n in LOCALS[1:])]