xwords/xwords4/common/model.h
Eric House b8f359c3e5 add filtering to wordlist browser
Add a basic regular expression engine to the dictiter, and to the UI add
the ability to filter for "starts with", "contains" and "ends with",
which translate into ANDed RE_*, _*RE_* and _*RE, respectively (with
_ standing for blank/wildcard). The engine's tightly integrated with the
next/prevWord() functions for greatest possible speed, but unless
there's no pattern does slow things down a bit (especially when "ENDS
WITH" is used.) The full engine is not exposed (users can't provide raw
REs), and while the parser will accept nesting (e.g. ([AB]_*[CD]){2,5}
to mean words from 2-5 tiles long starting with A or B and ending with C
or D) the engine can't handle it. Which is why filtering for word length
is handled separately from REs (but also tightly integrated.)

Users can enter strings that don't map to tiles. They now get an
error. It made sense for the error alert to have a "Show tiles"
button, so there's now a dialog listing all the tiles in a wordlist,
something the browser has needed all along.
2020-08-05 09:47:44 -07:00

338 lines
14 KiB
C

/* -*- fill-column: 78; compile-command: "cd ../linux && make -j3 MEMDEBUG=TRUE"; -*- */
/*
* Copyright 1997 - 2009 by Eric House (xwords@eehouse.org). All rights
* reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifndef _MODEL_H_
#define _MODEL_H_
#include "comtypes.h"
#include "dictnry.h"
#include "mempool.h"
#include "dutil.h"
#ifdef CPLUS
extern "C" {
#endif
#define NUMCOLS_NBITS_4 4
#if 16 < MAX_COLS && MAX_COLS <= 32
# define NUMCOLS_NBITS_5 5
#endif
#ifdef EIGHT_TILES
# define NTILES_NBITS 4
#else
# define NTILES_NBITS 3
#endif
/* apply to CellTile */
#define TILE_VALUE_MASK 0x003F
#define TILE_BLANK_BIT 0x0040
#define IS_BLANK(t) (((t)&TILE_BLANK_BIT)!= 0)
#define TILE_EMPTY_BIT 0x0080
#define TILE_PENDING_BIT 0x0100
#define PREV_MOVE_BIT 0x200
#define CELL_OWNER_OFFSET 10
#define CELL_OWNER_MASK (0x0003 << CELL_OWNER_OFFSET)
#define CELL_OWNER(t) (((t)&CELL_OWNER_MASK) >> CELL_OWNER_OFFSET)
#define MAX_UNIQUE_TILES 64 /* max tile non-blank faces */
#define MAX_NUM_BLANKS 4
typedef struct BlankQueue {
XP_U16 nBlanks;
XP_U8 col[MAX_NUM_BLANKS];
XP_U8 row[MAX_NUM_BLANKS];
} BlankQueue;
typedef XP_U8 TileBit; /* bits indicating selection of tiles in tray */
#define ALLTILES ((TileBit)~(0xFF<<(MAX_TRAY_TILES)))
#define ILLEGAL_MOVE_SCORE (-1)
#define EMPTY_TILE TILE_EMPTY_BIT
#define TILE_IS_EMPTY(t) (((t)&TILE_EMPTY_BIT)!=0)
#define REVERSED_TILE TILE_PENDING_BIT /* reuse that bit for tile drawing
only */
ModelCtxt* model_make( MPFORMAL XWEnv xwe, const DictionaryCtxt* dict,
const PlayerDicts* dicts, XW_UtilCtxt* util, XP_U16 nCols );
ModelCtxt* model_makeFromStream( MPFORMAL XWEnv xwe, XWStreamCtxt* stream,
const DictionaryCtxt* dict, const PlayerDicts* dicts,
XW_UtilCtxt* util );
void model_writeToStream( const ModelCtxt* model, XWStreamCtxt* stream );
#ifdef TEXT_MODEL
void model_writeToTextStream( const ModelCtxt* model, XWStreamCtxt* stream );
#endif
void model_setSize( ModelCtxt* model, XP_U16 boardSize );
void model_destroy( ModelCtxt* model, XWEnv xwe );
XP_U32 model_getHash( const ModelCtxt* model );
XP_Bool model_hashMatches( const ModelCtxt* model, XP_U32 hash );
XP_Bool model_popToHash( ModelCtxt* model, XWEnv xwe, const XP_U32 hash,
PoolContext* pool );
void model_setNPlayers( ModelCtxt* model, XP_U16 numPlayers );
XP_U16 model_getNPlayers( const ModelCtxt* model );
void model_setDictionary( ModelCtxt* model, XWEnv xwe, const DictionaryCtxt* dict );
const DictionaryCtxt* model_getDictionary( const ModelCtxt* model );
void model_setPlayerDicts( ModelCtxt* model, XWEnv xwe, const PlayerDicts* dicts );
const DictionaryCtxt* model_getPlayerDict( const ModelCtxt* model, XP_S16 playerNum );
XP_Bool model_getTile( const ModelCtxt* model, XP_U16 col, XP_U16 row,
XP_Bool getPending, XP_S16 turn,
Tile* tile, XP_Bool* isBlank,
XP_Bool* isPending, XP_Bool* isRecent );
void model_listPlacedBlanks( ModelCtxt* model, XP_U16 turn,
XP_Bool includePending, BlankQueue* bcp );
XP_U16 model_getCellOwner( ModelCtxt* model, XP_U16 col, XP_U16 row );
void model_addNewTiles( ModelCtxt* model, XP_S16 turn,
const TrayTileSet* tiles );
void model_assignPlayerTiles( ModelCtxt* model, XP_S16 turn,
const TrayTileSet* tiles );
void model_assignDupeTiles( ModelCtxt* model, XWEnv xwe, const TrayTileSet* tiles );
Tile model_getPlayerTile( const ModelCtxt* model, XP_S16 turn, XP_S16 index );
Tile model_removePlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index );
void model_removePlayerTiles( ModelCtxt* model, XP_S16 turn, const TrayTileSet* tiles );
void model_addPlayerTile( ModelCtxt* model, XP_S16 turn, XP_S16 index,
Tile tile );
void model_moveTileOnTray( ModelCtxt* model, XP_S16 turn, XP_S16 indexCur,
XP_S16 indexNew );
XP_U16 model_getDividerLoc( const ModelCtxt* model, XP_S16 turn );
void model_setDividerLoc( ModelCtxt* model, XP_S16 turn, XP_U16 loc );
/* As an optimization, return a pointer to the model's array of tiles for a
player. Don't even think about modifying the array!!!! */
const TrayTileSet* model_getPlayerTiles( const ModelCtxt* model, XP_S16 turn );
#ifdef DEBUG
XP_UCHAR* formatTileSet( const TrayTileSet* tiles, XP_UCHAR* buf, XP_U16 len );
void model_printTrays( const ModelCtxt* model );
#endif
void model_sortTiles( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesInTray( ModelCtxt* model, XP_S16 turn );
XP_U16 model_getNumTilesTotal( ModelCtxt* model, XP_S16 turn );
void model_moveBoardToTray( ModelCtxt* model, XWEnv xwe, XP_S16 turn,
XP_U16 col, XP_U16 row, XP_U16 trayOffset );
void model_moveTrayToBoard( ModelCtxt* model, XWEnv xwe, XP_S16 turn, XP_U16 col,
XP_U16 row, XP_S16 tileIndex, Tile blankFace );
XP_Bool model_moveTileOnBoard( ModelCtxt* model, XWEnv xwe, XP_S16 turn,
XP_U16 colCur, XP_U16 rowCur, XP_U16 colNew,
XP_U16 rowNew );
XP_Bool model_redoPendingTiles( ModelCtxt* model, XWEnv xwe, XP_S16 turn );
XP_Bool model_setBlankValue( ModelCtxt* model, XP_U16 XP_UNUSED(player),
XP_U16 col, XP_U16 row, XP_U16 tileIndex );
XP_S16 model_trayContains( ModelCtxt* model, XP_S16 turn, Tile tile );
XP_U16 model_numRows( const ModelCtxt* model );
XP_U16 model_numCols( const ModelCtxt* model );
/* XP_U16 model_numTilesCurrentTray( ModelCtxt* model ); */
/* Tile model_currentTrayTile( ModelCtxt* model, XP_U16 index ); */
void model_addToCurrentMove( ModelCtxt* model, XP_S16 turn,
XP_U16 col, XP_U16 row,
Tile tile, XP_Bool isBlank );
XP_U16 model_getCurrentMoveCount( const ModelCtxt* model, XP_S16 turn );
XP_Bool model_getCurrentMoveIsVertical( const ModelCtxt* model, XP_S16 turn,
XP_Bool* isHorizontal );
void model_getCurrentMoveTile( ModelCtxt* model, XP_S16 turn, XP_S16* index,
Tile* tile, XP_U16* col, XP_U16* row,
XP_Bool* isBlank );
XP_Bool model_commitTurn( ModelCtxt* model, XWEnv xwe, XP_S16 player,
TrayTileSet* newTiles );
void model_commitDupeTurn( ModelCtxt* model, XWEnv xwe,
const MoveInfo* moveInfo,
XP_U16 nScores, XP_U16* scores,
TrayTileSet* newTiles );
void model_commitDupeTrade( ModelCtxt* model, const TrayTileSet* oldTiles,
const TrayTileSet* newTiles );
void model_noteDupePause( ModelCtxt* model, XWEnv xwe, DupPauseType typ,
XP_S16 turn, const XP_UCHAR* msg );
void model_cloneDupeTrays( ModelCtxt* model, XWEnv xwe );
void model_commitRejectedPhony( ModelCtxt* model, XP_S16 player );
void model_makeTileTrade( ModelCtxt* model, XP_S16 player,
const TrayTileSet* oldTiles,
const TrayTileSet* newTiles );
XP_Bool model_canUndo( const ModelCtxt* model );
XP_Bool model_undoLatestMoves( ModelCtxt* model, XWEnv xwe, PoolContext* pool,
XP_U16 nMovesSought, XP_U16* turn,
XP_S16* moveNum );
void model_rejectPreviousMove( ModelCtxt* model, XWEnv xwe,
PoolContext* pool, XP_U16* turn );
void model_trayToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream );
void model_currentMoveToStream( ModelCtxt* model, XP_S16 turn,
XWStreamCtxt* stream );
void model_currentMoveToMoveInfo( ModelCtxt* model, XP_S16 turn,
MoveInfo* moveInfo );
XP_Bool model_makeTurnFromStream( ModelCtxt* model, XWEnv xwe, XP_U16 playerNum,
XWStreamCtxt* stream );
void model_makeTurnFromMoveInfo( ModelCtxt* model, XWEnv xwe, XP_U16 playerNum,
const MoveInfo* newMove );
#ifdef DEBUG
void juggleMoveIfDebug( MoveInfo* move );
void model_dumpSelf( const ModelCtxt* model, const XP_UCHAR* msg );
#else
# define juggleMoveIfDebug(newMove)
# define model_dumpSelf( model, msg )
#endif
#ifdef XWFEATURE_ROBOTPHONIES
void reverseTiles( MoveInfo* move );
#endif
void model_resetCurrentTurn( ModelCtxt* model, XWEnv xwe, XP_S16 turn );
XP_S16 model_getNMoves( const ModelCtxt* model );
/* Are there two or more tiles visible */
XP_U16 model_visTileCount( const ModelCtxt* model, XP_U16 turn,
XP_Bool trayVisible );
XP_Bool model_canShuffle( const ModelCtxt* model, XP_U16 turn,
XP_Bool trayVisible );
XP_Bool model_canTogglePending( const ModelCtxt* model, XP_U16 turn );
/********************* notification ********************/
typedef void (*BoardListener)( XWEnv xwe, void* data, XP_U16 turn, XP_U16 col,
XP_U16 row, XP_Bool added );
void model_setBoardListener( ModelCtxt* model, BoardListener bl,
void* data );
typedef void (*TrayListener)( void* data, XP_U16 turn,
XP_S16 index1, XP_S16 index2 );
void model_setTrayListener( ModelCtxt* model, TrayListener bl,
void* data );
typedef void (*DictListener)( void* data, XWEnv xwe, XP_S16 playerNum,
const DictionaryCtxt* oldDict,
const DictionaryCtxt* newDict );
void model_setDictListener( ModelCtxt* model, DictListener dl,
void* data );
void model_foreachPendingCell( ModelCtxt* model, XWEnv xwe, XP_S16 turn,
BoardListener bl, void* data );
void model_foreachPrevCell( ModelCtxt* model, XWEnv xwe, BoardListener bl, void* data );
void model_writeGameHistory( ModelCtxt* model, XWEnv xwe, XWStreamCtxt* stream,
ServerCtxt* server, /* for player names */
XP_Bool gameOver );
/* for the tile values dialog: total all the tiles in players trays and
tentatively placed on the board. */
void model_countAllTrayTiles( ModelCtxt* model, XP_U16* counts,
XP_S16 excludePlayer );
/********************* scoring ********************/
typedef struct _WNParams {
const XP_UCHAR* word;
XP_Bool isLegal;
const DictionaryCtxt* dict;
#ifdef XWFEATURE_BOARDWORDS
const MoveInfo* movei;
XP_U16 start;
XP_U16 end;
#endif
} WNParams;
typedef void (*WordNotifierProc)( const WNParams* wnp, void* closure );
typedef struct WordNotifierInfo {
WordNotifierProc proc;
void* closure;
} WordNotifierInfo;
XP_Bool getCurrentMoveScoreIfLegal( ModelCtxt* model, XWEnv xwe,
XP_S16 turn, XWStreamCtxt* stream,
WordNotifierInfo* wni, XP_S16* score );
XP_S16 model_getPlayerScore( ModelCtxt* model, XP_S16 player );
XP_Bool model_getPlayersLastScore( ModelCtxt* model, XWEnv xwe, XP_S16 player,
LastMoveInfo* info );
#ifdef XWFEATURE_BOARDWORDS
XP_Bool model_listWordsThrough( ModelCtxt* model, XWEnv xwe, XP_U16 col,
XP_U16 row, XP_S16 turn, XWStreamCtxt* stream );
#endif
/* Have there been too many passes (so game should end)? */
XP_Bool model_recentPassCountOk( ModelCtxt* model );
XWBonusType model_getSquareBonus( const ModelCtxt* model,
XWEnv xwe, XP_U16 col, XP_U16 row );
#ifdef STREAM_VERS_BIGBOARD
void model_setSquareBonuses( ModelCtxt* model, XWBonusType* bonuses,
XP_U16 nBonuses );
#endif
XP_Bool model_checkMoveLegal( ModelCtxt* model, XWEnv xwe, XP_S16 player,
XWStreamCtxt* stream,
WordNotifierInfo* notifyInfo );
typedef struct _ScoresArray { XP_S16 arr[MAX_NUM_PLAYERS]; } ScoresArray;
void model_figureFinalScores( ModelCtxt* model, ScoresArray* scores,
ScoresArray* tilePenalties );
/* figureMoveScore is meant only for the engine's use */
XP_U16 figureMoveScore( const ModelCtxt* model, XWEnv xwe, XP_U16 turn,
const MoveInfo* mvInfo, EngineCtxt* engine,
XWStreamCtxt* stream, WordNotifierInfo* notifyInfo );
/* tap into internal WordNotifierInfo */
WordNotifierInfo* model_initWordCounter( ModelCtxt* model, XWStreamCtxt* stream );
/********************* persistence ********************/
#ifdef INCLUDE_IO_SUPPORT
void model_load( ModelCtxt* model, XP_Stream* inStream );
void model_store( ModelCtxt* model, XP_Stream* outStream );
#endif
/* a utility function needed by server too. Not a clean design, this. */
void model_packTilesUtil( ModelCtxt* model, PoolContext* pool,
XP_Bool includeBlank,
XP_U16* nUsed, const XP_UCHAR** texts,
Tile* tiles );
Tile model_askBlankTile( ModelCtxt* model, XWEnv xwe, XP_U16 turn,
XP_U16 col, XP_U16 row);
XP_S16 model_getNextTurn( const ModelCtxt* model );
#ifdef CPLUS
}
#endif
#endif