-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.h
909 lines (711 loc) · 26.3 KB
/
game.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
/*
* Quackle -- Crossword game artificial intelligence and analysis tool
* Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#ifndef QUACKLE_GAME_H
#define QUACKLE_GAME_H
#include <vector>
#include <map>
#include "alphabetparameters.h"
#include "bag.h"
#include "board.h"
#include "player.h"
#include "playerlist.h"
namespace Quackle
{
class ComputerPlayer;
class HistoryLocation
{
public:
HistoryLocation(int playerId, int turnNumber);
int playerId() const;
int turnNumber() const;
private:
int m_playerId;
int m_turnNumber;
};
inline int HistoryLocation::playerId() const
{
return m_playerId;
}
inline int HistoryLocation::turnNumber() const
{
return m_turnNumber;
}
class GamePosition
{
public:
// Constructs a new position with empty, prepared board
// and full, prepared bag
// using players playing the game
// where the first player of players starts the game (at turn number 1)
// after Game::addPosition() is called to get things going.
// You *must* call increment() if you do not use Game::addPosition()
// because this constructor leaves current player pointing to the end of
// the player list, which is invalid.
GamePosition(const PlayerList &players);
// copy constructor that preserves current-player iterator
// and includes copying the move list and moveMade
// Natch, preserves board and bag.
// MAKE SURE THAT ANY NEW FIELDS YOU ADD
// ALSO GET COPIED!!!!!!!!!!!!!!!!!!!!!!
GamePosition(const GamePosition &position);
// empty constructor to appease value lists
// empty prepared board, full prepared bag
GamePosition();
// operator= that preserves current-player iterator
// and includes copying the move list and moveMade
// Natch, preserves board and bag.
// MAKE SURE THAT ANY NEW FIELDS YOU ADD
// ALSO GET COPIED!!!!!!!!!!!!!!!!!!!!!!
const GamePosition &operator=(const GamePosition &position);
// kibitz up to nmoves best moves; stored in move list
void kibitz(int nmoves = 10);
// get what's in the move list
const MoveList &moves() const;
void setMoves(const MoveList &moves);
// kibitz (destroying previous move list)
// and return the best move based on static evaluation
const Move &staticBestMove();
// erase a move from move list that equals move
void removeMove(const Move &move);
// erase all moves from move list
void removeAllMoves();
// Add move to move list (so list is still ordered).
// Does *not* check if the move is already in the move list,
// so this could duplicate a move in the list.
// Does nothing is move is a nonmove.
// Ensures the move's prettiness and sanity of playthru tiles
// in the tiles.
void addMove(const Move &move);
// for each move in list, add it to our list if we don't
// already have it
void makeSureMoveListContainsMoves(const MoveList &moves);
// fill the move list with computerPlayer's moves, or
// the current player's computer player's moves
void kibitzAs(ComputerPlayer *computerPlayer, int nmoves);
// if move does not have a pretty tiles field set, make it
void ensureMovePrettiness(Move &move);
// If the move's tiles are PANELINg, and the P is played through,
// turn the tiles into .ANELINg
void ensureMoveTilesDoNotIncludePlayThru(Move &move);
// add move to move list and set as move made for this position
void addAndSetMoveMade(const Move &move);
// ValidMode - okeydokey
// TooLateExchange - not enough tiles in the bag for an exchange
// InvalidTiles - tiles to play or exchange aren't on rack
// InvalidPlace - word does not connect to rest of board
// InvalidOpeningPlace - word does not connect to start square
// of empty board
// UnacceptableWord - word is not in our lexicon
// InvalidAction - type of move is not Place, Pass, or Exchange
enum MoveValidity { ValidMove = 0x0000, InvalidTiles = 0x0001, TooLateExchange = 0x0002, InvalidPlace = 0x0004, InvalidOpeningPlace = 0x0008, UnacceptableWord = 0x0010, InvalidAction = 0x0020 };
// Returns validity of this move for current player.
// MoveValidity flags are OR'ed together into the return int.
int validateMove(const Move &move) const;
// returns whether the words formed by this move are all acceptable
bool formsAcceptableWords(const Move &move) const;
// returns whether the board is empty or tiles of move touch
// at least one other square of the board
bool isConnected(const Move &move) const;
// returns whether the tiles of move are a word in our lexicon
bool isAcceptableWord(const LetterString &word) const;
// returns true if any exchange is allowed right now
bool exchangeAllowed() const;
enum OverdrawValidity { ValidOverdraw = 0x0000, InvalidOverdrawNumber = 0x0001, OverdrawnTilesNotUnseen = 0x0002};
// computers letters that should be thrown back if specified letters
// are the faceup tiles and returns if these faceup tiles are valid
int handleOverdraw(const LetterString &letters, LetterString *throwback) const;
// Make move on the board and add score of move
// to current player's score.
// If the move is an exchange, tosses the exchanged tiles
// back in the bag.
// If maintainBoard is false, the board can no longer be used
// with kibitzing capabilities.
void makeMove(const Move &move, bool maintainBoard = true);
// Used when modifying the board without going through the motions,
// or preparing a freshly-loaded-from-file board for analysis
void ensureBoardIsPreparedForAnalysis();
// score specified move and return a move with score field
// filled in and equity field guessed at; score of
// non-place moves is zero
void scoreMove(Move &move);
// equity value is a combination of static score, player consideration,
// and shared consideration heuristics: (rack is assumed to be
// current player's rack if not specified)
double calculateEquity(const Move &move);
// 1. score of play
int calculateScore(const Move &move);
// 2. definitionally, rack leave value
double calculatePlayerConsideration(const Move &move);
// just get me the leave value, please
double leaveValue(const LetterString &leave) const;
// 3. other considerations like board geometry
double calculateSharedConsideration(const Move &move);
void setBoard(const Board &board);
const Board &board() const;
// all tiles not on board or players'
// (probably) filled racks
const Bag &bag() const;
// Set drawing order, starting from back of drawingOrder.
// The drawing order is reset to randomness after letters
// are drawn when incremented.
// This only is meaningful if you don't
// manually set racks and make only valid plays.
// (Otherwise there may be letters in the drawing order
// that are no longer in the bag. Incidentally the drawing
// order is also followed if you do set rack.)
void setDrawingOrder(const LetterString &drawingOrder);
const LetterString &drawingOrder() const;
// get tiles unseen to current player
Bag unseenBag() const;
Bag unseenBagFromPlayerPerspective(const Player &player) const;
// this must be called if you call makeMove with a nonvalid-for-
// current-player move
void ensureProperBag() const;
// reset board to be empty and prepared for a new game
void setEmptyBoard();
// current player *really* on turn
const Player ¤tPlayer() const;
Player ¤tPlayer();
// Returns true if currentPlayer was set, false if no player with
// this id was found.
bool setCurrentPlayer(int playerID);
// Fake player on turn.
// Of all libquackle classes, only History uses this field.
// Player on turn is reset to the current player whenever this
// position is incremented.
const Player &playerOnTurn() const;
Player &playerOnTurn();
// returns true if currentPlayer was set, false if no player with
// this id was found
bool setPlayerOnTurn(int playerID);
// Returns the current player if he or she is human,
// or the next human player if one exists,
// or the current player if all players are nonhuman.
const Player &humanPlayer() const;
// returns a pointer to the next player
PlayerList::const_iterator nextPlayer() const;
// Returns a pointer to the next player of a type.
// This starts its search on the player after the current player,
// so the current player will only be returned if it is the only player
// of the specified type.
// If no player of that type exists, the current player is returned
// and found is set to false.
PlayerList::const_iterator nextPlayerOfType(Player::PlayerType type, bool &found) const;
// Returns the player with id specified, or the current player
// if not found.
PlayerList::const_iterator playerWithId(int id, bool &found) const;
const PlayerList &players() const;
// If in the endgame, returns players with adjusted scores.
// Otherwise returns players.
PlayerList endgameAdjustedScores() const;
// returns the players with the highest score
PlayerList leadingPlayers() const;
// returns point difference between current player and
// highest-scoring other player
int spread() const;
// as above, but treating playerID as current player
int spread(int playerID) const;
// returns true when there is a player with an empty rack and the bag
// is empty, so in all frames after the frame containing a player
// making the final move, gameOver is true
bool gameOver() const;
// the move made will
// be set to the proper UnusedTileBonus move.
// The score of the bonus is *not* added to score of current player,
// therefor this method is quite misnamed.
void adjustScoresToFinishGame();
// this is a little special: don't subtract score of bonus from
// current player, but subtract it from other players; set move made
// to a negative UnusedTilesBonus for the current player
void adjustScoresToFinishPassedOutGame();
// returns true if the game will be over after committing this move
bool doesMoveEndGame(const Move &move) const;
// if the game is over or will be after this turn,
// returns how many extra points the current player will get,
// and which tiles those are
int deadwood(LetterString *tiles) const;
// replenishes previousRack from bag and sets it as current player's rack
void replenishAndSetRack(const Rack &previousRack);
void replenishAndSetRack(const Rack &previousRack, Player &player);
// set rack of current player.
// This isn't simple because we must ensure the bag contains the proper tiles,
// if adjustBag is true, by readding to bag tiles were on rack but no longer
void setCurrentPlayerRack(const Rack &rack, bool adjustBag = true);
// same as above for a given player
void setPlayerRack(int playerID, const Rack &rack, bool adjustBag = true);
// Set rack of current player's opponent.
// This is broken for number of players != 2 and but I don't care much.
// This isn't simple because we must ensure the bag contains the proper tiles,
// if adjustBag is true, by readding to bag tiles were on rack but no longer
void setOppRack(const Rack &rack, bool adjustBag = true);
// return opponent's rack (for preendgame win% usage)
Rack oppRack();
// if we set player's rack to this, will there be more tiles in
// play than before?
bool canSetCurrentPlayerRackWithoutBagExpansion(const Rack &rack) const;
bool canSetPlayerRackWithoutBagExpansion(int playerID, const Rack &rack) const;
// set bag to a full bag of tiles
void resetBag();
// set bag to whatever you want
void setBag(const Bag &bag);
// move made FROM this position, or candidate play being considered
void setMoveMade(const Move &move);
const Move &moveMade() const;
// Move committed from this position. Can be different from the moveMade
// for example if the user loads a gcg file then looks at different
// possible plays without committing them.
void setCommittedMove(const Move &move);
const Move &committedMove() const;
// saves the current candidate as the committedMove.
void prepareForCommit();
// returns the board with the moveMade made on it.
// Note that this board won't be ready to be fed to a Generator!
// It should only be used to have a representation of a board for
// example to display.
Board boardAfterMoveMade() const;
// set move made to the default, a nonmove
void resetMoveMade();
// The new position is made by incrementing
// the current player.If the current player was the last in the player list,
// turnNumber is incremented in the created position and true is returned,
// otherwise false is returned.
// Player on turn is set to the new current player.
// If after incrementing, gameOver() is true, then the current player is
// reset to be the player who made the last turn of the game.
// If needed, current player at time of call's rack is refilled
// from the bag based on move made.
// Furthermove, any empty player's rack is refilled (this happens
// at start of game.)
// If applicable, this player's score is also incremented by score of move
// made.
bool incrementTurn();
// Turn numbers in games start from 1.
// A turn number of zero indicates a position that is pregame.
int turnNumber() const;
// nestedness of this position in a recursive calculation
void setNestedness(unsigned int nestedness);
void incrementNestedness();
unsigned int nestedness() const;
// returns a blank string that is one tab per nestedness
UVString nestednessIndentation() const;
// number of plays in a row that have been scoreless
int scorelessTurnsInARow() const;
HistoryLocation location() const;
const UVString &explanatoryNote() const;
void setExplanatoryNote(const UVString &explanatoryNote);
// not recommended to use this!
Board &underlyingBoardReference();
protected:
PlayerList m_players;
PlayerList::iterator m_currentPlayer;
PlayerList::iterator m_playerOnTurn;
// moves originally from kibitzer
MoveList m_moves;
// It is very important that these two variables almost always be
// modified together, unless one is experimenting with different
// candidates before deciding on the move to commit.
// For instance, the end-of-game bonus move is stored in both fields.
Move m_moveMade;
Move m_committedMove;
// I feel safer with this not being public
void setTurnNumber(int turnNumber);
int m_turnNumber;
unsigned int m_nestedness;
int m_scorelessTurnsInARow;
bool m_gameOver;
Quackle::Board m_board;
// Keeping the bag holding the correct letters is VOUDOU!
// If you break its behavior, I will be bemaddened!
Quackle::Bag m_bag;
LetterString m_drawingOrder;
UVString m_explanatoryNote;
// Use this instead of m_bag.removeTiles(); if the bag
// doesn't contain the tiles to remove, it removes from
// non-current player racks if they have tiles and then refills.
// Returns false if one of the letters was found nowhere to be
// removed from.
bool removeLetters(const LetterString &letters);
};
inline const Player &GamePosition::currentPlayer() const
{
return *m_currentPlayer;
};
inline Player &GamePosition::currentPlayer()
{
return *m_currentPlayer;
};
inline const Player &GamePosition::playerOnTurn() const
{
return *m_playerOnTurn;
};
inline Player &GamePosition::playerOnTurn()
{
return *m_playerOnTurn;
};
inline const MoveList &GamePosition::moves() const
{
return m_moves;
}
inline void GamePosition::setMoves(const MoveList &moves)
{
m_moves = moves;
}
inline void GamePosition::setBoard(const Board &board)
{
m_board = board;
}
inline const Board &GamePosition::board() const
{
return m_board;
}
inline Board &GamePosition::underlyingBoardReference()
{
return m_board;
}
inline const Bag &GamePosition::bag() const
{
return m_bag;
}
inline void GamePosition::setBag(const Bag &bag)
{
m_bag = bag;
}
inline void GamePosition::setDrawingOrder(const LetterString &drawingOrder)
{
m_drawingOrder = drawingOrder;
}
inline const LetterString &GamePosition::drawingOrder() const
{
return m_drawingOrder;
}
inline const PlayerList &GamePosition::players() const
{
return m_players;
}
inline int GamePosition::spread() const
{
return spread(currentPlayer().id());
}
inline void GamePosition::replenishAndSetRack(const Rack &previousRack)
{
replenishAndSetRack(previousRack, currentPlayer());
}
inline bool GamePosition::gameOver() const
{
return m_gameOver;
}
inline void GamePosition::setMoveMade(const Move &move)
{
m_moveMade = move;
}
inline const Move &GamePosition::moveMade() const
{
return m_moveMade;
}
inline void GamePosition::setCommittedMove(const Move &move)
{
m_committedMove = move;
}
inline const Move &GamePosition::committedMove() const
{
return m_committedMove;
}
inline void GamePosition::setTurnNumber(int turnNumber)
{
m_turnNumber = turnNumber;
}
inline void GamePosition::setNestedness(unsigned int nestedness)
{
m_nestedness = nestedness;
}
inline void GamePosition::incrementNestedness()
{
++m_nestedness;
}
inline int GamePosition::scorelessTurnsInARow() const
{
return m_scorelessTurnsInARow;
}
inline HistoryLocation GamePosition::location() const
{
return HistoryLocation(currentPlayer().id(), turnNumber());
}
inline int GamePosition::turnNumber() const
{
return m_turnNumber;
}
inline unsigned int GamePosition::nestedness() const
{
return m_nestedness;
}
inline const UVString &GamePosition::explanatoryNote() const
{
return m_explanatoryNote;
}
inline void GamePosition::setExplanatoryNote(const UVString &explanatoryNote)
{
m_explanatoryNote = explanatoryNote;
}
// simple modifiable list of positions
typedef vector<GamePosition> PositionList;
class Game;
// unmodifiable list of positions in a game
class History : public PositionList
{
public:
History()
: /* a position in the past */ m_currentLocation(0, -1)
{
}
const PlayerList &players() const;
const Player &lastPlayer() const;
const GamePosition &lastPosition() const;
// returns number of levels of turns so far in this game
// If this contains no positions, returns 0.
// Remember turns are numbered starting at 1.
int maximumTurnNumber() const;
// location at [maximumTurnNumber(), lastPlayer().id()]
HistoryLocation lastLocation() const;
// current location and position -- usually the last one unless
// it's been manually set with Game::setCurrentLocation.
HistoryLocation currentLocation() const;
const GamePosition ¤tPosition() const;
GamePosition ¤tPosition();
// all positions this player has been in
PositionList positionsFacedBy(int playerID) const;
// the next position after the current position
const GamePosition &nextPosition(bool *exists = 0) const;
// the next item of positionsFacedBy(playerID)
const GamePosition &nextPositionFacedBy(int playerID, bool *exists = 0) const;
// the previous position before the current position
const GamePosition &previousPosition(bool *exists = 0) const;
// the first position in the game
const GamePosition &firstPosition(bool *exists = 0) const;
const GamePosition &positionAt(const HistoryLocation &location, bool *exists = 0) const;
private:
PlayerList m_players;
void setPlayers(const PlayerList &list);
HistoryLocation m_currentLocation;
void setCurrentLocation(const HistoryLocation &location);
// erase all positions that are after location gamewise
void eraseAfter(const HistoryLocation &location);
GamePosition &mutablePositionAt(const HistoryLocation &location, bool *exists = 0);
GamePosition &lastPosition();
friend class Game;
};
inline void History::setPlayers(const PlayerList &list)
{
m_players = list;
}
inline const PlayerList &History::players() const
{
return m_players;
}
inline const Player &History::lastPlayer() const
{
return back().playerOnTurn();
}
inline const GamePosition &History::lastPosition() const
{
return back();
}
inline GamePosition &History::lastPosition()
{
return back();
}
inline HistoryLocation History::lastLocation() const
{
return lastPosition().location();
}
inline HistoryLocation History::currentLocation() const
{
return m_currentLocation;
}
inline const GamePosition &History::currentPosition() const
{
return positionAt(currentLocation());
}
inline GamePosition &History::currentPosition()
{
return mutablePositionAt(currentLocation());
}
inline void History::setCurrentLocation(const HistoryLocation &location)
{
m_currentLocation = location;
}
class Game
{
public:
Game();
~Game();
// return to like-new board and empty player list.
// Use to make new game. See setPlayers() and addPosition();
// both should each be called at least once manually
// (with setPlayers() coming before addPosition())
void reset();
// game starts with first player of list.
// This method sets the ids of the players to
// numbers in ascending order, starting at 0.
// Because the player IDs cannot be later changed,
// this is a representation invariant.
void setPlayers(const PlayerList &list);
const PlayerList &players() const;
// returns whether or not addPosition has been called after a
// reset or being newly constructed
bool hasPositions() const;
// history of all positions encountered this game, including
// the current position
const History &history() const;
// This should be called after players() to get the first player
// started off with his or her first rack and the game underway.
//
// implementation details:
// Create a new GamePosition that is a copy of the current position
// and add it to the end of our positions list, making it the
// current position.
// The first position added after a reset() is the first player
// going first to start the game.
// In other cases, the new current position is the
// GamePosition::incrementTurn() of the older position,
// with racks replenished from suitable bag
void addPosition();
// don't call without having called addPosition!
const GamePosition ¤tPosition() const;
GamePosition ¤tPosition();
// moves our current location pointer
void setCurrentPosition(const HistoryLocation &location);
// set current position to position and point current
// position to this position (strange that this must
// be explicit, but it must be)
void setCurrentPosition(const GamePosition &position);
// Sets the computer player that will play for player with
// given playerId. Game does not take control of deleting this pointer.
void associateComputerPlayer(int playerId, ComputerPlayer *computerPlayer);
// returns the computer player associated with given playerId
ComputerPlayer *computerPlayer(int playerId);
// sets the computer player that will play for players for whom
// a computer player has not been associated
void setDefaultComputerPlayer(ComputerPlayer *computerPlayer);
// associates computer players for players with known ComputerPlayer
// type
void associateKnownComputerPlayers();
ComputerPlayer *defaultComputerPlayer();
// Have quackle commit a move for the current player
// using the specified computerPlayer, or, if computerPlayer is 0,
// use the associated computer player,
// or a default computer player if none has been associated.
// Returns the move that is played.
// Does nothing if the game is over.
Move haveComputerPlay(ComputerPlayer *computerPlayer = 0);
// haveComputerPlay() until the current player type is not
// ComputerPlayer or the game is over
void advanceToNoncomputerPlayer();
// the candidate play is stored in currentPosition().moveMade()
// and defaults to a nonmove.
//
// put candidate on platform, the current position
void setCandidate(const Move &move);
const Move &candidate() const;
// reset candidate to a nonmove
void resetCandidature();
// Copy current candidate play as the moveMade in the current position
// then addPosition and set up the new position with the play on the board.
// Also makes sure the bag is full of only kosher letters afterward.
// maintainBoard must be true if you want to keep using this game.
// If the game is over, does nothing.
void commitCandidate(bool maintainBoard = true);
// convience to set move as candidate and then commit the candidate
void commitMove(const Move &move);
const UVString &description() const;
void setDescription(const UVString &description);
const UVString &title() const;
void setTitle(const UVString &title);
protected:
History m_positions;
typedef map<int, ComputerPlayer *> ComputerPlayerMap;
ComputerPlayer *m_defaultComputerPlayer;
ComputerPlayerMap m_computerPlayers;
UVString m_title;
UVString m_description;
// create a new GamePosition that is a copy of the current position
// without incrementing. Adds it after the current position and
// erases all later positions.
void addClonePosition();
};
inline const PlayerList &Game::players() const
{
return m_positions.players();
}
inline bool Game::hasPositions() const
{
return !m_positions.empty();
}
inline const History &Game::history() const
{
return m_positions;
}
inline void Game::setCandidate(const Move &move)
{
currentPosition().setMoveMade(move);
}
inline const Move &Game::candidate() const
{
return currentPosition().moveMade();
}
inline void Game::resetCandidature()
{
currentPosition().resetMoveMade();
}
inline const GamePosition &Game::currentPosition() const
{
return m_positions.currentPosition();
}
inline GamePosition &Game::currentPosition()
{
return m_positions.currentPosition();
}
inline void Game::setCurrentPosition(const HistoryLocation &location)
{
m_positions.setCurrentLocation(location);
}
inline const UVString &Game::description() const
{
return m_description;
}
inline void Game::setDescription(const UVString &description)
{
m_description = description;
}
inline const UVString &Game::title() const
{
return m_title;
}
inline void Game::setTitle(const UVString &title)
{
m_title = title;
}
}
// comparison based on turn number then player id
bool operator<(const Quackle::HistoryLocation &hl1, const Quackle::HistoryLocation &hl2);
bool operator==(const Quackle::HistoryLocation &hl1, const Quackle::HistoryLocation &hl2);
UVOStream& operator<<(UVOStream& o, const Quackle::GamePosition &position);
UVOStream& operator<<(UVOStream& o, const Quackle::PositionList &positions);
UVOStream& operator<<(UVOStream& o, const Quackle::HistoryLocation &historyLocation);
#endif