• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdegames API Reference
  • KDE Home
  • Contact Us
 

kigo

  • sources
  • kde-4.14
  • kdegames
  • kigo
  • src
  • game
game.cpp
Go to the documentation of this file.
1 /*
2  Copyright 2008 Sascha Peilicke <sasch.pe@gmx.de>
3 
4  This program is free software; you can redistribute it and/or
5  modify it under the terms of the GNU General Public License as
6  published by the Free Software Foundation; either version 2 of
7  the License or (at your option) version 3 or any later version
8  accepted by the membership of KDE e.V. (or its successor approved
9  by the membership of KDE e.V.), which shall act as a proxy
10  defined in Section 14 of version 3 of the license.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "game.h"
22 #include "score.h"
23 
24 #include <KDebug>
25 #include <KLocale>
26 
27 #include <QApplication>
28 #include <QFile>
29 
30 namespace Kigo {
31 
32 class UndoCommand : public QUndoCommand
33 {
34  public:
35 
36  enum MoveType { Stone, Passed, Resigned };
37 
38  UndoCommand(Player *player, MoveType moveType, const QString &undoStr)
39  : QUndoCommand(undoStr), m_player(player), m_moveType(moveType)
40  {}
41 
42  Player *player () const { return m_player; }
43  MoveType moveType () const { return m_moveType; }
44 
45  private:
46 
47  Player *m_player;
48  MoveType m_moveType;
49 };
50 
51 Game::Game(QObject *parent)
52  : QObject(parent)
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)
57 {
58  connect(&m_process, SIGNAL(readyRead()), this, SLOT(readyRead()));
59  connect(&m_undoStack, SIGNAL(canRedoChanged(bool)), this, SIGNAL(canRedoChanged(bool)));
60  connect(&m_undoStack, SIGNAL(canUndoChanged(bool)), this, SIGNAL(canUndoChanged(bool)));
61  connect(&m_undoStack, SIGNAL(indexChanged(int)), this, SLOT(undoIndexChanged(int)));
62 }
63 
64 Game::~Game()
65 {
66  stop();
67 }
68 
69 bool Game::start(const QString &command)
70 {
71  stop(); // Close old session if there's one
72  m_process.start(command.toLatin1()); // Start new process with provided command
73  if (!m_process.waitForStarted()) { // Blocking wait for process start
74  m_response = "Unable to execute command: " + command;
75  kDebug() << m_response;
76  return false;
77  }
78  m_engineCommand = command;
79  //kDebug() << "Game" << command << "started...";
80 
81  // Test if we started a GTP-compatible Go game
82  m_process.write("name\n");
83  m_process.waitForReadyRead();
84  QString response = m_process.readAllStandardOutput();
85  if (response.isEmpty() || !response.startsWith(QLatin1String("="))) {
86  m_response = "Game did not respond to GTP command \"name\"";
87  kDebug() << m_response;
88  stop();
89  return false;
90  } else {
91  m_engineName = m_response;
92  }
93  //kDebug() << "Game is a GTP-compatible Go game";
94 
95  m_process.write("version\n");
96  if (waitResponse()) {
97  m_engineVersion = m_response;
98  }
99  return true;
100 }
101 
102 void Game::stop()
103 {
104  if (m_process.isOpen()) {
105  m_process.write("quit\n");
106  m_process.close();
107  }
108 }
109 
110 bool Game::init()
111 {
112  if (!isRunning()) {
113  return false;
114  }
115 
116  //kDebug() << "Init game!";
117 
118  m_process.write("clear_board\n");
119  if (waitResponse()) {
120  // The board is wiped empty, start again with black player
121  setCurrentPlayer(m_blackPlayer);
122  m_fixedHandicap = 0;
123  m_consecutivePassMoveNumber = 0;
124  m_currentMove = 0;
125  m_gameFinished = false;
126  m_movesList.clear();
127  m_undoStack.clear();
128 
129  emit boardChanged();
130  return true;
131  } else {
132  return false;
133  }
134 }
135 
136 bool Game::init(const QString &fileName, int moveNumber)
137 {
138  Q_ASSERT(moveNumber >= 0);
139  if (!isRunning() || fileName.isEmpty() || !QFile::exists(fileName)) {
140  return false;
141  }
142 
143  m_process.write("loadsgf " + fileName.toLatin1() + ' ' + QByteArray::number(moveNumber) + '\n');
144  if (waitResponse()) {
145  if (m_response.startsWith(QLatin1String("white"))) { // Check which player is current
146  setCurrentPlayer(m_whitePlayer);
147  } else {
148  setCurrentPlayer(m_blackPlayer);
149  }
150 
151  m_process.write("query_boardsize\n"); // Query board size from game
152  if (waitResponse()) {
153  m_boardSize = m_response.toInt();
154  }
155  m_process.write("get_komi\n"); // Query komi from game and store it
156  if (waitResponse()) {
157  m_komi = m_response.toFloat();
158  }
159  m_process.write("get_handicap\n"); // Query fixed handicap and store it
160  if (waitResponse()) {
161  m_fixedHandicap = m_response.toInt();
162  }
163  //kDebug() << "Loaded komi is" << m_komi << "and handicap is" << m_fixedHandicap;
164 
165  m_consecutivePassMoveNumber = 0;
166  m_currentMove = moveNumber;
167  m_gameFinished = false;
168  m_movesList.clear();
169  m_undoStack.clear();
170 
171  emit boardSizeChanged(m_boardSize);
172  emit boardChanged(); // All done, tell the world!
173  return true;
174  } else {
175  return false;
176  }
177 }
178 
179 bool Game::save(const QString &fileName)
180 {
181  if (!isRunning() || fileName.isEmpty()) {
182  return false;
183  }
184 
185  m_process.write("printsgf " + fileName.toLatin1() + '\n');
186  return waitResponse();
187 }
188 
189 bool Game::setBoardSize(int size)
190 {
191  Q_ASSERT(size >= 1 && size <= 19);
192  if (!isRunning()) {
193  return false;
194  }
195 
196  m_process.write("boardsize " + QByteArray::number(size) + '\n');
197  if (waitResponse()) {
198  // Changing size wipes the board, start again with black player.
199  setCurrentPlayer(m_blackPlayer);
200  m_boardSize = size;
201  m_fixedHandicap = 0;
202  m_consecutivePassMoveNumber = 0;
203  m_currentMove = 0;
204  m_movesList.clear();
205  m_undoStack.clear();
206  emit boardSizeChanged(size);
207  emit boardChanged();
208  return true;
209  } else {
210  return false;
211  }
212 }
213 
214 bool Game::setKomi(float komi)
215 {
216  Q_ASSERT(komi >= 0);
217  if (!isRunning()) {
218  return false;
219  }
220 
221  m_process.write("komi " + QByteArray::number(komi) + '\n');
222  if (waitResponse()) {
223  m_komi = komi;
224  return true;
225  } else {
226  return false;
227  }
228 }
229 
230 bool Game::setFixedHandicap(int handicap)
231 {
232  Q_ASSERT(handicap >= 2 && handicap <= 9);
233  if (!isRunning()) {
234  return false;
235  }
236 
237  if (handicap <= fixedHandicapUpperBound()) {
238  m_process.write("fixed_handicap " + QByteArray::number(handicap) + '\n');
239  if (waitResponse()) {
240  // Black starts with setting his (fixed) handicap as it's first turn
241  // which means, white is next.
242  setCurrentPlayer(m_whitePlayer);
243  m_fixedHandicap = handicap;
244  emit boardChanged();
245  return true;
246  } else {
247  return false;
248  }
249  } else {
250  //kWarning() << "Handicap" << handicap << " not set, it is too high!";
251  return false;
252  }
253 }
254 
255 int Game::fixedHandicapUpperBound()
256 {
257  switch (m_boardSize) { // Handcrafted values reflect what GnuGo accepts
258  case 7:
259  case 8:
260  case 10:
261  case 12:
262  case 14:
263  case 16:
264  case 18: return 4;
265  case 9:
266  case 11:
267  case 13:
268  case 15:
269  case 17:
270  case 19: return 9;
271  default: return 0;
272  }
273 }
274 
275 bool Game::playMove(const Move &move, bool undoable)
276 {
277  return playMove(*move.player(), move.stone(), undoable);
278 }
279 
280 bool Game::playMove(const Player &player, const Stone &stone, bool undoable)
281 {
282  if (!isRunning()) {
283  return false;
284  }
285 
286  const Player *tmp = &player;
287  if (!tmp->isValid()) {
288  //kDebug() << "Invalid player argument, using current player!";
289  tmp = m_currentPlayer;
290  }
291 
292  QByteArray msg("play "); // The command to be sent
293  if (tmp->isWhite()) {
294  msg.append("white ");
295  } else {
296  msg.append("black ");
297  }
298  if (stone.isValid()) {
299  msg.append(stone.toLatin1() + '\n');
300  } else {
301  msg.append("pass\n");
302  }
303 
304  m_process.write(msg); // Send command to backend
305  if (waitResponse()) {
306  if (stone.isValid()) { // Normal move handling
307  m_movesList.append(Move(tmp, stone));
308  m_consecutivePassMoveNumber = 0;
309  } else { // And pass move handling
310  m_movesList.append(Move(tmp, Stone::Pass));
311  emit passMovePlayed(*m_currentPlayer);
312  if (m_consecutivePassMoveNumber > 0) {
313  emit consecutivePassMovesPlayed(m_consecutivePassMoveNumber);
314  }
315  m_consecutivePassMoveNumber++;
316  }
317  m_currentMove++;
318 
319  if (undoable) { // Do undo stuff if desired
320  Player *player;
321  UndoCommand::MoveType moveType;
322  QString undoStr;
323  if (tmp->isWhite()) {
324  player = &m_whitePlayer;
325  if (stone.isValid()) {
326  moveType = UndoCommand::Stone;
327  undoStr = i18nc("%1 stone coordinate", "White %1", stone.toString());
328  } else {
329  moveType = UndoCommand::Passed;
330  undoStr = i18n("White passed");
331  }
332  } else {
333  player = &m_blackPlayer;
334  if (stone.isValid()) {
335  moveType = UndoCommand::Stone;
336  undoStr = i18nc("%1 stone coordinate", "Black %1", stone.toString());
337  } else {
338  moveType = UndoCommand::Passed;
339  undoStr = i18n("Black passed");
340  }
341  }
342  //kDebug() << "Push new undo command" << undoStr;
343  m_undoStack.push(new UndoCommand(player, moveType, undoStr));
344  }
345 
346  if (tmp->isWhite()) { // Determine the next current player
347  setCurrentPlayer(m_blackPlayer);
348  } else {
349  setCurrentPlayer(m_whitePlayer);
350  }
351 
352  emit boardChanged();
353  return true;
354  } else {
355  return false;
356  }
357 }
358 
359 bool Game::generateMove(const Player &player, bool undoable)
360 {
361  if (!isRunning()) {
362  return false;
363  }
364  bool boardChange = false;
365  const Player *tmp = &player;
366  if (!tmp->isValid()) {
367  //kDebug() << "Invalid player argument, using current player!";
368  tmp = m_currentPlayer;
369  }
370 
371  if (tmp->isWhite()) {
372  m_process.write("level " + QByteArray::number(m_whitePlayer.strength()) + '\n');
373  waitResponse(); // Setting level is not mission-critical, no error checking
374  m_process.write("genmove white\n");
375  } else {
376  m_process.write("level " + QByteArray::number(m_blackPlayer.strength()) + '\n');
377  waitResponse(); // Setting level is not mission-critical, no error checking
378  m_process.write("genmove black\n");
379  }
380  if (waitResponse(true)) {
381  Player *player;
382  UndoCommand::MoveType moveType;
383  QString undoStr;
384 
385  if (tmp->isWhite()) {
386  player = &m_whitePlayer;
387  } else {
388  player = &m_blackPlayer;
389  }
390  if (m_response == "PASS") {
391  m_currentMove++;
392  emit passMovePlayed(*m_currentPlayer);
393  if (m_consecutivePassMoveNumber > 0) {
394  emit consecutivePassMovesPlayed(m_consecutivePassMoveNumber);
395  m_gameFinished = true;
396  }
397  m_consecutivePassMoveNumber++;
398  moveType = UndoCommand::Passed;
399  if (tmp->isWhite()) {
400  undoStr = i18n("White passed");
401  } else {
402  undoStr = i18n("Black passed");
403  }
404  } else if (m_response == "resign") {
405  emit resigned(*m_currentPlayer);
406  m_gameFinished = true;
407  moveType = UndoCommand::Resigned;
408  if (tmp->isWhite()) {
409  undoStr = i18n("White resigned");
410  } else {
411  undoStr = i18n("Black resigned");
412  }
413  } else {
414  m_currentMove++;
415  m_movesList.append(Move(tmp, Stone(m_response)));
416  m_consecutivePassMoveNumber = 0;
417  moveType = UndoCommand::Stone;
418  if (tmp->isWhite()) {
419  undoStr = i18nc("%1 response from Go engine", "White %1", m_response);
420  } else {
421  undoStr = i18nc("%1 response from Go engine", "Black %1", m_response);
422  }
423  boardChange = true;
424  }
425 
426  if (undoable) {
427  //kDebug() << "Push new undo command" << undoStr;
428  m_undoStack.push(new UndoCommand(player, moveType, undoStr));
429  }
430  if (tmp->isWhite()) {
431  setCurrentPlayer(m_blackPlayer);
432  } else {
433  setCurrentPlayer(m_whitePlayer);
434  }
435  if (boardChange) {
436  emit boardChanged();
437  }
438  return true;
439  } else {
440  return false;
441  }
442 }
443 
444 bool Game::undoMove()
445 {
446  if (!isRunning()) {
447  return false;
448  }
449 
450  m_process.write("undo\n");
451  if (waitResponse()) {
452  Move lastMove = m_movesList.takeLast();
453  m_currentMove--;
454  if (lastMove.player()->isComputer()) {
455  // Do one more undo
456  m_process.write("undo\n");
457  if (waitResponse()) {
458  lastMove = m_movesList.takeLast();
459  m_currentMove--;
460  }
461  m_undoStack.undo();
462  }
463  if (lastMove.player()->isWhite()) {
464  setCurrentPlayer(m_whitePlayer);
465  } else {
466  setCurrentPlayer(m_blackPlayer);
467  }
468  if (m_consecutivePassMoveNumber > 0) {
469  m_consecutivePassMoveNumber--;
470  }
471  //TODO: What happens with pass moves deeper in the history?
472  m_undoStack.undo();
473 
474  emit boardChanged();
475  return true;
476  } else {
477  return false;
478  }
479 }
480 
481 bool Game::redoMove()
482 {
483  if (!isRunning()) {
484  return false;
485  }
486 
487  const UndoCommand *undoCmd = static_cast<const UndoCommand*>(m_undoStack.command(m_undoStack.index()));
488 
489  Player *player = undoCmd->player();
490 
491  if (undoCmd->moveType() == UndoCommand::Passed) {
492  kDebug() << "Redo a pass move for" << player << undoCmd->text();
493  playMove(*player, Stone(), false); // E.g. pass move
494  } else if (undoCmd->moveType() == UndoCommand::Resigned) {
495  // Note: Altough it is possible to undo after a resign and redo it,
496  // it is a bit questionable whether this makes sense logically.
497  kDebug() << "Redo a resign for" << player << undoCmd->text();
498  emit resigned(*player);
499  m_gameFinished = true;
500  //emit resigned(*m_currentPlayer);
501  } else {
502  kDebug() << "Redo a normal move for" << player << undoCmd->text();
503  playMove(*player, Stone(undoCmd->text()), false);
504  }
505  m_undoStack.redo();
506  return false;
507 }
508 
509 Move Game::lastMove() const
510 {
511  Q_ASSERT(!m_movesList.isEmpty());
512  /*if (m_movesList.isEmpty())
513  return Move(m_currentPlayer, Stone());
514  else*/
515  return m_movesList.last();
516 }
517 
518 int Game::moveCount()
519 {
520  if (!isRunning()) {
521  return 0;
522  }
523 
524  m_process.write("move_history\n"); // Query fixed handicap and store it
525  if (waitResponse()) {
526  return m_response.count('\n') + 1;
527  }
528  return 0;
529 }
530 
531 QList<Stone> Game::stones(const Player &player)
532 {
533  QList<Stone> list;
534  if (!isRunning()) {
535  return list;
536  }
537 
538  // Invalid player means all stones
539  if (!player.isWhite()) {
540  m_process.write("list_stones black\n");
541  if (waitResponse() && !m_response.isEmpty()) {
542  foreach (const QString &pos, m_response.split(' ')) {
543  list.append(Stone(pos));
544  }
545  }
546  }
547  if (!player.isBlack()) {
548  m_process.write("list_stones white\n");
549  if (waitResponse() && !m_response.isEmpty()) {
550  foreach (const QString &pos, m_response.split(' ')) {
551  list.append(Stone(pos));
552  }
553  }
554  }
555  return list;
556 }
557 
558 QList<Move> Game::moves(const Player &player)
559 {
560  QList<Move> list;
561  if (!isRunning()) {
562  return list;
563  }
564 
565  if (!player.isValid()) {
566  list = m_movesList;
567  } else {
568  foreach (const Move &move, m_movesList) {
569  if (move.player()->color() == player.color()) {
570  list.append(move);
571  }
572  }
573  }
574  return list;
575 }
576 
577 QList<Stone> Game::liberties(const Stone &stone)
578 {
579  QList<Stone> list;
580  if (!isRunning() || !stone.isValid()) {
581  return list;
582  }
583 
584  m_process.write("findlib " + stone.toLatin1() + '\n');
585  if (waitResponse() && !m_response.isEmpty()) {
586  foreach (const QString &entry, m_response.split(' ')) {
587  list.append(Stone(entry));
588  }
589  }
590  return list;
591 }
592 
593 QList<Stone> Game::bestMoves(const Player &player)
594 {
595  QList<Stone> list;
596  if (!isRunning() || !player.isValid()) {
597  return list;
598  }
599 
600  if (player.isWhite()) {
601  m_process.write("top_moves_white\n");
602  } else {
603  m_process.write("top_moves_black\n");
604  }
605  if (waitResponse(true) && !m_response.isEmpty()) {
606  QStringList parts = m_response.split(' ');
607  if (parts.size() % 2 == 0) {
608  for (int i = 0; i < parts.size(); i += 2) {
609  list.append(Stone(parts[i], QString(parts[i + 1]).toFloat()));
610  }
611  }
612  }
613  return list;
614 }
615 
616 QList<Stone> Game::legalMoves(const Player &player)
617 {
618  QList<Stone> list;
619  if (!isRunning() || !player.isValid()) {
620  return list;
621  }
622 
623  if (player.isWhite()) {
624  m_process.write("all_legal white\n");
625  } else {
626  m_process.write("all_legal black\n");
627  }
628  if (waitResponse() && !m_response.isEmpty()) {
629  foreach (const QString &entry, m_response.split(' ')) {
630  list.append(Stone(entry));
631  }
632  }
633  return list;
634 }
635 
636 int Game::captures(const Player &player)
637 {
638  if (!isRunning() || !player.isValid()) {
639  return 0;
640  }
641 
642  if (player.isWhite()) {
643  m_process.write("captures white\n");
644  } else {
645  m_process.write("captures black\n");
646  }
647  return waitResponse() ? m_response.toInt() : 0;
648 }
649 
650 Game::FinalState Game::finalState(const Stone &stone)
651 {
652  if (!isRunning() || !stone.isValid()) {
653  return FinalStateInvalid;
654  }
655 
656  m_process.write("final_status " + stone.toLatin1() + '\n');
657  if (waitResponse()) {
658  if (m_response == "alive") return FinalAlive;
659  else if (m_response == "dead") return FinalDead;
660  else if (m_response == "seki") return FinalSeki;
661  else if (m_response == "white_territory") return FinalWhiteTerritory;
662  else if (m_response == "blacK_territory") return FinalBlackTerritory;
663  else if (m_response == "dame") return FinalDame;
664  else return FinalStateInvalid;
665  } else
666  return FinalStateInvalid;
667 }
668 
669 QList<Stone> Game::finalStates(FinalState state)
670 {
671  QList<Stone> list;
672  if (!isRunning() || state == FinalStateInvalid) {
673  return list;
674  }
675 
676  QByteArray msg("final_status_list ");
677  switch (state) {
678  case FinalAlive: msg.append("alive"); break;
679  case FinalDead: msg.append("dead"); break;
680  case FinalSeki: msg.append("seki"); break;
681  case FinalWhiteTerritory: msg.append("white_territory"); break;
682  case FinalBlackTerritory: msg.append("black_territory"); break;
683  case FinalDame: msg.append("dame"); break;
684  case FinalStateInvalid: /* Will never happen */ break;
685  }
686  msg.append('\n');
687  m_process.write(msg);
688  if (waitResponse() && !m_response.isEmpty()) {
689  foreach (const QString &entry, m_response.split(' ')) {
690  list.append(Stone(entry));
691  }
692  }
693  return list;
694 }
695 
696 Score Game::finalScore()
697 {
698  if (!isRunning()) {
699  return Score();
700  }
701 
702  m_process.write("final_score\n");
703  return waitResponse() ? Score(m_response) : Score();
704 }
705 
706 Score Game::estimatedScore()
707 {
708  if (!isRunning()) {
709  return Score();
710  }
711 
712  m_process.write("estimate_score\n");
713  return waitResponse() ? Score(m_response) : Score();
714 }
715 
716 bool Game::waitResponse(bool nonBlocking)
717 {
718  if (m_process.state() != QProcess::Running) { // No GTP connection means no computing fun!
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;
723  case QProcess::WriteError: m_response = m_process.readAllStandardError(); break;
724  case QProcess::ReadError: m_response = m_process.readAllStandardError(); break;
725  case QProcess::UnknownError: m_response = "Unknown error!"; break;
726  }
727  kWarning() << "Command failed:" << m_response;
728  return false;
729  }
730 
731  if (nonBlocking) {
732  emit waiting(true);
733  }
734 
735  // Wait for finished command execution. We have to do this untill '\n\n' (or '\r\n\r\n' or
736  // '\r\r' in case of MS Windows and Mac, respectively) arrives in our input buffer to show
737  // that the Go game is done processing our request. The 'nonBlocking' parameter decides
738  // whether we block and wait (suitable for short commands) or if we continue processing
739  // events in between to stop the UI from blocking (suitable for longer commands).
740  // The latter may introduce flickering.
741  m_response.clear();
742  do {
743  if (nonBlocking) {
744  while (m_waitAndProcessEvents) { // The flag is modified by another slot
745  qApp->processEvents(); // called when QProcess signals readyRead()
746  }
747  m_waitAndProcessEvents = true;
748  } else {
749  m_process.waitForReadyRead(); // Blocking wait
750  }
751  m_response += m_process.readAllStandardOutput();
752  } while (!m_response.endsWith(QLatin1String("\r\r")) &&
753  !m_response.endsWith(QLatin1String("\n\n")) &&
754  !m_response.endsWith(QLatin1String("\r\n\r\n")));
755 
756  if (nonBlocking) {
757  emit waiting(false);
758  }
759 
760  if (m_response.size() < 1) {
761  return false;
762  }
763  QChar tmp = m_response[0]; // First message character indicates success or error
764  m_response.remove(0, 2); // Remove the first two chars (e.g. "? " or "= ")
765  m_response = m_response.trimmed(); // Remove further whitespaces, newlines, ...
766  return tmp != '?'; // '?' Means the game didn't understand the query
767 }
768 
769 void Game::gameSetup()
770 {
771  emit boardInitialized();
772 }
773 
774 void Game::readyRead()
775 {
776  m_waitAndProcessEvents = false;
777 }
778 
779 void Game::undoIndexChanged(int index)
780 {
781  /*int diff = index - m_lastUndoIndex;
782 
783  //TODO: Fix this ugly mess!
784  kDebug() << "Undo stack index changed by " << diff << "...";
785  // Bigger index changed usually mean that some QUndoView got a MouseClickEvent.
786  // This means we have to replay more undo/redo steps than normally. If the
787  // difference is greater than 1 we have to replay diff-1 steps.
788  if (diff > 1) {
789  for (int i = 0; i < diff; i++) {
790  redoMove();
791  }
792  } else if (diff < -1) {
793  for (int i = 0; i > diff; i--) {
794  undoMove();
795  }
796  }*/
797  m_lastUndoIndex = index;
798 }
799 
800 void Game::setCurrentPlayer(Player &player)
801 {
802  m_currentPlayer = &player;
803  emit currentPlayerChanged(*m_currentPlayer);
804 }
805 
806 } // End of namespace Kigo
807 
808 #include "moc_game.cpp"
Kigo::Player::isValid
bool isValid() const
Definition: player.h:73
Kigo::Score
This class represents a Go score for either player.
Definition: score.h:38
QUndoCommand
Kigo::Game::canRedoChanged
void canRedoChanged(bool)
This signal is emitted when availability of redo moves changes.
Kigo::Game::finalStates
QList< Stone > finalStates(FinalState state)
Report fields with a specified final status in a finished game.
Definition: game.cpp:669
Kigo::Game::setKomi
bool setKomi(float komi)
Set the komi.
Definition: game.cpp:214
Kigo::Move::stone
const Stone & stone() const
Definition: move.h:44
QByteArray
QProcess::waitForReadyRead
virtual bool waitForReadyRead(int msecs)
Kigo::Game::Game
Game(QObject *parent=0)
Definition: game.cpp:51
QProcess::error
QProcess::ProcessError error() const
QUndoStack::clear
void clear()
QChar
QString::split
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
Kigo::Game::finalState
FinalState finalState(const Stone &field)
Report the final status of a field in a finished game.
Definition: game.cpp:650
Kigo::Player::color
Color color() const
Definition: player.h:66
Kigo::Game::estimatedScore
Score estimatedScore()
Returns an estimate of the final score based on the current game situation.
Definition: game.cpp:706
QString::size
int size() const
Kigo::Game::FinalStateInvalid
The state is invalid, shows error.
Definition: game.h:75
Kigo::Game::moves
QList< Move > moves()
Definition: game.h:194
Kigo::Game::boardSizeChanged
void boardSizeChanged(int)
This signal is emitted when the board size was changed.
Kigo::Game::start
bool start(const QString &command="gnugo --mode gtp")
Connect to the given Go game game in GTP mode.
Definition: game.cpp:69
Kigo::Game::FinalBlackTerritory
The field belongs to the black player.
Definition: game.h:73
Kigo::Game::legalMoves
QList< Stone > legalMoves(const Player &player)
List all legal moves for either player.
Definition: game.cpp:616
QUndoStack::command
const QUndoCommand * command(int index) const
QFile::exists
bool exists() const
QString::remove
QString & remove(int position, int n)
game.h
Kigo::Game::gameSetup
void gameSetup()
Definition: game.cpp:769
Kigo::Game::consecutivePassMovesPlayed
void consecutivePassMovesPlayed(int)
This signal is emitted when both players played a pass move after another.
Kigo::Player::strength
int strength() const
Definition: player.h:63
QList::size
int size() const
Kigo::Game::waiting
void waiting(bool)
This signal is emitted when the game starts or ends a non-blocking wait.
QString::clear
void clear()
Kigo::Game::FinalAlive
The stone on the field is alive.
Definition: game.h:69
Kigo::Stone::isValid
bool isValid() const
Definition: stone.cpp:57
QList::append
void append(const T &value)
Kigo::Game::redoMove
bool redoMove()
Definition: game.cpp:481
Kigo::Game::currentPlayerChanged
void currentPlayerChanged(const Player &)
This signal is emitted when the current player changes.
QObject
Kigo::Game::bestMoves
QList< Stone > bestMoves(const Player &player)
Generate a list of the best moves for a player with weights.
Definition: game.cpp:593
Kigo::Stone::Pass
static Stone Pass
A standard pass move object.
Definition: stone.h:34
QString::toInt
int toInt(bool *ok, int base) const
Kigo::Stone
This class represents a stone on a field of the game board.
Definition: stone.h:31
QString::isEmpty
bool isEmpty() const
QString::trimmed
QString trimmed() const
QByteArray::number
QByteArray number(int n, int base)
QString::startsWith
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
Kigo::Game::moveCount
int moveCount()
Returns the number of moves in the game so far.
Definition: game.cpp:518
Kigo::Game::finalScore
Score finalScore()
Compute the score of a finished game.
Definition: game.cpp:696
QString::endsWith
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
QIODevice::isOpen
bool isOpen() const
Kigo::Game::stones
QList< Stone > stones(const Player &player)
Returns a list of all stones of that player on the board.
Definition: game.cpp:531
Kigo::Game::lastMove
Move lastMove() const
Returns the last move made by either player.
Definition: game.cpp:509
QString
QList
Kigo::Move::player
const Player * player() const
Definition: move.h:43
Kigo::Game::boardInitialized
void boardInitialized()
This signal is emitted when the board is first started and can be used to trigger an update to a visu...
Kigo::Game::setBoardSize
bool setBoardSize(int size)
Set the board size to NxN.
Definition: game.cpp:189
Kigo::Game::komi
float komi() const
Definition: game.h:140
QStringList
Kigo::Game::isRunning
bool isRunning() const
Check whether the Game object is connected to a Go game, running and waiting for commands to be fed w...
Definition: game.h:99
Kigo::Game::captures
int captures(const Player &player)
List the number of captures taken by either player.
Definition: game.cpp:636
Kigo::Player
The Player class holds all basic attributes of a Go player.
Definition: player.h:36
QProcess::close
virtual void close()
QByteArray::append
QByteArray & append(char ch)
Kigo::Game::fixedHandicapUpperBound
int fixedHandicapUpperBound()
Returns the maximum amount fixed handicap stones placeable at the current Go board size...
Definition: game.cpp:255
QProcess::waitForStarted
bool waitForStarted(int msecs)
Kigo::Stone::toLatin1
QByteArray toLatin1() const
Definition: stone.cpp:63
Kigo::Game::FinalWhiteTerritory
The field belongs to the white player.
Definition: game.h:72
Kigo::Game::FinalDame
The field belongs to no player.
Definition: game.h:74
Kigo::Game::liberties
QList< Stone > liberties(const Stone &field)
Returns the positions of the liberties for the stone at 'field'.
Definition: game.cpp:577
score.h
Kigo::Game::init
bool init()
Initialize a new game.
Definition: game.cpp:110
QString::toLatin1
QByteArray toLatin1() const
Kigo::Player::isComputer
bool isComputer() const
Definition: player.h:75
QUndoStack::redo
void redo()
QLatin1String
QUndoStack::index
int index() const
Kigo::Player::isBlack
bool isBlack() const
Definition: player.h:72
QString::count
int count() const
QString::toFloat
float toFloat(bool *ok) const
Kigo::Stone::toString
QString toString() const
Definition: stone.cpp:68
Kigo::Game::FinalDead
The stone on the field is dead.
Definition: game.h:70
Kigo::Move
The Move class is a light-weight representation of a Go move (to be) made by a Go player...
Definition: move.h:36
Kigo::Game::resigned
void resigned(const Player &)
This signal is emitted when a player resigns.
QUndoStack::undo
void undo()
QIODevice::write
qint64 write(const char *data, qint64 maxSize)
Kigo::Game::stop
void stop()
Gracefully stop and exit the Go game game.
Definition: game.cpp:102
Kigo::Game::setFixedHandicap
bool setFixedHandicap(int handicap)
Set up fixed placement handicap stones.
Definition: game.cpp:230
Kigo::Game::passMovePlayed
void passMovePlayed(const Player &)
Kigo::Game::FinalState
FinalState
Enumeration of all possible final states of a field when a game is over.
Definition: game.h:68
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
Kigo::Player::isWhite
bool isWhite() const
Definition: player.h:71
Kigo::Game::save
bool save(const QString &fileName)
Save the current game as a SGF file.
Definition: game.cpp:179
Kigo::Game::playMove
bool playMove(const Move &move, bool undoable=true)
Definition: game.cpp:275
QProcess::readAllStandardOutput
QByteArray readAllStandardOutput()
Kigo::Game::undoMove
bool undoMove()
Definition: game.cpp:444
QProcess::start
void start(const QString &program, const QStringList &arguments, QFlags< QIODevice::OpenModeFlag > mode)
QProcess::state
QProcess::ProcessState state() const
QProcess::readAllStandardError
QByteArray readAllStandardError()
Kigo::Game::boardChanged
void boardChanged()
This signal is emitted when the board situation changed and can be used to trigger an update to a vis...
QUndoStack::push
void push(QUndoCommand *cmd)
Kigo::Game::canUndoChanged
void canUndoChanged(bool)
This signal is emitted when availability of undo moves changes.
Kigo::Game::generateMove
bool generateMove(const Player &player, bool undoable=true)
Definition: game.cpp:359
Kigo::Game::~Game
~Game()
Definition: game.cpp:64
Kigo::Game::FinalSeki
Definition: game.h:71
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:18:29 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kigo

Skip menu "kigo"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdegames API Reference

Skip menu "kdegames API Reference"
  • granatier
  • kapman
  • kblackbox
  • kgoldrunner
  • kigo
  • kmahjongg
  • KShisen
  • ksquares
  • libkdegames
  •   highscore
  •   libkdegamesprivate
  •     kgame
  • libkmahjongg
  • palapeli
  •   libpala

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal