diff --git a/src/field2d.cpp b/src/field2d.cpp new file mode 100644 index 0000000..c905078 --- /dev/null +++ b/src/field2d.cpp @@ -0,0 +1,131 @@ +/*************************************************************************** + * Copyright (C) 2009 by Stefan Bühler * + * stbuehler@web.de * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "field2d.h" + +#include + +#include +#include +#include +#include + +namespace toruschess { + + Field2D::Field2D(Game *game, QWidget *parent) + : QWidget(parent), m_lib(new PieceLibrary(this)), m_game(game), m_fieldWidth(360), m_fieldHeight(360), m_originX(23), m_originY(23) { + for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) m_marked[x][y] = false; + m_lib->setSize(m_fieldWidth / 8, m_fieldHeight / 8); + } + + void Field2D::markMoves(const QList &moves) { + foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = false; + m_markedMoves = moves; + foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = true; + update(); + } + + QSize Field2D::sizeHint() const { + return QSize(17*m_fieldWidth/8, 17*m_fieldHeight/8); + } + + void Field2D::paintEvent(QPaintEvent *) { + QPainter pt(this); + int pieceWidth = m_fieldWidth / 8, pieceHeight = m_fieldHeight / 8; + int winWidth = width(), winHeight = height(); + for (int x = 0; x < 8; x++) + for (int y = 0; y < 8; y++) { + bool marked = m_marked[x][y]; + int place = m_game->field()->place(Pos(x, y)); + /* #D18B47 for dark, #FFCE9E for white */ + QBrush fieldBG = ((x+y) % 2 == 1) ? QBrush(QColor(0xD1, 0x8B, 0x47)) : QBrush(QColor(0xFF, 0xCE, 0x9E)); + + for (int px = m_originX - m_fieldWidth + x*pieceWidth; px < winWidth; px += m_fieldWidth) + for (int py = m_originY - m_fieldHeight + y*pieceHeight; py < winHeight; py += m_fieldHeight) { + QRect prect(px, py, pieceWidth - 1, pieceHeight - 1); + pt.fillRect(prect, fieldBG); + m_lib->paint(pt, place, prect); + if (marked) { + int r = qMax(1, (pieceWidth + pieceHeight) / 36); + pt.setBrush(place != 0 ? Qt::green : Qt::white); + pt.setPen(Qt::red); + pt.drawEllipse(QPoint(px + pieceWidth / 2, py + pieceHeight / 2), r, r); + } + } + } + } + + void Field2D::mousePressEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton) { + QList moves; + Pos p = findPos(event->x(), event->y()); + moves = m_game->field()->validMoves(p); + markMoves(moves); + } + m_mouseLastX = event->x(); + m_mouseLastY = event->y(); + } + + void Field2D::mouseMoveEvent(QMouseEvent *event) { + if (event->buttons() & Qt::RightButton) { + m_originX += event->x() - m_mouseLastX; + m_originY += event->y() - m_mouseLastY; + checkSizes(); + update(); + } + m_mouseLastX = event->x(); + m_mouseLastY = event->y(); + } + + void Field2D::mouseReleaseEvent(QMouseEvent *event) { + if (event->button() == Qt::LeftButton) { + if (m_markedMoves.size() == 0) return; + Pos from = m_markedMoves[0].from(); + QList moves; + markMoves(QList()); + Pos p = findPos(event->x(), event->y()); + Move m(m_game->field(), from, p); + m_game->move(m); + } + m_mouseLastX = event->x(); + m_mouseLastY = event->y(); + } + + void Field2D::wheelEvent(QWheelEvent *event) { + double zoom = pow(1.2, event->delta() / 120); + int max = qMin(width(), height()); + m_fieldWidth = qMax(120, qMin(max, (int) (m_fieldWidth * zoom))) & ~(0x7); + m_fieldHeight = qMax(120, qMin(max, (int) (m_fieldHeight * zoom))) & ~(0x7); + m_lib->setSize(m_fieldWidth / 8, m_fieldHeight / 8); + checkSizes(); + update(); + } + + void Field2D::checkSizes() { + m_originX = m_originX % m_fieldWidth; + m_originY = m_originY % m_fieldHeight; + if (m_originX < 0) m_originX += m_fieldWidth; + if (m_originY < 0) m_originY += m_fieldHeight; + } + + Pos Field2D::findPos(int x, int y) { + return Pos(8 * ((x - m_originX) % m_fieldWidth) / m_fieldWidth, 8 * ((y - m_originY) % m_fieldHeight) / m_fieldHeight); + } + +} diff --git a/src/field2d.h b/src/field2d.h new file mode 100644 index 0000000..d3aa48a --- /dev/null +++ b/src/field2d.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2009 by Stefan Bühler * + * stbuehler@web.de * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TORUSCHESSFIELD2D_H +#define TORUSCHESSFIELD2D_H + +#include "toruschess.h" +#include "piecelibrary.h" + +#include +#include + +/** + @author Stefan Bühler +*/ +namespace toruschess { + + class Field2D : public QWidget { + Q_OBJECT + public: + Field2D(Game *game, QWidget *parent = 0); + + void markMoves(const QList &moves); + + virtual QSize sizeHint() const; + + protected: + virtual void paintEvent(QPaintEvent *event); + virtual void mousePressEvent(QMouseEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void mouseReleaseEvent(QMouseEvent *event); + virtual void wheelEvent(QWheelEvent *event); + + void checkSizes(); + + Pos findPos(int x, int y); + + private: + PieceLibrary *m_lib; + Game *m_game; + bool m_marked[8][8]; + QList m_markedMoves; + + int m_fieldWidth, m_fieldHeight; + int m_originX, m_originY; + + int m_mouseLastX, m_mouseLastY; + }; + +} + +#endif diff --git a/src/piecelibrary.cpp b/src/piecelibrary.cpp new file mode 100644 index 0000000..35d98fd --- /dev/null +++ b/src/piecelibrary.cpp @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright (C) 2009 by Stefan Bühler * + * stbuehler@web.de * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "piecelibrary.h" + +#include + +namespace toruschess { + + PieceLibrary::PieceLibrary(QObject *parent) + : QObject(parent), m_pieces(0), m_buffers(0) { + setSize(45, 45); + } + + PieceLibrary::~PieceLibrary() { + delete [] m_pieces; + delete [] m_buffers; + } + + void PieceLibrary::setSize(int width, int height) { + m_width = qMax(10, width); + m_height = qMax(10, height); + if (m_buffers) { + for (int i = 0; i < 12; i++) { + delete m_buffers[i]; + } + } else { + m_pieces = new QSvgRenderer* [12]; + m_buffers = new QImage* [12]; + for (int i = 0; i < 12; i++) { + m_pieces[i] = new QSvgRenderer(QString(":/media/chess_%1.svg").arg(i+1), this); + } + } + for (int i = 0; i < 12; i++) { + m_buffers[i] = new QImage(QSize(m_width, m_height), QImage::Format_ARGB32_Premultiplied); + m_buffers[i]->fill(0x0); + QPainter p(m_buffers[i]); + m_pieces[i]->render(&p); + } + } + + void PieceLibrary::paint(QPainter &pt, int place) const { + if (place == 0) return; + if (place < 0) place = 6 - place; + if (place <= 0 || place > 12) return; + pt.drawImage(0, 0, *m_buffers[place-1]); + } + + void PieceLibrary::paint(QPainter &pt, int place, const QRect &rect) const { + if (place == 0) return; + if (place < 0) place = 6 - place; + if (place <= 0 || place > 12) return; + pt.drawImage(rect, *m_buffers[place-1]); + } + +} diff --git a/src/piecelibrary.h b/src/piecelibrary.h new file mode 100644 index 0000000..c709201 --- /dev/null +++ b/src/piecelibrary.h @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2009 by Stefan Bühler * + * stbuehler@web.de * + * * + * 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 * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TORUSCHESSPIECELIBRARY_H +#define TORUSCHESSPIECELIBRARY_H + +#include + +/** + @author Stefan Bühler +*/ +namespace toruschess { + + class PieceLibrary : public QObject { + Q_OBJECT + public: + PieceLibrary(QObject *parent); + virtual ~PieceLibrary(); + + QSize pieceSize() const { return QSize(m_width, m_height); } + QSize minPieceSize() const { return QSize(20, 20); } + void setSize(int width, int height); + + void paint(QPainter &pt, int place) const; + void paint(QPainter &pt, int place, const QRect &rect) const; + + private: + QSvgRenderer **m_pieces; + QImage **m_buffers; + int m_width, m_height; + }; + +} + +#endif diff --git a/src/src.pro b/src/src.pro index 51d578a..2f0fa58 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,6 +1,8 @@ SOURCES += main.cpp \ testgame.cpp \ - toruschess.cpp + toruschess.cpp \ + field2d.cpp \ + piecelibrary.cpp TEMPLATE = app CONFIG += warn_on \ thread \ @@ -14,6 +16,8 @@ RESOURCES += media.qrc QT += svg \ opengl HEADERS += testgame.h \ -toruschess.h +toruschess.h \ + field2d.h \ + piecelibrary.h CONFIG -= release diff --git a/src/testgame.cpp b/src/testgame.cpp index 65d188d..88b9d2f 100644 --- a/src/testgame.cpp +++ b/src/testgame.cpp @@ -20,51 +20,14 @@ #include "testgame.h" #include -#include #include #include +#include + +#include "field2d.h" namespace toruschess { - PieceLibrary::PieceLibrary(QObject *parent) - : QObject(parent), m_pieces(0), m_buffers(0) { - setSize(45, 45); - } - - PieceLibrary::~PieceLibrary() { - delete [] m_pieces; - delete [] m_buffers; - } - - void PieceLibrary::setSize(int width, int height) { - m_width = qMax(10, width); - m_height = qMax(10, height); - if (m_buffers) { - for (int i = 0; i < 12; i++) { - delete m_buffers[i]; - } - } else { - m_pieces = new QSvgRenderer* [12]; - m_buffers = new QImage* [12]; - for (int i = 0; i < 12; i++) { - m_pieces[i] = new QSvgRenderer(QString(":/media/chess_%1.svg").arg(i+1), this); - } - } - for (int i = 0; i < 12; i++) { - m_buffers[i] = new QImage(QSize(m_width, m_height), QImage::Format_ARGB32_Premultiplied); - m_buffers[i]->fill(0x0); - QPainter p(m_buffers[i]); - m_pieces[i]->render(&p); - } - } - - void PieceLibrary::paint(QPainter &pt, int piece) const { - if (piece == 0) return; - if (piece < 0) piece = 6 - piece; - if (piece <= 0 || piece > 12) return; - pt.drawImage(0, 0, *m_buffers[piece-1]); - } - TestPlace::TestPlace(QWidget *parent, const PieceLibrary *lib, const Field *field, const Pos &p) : QWidget(parent), m_field(field), m_lib(lib), m_pos(p), m_mark(false) { setMinimumSize(m_lib->minPieceSize()); @@ -149,8 +112,9 @@ namespace toruschess { TestGame::TestGame(QWidget *parent) : QMainWindow(parent), m_game(new Game()) { setWindowTitle("Torus Chess"); - PieceLibrary *lib = new PieceLibrary(this); - setCentralWidget(new TestField(this, lib, m_game)); +// PieceLibrary *lib = new PieceLibrary(this); +// setCentralWidget(new TestField(this, lib, m_game)); + setCentralWidget(new Field2D(m_game, this)); } } diff --git a/src/testgame.h b/src/testgame.h index 0cdd4b1..fdd6d91 100644 --- a/src/testgame.h +++ b/src/testgame.h @@ -26,30 +26,13 @@ #include #include "toruschess.h" +#include "piecelibrary.h" /** @author Stefan Bühler */ namespace toruschess { - class PieceLibrary : public QObject { - Q_OBJECT - public: - PieceLibrary(QObject *parent); - virtual ~PieceLibrary(); - - QSize pieceSize() const { return QSize(m_width, m_height); } - QSize minPieceSize() const { return QSize(20, 20); } - void setSize(int width, int height); - - void paint(QPainter &pt, int piece) const; - - private: - QSvgRenderer **m_pieces; - QImage **m_buffers; - int m_width, m_height; - }; - class TestPlace : public QWidget { Q_OBJECT public: diff --git a/src/toruschess.cpp b/src/toruschess.cpp index 9f1beb4..9cd6636 100644 --- a/src/toruschess.cpp +++ b/src/toruschess.cpp @@ -21,6 +21,17 @@ namespace toruschess { + QString state2string(GameState state) { + switch (state) { + case TURN_WHITE: return "White's turn"; + case TURN_BLACK: return "Black's turn"; + case WON_WHITE: return "White is the winner"; + case WON_BLACK: return "Black is the winner"; + case DRAW: return "Draw game"; + } + return "Invalid GameState"; + } + Move::Move(const Field* field, const Pos &from, const Pos &to) : m_from(from), m_to(to), m_prevFrom(field->place(from)), m_prevTo(field->place(to)), m_player(field->player(from)) { } @@ -39,6 +50,10 @@ namespace toruschess { memcpy(m_places, start_field, sizeof(start_field)); } + void Field::reset() { + memcpy(m_places, start_field, sizeof(start_field)); + } + void tryDirection(QList &moves, const Field *field, int thePlace, const Pos &from, int dx, int dy) { for (int d = 1; d < 8; d++) { Pos to = Pos(from.x() + d*dx, from.y() + d*dy); @@ -152,16 +167,12 @@ foundking: Field testField(*this); testField.place(from) = 0; bool movedKing = (from == pking); + Player player = place2player(thePlace); foreach(Move m, moves) { testField.place(m.to()) = thePlace; - for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) { - Pos curp(x, y); - if (0 > testField.place(curp) * thePlace && testField.simpleValidMoves(curp).contains(Move(&testField, curp, movedKing ? m.to() : pking))) { - goto invalidmove; - } + if (!testField.inCheck(player, movedKing ? m.to() : pking)) { + resMoves.push_back(m); } - resMoves.push_back(m); -invalidmove: testField.place(m.to()) = m.prevTo(); } return resMoves; @@ -184,12 +195,101 @@ invalidmove: place(m.to()) = m.prevTo(); return false; } - + + bool Field::inCheck(Player player, const Pos &pking) const { + for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) { + Pos curp(x, y); + if (0 > place(curp) * player && simpleValidMoves(curp).contains(Move(this, curp, pking))) { + return true; + } + } + return false; + } + + bool Field::inCheck(Player player) const { + /* find king */ + for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) { + Pos curp(x, y); + if (piece(curp) == KING && 0 < place(curp) * player) { + return inCheck(player, curp); + } + } + return false; /* error, no king found */ + } + Game::Game(QObject *parent) - : QObject(parent), m_field(new Field()) { } + : QObject(parent), m_field(new Field()), m_state(TURN_WHITE) { } Game::~Game() { delete m_field; } - + + bool Game::move(const Move &m) { + switch (m_state) { + case TURN_WHITE: + if (m.player() != WHITE) return false; + break; + case TURN_BLACK: + if (m.player() != BLACK) return false; + break; + default: + return false; + } + if (m_field->move(m)) { + m_moves.push_back(m); + if (m_state == TURN_WHITE) { + m_state = TURN_BLACK; + if (possibleMoves().empty()) { + if (m_field->inCheck(BLACK)) { + m_state = WON_WHITE; + } else { + m_state = DRAW; + } + } + } else { + m_state = TURN_WHITE; + if (possibleMoves().empty()) { + if (m_field->inCheck(WHITE)) { + m_state = WON_BLACK; + } else { + m_state = DRAW; + } + } + } + emit moved(m); + emit changed(m_state); + emit updated(); + return true; + } + return false; + } + + void Game::restart() { + m_state = TURN_WHITE; + m_field->reset(); + emit changed(m_state); + emit started(); + emit updated(); + } + + QList Game::possibleMoves() const { + Player player = NOPLAYER; + switch (m_state) { + case TURN_WHITE: + player = WHITE; + break; + case TURN_BLACK: + player = BLACK; + break; + default: + return QList(); + } + QList moves; + for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) { + Pos curp(x, y); + if (0 < m_field->place(curp) * player) + moves += m_field->validMoves(curp); + } + return moves; + } } diff --git a/src/toruschess.h b/src/toruschess.h index a7d7ece..2f5db52 100644 --- a/src/toruschess.h +++ b/src/toruschess.h @@ -34,9 +34,21 @@ namespace toruschess { typedef enum { BLACK = -1, NOPLAYER = 0, WHITE = 1 } Player; typedef enum { NOPIECE = 0, PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 } Piece; + typedef enum { TURN_WHITE, TURN_BLACK, WON_WHITE, WON_BLACK, DRAW } GameState; + + inline Player place2player(int place) { + return (place < 0) ? BLACK : (place > 0) ? WHITE : NOPLAYER; + } + + inline Piece place2piece(int place) { + return (Piece) qAbs(place); + } + + QString state2string(GameState state); class Pos { public: + Pos() : m_x(0), m_y(0) { } Pos(int x, int y) : m_x(x % 8), m_y(y % 8) { } int x() const { return m_x; } @@ -78,11 +90,10 @@ namespace toruschess { public: Field(); - Player player(const Pos &p) const { - int pl = place(p); - return (pl < 0) ? BLACK : (pl > 0) ? WHITE : NOPLAYER; - } - Piece piece(const Pos &p) const { return (Piece) qAbs(place(p)); } + void reset(); + + Player player(const Pos &p) const { return place2player(place(p)); } + Piece piece(const Pos &p) const { return place2piece(place(p)); } 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()]; } @@ -91,6 +102,9 @@ namespace toruschess { bool move(const Move &m); bool undo(const Move &m); + bool inCheck(Player player, const Pos &pking) const; + bool inCheck(Player player) const; + private: QList simpleValidMoves(const Pos &from) const; @@ -104,11 +118,26 @@ namespace toruschess { virtual ~Game(); const Field* field() const { return m_field; } - - bool move(const Move &m) { return m_field->move(m); } + GameState state() const { return m_state; } + QList moves() const { return m_moves; } + + bool move(const Move &m); + void restart(); + + QList possibleMoves() const; + + signals: + void moved(Move m); + void updated(); + void undone(Move m); + void changed(GameState state); + void started(); private: Field *m_field; + GameState m_state; + + QList m_moves; }; }