Commit 191fb162 authored by Denis Kuplyakov's avatar Denis Kuplyakov

KReversiView now controls game-flow, as it was done by KReversiScene

What's new:
1) KReversiView now have same signal pattern as KReversiScene, used
before. Now game is playable. You can do only allowed moves and so on.
2) Showing of legal moves and last move already works.
3) You can change chip color.
Known issues:
1) History works, but Undo doesn't.
2) Game sometimes crashes, when you close it while demomode is running.
3) QML layout don't fit board image well, so you can see that last cells
have some bias.
parent 31b2e13a
/*
Copyright 2013 Denis Kuplyakov <dener.kup@gmail.com>
Copyright 2006-2007 Dmitry Suzdalev <dimsuz@gmail.com>
Copyright 2010 Brian Croom <brian.s.croom@gmail.com>
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
......@@ -19,7 +21,9 @@
#include "kreversiscene.h"
KReversiView::KReversiView(KReversiGame* game, QWidget *parent) :
KgDeclarativeView(parent), m_game(0)
KgDeclarativeView(parent), m_delay(500), m_game(0),
m_demoMode(false), m_showLastMove(false), m_showLegalMoves(false),
m_showLabels(false)
{
m_provider = new KgThemeProvider();
m_provider->discoverThemes("appdata", QLatin1String("pics"));
......@@ -35,56 +39,192 @@ KReversiView::KReversiView(KReversiGame* game, QWidget *parent) :
connect(m_qml_root, SIGNAL(cellClicked(int, int)),
this, SLOT(onPlayerMove(int, int)));
setGame(game);
connect(&m_delayTimer, SIGNAL(timeout()), SLOT(slotOnDelay()));
}
void KReversiView::setGame(KReversiGame *game)
{
if (m_game)
this->disconnect(m_game, 0, 0, 0);
// disconnect signals from previous game if it exists,
// we are not interested in them anymore
if (m_game) {
disconnect(m_game, 0, 0, 0);
// TODO: Why code below get runtime error?
// disconnect( m_game, SIGNAL(boardChanged()), this, SLOT(updateBoard()) );
// disconnect( m_game, SIGNAL(moveFinished()), this, SLOT(slotGameMoveFinished()) );
// disconnect( m_game, SIGNAL(gameOver()), this, SLOT(slotGameOver()) );
// disconnect( m_game, SIGNAL(computerCantMove()), this, SLOT(slotComputerCantMove()) );
// disconnect( m_game, SIGNAL(playerCantMove()), this, SLOT(slotPlayerCantMove()) );
}
m_game = game;
if (m_game) {
connect(m_game, SIGNAL(boardChanged()), this, SLOT(onGameUpdate()));
connect(m_game, SIGNAL(moveFinished()), this, SLOT(onGameUpdate()));
connect(m_game, SIGNAL(boardChanged()), this, SLOT(updateBoard()));
connect(m_game, SIGNAL(moveFinished()), this, SLOT(slotGameMoveFinished()));
connect(m_game, SIGNAL(gameOver()), this, SLOT(slotGameOver()));
connect(m_game, SIGNAL(computerCantMove()), this, SLOT(slotComputerCantMove()));
connect(m_game, SIGNAL(playerCantMove()), this, SLOT(slotPlayerCantMove()));
m_hint = KReversiPos();
m_demoMode = false;
updateBoard();
}
void KReversiView::setChipsPrefix(const QString &chipsPrefix)
{
QMetaObject::invokeMethod(m_qml_root, "setChipsPrefix",
Q_ARG(QVariant, chipsPrefix));
}
void KReversiView::setShowBoardLabels(bool show)
{
m_showLabels = show;
updateBoard();
}
void KReversiView::updateBoard()
{
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
QString new_state = "";
if (m_game) // showing empty board if has no game
switch (m_game->chipColorAt(i, j)) {
case Black:
new_state = "Black";
break;
case White:
new_state = "White";
break;
case NoColor:
new_state = "";
break;
}
QMetaObject::invokeMethod(m_qml_root, "setChipState",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, new_state));
// clearing legal markers, hints and lastmove
QMetaObject::invokeMethod(m_qml_root, "setLegal",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, false));
QMetaObject::invokeMethod(m_qml_root, "setHint",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, false));
QMetaObject::invokeMethod(m_qml_root, "setLastMove",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, false));
}
if (m_game && m_showLegalMoves) {
PosList possible_moves = m_game->possibleMoves();
for (int i = 0; i < possible_moves.size(); i++) {
QMetaObject::invokeMethod(m_qml_root, "setLegal",
Q_ARG(QVariant, possible_moves.at(i).row),
Q_ARG(QVariant, possible_moves.at(i).col),
Q_ARG(QVariant, true));
}
}
QMetaObject::invokeMethod(m_qml_root, "setLabels",
Q_ARG(QVariant, m_showLabels));
if (m_hint.isValid()) {
QMetaObject::invokeMethod(m_qml_root, "setHint",
Q_ARG(QVariant, m_hint.row),
Q_ARG(QVariant, m_hint.col),
Q_ARG(QVariant, true));
}
onGameUpdate();
if (m_game && m_showLastMove) {
KReversiPos lastmove = m_game->getLastMove();
if (lastmove.isValid())
QMetaObject::invokeMethod(m_qml_root, "setLastMove",
Q_ARG(QVariant, lastmove.row),
Q_ARG(QVariant, lastmove.col),
Q_ARG(QVariant, true));
}
}
void KReversiView::onGameUpdate()
void KReversiView::setShowLastMove(bool show)
{
if (!m_game)
m_showLastMove = show;
updateBoard();
}
void KReversiView::setShowLegalMoves(bool show)
{
m_showLegalMoves = show;
updateBoard();
}
void KReversiView::slotHint()
{
if (m_game->isComputersTurn()) {
kDebug() << "It is computer turn now. Can't provide hint.";
return;
}
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++)
switch (m_game->chipColorAt(i, j)) {
case Black:
QMetaObject::invokeMethod(m_qml_root, "setChipState",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, "Black"));
break;
case White:
QMetaObject::invokeMethod(m_qml_root, "setChipState",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, "White"));
break;
case NoColor:
QMetaObject::invokeMethod(m_qml_root, "setChipState",
Q_ARG(QVariant, i),
Q_ARG(QVariant, j),
Q_ARG(QVariant, ""));
break;
}
m_hint = m_game->getHint();
updateBoard();
}
void KReversiView::setDemoMode(bool state)
{
if (m_game->isGameOver())
return;
m_demoMode = state;
if (!m_game->isComputersTurn())
m_game->startNextTurn(m_demoMode);
}
void KReversiView::onPlayerMove(int row, int col)
{
if (!m_game)
// user moves not allowed in demo mode
if (m_demoMode)
return;
if (m_game->isComputersTurn())
{
kDebug() << "It is computer turn now. You player can't do moves.";
return;
}
m_game->makePlayerMove(row, col, false);
m_game->makeComputerMove();
}
void KReversiView::slotGameMoveFinished()
{
m_hint = KReversiPos();
updateBoard();
emit moveFinished();
m_delayTimer.start(m_delay);
}
void KReversiView::slotGameOver()
{
m_demoMode = false;
}
void KReversiView::slotComputerCantMove()
{
// TODO: 3000 ms message "Computer can not move. It is your turn again.";
}
void KReversiView::slotPlayerCantMove()
{
// TODO: 3000 ms message "You can not perform any move. Computer takes next turn now.";
}
void KReversiView::slotOnDelay()
{
m_game->startNextTurn(m_demoMode);
}
/*
Copyright 2013 Denis Kuplyakov <dener.kup@gmail.com>
Copyright 2006 Dmitry Suzdalev <dimsuz@gmail.com>
Copyright 2010 Brian Croom <brian.s.croom@gmail.com>
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
......@@ -24,19 +26,106 @@
#include <KGameRenderer>
#include <KStandardDirs>
#include <KgThemeProvider>
#include <QTimer>
#include <kdebug.h>
class KReversiView : public KgDeclarativeView
{
Q_OBJECT
public:
KReversiView(KReversiGame* game, QWidget *parent = 0);
/**
* Sets the game object which this view will visualize/use.
*/
void setGame(KReversiGame* game);
private slots:
void onGameUpdate();
/**
* Sets the chips pixmap to be one found in chipsPrefix
*/
void setChipsPrefix(const QString& chipsPrefix);
/**
* Sets whether to show board labels.
*/
void setShowBoardLabels(bool show);
/**
* @return whether the view is in demo-mode
*/
bool isDemoModeToggled() const {
return m_demoMode;
}
/**
* Sets the animation speed (0 - slow, 1 - normal, 2 - fast)
*/
// void setAnimationSpeed(int speed);
public slots:
void onPlayerMove(int row, int col);
/**
* Synchronizes graphical board with m_game's board
*/
void updateBoard();
/**
* This will make view visually mark the last made move
*/
void setShowLastMove(bool show);
/**
* This will make view visually mark squares with possible moves
*/
void setShowLegalMoves(bool show);
/**
* Shows hint for player
*/
void slotHint();
/**
* Sets Demo Mode.
* In this mode KReversiScene would not wait for user
* clicks to produce his turn. It will let computer
* play for user
* @see m_demoMode
*/
void setDemoMode(bool state);
/**
* @return whether game is in demo mode
*/
bool isInDemoMode() const {
return m_demoMode;
}
private slots:
void slotGameMoveFinished();
void slotGameOver();
void slotComputerCantMove();
void slotPlayerCantMove();
void slotOnDelay();
signals:
void moveFinished();
private:
KReversiGame* m_game;
QObject *m_qml_root;
KgThemeProvider *m_provider;
QTimer m_delayTimer;
KReversiPos m_hint;
int m_delay;
/**
* The Game object
*/
KReversiGame *m_game;
/**
* The SVG element prefix for the current chip set
*/
QString m_chipsPrefix;
/**
* Specifies whether computer should play human moves
*/
bool m_demoMode;
/**
* If true, then last made turn will be shown to the player
*/
bool m_showLastMove;
/**
* If true, then all possible moves will be shown to the player
*/
bool m_showLegalMoves;
/**
* If true board labels will be rendered
*/
bool m_showLabels;
};
#endif
......@@ -53,33 +53,33 @@
static const int PLAYER_STATUSBAR_ID = 1;
static const int COMP_STATUSBAR_ID = 2;
static QString moveToString( const KReversiPos& move )
static QString moveToString(const KReversiPos& move)
{
QString moveString;
if( Preferences::useColoredChips() )
moveString = (move.color == Black ? i18n("Blue") : i18n("Red") );
if (Preferences::useColoredChips())
moveString = (move.color == Black ? i18n("Blue") : i18n("Red"));
else
moveString = (move.color == Black ? i18n("Black") : i18n("White") );
moveString = (move.color == Black ? i18n("Black") : i18n("White"));
const char labelsHor[] = "ABCDEFGH";
const char labelsVer[] = "12345678";
moveString += QLatin1Char( ' ' );
moveString += QLatin1Char( labelsHor[move.col] );
moveString += QLatin1Char( labelsVer[move.row] );
moveString += QLatin1Char(' ');
moveString += QLatin1Char(labelsHor[move.col]);
moveString += QLatin1Char(labelsVer[move.row]);
return moveString;
}
KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo )
: KXmlGuiWindow(parent), /*m_scene(0),*/ m_game(0),
KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo)
: KXmlGuiWindow(parent), m_view(0), m_game(0),
m_historyDock(0), m_historyView(0),
m_firstShow( true ), m_startInDemoMode( startDemo ), m_lowestSkill(6),
m_firstShow(true), m_startInDemoMode(startDemo), m_lowestSkill(6),
m_undoAct(0), m_hintAct(0), m_demoAct(0)
{
statusBar()->insertItem( i18n("Your turn."), 0 );
statusBar()->insertItem( i18n("You: %1", 2), PLAYER_STATUSBAR_ID );
statusBar()->insertItem( i18n("%1: %2", opponentName(), 2), COMP_STATUSBAR_ID );
statusBar()->insertItem(i18n("Your turn."), 0);
statusBar()->insertItem(i18n("You: %1", 2), PLAYER_STATUSBAR_ID);
statusBar()->insertItem(i18n("%1: %2", opponentName(), 2), COMP_STATUSBAR_ID);
Kg::difficulty()->addStandardLevelRange(
KgDifficultyLevel::VeryEasy, KgDifficultyLevel::Impossible,
......@@ -89,14 +89,11 @@ KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo )
connect(Kg::difficulty(), SIGNAL(currentLevelChanged(const KgDifficultyLevel*)), SLOT(levelChanged()));
slotNewGame();
// m_scene is created in slotNewGame();
// m_view is created in slotNewGame();
m_view = new KReversiView(m_game, this);
setCentralWidget(m_view);
m_historyView = new QListWidget( this );
m_historyView->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Expanding );
m_historyDock = new QDockWidget( i18n("Move History") );
m_historyView = new QListWidget(this);
m_historyView->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
m_historyDock = new QDockWidget(i18n("Move History"));
m_historyDock->setWidget(m_historyView);
m_historyDock->setObjectName("history_dock");
......@@ -107,7 +104,7 @@ KReversiMainWindow::KReversiMainWindow(QWidget* parent, bool startDemo )
setupActions();
loadSettings();
setupGUI(qApp->desktop()->availableGeometry().size()*0.7);
setupGUI(qApp->desktop()->availableGeometry().size() * 0.7);
}
void KReversiMainWindow::setupActions()
......@@ -119,75 +116,75 @@ void KReversiMainWindow::setupActions()
// Move
m_undoAct = KStandardGameAction::undo(this, SLOT(slotUndo()), actionCollection());
m_undoAct->setEnabled( false ); // nothing to undo at the start of the game
// m_hintAct = KStandardGameAction::hint(m_scene, SLOT(slotHint()), actionCollection());
m_undoAct->setEnabled(false); // nothing to undo at the start of the game
m_hintAct = KStandardGameAction::hint(m_view, SLOT(slotHint()), actionCollection());
m_demoAct = KStandardGameAction::demo(this, SLOT(slotToggleDemoMode()), actionCollection());
// View
KToggleAction *showLast = new KToggleAction(KIcon( QLatin1String( "lastmoves") ), i18n("Show Last Move" ), this);
actionCollection()->addAction( QLatin1String( "show_last_move" ), showLast);
// connect( showLast, SIGNAL(triggered(bool)), m_scene, SLOT(setShowLastMove(bool)) );
KToggleAction *showLast = new KToggleAction(KIcon(QLatin1String("lastmoves")), i18n("Show Last Move"), this);
actionCollection()->addAction(QLatin1String("show_last_move"), showLast);
connect(showLast, SIGNAL(triggered(bool)), m_view, SLOT(setShowLastMove(bool)));
KToggleAction *showLegal = new KToggleAction(KIcon( QLatin1String( "legalmoves") ), i18n("Show Legal Moves" ), this);
actionCollection()->addAction( QLatin1String( "show_legal_moves" ), showLegal);
// connect( showLegal, SIGNAL(triggered(bool)), m_scene, SLOT(setShowLegalMoves(bool)) );
KToggleAction *showLegal = new KToggleAction(KIcon(QLatin1String("legalmoves")), i18n("Show Legal Moves"), this);
actionCollection()->addAction(QLatin1String("show_legal_moves"), showLegal);
connect(showLegal, SIGNAL(triggered(bool)), m_view, SLOT(setShowLegalMoves(bool)));
m_animSpeedAct = new KSelectAction(i18n("Animation Speed"), this);
actionCollection()->addAction( QLatin1String( "anim_speed" ), m_animSpeedAct);
actionCollection()->addAction(QLatin1String("anim_speed"), m_animSpeedAct);
QStringList acts;
acts << i18n("Slow") << i18n("Normal") << i18n("Fast");
m_animSpeedAct->setItems(acts);
connect( m_animSpeedAct, SIGNAL(triggered(int)), SLOT(slotAnimSpeedChanged(int)) );
connect(m_animSpeedAct, SIGNAL(triggered(int)), SLOT(slotAnimSpeedChanged(int)));
m_coloredChipsAct = new KToggleAction( i18n("Use Colored Chips"), this );
actionCollection()->addAction( QLatin1String( "use_colored_chips" ), m_coloredChipsAct );
connect( m_coloredChipsAct, SIGNAL(triggered(bool)), SLOT(slotUseColoredChips(bool)) );
m_coloredChipsAct = new KToggleAction(i18n("Use Colored Chips"), this);
actionCollection()->addAction(QLatin1String("use_colored_chips"), m_coloredChipsAct);
connect(m_coloredChipsAct, SIGNAL(triggered(bool)), SLOT(slotUseColoredChips(bool)));
// NOTE: read/write this from/to config file? Or not necessary?
KToggleAction *showMovesAct = new KToggleAction( KIcon( QLatin1String( "view-history") ), i18n("Show Move History" ), this );
actionCollection()->addAction( QLatin1String( "show_moves" ), showMovesAct);
connect( showMovesAct, SIGNAL(triggered(bool)), SLOT(slotShowMovesHistory(bool)) );
KToggleAction *showMovesAct = new KToggleAction(KIcon(QLatin1String("view-history")), i18n("Show Move History"), this);
actionCollection()->addAction(QLatin1String("show_moves"), showMovesAct);
connect(showMovesAct, SIGNAL(triggered(bool)), SLOT(slotShowMovesHistory(bool)));
}
void KReversiMainWindow::loadSettings()
{
m_animSpeedAct->setCurrentItem( Preferences::animationSpeed() );
m_coloredChipsAct->setChecked( Preferences::useColoredChips() );
m_animSpeedAct->setCurrentItem(Preferences::animationSpeed());
m_coloredChipsAct->setChecked(Preferences::useColoredChips());
}
void KReversiMainWindow::levelChanged()
{
int skill;
switch(Kg::difficultyLevel()) {
case KgDifficultyLevel::VeryEasy:
skill=0;
break;
case KgDifficultyLevel::Easy:
default:
skill=1;
break;
case KgDifficultyLevel::Medium:
skill=2;
break;
case KgDifficultyLevel::Hard:
skill=3;
break;
case KgDifficultyLevel::VeryHard:
skill=4;
break;
case KgDifficultyLevel::ExtremelyHard:
skill=5;
break;
case KgDifficultyLevel::Impossible:
skill=6;
break;
switch (Kg::difficultyLevel()) {
case KgDifficultyLevel::VeryEasy:
skill = 0;
break;
case KgDifficultyLevel::Easy:
default:
skill = 1;
break;
case KgDifficultyLevel::Medium:
skill = 2;
break;
case KgDifficultyLevel::Hard:
skill = 3;
break;
case KgDifficultyLevel::VeryHard:
skill = 4;
break;
case KgDifficultyLevel::ExtremelyHard:
skill = 5;
break;
case KgDifficultyLevel::Impossible:
skill = 6;
break;
}
m_lowestSkill = qMin(m_lowestSkill, skill);
// m_game takes it from 1 to 7
m_game->setComputerSkill( skill + 1 );
m_game->setComputerSkill(skill + 1);
}
void KReversiMainWindow::slotAnimSpeedChanged(int speed)
......@@ -199,8 +196,8 @@ void KReversiMainWindow::slotAnimSpeedChanged(int speed)
void KReversiMainWindow::slotUseColoredChips(bool toggled)
{
QString chipsPrefix = m_coloredChipsAct->isChecked() ? QLatin1String( "chip_color" ) : QLatin1String( "chip_bw" );
// m_scene->setChipsPrefix( chipsPrefix );
QString chipsPrefix = m_coloredChipsAct->isChecked() ? QLatin1String("chip_color") : QLatin1String("chip_bw");
m_view->setChipsPrefix(chipsPrefix);
Preferences::setUseColoredChips(toggled);
Preferences::self()->writeConfig();
}
......@@ -209,73 +206,65 @@ void KReversiMainWindow::slotShowMovesHistory(bool toggled)
{
m_historyDock->setVisible(toggled);
// m_scene->setShowBoardLabels( toggled );
m_view->resetCachedContent();
m_view->update();
m_view->setShowBoardLabels(toggled);
}
void KReversiMainWindow::slotToggleDemoMode()
{
bool toggled = false;
// if( m_scene->isInDemoMode() )
// {
// toggled = false;
// m_demoAct->setIcon( KIcon( QLatin1String( "media-playback-start" )) );
// m_demoAct->setChecked( false );
// }
// else
// {
// // if game is over when user launched Demo, start new game
// // before Demo starts
// if (m_game && m_game->isGameOver())
// slotNewGame();
//
// toggled = true;
// m_demoAct->setIcon( KIcon( QLatin1String( "media-playback-pause" )) );
// m_demoAct->setChecked( true );
// }
// m_scene->toggleDemoMode(toggled);
m_undoAct->setEnabled( !toggled );
m_hintAct->setEnabled( !toggled );
if (m_view->isInDemoMode()) {
toggled = false;
m_demoAct->setIcon(KIcon(QLatin1String("media-playback-start")));
m_demoAct->setChecked(false);
} else {
// if game is over when user launched Demo, start new game
// before Demo starts
if (m_game && m_game->isGameOver())
slotNewGame();
toggled = true;
m_demoAct->setIcon(KIcon(QLatin1String("media-playback-pause")));
m_demoAct->setChecked(true);
}
m_view->setDemoMode(toggled);
m_undoAct->setEnabled(!toggled);
m_hintAct->setEnabled(!toggled);
}
void KReversiMainWindow::slotNewGame()
{
m_lowestSkill = 6;
if(m_hintAct)
m_hintAct->setEnabled( true );
if (m_hintAct)
m_hintAct->setEnabled(true);
if(m_demoAct)
{
m_demoAct->setChecked( false );
m_demoAct->setIcon( KIcon( QLatin1String( "media-playback-start" )) );
if (m_demoAct) {
m_demoAct->setChecked(false);
m_demoAct->setIcon(KIcon(QLatin1String("media-playback-start")));
}
if(m_undoAct)