diff --git a/media/pawn.3ds b/media/pawn.3ds index 78f3e90..01d32e9 100644 Binary files a/media/pawn.3ds and b/media/pawn.3ds differ diff --git a/media/pawn.blend b/media/pawn.blend index 79d882b..497bccb 100644 Binary files a/media/pawn.blend and b/media/pawn.blend differ diff --git a/src/ai.cpp b/src/ai.cpp index fe61551..f9190a7 100644 --- a/src/ai.cpp +++ b/src/ai.cpp @@ -29,6 +29,10 @@ namespace toruschess { static const int NOMOVE = -MAXVAL - 1; static const int DRAWVAL = -7; + /* standard minimax search; saves the best move in *move if move != NULL; + * balance is the current "heuristic value" of a state (mult with curp to get the + * rating for a specifig player) + */ static int minimax(Field &field, int bal, Player curp, unsigned int depth, Move *move) { if (depth == 0) return curp * bal; @@ -72,21 +76,4 @@ namespace toruschess { return move; } - - Move ai::getMove(const Game *game) { - Player curp = NOPLAYER; - switch (game->state()) { - case TURN_WHITE: - curp = WHITE; - break; - case TURN_BLACK: - curp = BLACK; - break; - default: - return Move(); - } - - Field field = *game->field(); - return getMove(field, curp, 2); - } } diff --git a/src/ai.h b/src/ai.h index f0a058e..018e234 100644 --- a/src/ai.h +++ b/src/ai.h @@ -26,10 +26,14 @@ @author Stefan Bühler */ namespace toruschess { - class ai{ + class ai { public: + /** Modifies the field while calculating, so not thread-safe + * (just create a copy of the real field); + * curp: which players turn + * depth: how many moves to be calculated + */ static Move getMove(Field &field, Player curp, int depth); - static Move getMove(const Game *game); }; } diff --git a/src/field2d.h b/src/field2d.h index 9ce7347..b73256e 100644 --- a/src/field2d.h +++ b/src/field2d.h @@ -31,12 +31,14 @@ */ namespace toruschess { + /** Widget for the 2d field view (Game* is shared with other widgets) */ class Field2D : public QWidget { Q_OBJECT public: Field2D(Game *game, QWidget *parent = 0); virtual ~Field2D(); + /** Mark the destination fields */ void markMoves(const QList &moves); virtual QSize sizeHint() const; @@ -48,12 +50,16 @@ namespace toruschess { virtual void mouseReleaseEvent(QMouseEvent *event); virtual void wheelEvent(QWheelEvent *event); + /* update field buffer size */ void checkSizes(); + /* update field buffer */ void updateFieldBuffer(); + /* find position on board from window coords */ Pos findPos(int x, int y); protected slots: + /* slot for game updated event */ void fieldUpdated(); private: @@ -63,7 +69,7 @@ namespace toruschess { QList m_markedMoves; int m_fieldWidth, m_fieldHeight; - int m_originX, m_originY; + int m_originX, m_originY; /* offset for moving board in window */ int m_mouseLastX, m_mouseLastY; diff --git a/src/field3d.cpp b/src/field3d.cpp index 5a8263a..1311535 100644 --- a/src/field3d.cpp +++ b/src/field3d.cpp @@ -27,8 +27,6 @@ #include -#include - #include template @@ -55,13 +53,11 @@ namespace toruschess { m_originX(0), m_originY(0), m_mouseLastX(0), m_mouseLastY(0), m_skySphere(0), m_skyTextureID(0), m_skyTimerID(0), m_skyOffset(0), m_pickTextureID(0) { - qDebug("Field3d::Field3D"); for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) m_marked[x][y] = false; m_lib->setSize(m_textureSize / 8, m_textureSize / 8); } Field3D::~Field3D() { - qDebug("Field3d::~Field3D"); freeTexture(); if (m_skySphere) gluDeleteQuadric(m_skySphere); @@ -83,21 +79,23 @@ namespace toruschess { void Field3D::initializeGL() { glClearColor(0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); -// glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); + glDisable(GL_LIGHT1); glEnable(GL_COLOR_MATERIAL); { const GLfloat white[] = { 1.0, 1.0, 1.0, 1.0 }, inv[] = { 0.0, 0.0, 0.0, 0.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, white); - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, inv); + glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, white); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, inv); glLightfv(GL_LIGHT0, GL_AMBIENT, white); glLightfv(GL_LIGHT0, GL_DIFFUSE, inv); + glLightfv(GL_LIGHT1, GL_AMBIENT, inv); + glLightfv(GL_LIGHT1, GL_DIFFUSE, white); } glFogi(GL_FOG_MODE, GL_EXP2); @@ -127,8 +125,8 @@ namespace toruschess { void Field3D::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_TEXTURE_2D); -// m_lib->paint_pawn(); -// return; + glEnable(GL_LIGHT0); + glDisable(GL_LIGHT1); if (m_boardMode == TORUS) { glDisable(GL_FOG); @@ -180,6 +178,29 @@ namespace toruschess { glTexCoord2f( repeat+ox, -repeat+oy); glVertex3f( 10*repeat, 0, 10*repeat); glTexCoord2f( repeat+ox, repeat+oy); glVertex3f( 10*repeat, 0, -10*repeat); glEnd(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHT1); + glDisable(GL_LIGHT0); + glMatrixMode(GL_MODELVIEW); + ox *= -10.0; oy *= 10.0; + ox += 10.0/16; oy += 10.0/16; + for (int i = 0; i < 8; i++) for (int j = 0; j < 8; j++) { + if (m_game->field()->piece(Pos(i, j)) == PAWN) { + if (m_game->field()->player(Pos(i, j)) == WHITE) { + glColor3f(1.0, 1.0, 1.0); + } else { + glColor3f(.0, .0, .0); + } + for (int x = -10; x <= 10; x++) for (int y = -10; y <= 10; y++) { + glPushMatrix(); + glTranslatef(ox + 10.0/8.0*i + 10*x, 0, oy + 10.0/8.0*j + 10*y); + glRotatef(-90, 1, 0, 0); + m_lib->paint_pawn(); + glPopMatrix(); + } + } + } } glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); @@ -235,7 +256,6 @@ namespace toruschess { unsigned char pxbuf[3]; glReadPixels(mx, height() - 1 - my, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, &pxbuf); paintGL(); - qDebug() << pxbuf[0] << pxbuf[1]; p = Pos(pxbuf[0] - 50, pxbuf[1] - 50); return pxbuf[0] >= 50; } @@ -256,9 +276,7 @@ namespace toruschess { void Field3D::createTexture() { freeTexture(); -// m_textureBuffer = new QGLPixelBuffer(QSize(m_textureSize, m_textureSize), QGLFormat::defaultFormat(), this); m_textureBuffer = new QImage(QSize(m_textureSize, m_textureSize), QImage::Format_ARGB32_Premultiplied); -// m_textureID = m_textureBuffer->generateDynamicTexture(); updateTexture(); @@ -305,13 +323,12 @@ namespace toruschess { m_textureID = 0; } if (m_textureBuffer) { -// delete m_textureBuffer; + delete m_textureBuffer; m_textureBuffer = 0; } } void Field3D::updateTexture() { -// m_textureBuffer->fill(Qt::black); QPainter pt(m_textureBuffer); pt.fillRect(QRect(0, 0, m_textureSize, m_textureSize), Qt::black); @@ -334,7 +351,6 @@ namespace toruschess { pt.setCompositionMode(QPainter::CompositionMode_Plus); pt.fillRect(0, 0, m_textureSize, m_textureSize, Qt::black); pt.setCompositionMode(QPainter::CompositionMode_SourceOver); -// m_textureBuffer->updateDynamicTexture(m_textureID); if (m_textureID) deleteTexture(m_textureID); m_textureID = bindTexture(*m_textureBuffer); } diff --git a/src/field3d.h b/src/field3d.h index e30b374..2958a99 100644 --- a/src/field3d.h +++ b/src/field3d.h @@ -31,6 +31,9 @@ */ namespace toruschess { + /** Widget for the 3d field view (Game* is shared with other widgets) + * Available modes: Plane vs. Torus and 2d vs 3d Figures + */ class Field3D : public QGLWidget { Q_OBJECT public: @@ -42,6 +45,7 @@ namespace toruschess { void markMoves(const QList &moves); + /** Mark the destination fields */ virtual QSize sizeHint() const; BoardMode boardMode() const { return m_boardMode; } @@ -63,15 +67,20 @@ namespace toruschess { virtual void timerEvent(QTimerEvent *event); protected slots: + /* slot for game updated event */ void fieldUpdated(); private: + /* update the GL_MODELVIEW matrix */ void updateCam(); + /* create textures: board texture, pick texture, skybox (sphere) texture */ void createTexture(); void freeTexture(); + /* update board texture */ void updateTexture(); + /* find position on board from window coords */ bool findPos(int mx, int my, Pos &p); Game *m_game; diff --git a/src/optiondlg.cpp b/src/optiondlg.cpp index 00322c0..71d6628 100644 --- a/src/optiondlg.cpp +++ b/src/optiondlg.cpp @@ -26,6 +26,7 @@ namespace toruschess { : QDialog(parent), m_game(game) { m_curMode = m_game->gameMode(); m_curAIStrength = m_game->aiStrength(); + m_curViewMode = VIEW_2D; setupUi(this); m_bgMode = new QButtonGroup(this); @@ -34,6 +35,11 @@ namespace toruschess { m_bgMode->addButton(rb_mode3, COMPUTER_HUMAN); m_bgMode->addButton(rb_mode4, COMPUTER_COMPUTER); + m_vMode = new QButtonGroup(this); + m_vMode->addButton(rb_view1, VIEW_2D); + m_vMode->addButton(rb_view2, VIEW_3D); + m_vMode->addButton(rb_view3, VIEW_TORUS); + dlgReset(); connect(m_game, SIGNAL(changedMode(GameMode)), this, SLOT(gameChangedMode(GameMode))); @@ -66,9 +72,19 @@ namespace toruschess { } } + void OptionDlg::changedViewMode(ViewMode viewMode) { + if (m_curViewMode != viewMode) { + if (m_vMode->checkedId() == m_curViewMode) { + m_vMode->button(viewMode)->setChecked(true); + } + m_curViewMode = viewMode; + } + } + void OptionDlg::dlgReset() { sb_aiStrength->setValue(m_curAIStrength); m_bgMode->button(m_curMode)->setChecked(true); + m_vMode->button(m_curViewMode)->setChecked(true); } void OptionDlg::dlgAccept() { @@ -76,6 +92,8 @@ namespace toruschess { m_game->setAiStrength(m_curAIStrength); m_curMode = (GameMode) m_bgMode->checkedId(); m_game->setGameMode(m_curMode); + m_curViewMode = (ViewMode) m_vMode->checkedId(); + emit setViewMode(m_curViewMode); } } diff --git a/src/optiondlg.h b/src/optiondlg.h index 642f3f8..064482a 100644 --- a/src/optiondlg.h +++ b/src/optiondlg.h @@ -30,11 +30,23 @@ @author Stefan Bühler */ namespace toruschess { + typedef enum { VIEW_2D, VIEW_3D, VIEW_TORUS } ViewMode; + + /** Option Dialog + * Game options are set directly (so one dialog contols only one game instance) + * The view mode option is handled via signal/slot (default is VIEW_2D) + */ class OptionDlg : public QDialog, private Ui::OptionDlg { Q_OBJECT public: OptionDlg(Game *game, QWidget *parent = 0); + signals: + void setViewMode(ViewMode viewMode); + + public slots: + void changedViewMode(ViewMode viewMode); + private slots: void gameChangedMode(GameMode mode); void gameChangedAIStrength(int aiStrength); @@ -46,7 +58,9 @@ namespace toruschess { Game *m_game; GameMode m_curMode; int m_curAIStrength; + ViewMode m_curViewMode; QButtonGroup *m_bgMode; + QButtonGroup *m_vMode; }; } diff --git a/src/optiondlg.ui b/src/optiondlg.ui index b8c7789..a70cdcd 100644 --- a/src/optiondlg.ui +++ b/src/optiondlg.ui @@ -116,9 +116,9 @@ - + - Torus + 2D @@ -130,9 +130,9 @@ - + - 2D + Torus diff --git a/src/piecelibrary.h b/src/piecelibrary.h index ff0ba8b..4be4f9e 100644 --- a/src/piecelibrary.h +++ b/src/piecelibrary.h @@ -29,21 +29,29 @@ */ namespace toruschess { + /** Handles texture/3ds loading from compiled-in Qt-Resource file + */ class PieceLibrary : public QObject { Q_OBJECT public: PieceLibrary(QObject *parent); virtual ~PieceLibrary(); + /* one PieceLibrary instance manages one set of images with a specific size + */ QSize pieceSize() const { return QSize(m_width, m_height); } QSize minPieceSize() const { return QSize(20, 20); } void setSize(int width, int height); + /* just paint the 2d picture of the "place" figure */ void paint(QPainter &pt, int place) const; + /* just paint the 2d picture of the "place" figure into the given rect */ void paint(QPainter &pt, int place, const QRect &rect) const; + /* paint background of a field into the given rect */ void paint_board(QPainter &pt, bool darkField, const QRect &rect) const; + /* paint a 3d pawn */ void paint_pawn(); private: diff --git a/src/testgame.cpp b/src/testgame.cpp index d1875e2..21ed3a9 100644 --- a/src/testgame.cpp +++ b/src/testgame.cpp @@ -44,13 +44,16 @@ namespace toruschess { TestGame::TestGame(QWidget *parent) : QMainWindow(parent), m_game(new Game()), m_optionDlg(new OptionDlg(m_game, this)), m_viewMode((ViewMode) -1) { + connect(this, SIGNAL(changedViewMode(ViewMode)), m_optionDlg, SLOT(changedViewMode(ViewMode))); + connect(m_optionDlg, SIGNAL(setViewMode(ViewMode)), this, SLOT(setViewMode(ViewMode))); + setWindowTitle("Torus Chess"); QWidget *centralWidget = new QWidget(this); m_fieldsLayout = new QStackedLayout(centralWidget); m_field2d = new Field2D(m_game, centralWidget); m_field3d = new Field3D(m_game, centralWidget); - m_fieldsLayout->addWidget(m_field2d); + m_fieldsLayout->addWidget(m_field2d); m_fieldsLayout->addWidget(m_field3d); centralWidget->setLayout(m_fieldsLayout); setCentralWidget(centralWidget); diff --git a/src/testgame.h b/src/testgame.h index ce1858f..e158069 100644 --- a/src/testgame.h +++ b/src/testgame.h @@ -39,8 +39,6 @@ */ namespace toruschess { - typedef enum { VIEW_2D, VIEW_3D, VIEW_TORUS } ViewMode; - class TestGame : public QMainWindow { Q_OBJECT public: diff --git a/src/toruschess.h b/src/toruschess.h index c74df18..44e8502 100644 --- a/src/toruschess.h +++ b/src/toruschess.h @@ -29,7 +29,12 @@ @author Stefan Bühler */ namespace toruschess { - + + /* Game logic + * + * "piece" and "player" are combined to "place": place = piece * player; + */ + class Pos; class Field; class Game; @@ -50,6 +55,7 @@ namespace toruschess { QString state2string(GameState state); + /* coords for a field on the board */ class Pos { public: Pos() : m_x(0), m_y(0) { } @@ -66,6 +72,9 @@ namespace toruschess { int m_x, m_y; }; + /* some basic information for one move: from and to, + * which figure got moved, which (if any) got beaten + */ class Move { public: Move(); @@ -73,8 +82,11 @@ namespace toruschess { const Pos& from() const { return m_from; } const Pos& to() const { return m_to; } + /** figure which gets moved */ int prevFrom() const { return m_prevFrom; } + /** figure which gets beaten */ int prevTo() const { return m_prevTo; } + /** which player is moving (player from prevFrom()) */ Player player() const { return m_player; } bool operator==(const Move &other) const { @@ -92,7 +104,9 @@ namespace toruschess { int m_prevFrom, m_prevTo; Player m_player; }; - + + + /** the raw field: only knows which figures is where */ class Field { public: Field(); @@ -104,23 +118,32 @@ namespace toruschess { int place(const Pos &p) const { return m_places[p.y()][p.x()]; } int& place(const Pos &p) { return m_places[p.y()][p.x()]; } + /* all valid moves for a piece */ QList validMoves(const Pos &from) const; bool validMove(const Move &m) const; /* does not check for inCheck */ - bool move(const Move &m); - bool undo(const Move &m); + bool move(const Move &m); /* check if the move is valid */ + bool undo(const Move &m); /* check if the undo is valid */ + /* check if the king of a player at pos pking is in check */ bool inCheck(Player player, const Pos &pking) const; + /* check if the king of a player is in check */ bool inCheck(Player player) const; void move_unchecked(const Move &m); void undo_unchecked(const Move &m); private: + /* does not check for "in check" */ QList simpleValidMoves(const Pos &from) const; Pos m_wking, m_bking; int m_places[8][8]; }; - + + + /* contains a field plus addition information about the game state + * such as which turn it is or who has won + * also is responsible to do the ai moves, if activated + */ class Game : public QObject { Q_OBJECT public: @@ -131,9 +154,12 @@ namespace toruschess { GameState state() const { return m_state; } QList moves() const { return m_moves; } + /* only valid moves will be done */ bool move(const Move &m); + /* restart the game */ void restart(); + /* all valid moves for the current player */ QList possibleMoves() const; GameMode gameMode() const { return m_gameMode; } @@ -170,6 +196,7 @@ namespace toruschess { bool m_ignoreAI; }; + /* Calculate the ai move in another thread so the main thread (gui) isn't blocked */ class GameAIThread : public QThread { Q_OBJECT public: