/*
* Quackle -- Crossword game artificial intelligence and analysis tool
* Copyright (C) 2005-2019 Jason Katz-Brown, John O'Laughlin, and John Fultz.
*
* 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 3 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, see .
*/
#ifndef QUACKLE_MOVE_H
#define QUACKLE_MOVE_H
#include
#include "alphabetparameters.h"
namespace Quackle
{
// A move represents one of a Place, Exchange, Pass, Unused Tile Bonus,
// Time Penalty, Score Addition, or a lack of a move.
// Passes are special because they start at equity of -50
// (for this use createPassMove).
// Tiles to exchange are specified in tiles.
// Tiles of place word are also specified in tiles.
// tiles is empty for a pass.
// tiles contains unused tiles for an UnusedTilesBonus
// and score contains the bonus.
// A Nonmove represents absence of a choice of move and has extremely
// negative score and equity (so it is sorted below other moves)
class Move
{
public:
enum Action { Place = 0, PlaceError, Exchange, BlindExchange, Pass, UnusedTilesBonus, UnusedTilesBonusError, TimePenalty, Nonmove };
int score = 0;
bool isBingo = false;
// 0 if this is a challenged phoney; score field otherwise
int effectiveScore() const;
double equity = 0.;
double win = 0.; // between 0 and 1 inclusive
double possibleWin = 0.;
Action action = Move::Pass;
bool horizontal = false;
int startrow = 0;
int startcol = 0;
// Human-readable outcomes for pre-endgame solving
std::string outcomes;
// returns whether this is not a Nonmove
bool isAMove() const;
// tiles like .ANELINg
void setTiles(const LetterString &tiles);
// tiles like (P)ANELINg
void setPrettyTiles(const LetterString &prettyTiles);
// returns tiles like (P)ANELINg
const LetterString &prettyTiles() const;
// Returns tiles like .ANELIN?.
// QUACKLE_PLAYED_THRU_MARK will take place of a letter already on the board
// and blanks are special -- so use isAlreadyOnBoard and
// AlphabetParameters->isBlankLetter well.
// Returns an empty string if this is a challenged phoney.
LetterString usedTiles() const;
// Returns tiles like PANELING (pretty tiles, nonblank,
// without playthru markings)
LetterString wordTiles() const;
// Returns tiles like ANELING (pretty tiles, nonblank,
// without playthru markings or played thru letters)
LetterString wordTilesWithNoPlayThru() const;
// returns tiles like .ANELINg
const LetterString &tiles() const;
bool isChallengedPhoney() const;
void setIsChallengedPhoney(bool isChallengedPhoney);
int scoreAddition() const;
void setScoreAddition(int scoreAddition);
// tests for equality to played-thru mark
static bool isAlreadyOnBoard(Letter letter);
UVString xml() const;
// returns a unique identifier for this move (does not specify
// whether move is challenged off)
UVString toString() const;
// returns string with all information (including score and equity)
UVString debugString() const;
// for a place move, a position like 8H
UVString positionString() const;
// eg place("8h", ".EaTY"); word is like setTiles(), and no
// pretty tiles are set.
static Move createPlaceMove(UVString placeString, LetterString word);
static Move createPlaceMove(int zeroIndexedRow, int zeroIndexedColumn, bool horizontal, LetterString word);
static Move createChallengedPhoney(UVString placeString, LetterString word);
static Move createChallengedPhoney(int zeroIndexedRow, int zeroIndexedColumn, bool horizontal, LetterString word);
static Move createExchangeMove(LetterString tilesToExchange, bool isBlind);
static Move createUnusedTilesBonus(LetterString unusedTiles, int bonus);
static Move createTimePenalty(int penalty);
static Move createPassMove();
static Move createNonmove();
private:
LetterString m_tiles;
LetterString m_prettyTiles;
bool m_isChallengedPhoney = false;
int m_scoreAddition = 0;
};
// comparison based on action, then tiles, then horizontalness, then startrow, then endcol
bool operator<(const Quackle::Move &move1, const Quackle::Move &move2);
class MoveList : public std::vector
{
public:
enum SortType { Equity, Score, Alphabetical, Win};
// perform stable sort
static void sort(MoveList &list, SortType type = Equity);
// sort in opposite direction
static void sortNonReverse(MoveList &list, SortType type = Equity);
static bool winComparator(const Move &move1, const Move &move2);
static bool equityComparator(const Move &move1, const Move &move2);
static bool scoreComparator(const Move &move1, const Move &move2);
static bool alphabeticalComparator(const Move &move1, const Move &move2);
static bool wordPosComparator(const Move &move1, const Move &move2);
bool contains(const Move &move) const;
private:
static SortType m_sortType;
};
inline bool Move::isAMove() const
{
return action != Nonmove;
}
inline int Move::effectiveScore() const
{
return m_isChallengedPhoney? 0 : (score + m_scoreAddition);
}
inline void Move::setTiles(const LetterString &tiles)
{
m_tiles = tiles;
}
inline void Move::setPrettyTiles(const LetterString &prettyTiles)
{
m_prettyTiles = prettyTiles;
}
inline const LetterString &Move::prettyTiles() const
{
return m_prettyTiles;
}
inline const LetterString &Move::tiles() const
{
return m_tiles;
}
inline bool Move::isChallengedPhoney() const
{
return m_isChallengedPhoney;
}
inline void Move::setIsChallengedPhoney(bool isChallengedPhoney)
{
m_isChallengedPhoney = isChallengedPhoney;
}
inline int Move::scoreAddition() const
{
return m_scoreAddition;
}
inline void Move::setScoreAddition(int scoreAddition)
{
m_scoreAddition = scoreAddition;
}
inline bool Move::isAlreadyOnBoard(Letter letter)
{
return letter == QUACKLE_PLAYED_THRU_MARK;
}
}
// we gotta overload so plays with diff equity
// are equal
bool operator==(const Quackle::Move &move1, const Quackle::Move &move2);
UVOStream& operator<<(UVOStream& o, const Quackle::Move& m);
UVOStream& operator<<(UVOStream& o, const Quackle::MoveList& moves);
#endif