1
/*******************************************************************
3
* This file is part of the KDE project "Bovo"
5
* Bovo is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2, or (at your option)
10
* Bovo is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with Bovo; see the file COPYING. If not, write to
17
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
18
* Boston, MA 02110-1301, USA.
20
********************************************************************/
22
/** @file game.cc implements class Game in namespace bovo */
26
#include <QtCore/QTimer>
27
#include <QtCore/QString>
28
#include <QtCore/QStringList>
33
#include "dimension.h"
38
/** namespace for gui stuff */
42
Game::Game(const Dimension& dimension, Player startingPlayer,
43
KGameDifficulty::standardLevel skill, DemoMode demoMode,
44
unsigned int playTime)
45
: m_curPlayer(startingPlayer),m_computerMark(O), m_demoMode(demoMode),
46
m_inUndoState(false), m_playerMark(X), m_playTime(playTime),
48
m_board = new Board(dimension);
49
m_ai = new Ai(dimension, skill, m_computerMark);
52
connect(this, SIGNAL(boardChanged(const Move&)),
53
m_ai, SLOT(changeBoard(const Move&)));
54
connect(this, SIGNAL(oposerTurn()), m_ai, SLOT(slotMove()),
55
Qt::QueuedConnection);
56
connect(m_ai, SIGNAL(move(const Move&)),
57
this, SLOT(move(const Move&)));
60
Game::Game(const Dimension& dimension, const QStringList &restoreGame,
61
KGameDifficulty::standardLevel skill, unsigned int playTime)
62
: m_computerMark(O), m_demoMode(NotDemo), m_inUndoState(false),
63
m_playerMark(X), m_playTime(playTime), m_replaying(false) {
64
m_board = new Board(dimension);
65
m_ai = new Ai(dimension, skill, m_computerMark);
69
foreach (const QString &turn, restoreGame) {
70
QStringList tmp = turn.split(':');
71
if (tmp.count() != 2) {
72
qFatal("Wrong save file format!");
74
Player tmpPlayer = (tmp[0] == "1") ? X : O;
75
if (m_curPlayer == No) {
76
m_curPlayer = tmpPlayer;
78
tmp = tmp[1].split(',');
79
if (tmp.count() != 2) {
80
qFatal("Wrong save file format!");
83
uint x = tmp[0].toUInt(&ok);
85
qFatal("Wrong save file format!");
87
uint y = tmp[1].toUInt(&ok);
89
qFatal("Wrong save file format!");
91
Move tmpMove(tmpPlayer, Coord(x, y));
92
m_board->setPlayer(tmpMove);
102
bool Game::computerTurn() const {
103
return m_curPlayer == m_computerMark;
106
DemoMode Game::demoMode() const {
110
bool Game::isGameOver() const {
111
return m_gameOver || m_demoMode;
114
QList<Move> Game::history() const {
118
Move Game::latestMove() const {
119
if (m_history.empty()) {
122
return m_history.back();
126
bool Game::ok(const Coord& coord) const {
127
return m_board->ok(coord);
130
Player Game::player() const {
134
Player Game::player(const Coord& coord) const {
135
return m_board->player(coord);
138
bool Game::save(const QString& filename) const {
139
Q_UNUSED( filename );
142
fileContent.append(QString("<bovo width=\"%1\" height=\"%2\">")
144
foreach (const Move &move, m_history) {
145
fileContent.append(QString("<move player=\"%1\" x=\"%2\" y=\"%3\" />").
146
arg(move.player()).arg(move.x()).arg(move.y()));
148
fileContent.append("</bovo>");
152
QStringList Game::saveLast() const {
154
foreach (const Move &move, m_history) {
155
save << QString("%1:%2,%3").arg(move.player())
156
.arg(move.x()).arg(move.y());
161
void Game::setSkill(KGameDifficulty::standardLevel skill) {
163
m_ai->setSkill(skill);
167
if (computerTurn()) {
174
void Game::startRestored() {
175
connect(this, SIGNAL(boardChanged(const Move&)),
176
m_ai, SLOT(changeBoard(const Move&)));
177
foreach (const Move &move, m_history) {
178
emit boardChanged(move);
180
connect(this, SIGNAL(oposerTurn()), m_ai, SLOT(slotMove()),
181
Qt::QueuedConnection);
182
connect(m_ai, SIGNAL(move(const Move&)),
183
this, SLOT(move(const Move&)));
184
if (!m_history.isEmpty() && m_history.last().player() == X) {
191
if (!m_history.isEmpty()) {
196
short Game::winDir() const {
202
void Game::move(const Move& move) {
203
bool tmp_emptyHistory = m_history.empty();
204
if (!m_board->empty(move.coord()) || move.player() != m_curPlayer
209
if (tmp_emptyHistory && !m_history.empty() && !m_demoMode) {
214
void Game::replay() {
215
m_replayIterator = m_history.constBegin();
216
m_replayIteratorEnd = m_history.constEnd();
217
if (m_gameOver && !m_replaying) {
218
disconnect(this, SIGNAL(replayBegin()), this, SLOT(replayNext()));
219
connect(this, SIGNAL(replayBegin()), this, SLOT(replayNext()));
224
void Game::undoLatest() {
225
m_inUndoState = true;
226
if (m_history.empty() || m_demoMode || m_gameOver) {
227
m_inUndoState = false;
229
} else if (m_curPlayer == m_computerMark) {
230
Move move(No, m_history.last().coord());
231
m_history.removeLast();
232
m_board->setPlayer(move);
233
emit boardChanged(move);
234
m_curPlayer = m_playerMark;
236
} else if (m_curPlayer == m_playerMark && m_history.count() == 1) {
237
Move move(No, m_history.last().coord());
238
m_history.removeLast();
239
m_board->setPlayer(move);
240
emit boardChanged(move);
241
m_curPlayer = m_computerMark;
243
} else if (m_curPlayer == m_playerMark && m_history.count() > 1 ) {
244
Move move(No, m_history.last().coord());
245
m_history.removeLast();
246
m_board->setPlayer(move);
247
emit boardChanged(move);
248
Move move2(No, m_history.last().coord());
249
m_history.removeLast();
250
m_board->setPlayer(move2);
251
emit boardChanged(move2);
254
if (m_history.empty() && !m_demoMode) {
257
m_inUndoState = false;
262
void Game::replayNext() {
263
if (m_replayIterator != m_replayIteratorEnd) {
264
QTimer::singleShot(m_playTime, this, SLOT(replayNext()));
265
emit boardChanged(*m_replayIterator);
269
emit replayEnd(winningMoves()); // FIX:!!!!!!!
273
/* private methods */
275
void Game::makeMove(const Move& move) {
276
if (move.player() != m_curPlayer) {
279
m_board->setPlayer(move);
280
m_winDir = win(move.coord());
281
if (m_winDir != -1) {
285
m_curPlayer = (m_curPlayer == X ? O : X );
286
emit boardChanged(move);
288
QList<Move> moves = winningMoves();
290
emit gameOver(moves);
291
this->disconnect(m_ai);
293
if (computerTurn()) {
295
QTimer::singleShot(m_playTime, this, SIGNAL(oposerTurn()));
301
QTimer::singleShot(m_playTime, this, SIGNAL(playerTurn()));
309
Coord Game::next(const Coord& coord, usi dir) const {
317
} else if (dir & RIGHT) {
322
} else if (dir & DOWN) {
328
short Game::win(const Coord& c) const {
333
usi DIR[8] = {LEFT, RIGHT, UP, DOWN, LEFT | UP, RIGHT | DOWN,
334
LEFT | DOWN, RIGHT | UP};
335
Player p = player(c);
336
for (int i = 0; i < 4; ++i) {
338
Coord tmp = next(c, DIR[2*i]);
339
while (m_board->ok(tmp) && player(tmp) == p) {
341
tmp = next(tmp, DIR[2*i]);
343
tmp = next(c, DIR[2*i+1]);
344
while (m_board->ok(tmp) && player(tmp) == p) {
346
tmp = next(tmp, DIR[2*i+1]);
355
QList<Move> Game::winningMoves() const {
356
if (m_winDir == -1) {
357
return QList<Move>();
362
case 0: dx = 1; dy = 0; break;
363
case 1: dx = 0; dy = 1; break;
364
case 2: dx = 1; dy = 1; break;
365
default: dx = 1; dy = -1; break;
367
usi x = latestMove().x();
368
usi y = latestMove().y();
369
Player winner = player(Coord(x, y));
371
while ((tmp = player(Coord(x, y))) == winner) {
372
moves << Move(player(Coord(x, y)), Coord(x, y));
376
x = latestMove().x() - dx;
377
y = latestMove().y() - dy;
378
while ((tmp = player(Coord(x, y))) == winner) {
379
moves << Move(player(Coord(x, y)), Coord(x, y));