Commit 18fcc965 authored by Dmitry Suzdalev's avatar Dmitry Suzdalev

Implemented hints.

Implemented (not well tested) handling of situations where one 
of the players (computer or human) have to skip turns due to 
a lack of postitions to make move into

svn path=/branches/work/kreversi_rewrite/; revision=573503
parent 7b9ee4cc
......@@ -214,6 +214,8 @@ public:
void setInterrupt(bool intr) { m_interrupt = intr; }
bool interrupted() const { return m_interrupt; }
void setStrength(uint strength) { m_strength = strength; }
uint strength() const { return m_strength; }
private:
KReversiMove ComputeFirstMove(const KReversiGame& game);
int ComputeMove2(int xplay, int yplay, ChipColor color, int level,
......
......@@ -42,10 +42,10 @@ void KReversiBoard::setChipColor(ChipColor color, int row, int col)
// if the cell contains some chip and is being replaced by NoColor,
// we'll decrease the score of that color
// Such replacements (with NoColor) occur during undoing
// and now replacing with chip of 'color'
if( m_cells[row][col] != NoColor && color == NoColor )
m_score[ m_cells[row][col] ]--;
// and now replacing with chip of 'color'
m_cells[row][col] = color;
if( color != NoColor )
......
......@@ -6,7 +6,7 @@
KReversiGame::KReversiGame()
: m_curPlayer(Black), m_computerColor( White )
: m_curPlayer(Black), m_playerColor(Black), m_computerColor( White )
{
m_board = new KReversiBoard();
m_engine = new Engine(1);
......@@ -20,14 +20,10 @@ KReversiGame::~KReversiGame()
void KReversiGame::makePlayerMove( int row, int col )
{
// FIXME dimsuz: debug temporary code
if( m_curPlayer == m_computerColor )
{
kDebug() << "Warning! Player turn called while it's computer turn" << endl;
return;
}
KReversiMove move( m_curPlayer, row, col );
m_curPlayer = m_playerColor;
KReversiMove move( m_playerColor, row, col );
// this can help you to see computer vs computer battle :)
//KReversiMove move = m_engine->computeMove( *this, true );
if( !isMovePossible(move) )
{
kDebug() << "No move possible" << endl;
......@@ -39,14 +35,11 @@ void KReversiGame::makePlayerMove( int row, int col )
void KReversiGame::makeComputerMove()
{
// FIXME dimsuz: debug temporary code
if( m_curPlayer != m_computerColor )
{
kDebug() << "Warning! Computer turn called while it's player turn" << endl;
return;
}
m_curPlayer = m_computerColor;
// FIXME dimsuz: m_competitive. Read from config. What's this btw? :)
// (also there's computeMove in getHint)
KReversiMove move = m_engine->computeMove( *this, true );
// FIXME dimsuz: if move.color == m_computerColor then this might mean that player already has won!
// FIXME dimsuz: if move.color != m_computerColor then this might mean that player already has won!
// Check it!
Q_ASSERT(move.color == m_computerColor);
kDebug() << "Computer plays ("<<move.row<<","<<move.col<<")" <<endl;
......@@ -191,8 +184,6 @@ void KReversiGame::makeMove( const KReversiMove& move )
m_curPlayer = (m_curPlayer == White ? Black : White );
kDebug() << "Now " << (m_curPlayer == White ? "White" : "Black" )<< " play" << endl;
// FIXME dimsuz: is it needed at all?
//emit boardChanged();
emit moveFinished();
}
......@@ -236,7 +227,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -257,7 +248,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -278,7 +269,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -299,7 +290,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -320,7 +311,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -341,7 +332,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -362,7 +353,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -383,7 +374,7 @@ bool KReversiGame::hasChunk( Direction dir, const KReversiMove& move ) const
{
opponentChipsNum++;
}
else if(color == m_curPlayer)
else if(color == move.color)
{
foundPlayerColor = true;
break; //bail out
......@@ -404,6 +395,50 @@ const KReversiBoard& KReversiGame::board() const
return *m_board;
}
bool KReversiGame::isGameOver() const
{
// trivial fast-check
if( m_board->playerScore(White) + m_board->playerScore(Black) == 64 )
return true; // the board is full
else
return !(isAnyPlayerMovePossible() || isAnyComputerMovePossible());
}
bool KReversiGame::isAnyPlayerMovePossible() const
{
for( int r=0; r<8; ++r )
for( int c=0; c<8; ++c )
{
if( m_board->chipColorAt(r,c) == NoColor )
{
// let's see if we can put chip here
if( isMovePossible( KReversiMove( m_playerColor, r, c ) ) )
return true;
}
}
return false;
}
bool KReversiGame::isAnyComputerMovePossible() const
{
for( int r=0; r<8; ++r )
for( int c=0; c<8; ++c )
{
if( m_board->chipColorAt(r,c) == NoColor )
{
// let's see if we can put chip here
if( isMovePossible( KReversiMove( m_computerColor, r, c ) ) )
return true;
}
}
return false;
}
KReversiMove KReversiGame::getHint() const
{
return m_engine->computeMove( *this, true );
}
int KReversiGame::playerScore( ChipColor player ) const
{
return m_board->playerScore( player );
......
......@@ -12,8 +12,21 @@ class Engine;
* KReversiGame incapsulates all of the game logic.
* It creates KReversiBoard and manages a chips on it.
* Whenever the board state changes it emits corresponding signals.
* FIXME dimsuz: enumerate them briefly
* The idea is also to abstract from any graphic representation of the game process
*
* KReversiGame is supposed to be driven by someone from outside.
* I.e. it receives commands and emits events when it's internal state changes
* due to this commands dispatching.
* The main commands are:
* makePlayerMove() and makeComputerMove()
* Also, after each turn is made a user of this class should check
* that isGameOver() returns false,
* and that next player can move
* (by calling isAnyPlayerMovePossible() || isAnyComputerMovePossible()).
* If, for example isAnyPlayerMovePossible() returns false, than player needs to skip and
* makeComputerMove() should be called.
*
* See KReversiScene for example of working with KReversiGame
*/
class KReversiGame : public QObject
{
......@@ -22,18 +35,45 @@ public:
KReversiGame();
~KReversiGame();
/**
* @returns the board so the callers can examine its current state
* Sets the strength of game engine (1 to 7)
*/
void setEngineStrength(uint strength);
/**
* @return strength of the game engine
*/
uint strength() const;
/**
* @return whether the game is already over
*/
bool isGameOver() const;
/**
* @return whether any player move is at all possible
*/
bool isAnyPlayerMovePossible() const;
/**
* @return whether any computer move is at all possible
*/
bool isAnyComputerMovePossible() const;
/**
* @return the board so the callers can examine its current state
*/
const KReversiBoard& board() const;
/**
* @return a color of the current player
*/
ChipColor currentPlayer() const { return m_curPlayer; }
// NOTE: this is just a wrapper around KReversiBoard::playerScore
// Maybe consider merging KReversiBoard into this class?
// same applies to chipColorAt
/**
* @return score (number of chips) of the player
*/
int playerScore( ChipColor player ) const;
// NOTE: this is just a wrapper around KReversiBoard::playerScore
ChipColor chipColorAt( int row, int col ) const;
/**
* This will put the player chip at row, col.
* This will make the player move at row, col.
* If that is possible of course
*/
void makePlayerMove(int row, int col);
......@@ -46,11 +86,19 @@ public:
* Undoes the last player-computer move pair
*/
void undo();
/**
* Returns a hint to current player
*/
KReversiMove getHint() const;
/**
* Returns true, if it's computer's turn now
*/
bool computersTurn() const { return m_curPlayer == m_computerColor; }
bool isComputersTurn() const { return m_curPlayer == m_computerColor; }
/**
* @return a list of chips which were changed during last move.
* First of them will be the move itself, and the rest - chips which
* were turned by that move
*/
QList<KReversiMove> changedChips() const { return m_changedChips; }
signals:
void boardChanged();
......@@ -85,6 +133,10 @@ private:
* Color of the current player
*/
ChipColor m_curPlayer;
/**
* The color of the human played chips
*/
ChipColor m_playerColor;
/**
* The color of the computer played chips
*/
......
......@@ -14,6 +14,7 @@
const int CHIP_SIZE = 36;
KReversiScene::KReversiScene( KReversiGame* game , const QPixmap& chipsPixmap )
: m_showingHint(false), m_hintChip(0)
{
setBackgroundBrush( Qt::lightGray );
......@@ -30,7 +31,6 @@ KReversiScene::KReversiScene( KReversiGame* game , const QPixmap& chipsPixmap )
void KReversiScene::setGame( KReversiGame* game )
{
m_game = game;
// FIXME dimsuz: is it needed?
connect( m_game, SIGNAL(boardChanged()), this, SLOT(updateBoard()) );
connect( m_game, SIGNAL(moveFinished()), this, SLOT(slotMoveFinished()) );
......@@ -93,27 +93,87 @@ void KReversiScene::slotMoveFinished()
void KReversiScene::slotAnimationStep()
{
KReversiMove move = m_changedChips.at(0);
KReversiChip *chip = static_cast<KReversiChip*>(itemAt( cellCenter(move.row, move.col) ));
if( !m_showingHint )
{ // we're animating chips move
bool animFinished = chip->nextFrame();
if(animFinished)
{
chip->setColor( move.color );
KReversiMove move = m_changedChips.at(0);
KReversiChip *chip = static_cast<KReversiChip*>(itemAt( cellCenter(move.row, move.col) ));
bool animFinished = chip->nextFrame();
if(animFinished)
{
chip->setColor( move.color );
m_changedChips.removeFirst(); // we finished animating it
m_changedChips.removeFirst(); // we finished animating it
if(m_changedChips.count() == 0)
{
kDebug() << "stopping timer" <<endl;
m_animTimer->stop();
// some better name maybe?
beginNextTurn();
}
}
}
else
{ // we're just showing hint to the user
m_hintChip->setVisible( !m_hintChip->isVisible() );
// FIXME dimsuz: update only bounding rect!
update();
}
}
if(m_changedChips.count() == 0)
void KReversiScene::beginNextTurn()
{
if( !m_game->isGameOver() )
{
if( m_game->isComputersTurn() )
{
kDebug() << "stopping timer" <<endl;
m_animTimer->stop();
// next turn
if( m_game->computersTurn() )
if(m_game->isAnyComputerMovePossible())
m_game->makeComputerMove();
// else we'll just do nothing and wait for
// player's mouse intput
}
else
{
// if player cant move there's no sence in waiting for mouseInput.
// Let the computer play again!
if( !m_game->isAnyPlayerMovePossible() )
{
// FIXME dimsuz: display this in GUI
kDebug() << "Player can't move!" << endl;
m_game->makeComputerMove();
}
}
}
else
{
// FIXME dimsuz: IMPLEMENT
kDebug() << "GAME OVER" << endl;
}
}
void KReversiScene::slotHint()
{
if( m_game->isComputersTurn() )
{
kDebug() << "It is not a very good time to ask me for a hint, human. I'm thinking..." << endl;
return;
}
if( m_animTimer->isActive() )
{
kDebug() << "Don't you see I'm animating? Be patient, human child..." << endl;
return;
}
KReversiMove hint = m_game->getHint();
if(hint.row == -1 || hint.col == -1)
return;
if( m_hintChip == 0 )
m_hintChip = new KReversiChip( hint.color, m_frameSet, this );
m_hintChip->setPos( cellTopLeft( hint.row, hint.col ) );
m_showingHint = true;
m_animTimer->start(500);
}
QPointF KReversiScene::cellCenter( int row, int col ) const
......@@ -158,9 +218,24 @@ void KReversiScene::drawBackground( QPainter *p, const QRectF& r)
void KReversiScene::mousePressEvent( QGraphicsSceneMouseEvent* ev )
{
if( m_game->computersTurn() )
if( m_game->isComputersTurn() )
{
kDebug() << "It is not your turn, human" << endl;
return;
}
if( m_animTimer->isActive() )
{
kDebug() << "Not your turn, player" << endl;
if( m_showingHint )
{
m_animTimer->stop();
m_hintChip->hide();
m_showingHint = false;
// FIXME dimsuz: update only m_hintChips bounding rect!
update();
}
else // scene is animating move now...
kDebug() << "Don't you see I'm animating? Be patient, human child..." << endl;
return;
}
......
......@@ -8,6 +8,7 @@
class KReversiGame;
class KReversiChipFrameSet;
class KReversiChip;
class QPainter;
class QTimer;
......@@ -34,6 +35,10 @@ public slots:
void updateBoard();
void slotMoveFinished();
void slotAnimationStep();
/**
* Shows hint for player
*/
void slotHint();
private:
/**
* Draws a background with 8x8 cell matrix.
......@@ -41,6 +46,13 @@ private:
*/
virtual void drawBackground( QPainter *p, const QRectF& rect );
virtual void mousePressEvent( QGraphicsSceneMouseEvent* );
/**
* Checks if players can move and if they can:
* if it's time for computer to move or user is locked and can't move,
* this function tells m_game to perform computer move.
* Else it just sits and wait for user's mouse input (= his turn)
*/
void beginNextTurn();
/**
* Returns the center point of cell (row,col)
*/
......@@ -69,6 +81,14 @@ private:
* Animation timer
*/
QTimer* m_animTimer;
/**
* Holds true if the scene is showing hint to the player
*/
bool m_showingHint;
/**
* This is our "hint-chip" - used to show hints
*/
KReversiChip* m_hintChip;
/**
* This list will hold a changed chips
* after each turn. It is received from the game object.
......
......@@ -3,11 +3,12 @@
<MenuBar>
<Menu name="game"><text>&amp;Game</text>
<Action name="new_game"/>
<Action name="quit"/>
<Action name="new_game" />
<Action name="hint" />
<Action name="quit" />
</Menu>
<Menu name="edit"><text>&amp;Edit</text>
<Action name="undo"/>
<Action name="undo" />
</Menu>
<Menu name="settings"><text>&amp;Settings</text>
<Action name="choose_bkgnd" />
......@@ -15,7 +16,8 @@
</MenuBar>
<ToolBar name="mainToolBar"><text>Main Toolbar</text>
<Action name="new_game"/>
<Action name="new_game" />
<Action name="hint" />
<Action name="undo" />
</ToolBar>
</kpartgui>
......@@ -4,12 +4,13 @@
#include "kreversiview.h"
#include <kaction.h>
#include <kapplication.h>
#include <kdebug.h>
#include <kicon.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <kstdaction.h>
#include <kselectaction.h>
#include <kapplication.h>
#include <QGraphicsView>
......@@ -29,9 +30,12 @@ KReversiMainWindow::KReversiMainWindow(QWidget* parent)
void KReversiMainWindow::setupActions()
{
KAction *quitAct = KStdAction::quit(this, SLOT(close()), actionCollection(), "quit");
KAction *newGameAct = KStdAction::openNew(this, SLOT(slotNewGame()), actionCollection(), "new_game");
KAction *quitAct = KStdAction::quit(this, SLOT(close()), actionCollection(), "quit");
KAction *undoAct = KStdAction::undo( this, SLOT(slotUndo()), actionCollection(), "undo" );
KAction *hintAct = new KAction( KIcon("wizard"), i18n("Hint"), actionCollection(), "hint" );
hintAct->setShortcut( Qt::Key_H );
connect( hintAct, SIGNAL(triggered(bool)), m_scene, SLOT(slotHint()) );
KSelectAction *bkgndAct = new KSelectAction(i18n("Choose background"), actionCollection(), "choose_bkgnd");
connect(bkgndAct, SIGNAL(triggered(const QString&)), SLOT(slotBackgroundChanged(const QString&)));
......@@ -47,13 +51,16 @@ void KReversiMainWindow::setupActions()
bkgndAct->addAction(str.mid(idx1+1,idx2-idx1-1));
}
// FIXME dimsuz: this should come from KConfig!
bkgndAct->setCurrentAction( "Hexagon" );
slotBackgroundChanged("Hexagon");
addAction(newGameAct);
addAction(quitAct);
addAction(undoAct);
addAction(hintAct);
}
void KReversiMainWindow::slotBackgroundChanged( const QString& text )
......@@ -89,6 +96,7 @@ void KReversiMainWindow::slotNewGame()
void KReversiMainWindow::slotUndo()
{
// scene will automatically notice that it needs to update
m_game->undo();
}
......
#define KREVERSI_VERSION "1.6"
#define KREVERSI_VERSION "1.99"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment