27 #include <QApplication>
36 enum MoveType {
Stone, Passed, Resigned };
38 UndoCommand(
Player *player, MoveType moveType,
const QString &undoStr)
39 :
QUndoCommand(undoStr), m_player(player), m_moveType(moveType)
42 Player *player ()
const {
return m_player; }
43 MoveType moveType ()
const {
return m_moveType; }
53 , m_currentMove(0), m_lastUndoIndex(0), m_currentPlayer(&m_blackPlayer)
54 , m_blackPlayer(
Player::Black), m_whitePlayer(
Player::White)
55 , m_komi(4.5), m_boardSize(19), m_fixedHandicap(5), m_consecutivePassMoveNumber(0)
56 , m_gameFinished(false)
58 connect(&m_process, SIGNAL(readyRead()),
this, SLOT(readyRead()));
61 connect(&m_undoStack, SIGNAL(indexChanged(
int)),
this, SLOT(undoIndexChanged(
int)));
74 m_response =
"Unable to execute command: " + command;
75 kDebug() << m_response;
78 m_engineCommand = command;
82 m_process.
write(
"name\n");
86 m_response =
"Game did not respond to GTP command \"name\"";
87 kDebug() << m_response;
91 m_engineName = m_response;
95 m_process.
write(
"version\n");
97 m_engineVersion = m_response;
105 m_process.
write(
"quit\n");
118 m_process.
write(
"clear_board\n");
119 if (waitResponse()) {
121 setCurrentPlayer(m_blackPlayer);
123 m_consecutivePassMoveNumber = 0;
125 m_gameFinished =
false;
138 Q_ASSERT(moveNumber >= 0);
144 if (waitResponse()) {
146 setCurrentPlayer(m_whitePlayer);
148 setCurrentPlayer(m_blackPlayer);
151 m_process.
write(
"query_boardsize\n");
152 if (waitResponse()) {
153 m_boardSize = m_response.
toInt();
155 m_process.
write(
"get_komi\n");
156 if (waitResponse()) {
159 m_process.
write(
"get_handicap\n");
160 if (waitResponse()) {
161 m_fixedHandicap = m_response.
toInt();
165 m_consecutivePassMoveNumber = 0;
166 m_currentMove = moveNumber;
167 m_gameFinished =
false;
186 return waitResponse();
191 Q_ASSERT(size >= 1 && size <= 19);
197 if (waitResponse()) {
199 setCurrentPlayer(m_blackPlayer);
202 m_consecutivePassMoveNumber = 0;
222 if (waitResponse()) {
232 Q_ASSERT(handicap >= 2 && handicap <= 9);
239 if (waitResponse()) {
242 setCurrentPlayer(m_whitePlayer);
243 m_fixedHandicap = handicap;
257 switch (m_boardSize) {
286 const Player *tmp = &player;
289 tmp = m_currentPlayer;
304 m_process.
write(msg);
305 if (waitResponse()) {
307 m_movesList.append(
Move(tmp, stone));
308 m_consecutivePassMoveNumber = 0;
312 if (m_consecutivePassMoveNumber > 0) {
315 m_consecutivePassMoveNumber++;
321 UndoCommand::MoveType moveType;
324 player = &m_whitePlayer;
326 moveType = UndoCommand::Stone;
327 undoStr = i18nc(
"%1 stone coordinate",
"White %1", stone.
toString());
329 moveType = UndoCommand::Passed;
330 undoStr = i18n(
"White passed");
333 player = &m_blackPlayer;
335 moveType = UndoCommand::Stone;
336 undoStr = i18nc(
"%1 stone coordinate",
"Black %1", stone.
toString());
338 moveType = UndoCommand::Passed;
339 undoStr = i18n(
"Black passed");
343 m_undoStack.
push(
new UndoCommand(player, moveType, undoStr));
347 setCurrentPlayer(m_blackPlayer);
349 setCurrentPlayer(m_whitePlayer);
364 bool boardChange =
false;
365 const Player *tmp = &player;
368 tmp = m_currentPlayer;
374 m_process.
write(
"genmove white\n");
378 m_process.
write(
"genmove black\n");
380 if (waitResponse(
true)) {
382 UndoCommand::MoveType moveType;
386 player = &m_whitePlayer;
388 player = &m_blackPlayer;
390 if (m_response ==
"PASS") {
393 if (m_consecutivePassMoveNumber > 0) {
395 m_gameFinished =
true;
397 m_consecutivePassMoveNumber++;
398 moveType = UndoCommand::Passed;
400 undoStr = i18n(
"White passed");
402 undoStr = i18n(
"Black passed");
404 }
else if (m_response ==
"resign") {
406 m_gameFinished =
true;
407 moveType = UndoCommand::Resigned;
409 undoStr = i18n(
"White resigned");
411 undoStr = i18n(
"Black resigned");
415 m_movesList.append(
Move(tmp,
Stone(m_response)));
416 m_consecutivePassMoveNumber = 0;
417 moveType = UndoCommand::Stone;
419 undoStr = i18nc(
"%1 response from Go engine",
"White %1", m_response);
421 undoStr = i18nc(
"%1 response from Go engine",
"Black %1", m_response);
428 m_undoStack.
push(
new UndoCommand(player, moveType, undoStr));
431 setCurrentPlayer(m_blackPlayer);
433 setCurrentPlayer(m_whitePlayer);
450 m_process.
write(
"undo\n");
451 if (waitResponse()) {
456 m_process.
write(
"undo\n");
457 if (waitResponse()) {
458 lastMove = m_movesList.takeLast();
464 setCurrentPlayer(m_whitePlayer);
466 setCurrentPlayer(m_blackPlayer);
468 if (m_consecutivePassMoveNumber > 0) {
469 m_consecutivePassMoveNumber--;
487 const UndoCommand *undoCmd =
static_cast<const UndoCommand*
>(m_undoStack.
command(m_undoStack.
index()));
489 Player *player = undoCmd->player();
491 if (undoCmd->moveType() == UndoCommand::Passed) {
492 kDebug() <<
"Redo a pass move for" << player << undoCmd->text();
494 }
else if (undoCmd->moveType() == UndoCommand::Resigned) {
497 kDebug() <<
"Redo a resign for" << player << undoCmd->text();
499 m_gameFinished =
true;
502 kDebug() <<
"Redo a normal move for" << player << undoCmd->text();
511 Q_ASSERT(!m_movesList.isEmpty());
515 return m_movesList.last();
524 m_process.
write(
"move_history\n");
525 if (waitResponse()) {
526 return m_response.
count(
'\n') + 1;
540 m_process.
write(
"list_stones black\n");
541 if (waitResponse() && !m_response.
isEmpty()) {
548 m_process.
write(
"list_stones white\n");
549 if (waitResponse() && !m_response.
isEmpty()) {
568 foreach (
const Move &move, m_movesList) {
585 if (waitResponse() && !m_response.
isEmpty()) {
601 m_process.
write(
"top_moves_white\n");
603 m_process.
write(
"top_moves_black\n");
605 if (waitResponse(
true) && !m_response.
isEmpty()) {
607 if (parts.
size() % 2 == 0) {
608 for (
int i = 0; i < parts.
size(); i += 2) {
624 m_process.
write(
"all_legal white\n");
626 m_process.
write(
"all_legal black\n");
628 if (waitResponse() && !m_response.
isEmpty()) {
643 m_process.
write(
"captures white\n");
645 m_process.
write(
"captures black\n");
647 return waitResponse() ? m_response.
toInt() : 0;
657 if (waitResponse()) {
659 else if (m_response ==
"dead")
return FinalDead;
660 else if (m_response ==
"seki")
return FinalSeki;
663 else if (m_response ==
"dame")
return FinalDame;
687 m_process.
write(msg);
688 if (waitResponse() && !m_response.
isEmpty()) {
702 m_process.
write(
"final_score\n");
703 return waitResponse() ?
Score(m_response) :
Score();
712 m_process.
write(
"estimate_score\n");
713 return waitResponse() ?
Score(m_response) :
Score();
716 bool Game::waitResponse(
bool nonBlocking)
718 if (m_process.
state() != QProcess::Running) {
719 switch (m_process.
error()) {
720 case QProcess::FailedToStart: m_response =
"No Go game is running!";
break;
721 case QProcess::Crashed: m_response =
"The Go game crashed!";
break;
722 case QProcess::Timedout: m_response =
"The Go game timed out!";
break;
725 case QProcess::UnknownError: m_response =
"Unknown error!";
break;
727 kWarning() <<
"Command failed:" << m_response;
744 while (m_waitAndProcessEvents) {
745 qApp->processEvents();
747 m_waitAndProcessEvents =
true;
760 if (m_response.
size() < 1) {
763 QChar tmp = m_response[0];
765 m_response = m_response.
trimmed();
774 void Game::readyRead()
776 m_waitAndProcessEvents =
false;
779 void Game::undoIndexChanged(
int index)
797 m_lastUndoIndex = index;
800 void Game::setCurrentPlayer(Player &player)
802 m_currentPlayer = &player;
808 #include "moc_game.cpp"
This class represents a Go score for either player.
void canRedoChanged(bool)
This signal is emitted when availability of redo moves changes.
QList< Stone > finalStates(FinalState state)
Report fields with a specified final status in a finished game.
bool setKomi(float komi)
Set the komi.
const Stone & stone() const
virtual bool waitForReadyRead(int msecs)
QProcess::ProcessError error() const
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
FinalState finalState(const Stone &field)
Report the final status of a field in a finished game.
Score estimatedScore()
Returns an estimate of the final score based on the current game situation.
The state is invalid, shows error.
void boardSizeChanged(int)
This signal is emitted when the board size was changed.
bool start(const QString &command="gnugo --mode gtp")
Connect to the given Go game game in GTP mode.
The field belongs to the black player.
QList< Stone > legalMoves(const Player &player)
List all legal moves for either player.
const QUndoCommand * command(int index) const
QString & remove(int position, int n)
void consecutivePassMovesPlayed(int)
This signal is emitted when both players played a pass move after another.
void waiting(bool)
This signal is emitted when the game starts or ends a non-blocking wait.
The stone on the field is alive.
void append(const T &value)
void currentPlayerChanged(const Player &)
This signal is emitted when the current player changes.
QList< Stone > bestMoves(const Player &player)
Generate a list of the best moves for a player with weights.
static Stone Pass
A standard pass move object.
int toInt(bool *ok, int base) const
This class represents a stone on a field of the game board.
QByteArray number(int n, int base)
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
int moveCount()
Returns the number of moves in the game so far.
Score finalScore()
Compute the score of a finished game.
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
QList< Stone > stones(const Player &player)
Returns a list of all stones of that player on the board.
Move lastMove() const
Returns the last move made by either player.
const Player * player() const
void boardInitialized()
This signal is emitted when the board is first started and can be used to trigger an update to a visu...
bool setBoardSize(int size)
Set the board size to NxN.
bool isRunning() const
Check whether the Game object is connected to a Go game, running and waiting for commands to be fed w...
int captures(const Player &player)
List the number of captures taken by either player.
The Player class holds all basic attributes of a Go player.
QByteArray & append(char ch)
int fixedHandicapUpperBound()
Returns the maximum amount fixed handicap stones placeable at the current Go board size...
bool waitForStarted(int msecs)
QByteArray toLatin1() const
The field belongs to the white player.
The field belongs to no player.
QList< Stone > liberties(const Stone &field)
Returns the positions of the liberties for the stone at 'field'.
bool init()
Initialize a new game.
QByteArray toLatin1() const
float toFloat(bool *ok) const
The stone on the field is dead.
The Move class is a light-weight representation of a Go move (to be) made by a Go player...
void resigned(const Player &)
This signal is emitted when a player resigns.
qint64 write(const char *data, qint64 maxSize)
void stop()
Gracefully stop and exit the Go game game.
bool setFixedHandicap(int handicap)
Set up fixed placement handicap stones.
void passMovePlayed(const Player &)
FinalState
Enumeration of all possible final states of a field when a game is over.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool save(const QString &fileName)
Save the current game as a SGF file.
bool playMove(const Move &move, bool undoable=true)
QByteArray readAllStandardOutput()
void start(const QString &program, const QStringList &arguments, QFlags< QIODevice::OpenModeFlag > mode)
QProcess::ProcessState state() const
QByteArray readAllStandardError()
void boardChanged()
This signal is emitted when the board situation changed and can be used to trigger an update to a vis...
void push(QUndoCommand *cmd)
void canUndoChanged(bool)
This signal is emitted when availability of undo moves changes.
bool generateMove(const Player &player, bool undoable=true)