commit b0afee3d6048e12e17f225bdde24ad3a9d18f5a1 Author: stianse Date: Mon Oct 15 19:32:30 2007 +0000 Initial import/add diff --git a/mancatux.pro b/mancatux.pro new file mode 100644 index 0000000..6e2f9f6 --- /dev/null +++ b/mancatux.pro @@ -0,0 +1,35 @@ +###################################################################### +# Automatically generated by qmake (2.01a) sų. okt. 14 20:36:22 2007 +###################################################################### + +OBJECTS_DIR = obj +UI_DIR = src/gui/uic +MOC_DIR = src/gui/moc +TEMPLATE = app +TARGET = mancatux +DEPENDPATH += . src src/gui src/gui/uic +INCLUDEPATH += . src src/gui src/gui/uic + +# Input +HEADERS += src/Board.h \ + src/EngineFactory.h \ + src/Game.h \ + src/IEngine.h \ + src/IGame.h \ + src/KalahEngine.h \ + src/Player.h \ + src/Session.h \ + src/gui/ClickLabel.h \ + src/gui/GuiWrapper.h \ + src/gui/Hole.h \ + src/gui/MainWindow.h +FORMS += src/gui/MainWindow.ui +SOURCES += src/Board.cpp \ + src/Game.cpp \ + src/KalahEngine.cpp \ + src/Main.cpp \ + src/Player.cpp \ + src/Session.cpp \ + src/gui/ClickLabel.cpp \ + src/gui/GuiWrapper.cpp \ + src/gui/MainWindow.cpp diff --git a/qmake.sh b/qmake.sh new file mode 100755 index 0000000..0270c0a --- /dev/null +++ b/qmake.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +TARGET=mancatux +OBJECTS_DIR=obj +UI_DIR=src/gui/uic +MOC_DIR=src/gui/moc + +qmake -project \ + "OBJECTS_DIR = $OBJECTS_DIR" \ + "UI_DIR = $UI_DIR" \ + "MOC_DIR = $MOC_DIR" \ + -o $TARGET.pro + +qmake + +make diff --git a/src/Board.cpp b/src/Board.cpp new file mode 100644 index 0000000..b67be37 --- /dev/null +++ b/src/Board.cpp @@ -0,0 +1,293 @@ +/** + * A class for a Mancala board. The following board layout is assumed + * + * Holes num: + * + * 13 12 11 10 9 8 + * 0 7 + * 1 2 3 4 5 6 + * + * + * Corresponding store num: + * + * 2 1 + * + * Corresponding house num: + * + * 12 11 10 9 8 7 + * + * 1 2 3 4 5 6 + * + * + * + * Any even number of houses is allowed. + * + */ +#include +#include +#include +#include +#include + +#include "Board.h" + +using namespace std; + +//const int INITIAL_NUMBER_OF_SEEDS_PER_HOUSE = 3; + + +/** Constructor. + * + * @param holes is the number of holes, including both house and store, in + * the board. Must be an even number. + * @param seeds is the initial number of seeds per hole. + * + * @exception std::invalid_argument. + */ + +Board::Board() {} + + +Board::Board(int houses, int seeds) +{ + int holes = houses + 2; + + if (holes % 2 != 0 || holes <= 0) + { + ostringstream msg; + msg << "'holes' = " << holes << ": " + << "The number of holes in a board must be equal"; + throw invalid_argument(msg.str()); + } + + store2 = 0; + store1 = holes / 2; + + board.reserve(holes); + for (int i = 0; i < holes; ++i) + { + if (i == store1 || i == store2) + board.push_back(0); + else + board.push_back(seeds); + } +} + + +int & Board::operator[] (int hole) +{ + if (hole < 0 || hole > static_cast(board.size()-1)) + throw invalid_argument("argument out of range"); + return board[hole]; +} + + +#if 0 +/* house number starts at 1 */ +bool Board::sow(int house, Half playersHalf, int * endId, Board::HoleType * endType) +{ + int hole; + + assert (playersHalf == SOUTH || playersHalf == NORTH); + + try + { + hole = house2hole(house); + } + catch (...) + { + return false; + } + + if (getHalf(hole) != playersHalf) + return false; + + int seeds = getSeedsInHouse(house); + + if (seeds == 0) + return false; + + board[hole] = 0; + + int opponentStore = getStoreHole((playersHalf == SOUTH) ? NORTH : SOUTH); + while (seeds) + { + hole = ( hole == static_cast( board.size())-1 ) ? 0 : hole + 1; + if (hole != opponentStore) + { + ++board[hole]; + --seeds; + } + } + + *endType = (hole == store1 || hole == store2) ? STORE : HOUSE; + *endId = *endType == STORE ? hole2store(hole) : hole2house(hole); + + return true; +} +#endif + + +bool Board::isHalfEmpty(Half half) const +{ + int isEmpty = true; + int hole = half == SOUTH ? store2 + 1 : store1 + 1; + int end = half == SOUTH ? store1 : store2; + + while (hole != end) + { + if (board[hole] != 0) + { + isEmpty = false; + break; + } + hole = ( hole == static_cast( board.size())-1 ) ? 0 : hole + 1; + } + + return isEmpty; +} + + + +int Board::getSeedsInHouse(int house) const +{ + return board[house2hole(house)]; +} + + +int Board::getSeedsInStore(int store) const +{ + return board[store2hole(store)]; +} + + +Board::Half Board::getHalf (int hole) const +{ + if (hole <= store1 && hole != store2) + return SOUTH; + else + return NORTH; +} + + +int Board::getOppositeHole( int hole ) const +{ + int opposite; + + checkRange (hole); + + if ( hole == 0 ) + opposite = board.size() / 2; + else if ( hole == static_cast(board.size()) / 2 ) + opposite = 0; + else + opposite = board.size() - hole; + + return opposite; +} + + +int Board::hole2house(int hole) const +{ + checkRange (hole); + return hole < store1 ? hole : hole - 1; +} + +/** + * Returns the store number which HOLE corresponds to. Returns 0 if hole does + * not correspond to any store. + */ +int Board::hole2store(int hole) const +{ + checkRange (hole); + if ( hole == store1 ) + return 1; + else if ( hole == store2) + return 2; + else + return 0; +} + +int Board::house2hole(int house) const +{ + if (house < 1 || house > static_cast(board.size()-2)) + throw invalid_argument("Invalid house number"); + return house < store1 ? house : house + 1; +} + +int Board::store2hole(int store) const +{ + if (store < 1 || store > 2) + throw invalid_argument("Invalid store number"); + return store == 1 ? store1 : store2; + +} + +#if 0 +// num is number from 1... +int Board::getHole(int num, HoleType type) const +{ + int hole; + if (type == HOUSE) + { + if (num < 1 || num > board.size() - 2) + throw invalid_argument("Invalid house number"); + hole = (num < store1) ? num : num + 1; + } + else if (type == STORE) + { + } + else + throw invalid_argument("Unknown hole type"); + return hole; +} +#endif + + +int Board::getStoreHole(Half half) const +{ + int hole; + if (half == SOUTH) + hole = store1; + else if (half == NORTH) + hole = store2; + else + throw invalid_argument("Only store for half SOUTH and NORTH"); + return hole; +} + +size_t Board::size () const +{ + return board.size(); +} + + +void Board::print(std::ostream & out) const +{ + int i; + std::ostringstream s1, s2, s3; + + for ( i = 1; i < static_cast(board.size())/2; ++i ) + { + s1 << getSeedsInHouse( board.size()-1 - i ) << "\t"; + s2 << "\t"; + s3 << getSeedsInHouse( i ) << "\t"; + } + + out << "\t" << s1.str() << "\n"; + out << getSeedsInStore( 2 ) << s2.str() << "\t "<< getSeedsInStore( 1 ) << "\n"; + out << "\t" << s3.str() << std::endl; +} + +bool Board::checkRange( int hole ) const +{ + if ( hole < 0 || hole > static_cast(board.size()-1) ) + throw invalid_argument("Invalid hole number"); + return true; +} + +// Board::~Board() +// { +// delete[] board; +// } + diff --git a/src/Board.h b/src/Board.h new file mode 100644 index 0000000..b521598 --- /dev/null +++ b/src/Board.h @@ -0,0 +1,48 @@ +#ifndef _BOARD_H_ +#define _BOARD_H_ + +#include +#include + +class Board +{ +public: + enum HoleType { HOUSE, STORE }; + enum Half {UNSPECIFIED, NORTH, SOUTH}; + + Board(); + Board(int houses, int seeds); +// ~Board(); + + int & operator[] (int hole); + +// bool sow(int house, Half playersHalf, int * endId, HoleType * endType); + int getSeedsInHouse(int house) const; + int getSeedsInStore(int store) const; + int getOppositeHole(int hole) const; + void print ( std::ostream & out) const; + + bool isHalfEmpty(Half half) const; + + int store2hole(int store) const; + int hole2store(int hole) const; + int hole2house(int hole) const; + + int getStoreHole(Half Half) const; + int house2hole(int house) const; + Half getHalf(int hole) const; + + size_t size() const; + +private: + + int store1; + int store2; + std::vector board; + + bool checkRange (int hole) const; +}; + + + +#endif diff --git a/src/EngineFactory.h b/src/EngineFactory.h new file mode 100644 index 0000000..86e7e73 --- /dev/null +++ b/src/EngineFactory.h @@ -0,0 +1,14 @@ +#ifndef _ENGINEFACTORY_H_ +#define _ENGINEFACTORY_H_ + +#include "KalahEngine.h" + +class IEngine; + +class EngineFactory +{ +public: + static IEngine * createKalah() { return new KalahEngine(); } +}; + +#endif diff --git a/src/Game.cpp b/src/Game.cpp new file mode 100644 index 0000000..e99eaaa --- /dev/null +++ b/src/Game.cpp @@ -0,0 +1,110 @@ + +#include +#include + +#include "Game.h" +#include "Board.h" +#include "Player.h" +#include "IEngine.h" + + +Game::Game ( IEngine * engine, int houses, int seeds ) + : engine_(engine), + board_( houses, seeds ), + finished_(false) +{ } + + +void Game::player1 ( std::string name, bool user ) +{ + player1_ = new Player(name, user ? Player::USER : Player::COMPUTER, &board_, Board::SOUTH); + activePlayer_ = player1_; +} + +void Game::player2 ( std::string name, bool user ) +{ + player2_ = new Player(name, user ? Player::USER : Player::COMPUTER, &board_, Board::NORTH); +} + + +/** + * Makes the active user make a move (seed) on HOUSE and updates game state. + * Returns TRUE if the move is valid. + */ +bool Game::makeMove( int house ) +{ + assert( player1_ ); + assert( player2_ ); + + if (finished_) + return false; + + IEngine::MoveResult res = engine_->makeMove( activePlayer_, &board_, house ); + switch (res) + { + case IEngine::INVALID_MOVE: + return false; + case IEngine::VALID_MOVE_EXTRA_TURN: + return true; + case IEngine::VALID_MOVE_CHANGE_TURN: + activePlayer_ = activePlayer_ == player1_ ? player2_ : player1_; + return true; + case IEngine::VALID_MOVE_GAME_FINISHED: + finished_ = true; + return true; + default: + std::cerr << __FUNCTION__ << ": Got invalid move result" << std::endl; + assert(true); + } + + return false; +} + +/** + * Returns the name of the winner or an empty string if the game ends in a + * draw. If the game is not finished a 0 pointer is returned. + */ +std::string Game::getWinner () const +{ + if (finished_) + { + Player * winner = engine_->getWinner (player1_, player2_, &board_); + if ( winner == 0 ) + return ""; + else + return winner->name(); + } + return 0; +} + +bool Game::isFinished () const +{ + return finished_; +} + +int Game::getSeedsInHouse ( int house ) const +{ + return board_.getSeedsInHouse( house ); +} + + +int Game::getSeedsInStorage ( int storage ) const +{ + return board_.getSeedsInStore ( storage ); +} + +std::string Game::getActivePlayerName () const +{ + return activePlayer_->name(); +} + +std::string Game::getPlayer1Name () const +{ + return player1_->name(); +} + +std::string Game::getPlayer2Name () const +{ + return player2_->name(); +} + diff --git a/src/Game.h b/src/Game.h new file mode 100644 index 0000000..feb2465 --- /dev/null +++ b/src/Game.h @@ -0,0 +1,45 @@ +#ifndef _GAME_H_ +#define _GAME_H_ + +#include +#include + +#include "IGame.h" +#include "Player.h" +#include "Board.h" + +class IEngine; + +class Game : public IGame +{ +public: + Game(IEngine * engine, int houses = 12, int seeds = 3); + + void player1(std::string name, bool user); + void player2(std::string name, bool user); + + virtual bool isFinished() const; + virtual std::string getWinner() const; + virtual std::string getActivePlayerName() const; + virtual std::string getPlayer1Name () const; + virtual std::string getPlayer2Name () const; +// Board & getBoard() const; + + virtual bool makeMove( int house ); + virtual int getSeedsInHouse( int house ) const; + virtual int getSeedsInStorage( int storage ) const; + +private: + std::auto_ptr engine_; + Player * player1_; + Player * player2_; + Player * activePlayer_; + Board board_; + bool finished_; + +// void updateState (int latestEndHoleId, Board::HoleType latestEndHoleType); +}; + + + +#endif diff --git a/src/IEngine.h b/src/IEngine.h new file mode 100644 index 0000000..a53693b --- /dev/null +++ b/src/IEngine.h @@ -0,0 +1,23 @@ +#ifndef _IENGINE_H_ +#define _IENGINE_H_ + +class Player; +class Board; + +class IEngine +{ +public: + enum MoveResult { + INVALID_MOVE, + VALID_MOVE_CHANGE_TURN, + VALID_MOVE_EXTRA_TURN, + VALID_MOVE_GAME_FINISHED + }; + + virtual ~IEngine() { } + + virtual MoveResult makeMove (const Player * player, Board * board, int house) = 0; + virtual Player * getWinner (Player * p1, Player * p2, const Board * board) const = 0; +}; + +#endif diff --git a/src/IGame.h b/src/IGame.h new file mode 100644 index 0000000..4fee71a --- /dev/null +++ b/src/IGame.h @@ -0,0 +1,28 @@ +#ifndef _IGAME_H_ +#define _IGAME_H_ + +#include + +class Player; + +class IGame +{ +public: + virtual ~IGame() {}; + + virtual bool makeMove ( int house ) = 0; + virtual int getSeedsInHouse ( int house ) const = 0; + virtual int getSeedsInStorage ( int storage ) const = 0; + virtual bool isFinished () const = 0; + virtual std::string getWinner () const = 0; + virtual std::string getActivePlayerName () const = 0; + virtual std::string getPlayer1Name () const = 0; + virtual std::string getPlayer2Name () const = 0; + + +}; + +#endif + + + diff --git a/src/KalahEngine.cpp b/src/KalahEngine.cpp new file mode 100644 index 0000000..2eff958 --- /dev/null +++ b/src/KalahEngine.cpp @@ -0,0 +1,102 @@ +#include +#include + +#include "KalahEngine.h" +#include "Player.h" +#include "Board.h" + +IEngine::MoveResult KalahEngine::makeMove (const Player * player, Board * board, int house) +{ + // TODO: Use an board iterator instead. + // We should not have to guess the relation between holes in this class + Board::Half playersHalf = player->half(); + assert (playersHalf == Board::SOUTH || playersHalf == Board::NORTH); + + // Check validity of move + int hole; + try + { + hole = board->house2hole(house); + } + catch (...) // insert correct exception: invalid_argument + { + return INVALID_MOVE; + } + + if (board->getHalf(hole) != playersHalf) + return INVALID_MOVE; + + int seeds = (*board)[hole]; + + if (seeds == 0) + return INVALID_MOVE; + + // Move seeds + (*board)[hole] = 0; + int opponentStoreHole = board->getStoreHole((playersHalf == Board::SOUTH) ? Board::NORTH : Board::SOUTH); + while (seeds) + { + hole = ( hole == static_cast( board->size())-1 ) ? 0 : hole + 1; + if (hole != opponentStoreHole) + { + ++(*board)[hole]; + --seeds; + } + } + + + // If the last sown seed lands in an empty house owned by the player, and + // the opposite house contains seeds, both the last seed and the opposite + // seeds are captured and placed into the player's store. + int storeHole = board->getStoreHole(playersHalf); + int oppositeHole = board->getOppositeHole(hole); + + if ( (*board)[hole] == 1 && + board->getHalf(hole) == playersHalf && + hole != storeHole && + (*board)[oppositeHole] > 0 ) + { + int oppositeHole = board->getOppositeHole(hole); + + (*board)[storeHole] += (*board)[hole] + (*board)[oppositeHole]; + (*board)[hole] = 0; + (*board)[oppositeHole] = 0; + } + + // Check if game is finished + if ( board->isHalfEmpty( playersHalf ) ) + { + // Move remaining seeds on opponents half into his/her store + int i = storeHole + 1; + while ( i != opponentStoreHole ) + { + (*board)[opponentStoreHole] += (*board)[i]; + (*board)[i] = 0; + i = ( i == static_cast( board->size())-1 ) ? 0 : i + 1; + } + return VALID_MOVE_GAME_FINISHED; + } + + // If last seed is sown in own store the player gets an extra turn + return hole == storeHole ? VALID_MOVE_EXTRA_TURN : VALID_MOVE_CHANGE_TURN; +} + + +/** + * Must only be called when a game is finished. If not, the return value is undefined. + * + * Returns the winning player, or 0 if the game ends in a draw. + */ +Player * KalahEngine::getWinner (Player * p1, Player * p2, const Board * b) const +{ + int point1 = b->getSeedsInStore( 1 ); + int point2 = b->getSeedsInStore( 2 ); + Player * winner = 0; + + if (point1 > point2) + winner = p1; + else if (point2 > point1) + winner = p2; + + return winner; +} diff --git a/src/KalahEngine.h b/src/KalahEngine.h new file mode 100644 index 0000000..56f26a0 --- /dev/null +++ b/src/KalahEngine.h @@ -0,0 +1,16 @@ +#ifndef _KALAHENGINE_H_ +#define _KALAHENGINE_H_ + +#include "IEngine.h" + +class Player; +class Board; + +class KalahEngine : public IEngine +{ +public: + virtual KalahEngine::MoveResult makeMove (const Player * player, Board * board, int house); + virtual Player * getWinner (Player * p1, Player * p2, const Board * board) const; +}; + +#endif diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..bb16526 --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,12 @@ +#include + +#include "GuiWrapper.h" +#include "Session.h" + +int main ( int argc, char * argv[] ) +{ + QApplication app ( argc, argv ); + GuiWrapper guiwrapper; + Session session( &guiwrapper ); + return app.exec(); +} diff --git a/src/Player.cpp b/src/Player.cpp new file mode 100644 index 0000000..f2f4a33 --- /dev/null +++ b/src/Player.cpp @@ -0,0 +1,28 @@ +#include "Player.h" +#include "Board.h" + + +Player::Player(std::string name, Player::type type, Board * board, Board::Half half ) : + name_(name), type_(type), board_(board), half_(half) +{} + + +Board::Half Player::half() const +{ + return half_; +} + +std::string Player::name() const +{ + return name_; +} + +// bool Player::sow(int house, int * endId, Board::HoleType * endType ) +// { +// if (!board_ || half_ == Board::UNSPECIFIED) +// return false; + +// return board_->sow( house, half_, endId, endType ); +// } + + diff --git a/src/Player.h b/src/Player.h new file mode 100644 index 0000000..bf8f2de --- /dev/null +++ b/src/Player.h @@ -0,0 +1,28 @@ +#ifndef _PLAYER_H_ +#define _PLAYER_H_ + +#include + +#include "Board.h" + +class Player +{ +public: + enum type {USER, COMPUTER}; + + Player( std::string name, type type, Board * board, Board::Half half ); + + Board::Half half() const; + std::string name() const; + +// bool sow(int house, int * endId, Board::HoleType * endType); + + +private: + std::string name_; + type type_; + Board * board_; + Board::Half half_; +}; + +#endif diff --git a/src/Session.cpp b/src/Session.cpp new file mode 100644 index 0000000..93d460b --- /dev/null +++ b/src/Session.cpp @@ -0,0 +1,28 @@ +#include "Session.h" +#include "EngineFactory.h" +#include "IGame.h" +#include "Game.h" +#include "GuiWrapper.h" + +Session::Session ( GuiWrapper * g) + : gui_(g) +{ + newGame(); + gui_->init(this); + gui_->start(); +} + +void Session::newGame () +{ + // todo: do this completely different +// delete game_; + Game * g = new Game( factory.createKalah(), 12); + g->player1("P1", true); + g->player2("P2", true); + game_ = g; +} + +IGame * Session::game () const +{ + return game_; +} diff --git a/src/Session.h b/src/Session.h new file mode 100644 index 0000000..8d0a9a1 --- /dev/null +++ b/src/Session.h @@ -0,0 +1,22 @@ +#ifndef _SESSION_H_ +#define _SESSION_H_ + +#include "IGame.h" +#include "EngineFactory.h" +class GuiWrapper; + +class Session +{ +public: + Session ( GuiWrapper * g ); // todo: change to GuiInterface + + void newGame(); + IGame * game() const; + +private: + IGame * game_; + GuiWrapper * gui_; + EngineFactory factory; +}; + +#endif diff --git a/src/gui/ClickLabel.cpp b/src/gui/ClickLabel.cpp new file mode 100644 index 0000000..0b8927d --- /dev/null +++ b/src/gui/ClickLabel.cpp @@ -0,0 +1,42 @@ +#include +#include + +#include "ClickLabel.h" + + +ClickLabel::ClickLabel ( QWidget * parent, Qt::WindowFlags f ) + : QLabel( parent, f ) +{} + + +ClickLabel::ClickLabel ( const QString & text, QWidget * parent , Qt::WindowFlags f ) + : QLabel( text, parent, f ) +{} + + +void ClickLabel::mousePressEvent ( QMouseEvent * event) +{ + if ( event->button() == Qt::LeftButton ) + { + emit clicked(); + } +} + + +#if 0 + +#include +#include +#include "ui_mainWindow.h" + +int main ( int argc, char * argv[] ) +{ + QApplication app ( argc, argv ); + QMainWindow qmw; + Ui::MainWindow mainwin; + mainwin.setupUi(&qmw); + QObject::connect( mainwin.label, SIGNAL(clicked()), &app, SLOT(quit()) ); + qmw.show(); + return app.exec(); +} +#endif diff --git a/src/gui/ClickLabel.h b/src/gui/ClickLabel.h new file mode 100644 index 0000000..628a3d1 --- /dev/null +++ b/src/gui/ClickLabel.h @@ -0,0 +1,24 @@ +#ifndef _CLICKLABEL_H_ +#define _CLICKLABEL_H_ + +#include +#include + +class ClickLabel : public QLabel +{ + Q_OBJECT + +public: + ClickLabel( QWidget * parent = 0, Qt::WindowFlags f = 0 ); + ClickLabel ( const QString & text, QWidget * parent = 0, Qt::WindowFlags f = 0 ); + +signals: + void clicked(); + +protected: + virtual void mousePressEvent (QMouseEvent * event); +}; + +#endif + + diff --git a/src/gui/GuiWrapper.cpp b/src/gui/GuiWrapper.cpp new file mode 100644 index 0000000..54e8f69 --- /dev/null +++ b/src/gui/GuiWrapper.cpp @@ -0,0 +1,26 @@ +#include "GuiWrapper.h" +#include "MainWindow.h" + + +GuiWrapper::GuiWrapper ( ) +{ } + + +GuiWrapper::~GuiWrapper () +{ + delete mainWin_; +} + + +void GuiWrapper::init (Session * session) +{ + session_ = session; + mainWin_ = new MainWindow(session_); +} + +void GuiWrapper::start () +{ + mainWin_->init(); + mainWin_->show(); +} + diff --git a/src/gui/GuiWrapper.h b/src/gui/GuiWrapper.h new file mode 100644 index 0000000..b5f967e --- /dev/null +++ b/src/gui/GuiWrapper.h @@ -0,0 +1,25 @@ +#ifndef _GUIWRAPPER_H_ +#define _GUIWRAPPER_H_ + +class MainWindow; +class Session; + +class GuiWrapper +{ +public: + GuiWrapper(); + ~GuiWrapper(); + void init( Session * session); + void start(); + +private: + MainWindow * mainWin_; + Session * session_; + + // Disable copy construction and assignment + GuiWrapper ( const GuiWrapper & ); + GuiWrapper & operator= ( const GuiWrapper & ); + +}; + +#endif diff --git a/src/gui/Hole.h b/src/gui/Hole.h new file mode 100644 index 0000000..a132511 --- /dev/null +++ b/src/gui/Hole.h @@ -0,0 +1,33 @@ +#ifndef _HOLE_H_ +#define _HOLE_H_ + +#include + +#include "ClickLabel.h" + + + +class Hole : public ClickLabel +{ + Q_OBJECT + +public: + Hole ( QWidget * parent = 0, Qt::WindowFlags f = 0 ) + : ClickLabel ( parent, f ), id_( 0 ) { } + + void setId ( int id ) { id_ = id; } + +signals: + void clicked ( int id ); + +private: + int id_; + + void mousePressEvent ( QMouseEvent * event ) + { + if ( event->button() == Qt::LeftButton ) + emit clicked( id_ ); + } +}; + +#endif diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp new file mode 100644 index 0000000..74200df --- /dev/null +++ b/src/gui/MainWindow.cpp @@ -0,0 +1,99 @@ +#include "MainWindow.h" +#include "Session.h" + +#include +#include + +MainWindow::MainWindow ( Session * session, QMainWindow * parent ) + : QMainWindow( parent ), session_(session) +{ + setupUi( this ); + + houses_.push_back( house1 ); + houses_.push_back( house2 ); + houses_.push_back( house3 ); + houses_.push_back( house4 ); + houses_.push_back( house5 ); + houses_.push_back( house6 ); + houses_.push_back( house7 ); + houses_.push_back( house8 ); + houses_.push_back( house9 ); + houses_.push_back( house10 ); + houses_.push_back( house11 ); + houses_.push_back( house12 ); + + for (size_t i = 0 ; i < houses_.size(); ++i ) + { + houses_[i]->setId( i + 1); + QObject::connect( houses_[i], SIGNAL( clicked(int) ), + this, SLOT( houseClicked(int) ) ); + } + + storages_.push_back( storage1 ); + storages_.push_back( storage2 ); + +} + + +void MainWindow::init() +{ + if (session_->game()) + { + player1->setText( session_->game()->getPlayer1Name().c_str() ); + player2->setText( session_->game()->getPlayer2Name().c_str() ); + + update(); + } +} + +void MainWindow::update() +{ + for (size_t i = 0 ; i < houses_.size(); ++i ) + houses_[i]->setNum( session_->game()->getSeedsInHouse(i+1) ); + + for (size_t i = 0 ; i < storages_.size(); ++i ) + storages_[i]->setNum( session_->game()->getSeedsInStorage(i+1) ); + + if (session_->game()->isFinished()) + { + QString winner = session_->game()->getWinner().c_str(); + if (winner != "") + status->setText(winner + " wins!"); + else + status->setText("It's a draw!"); + } + else + { + // Change font on players name to show current player + QFont font1(player1->font()); + QFont font2(player2->font()); + QString activePlayer = session_->game()->getActivePlayerName().c_str(); + // todo: this approach does not work when players have same name. + if (player1->text() == activePlayer) + { + font1.setUnderline(true); + font1.setBold(true); + font2.setUnderline(false); + font2.setBold(false); + } + else + { + font1.setUnderline(false); + font1.setBold(false); + font2.setUnderline(true); + font2.setBold(true); + } + player1->setFont(font1); + player2->setFont(font2); + status->setText("Waiting for " + activePlayer + " To make a move..."); + } + + QMainWindow::update(); +} + + +void MainWindow::houseClicked (int id) +{ + if ( session_->game()->makeMove(id) ) + update(); +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h new file mode 100644 index 0000000..a4ec3a0 --- /dev/null +++ b/src/gui/MainWindow.h @@ -0,0 +1,32 @@ +#ifndef _MAINWINDOW_H_ +#define _MAINWINOW_H_ + + +#include +#include +#include "ui_MainWindow.h" + +class Session; +class Hole; + +class MainWindow : public QMainWindow, public Ui::MainWindow +{ + Q_OBJECT + +public: + MainWindow (Session * session, QMainWindow * parent = 0 ); + + void init(); + void update(); + +private slots: + void houseClicked ( int id ); + +private: + Session * session_; + std::vector houses_; + std::vector storages_; +}; + + +#endif diff --git a/src/gui/MainWindow.ui b/src/gui/MainWindow.ui new file mode 100644 index 0000000..ea5741c --- /dev/null +++ b/src/gui/MainWindow.ui @@ -0,0 +1,408 @@ + + MainWindow + + + + 0 + 0 + 384 + 197 + + + + Mancala + + + + + + + + + + + + + Qt::Horizontal + + + + 61 + 28 + + + + + + + + Player 2 + + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + + + Player 1 + + + + + + + Qt::Horizontal + + + + 61 + 28 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + status + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + 384 + 29 + + + + + File + + + + + + + + + New game + + + + + + Hole + QLabel +
Hole.h
+
+
+ + +
diff --git a/src/gui/MainWindow.ui.bak b/src/gui/MainWindow.ui.bak new file mode 100644 index 0000000..dc52e9b --- /dev/null +++ b/src/gui/MainWindow.ui.bak @@ -0,0 +1,408 @@ + + MainWindow + + + + 0 + 0 + 384 + 197 + + + + Mancala + + + + + + + + + + + + + Qt::Horizontal + + + + 61 + 28 + + + + + + + + Player 2 + + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + QFrame::StyledPanel + + + 0 + + + Qt::AlignCenter + + + + + + + + + + + Player 1 + + + + + + + Qt::Horizontal + + + + 61 + 28 + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + status + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + 0 + 0 + 384 + 29 + + + + + File + + + + + + + + + New game + + + + + + Hole + QLabel +
hole.h
+
+
+ + +