AI
This commit is contained in:
parent
439b1822a6
commit
a1bac12ede
88
src/ai.cpp
Normal file
88
src/ai.cpp
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* 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 "ai.h"
|
||||||
|
|
||||||
|
namespace toruschess {
|
||||||
|
|
||||||
|
// NOPIECE = 0, PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
|
||||||
|
static int piece_value[] = {
|
||||||
|
0, 1, 3, 3, 5, 9, 10
|
||||||
|
};
|
||||||
|
static const int MAXVAL = 10000;
|
||||||
|
static const int NOMOVE = -MAXVAL - 1;
|
||||||
|
static const int DRAWVAL = -7;
|
||||||
|
|
||||||
|
static int minimax(Field &field, int bal, Player curp, unsigned int depth, Move *move) {
|
||||||
|
if (depth == 0) return curp * bal;
|
||||||
|
|
||||||
|
int mx = NOMOVE;
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
for (int y = 0; y < 8; y++) {
|
||||||
|
Pos p(x, y);
|
||||||
|
if (field.player(p) != curp) continue;
|
||||||
|
int cbal = bal;
|
||||||
|
QList<Move> moves = field.validMoves(p);
|
||||||
|
foreach(Move m, moves) {
|
||||||
|
field.move_unchecked(m);
|
||||||
|
cbal -= m.prevTo();
|
||||||
|
int val = -minimax(field, cbal, Player(-curp), depth-1, NULL);
|
||||||
|
if (val > mx) {
|
||||||
|
mx = val;
|
||||||
|
if (move) *move = m;
|
||||||
|
}
|
||||||
|
field.undo_unchecked(m);
|
||||||
|
if (mx == MAXVAL) return mx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mx == NOMOVE) {
|
||||||
|
if (field.inCheck(curp)) return -MAXVAL;
|
||||||
|
return curp * DRAWVAL;
|
||||||
|
}
|
||||||
|
return mx;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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();
|
||||||
|
int balance = 0;
|
||||||
|
for (int x = 0; x < 8; x++)
|
||||||
|
for (int y = 0; y < 8; y++) {
|
||||||
|
Pos p(x,y);
|
||||||
|
balance += field.player(p) * piece_value[field.piece(p)];
|
||||||
|
}
|
||||||
|
|
||||||
|
Move move;
|
||||||
|
minimax(field, balance, curp, 2, &move);
|
||||||
|
|
||||||
|
return move;
|
||||||
|
}
|
||||||
|
}
|
35
src/ai.h
Normal file
35
src/ai.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* 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 TORUSCHESSAI_H
|
||||||
|
#define TORUSCHESSAI_H
|
||||||
|
|
||||||
|
#include "toruschess.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
@author Stefan Bühler <stbuehler@web.de>
|
||||||
|
*/
|
||||||
|
namespace toruschess {
|
||||||
|
class ai{
|
||||||
|
public:
|
||||||
|
static Move getMove(const Game *game);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -29,15 +29,22 @@
|
|||||||
namespace toruschess {
|
namespace toruschess {
|
||||||
|
|
||||||
Field2D::Field2D(Game *game, QWidget *parent)
|
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) {
|
: QWidget(parent), m_lib(new PieceLibrary(this)), m_game(game), m_fieldWidth(360), m_fieldHeight(360), m_originX(23), m_originY(23), m_fieldBuffer(0) {
|
||||||
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) m_marked[x][y] = false;
|
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);
|
m_lib->setSize(m_fieldWidth / 8, m_fieldHeight / 8);
|
||||||
|
checkSizes();
|
||||||
|
connect(game, SIGNAL(updated()), this, SLOT(fieldUpdated()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Field2D::~Field2D() {
|
||||||
|
if (m_fieldBuffer) delete m_fieldBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Field2D::markMoves(const QList<Move> &moves) {
|
void Field2D::markMoves(const QList<Move> &moves) {
|
||||||
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = false;
|
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = false;
|
||||||
m_markedMoves = moves;
|
m_markedMoves = moves;
|
||||||
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = true;
|
foreach(Move m, m_markedMoves) m_marked[m.to().x()][m.to().y()] = true;
|
||||||
|
updateFieldBuffer();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,27 +54,12 @@ namespace toruschess {
|
|||||||
|
|
||||||
void Field2D::paintEvent(QPaintEvent *) {
|
void Field2D::paintEvent(QPaintEvent *) {
|
||||||
QPainter pt(this);
|
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)
|
int winWidth = width(), winHeight = height();
|
||||||
for (int py = m_originY - m_fieldHeight + y*pieceHeight; py < winHeight; py += m_fieldHeight) {
|
|
||||||
QRect prect(px, py, pieceWidth - 1, pieceHeight - 1);
|
for (int px = m_originX - m_fieldWidth; px < winWidth; px += m_fieldWidth)
|
||||||
pt.fillRect(prect, fieldBG);
|
for (int py = m_originY - m_fieldHeight; py < winHeight; py += m_fieldHeight) {
|
||||||
m_lib->paint(pt, place, prect);
|
pt.drawImage(QRect(px, py, m_fieldWidth, m_fieldHeight), *m_fieldBuffer);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,6 +86,8 @@ namespace toruschess {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Field2D::mouseReleaseEvent(QMouseEvent *event) {
|
void Field2D::mouseReleaseEvent(QMouseEvent *event) {
|
||||||
|
m_mouseLastX = event->x();
|
||||||
|
m_mouseLastY = event->y();
|
||||||
if (event->button() == Qt::LeftButton) {
|
if (event->button() == Qt::LeftButton) {
|
||||||
if (m_markedMoves.size() == 0) return;
|
if (m_markedMoves.size() == 0) return;
|
||||||
Pos from = m_markedMoves[0].from();
|
Pos from = m_markedMoves[0].from();
|
||||||
@ -103,8 +97,6 @@ namespace toruschess {
|
|||||||
Move m(m_game->field(), from, p);
|
Move m(m_game->field(), from, p);
|
||||||
m_game->move(m);
|
m_game->move(m);
|
||||||
}
|
}
|
||||||
m_mouseLastX = event->x();
|
|
||||||
m_mouseLastY = event->y();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Field2D::wheelEvent(QWheelEvent *event) {
|
void Field2D::wheelEvent(QWheelEvent *event) {
|
||||||
@ -122,10 +114,45 @@ namespace toruschess {
|
|||||||
m_originY = m_originY % m_fieldHeight;
|
m_originY = m_originY % m_fieldHeight;
|
||||||
if (m_originX < 0) m_originX += m_fieldWidth;
|
if (m_originX < 0) m_originX += m_fieldWidth;
|
||||||
if (m_originY < 0) m_originY += m_fieldHeight;
|
if (m_originY < 0) m_originY += m_fieldHeight;
|
||||||
|
if (!m_fieldBuffer || m_fieldBuffer->width() != m_fieldWidth || m_fieldBuffer->height() != m_fieldHeight) {
|
||||||
|
if (m_fieldBuffer) delete m_fieldBuffer;
|
||||||
|
m_fieldBuffer = new QImage(QSize(m_fieldWidth, m_fieldHeight), QImage::Format_ARGB32_Premultiplied);
|
||||||
|
m_fieldBuffer->fill(Qt::black);
|
||||||
|
updateFieldBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Field2D::updateFieldBuffer() {
|
||||||
|
QPainter pt(m_fieldBuffer);
|
||||||
|
|
||||||
|
int pieceWidth = m_fieldWidth / 8;
|
||||||
|
int pieceHeight = m_fieldHeight / 8;
|
||||||
|
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));
|
||||||
|
|
||||||
|
QRect prect(x * pieceWidth, y * pieceHeight, 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(prect.x() + pieceWidth / 2, prect.y() + pieceHeight / 2), r, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
Pos Field2D::findPos(int x, int y) {
|
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);
|
return Pos(8 * ((x - m_originX) % m_fieldWidth) / m_fieldWidth, 8 * ((y - m_originY) % m_fieldHeight) / m_fieldHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Field2D::fieldUpdated() {
|
||||||
|
markMoves(QList<Move>());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ namespace toruschess {
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Field2D(Game *game, QWidget *parent = 0);
|
Field2D(Game *game, QWidget *parent = 0);
|
||||||
|
virtual ~Field2D();
|
||||||
|
|
||||||
void markMoves(const QList<Move> &moves);
|
void markMoves(const QList<Move> &moves);
|
||||||
|
|
||||||
@ -48,9 +49,13 @@ namespace toruschess {
|
|||||||
virtual void wheelEvent(QWheelEvent *event);
|
virtual void wheelEvent(QWheelEvent *event);
|
||||||
|
|
||||||
void checkSizes();
|
void checkSizes();
|
||||||
|
void updateFieldBuffer();
|
||||||
|
|
||||||
Pos findPos(int x, int y);
|
Pos findPos(int x, int y);
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
void fieldUpdated();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PieceLibrary *m_lib;
|
PieceLibrary *m_lib;
|
||||||
Game *m_game;
|
Game *m_game;
|
||||||
@ -61,6 +66,8 @@ namespace toruschess {
|
|||||||
int m_originX, m_originY;
|
int m_originX, m_originY;
|
||||||
|
|
||||||
int m_mouseLastX, m_mouseLastY;
|
int m_mouseLastX, m_mouseLastY;
|
||||||
|
|
||||||
|
QImage *m_fieldBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ typedef size_t (*Lib3dsIoWriteFunc)(void *self, const void *buffer, size_t size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
PieceLibrary::~PieceLibrary() {
|
PieceLibrary::~PieceLibrary() {
|
||||||
lib3ds_file_free(m_pawn);
|
if (m_pawn) lib3ds_file_free(m_pawn);
|
||||||
delete [] m_pieces;
|
delete [] m_pieces;
|
||||||
delete [] m_buffers;
|
delete [] m_buffers;
|
||||||
}
|
}
|
||||||
|
11
src/src.pro
11
src/src.pro
@ -3,7 +3,8 @@ SOURCES += main.cpp \
|
|||||||
toruschess.cpp \
|
toruschess.cpp \
|
||||||
field2d.cpp \
|
field2d.cpp \
|
||||||
piecelibrary.cpp \
|
piecelibrary.cpp \
|
||||||
field3d.cpp
|
field3d.cpp \
|
||||||
|
ai.cpp
|
||||||
TEMPLATE = app
|
TEMPLATE = app
|
||||||
CONFIG += warn_on \
|
CONFIG += warn_on \
|
||||||
thread \
|
thread \
|
||||||
@ -20,8 +21,12 @@ HEADERS += testgame.h \
|
|||||||
toruschess.h \
|
toruschess.h \
|
||||||
field2d.h \
|
field2d.h \
|
||||||
piecelibrary.h \
|
piecelibrary.h \
|
||||||
field3d.h
|
field3d.h \
|
||||||
CONFIG -= release
|
ai.h
|
||||||
|
|
||||||
LIBS += -l3ds
|
LIBS += -l3ds
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS_RELEASE += -g
|
||||||
|
|
||||||
|
CONFIG -= release
|
||||||
|
|
||||||
|
@ -23,10 +23,13 @@
|
|||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
|
#include <QStatusBar>
|
||||||
|
|
||||||
#include "field2d.h"
|
#include "field2d.h"
|
||||||
#include "field3d.h"
|
#include "field3d.h"
|
||||||
|
|
||||||
|
#include "ai.h"
|
||||||
|
|
||||||
namespace toruschess {
|
namespace toruschess {
|
||||||
|
|
||||||
TestPlace::TestPlace(QWidget *parent, const PieceLibrary *lib, const Field *field, const Pos &p)
|
TestPlace::TestPlace(QWidget *parent, const PieceLibrary *lib, const Field *field, const Pos &p)
|
||||||
@ -115,7 +118,21 @@ namespace toruschess {
|
|||||||
setWindowTitle("Torus Chess");
|
setWindowTitle("Torus Chess");
|
||||||
// PieceLibrary *lib = new PieceLibrary(this);
|
// PieceLibrary *lib = new PieceLibrary(this);
|
||||||
// setCentralWidget(new TestField(this, lib, m_game));
|
// setCentralWidget(new TestField(this, lib, m_game));
|
||||||
setCentralWidget(new Field3D(m_game, this));
|
setCentralWidget(new Field2D(m_game, this));
|
||||||
|
statusBar()->show();
|
||||||
|
connect(m_game, SIGNAL(updated()), this, SLOT(gameUpdated()));
|
||||||
|
connect(m_game, SIGNAL(changed(GameState)), this, SLOT(gameChanged(GameState)));
|
||||||
|
m_game->restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestGame::gameUpdated() {
|
||||||
|
if (m_game->state() == TURN_BLACK) {
|
||||||
|
Move m = ai::getMove(m_game);
|
||||||
|
if (m.valid()) m_game->move(m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestGame::gameChanged(GameState state) {
|
||||||
|
statusBar()->showMessage(state2string(state));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,6 +80,10 @@ namespace toruschess {
|
|||||||
public:
|
public:
|
||||||
TestGame(QWidget *parent = 0);
|
TestGame(QWidget *parent = 0);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void gameUpdated();
|
||||||
|
void gameChanged(GameState state);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Game *m_game;
|
Game *m_game;
|
||||||
};
|
};
|
||||||
|
@ -35,6 +35,9 @@ namespace toruschess {
|
|||||||
Move::Move(const Field* field, const Pos &from, const Pos &to)
|
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)) { }
|
: m_from(from), m_to(to), m_prevFrom(field->place(from)), m_prevTo(field->place(to)), m_player(field->player(from)) { }
|
||||||
|
|
||||||
|
Move::Move()
|
||||||
|
: m_from(), m_to(), m_prevFrom(0), m_prevTo(0), m_player(NOPLAYER) { }
|
||||||
|
|
||||||
static const int start_field[8][8] = {
|
static const int start_field[8][8] = {
|
||||||
{ -KNIGHT, -PAWN, -ROOK, 0, 0, 0, 0, 0 },
|
{ -KNIGHT, -PAWN, -ROOK, 0, 0, 0, 0, 0 },
|
||||||
{ -BISHOP, -KING, -QUEEN, 0, 0, 0, 0, 0 },
|
{ -BISHOP, -KING, -QUEEN, 0, 0, 0, 0, 0 },
|
||||||
@ -46,11 +49,14 @@ namespace toruschess {
|
|||||||
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
{ 0, 0, 0, 0, 0, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
Field::Field() {
|
Field::Field()
|
||||||
|
: m_wking(5, 5), m_bking(1, 1) {
|
||||||
memcpy(m_places, start_field, sizeof(start_field));
|
memcpy(m_places, start_field, sizeof(start_field));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Field::reset() {
|
void Field::reset() {
|
||||||
|
m_wking = Pos(5, 5);
|
||||||
|
m_bking = Pos(1, 1);
|
||||||
memcpy(m_places, start_field, sizeof(start_field));
|
memcpy(m_places, start_field, sizeof(start_field));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +108,7 @@ namespace toruschess {
|
|||||||
QList<Move> Field::simpleValidMoves(const Pos &from) const {
|
QList<Move> Field::simpleValidMoves(const Pos &from) const {
|
||||||
QList<Move> moves;
|
QList<Move> moves;
|
||||||
int thePlace = place(from);
|
int thePlace = place(from);
|
||||||
Piece thePiece = (Piece) qAbs(thePlace);
|
Piece thePiece = place2piece(thePlace);
|
||||||
// qDebug("simpleValidMoves: %i", thePiece);
|
// qDebug("simpleValidMoves: %i", thePiece);
|
||||||
switch (thePiece) {
|
switch (thePiece) {
|
||||||
case NOPIECE:
|
case NOPIECE:
|
||||||
@ -152,69 +158,163 @@ namespace toruschess {
|
|||||||
|
|
||||||
QList<Move> Field::validMoves(const Pos &from) const {
|
QList<Move> Field::validMoves(const Pos &from) const {
|
||||||
QList<Move> moves = simpleValidMoves(from);
|
QList<Move> moves = simpleValidMoves(from);
|
||||||
int thePlace = place(from);
|
|
||||||
Pos pking(0,0);
|
|
||||||
/* find king */
|
|
||||||
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) {
|
|
||||||
if (piece(Pos(x, y)) == KING && 0 < place(Pos(x, y)) * thePlace) {
|
|
||||||
pking = Pos(x, y);
|
|
||||||
goto foundking;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QList<Move>(); /* error, no king found */
|
|
||||||
foundking:
|
|
||||||
QList<Move> resMoves;
|
QList<Move> resMoves;
|
||||||
Field testField(*this);
|
Field testField(*this);
|
||||||
testField.place(from) = 0;
|
Player pl = player(from);
|
||||||
bool movedKing = (from == pking);
|
|
||||||
Player player = place2player(thePlace);
|
|
||||||
foreach(Move m, moves) {
|
foreach(Move m, moves) {
|
||||||
testField.place(m.to()) = thePlace;
|
Q_ASSERT(validMove(m));
|
||||||
if (!testField.inCheck(player, movedKing ? m.to() : pking)) {
|
testField.move_unchecked(m);
|
||||||
|
if (!testField.inCheck(pl)) {
|
||||||
resMoves.push_back(m);
|
resMoves.push_back(m);
|
||||||
}
|
}
|
||||||
testField.place(m.to()) = m.prevTo();
|
testField.undo_unchecked(m);
|
||||||
}
|
}
|
||||||
return resMoves;
|
return resMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool checkReach(const Field *field, const Pos &from, const Pos &dest, int dx, int dy) {
|
||||||
|
for (int d = 1; d < 8; d++) {
|
||||||
|
Pos to = Pos(from.x() + d*dx, from.y() + d*dy);
|
||||||
|
if (dest == to) return true;
|
||||||
|
int toPlace = field->place(to);
|
||||||
|
if (0 == toPlace) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
for (int c = 7; c > d; c--) {
|
||||||
|
to = Pos(from.x() + c*dx, from.y() + c*dy);
|
||||||
|
if (dest == to) return true;
|
||||||
|
toPlace = field->place(to);
|
||||||
|
if (0 == toPlace) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool Field::validMove(const Move &m) const {
|
bool Field::validMove(const Move &m) const {
|
||||||
QList<Move> moves = validMoves(m.from());
|
if (player(m.from()) != m.player()) return false;
|
||||||
return moves.contains(m);
|
if (m.player() == NOPLAYER) return false;
|
||||||
|
if (place(m.from()) != m.prevFrom() || place(m.to()) != m.prevTo()) return false;
|
||||||
|
if (player(m.to()) == m.player()) return false;
|
||||||
|
switch (piece(m.from())) {
|
||||||
|
case NOPIECE:
|
||||||
|
return false;
|
||||||
|
case PAWN: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
if ((dx > 1 && dx < 7) || (dy > 1 && dy < 7)) return false;
|
||||||
|
if (dx != 0 && dy != 0) return player(m.to()) != NOPLAYER; /* beat */
|
||||||
|
return player(m.to()) == NOPLAYER; /* walk */
|
||||||
|
}
|
||||||
|
case KNIGHT: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
return ((dx == 2 || dx == 6) && (dy == 1 || dy == 7)) || ((dx == 1 || dx == 7) && (dy == 2 || dy == 6));
|
||||||
|
}
|
||||||
|
case BISHOP: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
if (dx == dy) {
|
||||||
|
if (dx == 4) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 1) || checkReach(this, m.from(), m.to(), 1, 7);
|
||||||
|
} else {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 1);
|
||||||
|
}
|
||||||
|
} else if (dx + dy == 8) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 7);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ROOK: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
if (dy == 0) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 0);
|
||||||
|
} else if (dx == 0) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 0, 1);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case QUEEN: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
if (dx == dy) {
|
||||||
|
if (dx == 4) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 1) || checkReach(this, m.from(), m.to(), 1, 7);
|
||||||
|
} else {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 1);
|
||||||
|
}
|
||||||
|
} else if (dx + dy == 8) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 7);
|
||||||
|
} else if (dy == 0) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 1, 0);
|
||||||
|
} else if (dx == 0) {
|
||||||
|
return checkReach(this, m.from(), m.to(), 0, 1);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case KING: {
|
||||||
|
int dx = (8 + m.from().x() - m.to().x()) % 8, dy = (8 + m.from().y() - m.to().y()) % 8;
|
||||||
|
if ((dx > 1 && dx < 7) || (dy > 1 && dy < 7)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Field::move_unchecked(const Move &m) {
|
||||||
|
place(m.from()) = 0;
|
||||||
|
place(m.to()) = m.prevFrom();
|
||||||
|
if (m.prevFrom() == KING) {
|
||||||
|
m_wking = m.to();
|
||||||
|
} else if (m.prevFrom() == -KING) {
|
||||||
|
m_bking = m.to();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Field::undo_unchecked(const Move &m) {
|
||||||
|
place(m.from()) = m.prevFrom();
|
||||||
|
place(m.to()) = m.prevTo();
|
||||||
|
if (m.prevFrom() == KING) {
|
||||||
|
m_wking = m.from();
|
||||||
|
} else if (m.prevFrom() == -KING) {
|
||||||
|
m_bking = m.from();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Field::move(const Move &m) {
|
bool Field::move(const Move &m) {
|
||||||
|
if (!m.valid()) return false;
|
||||||
if (!validMove(m)) return false;
|
if (!validMove(m)) return false;
|
||||||
place(m.from()) = 0;
|
move_unchecked(m);
|
||||||
place(m.to()) = m.prevFrom();
|
if (inCheck(m.player())) {
|
||||||
|
undo_unchecked(m);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool Field::undo(const Move &m) {
|
bool Field::undo(const Move &m) {
|
||||||
if (place(m.from()) != 0 || place(m.to()) != m.prevFrom()) return false;
|
if (place(m.from()) != 0 || place(m.to()) != m.prevFrom()) return false;
|
||||||
place(m.from()) = m.prevFrom();
|
undo_unchecked(m);
|
||||||
place(m.to()) = m.prevTo();
|
return true;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Field::inCheck(Player player, const Pos &pking) const {
|
bool Field::inCheck(Player player, const Pos &pking) const {
|
||||||
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) {
|
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) {
|
||||||
Pos curp(x, y);
|
Pos curp(x, y);
|
||||||
if (0 > place(curp) * player && simpleValidMoves(curp).contains(Move(this, curp, pking))) {
|
if (0 > place(curp) * player && validMove(Move(this, curp, pking))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Field::inCheck(Player player) const {
|
bool Field::inCheck(Player p) const {
|
||||||
/* find king */
|
const Pos &pos = (p == WHITE) ? m_wking : m_bking;
|
||||||
for (int x = 0; x < 8; x++) for (int y = 0; y < 8; y++) {
|
if (player(pos) != p || piece(pos) != KING) return false;
|
||||||
Pos curp(x, y);
|
return inCheck(p, pos);
|
||||||
if (piece(curp) == KING && 0 < place(curp) * player) {
|
|
||||||
return inCheck(player, curp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false; /* error, no king found */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Game::Game(QObject *parent)
|
Game::Game(QObject *parent)
|
||||||
@ -225,6 +325,7 @@ foundking:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Game::move(const Move &m) {
|
bool Game::move(const Move &m) {
|
||||||
|
if (!m.valid()) return false;
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case TURN_WHITE:
|
case TURN_WHITE:
|
||||||
if (m.player() != WHITE) return false;
|
if (m.player() != WHITE) return false;
|
||||||
|
@ -64,6 +64,7 @@ namespace toruschess {
|
|||||||
|
|
||||||
class Move {
|
class Move {
|
||||||
public:
|
public:
|
||||||
|
Move();
|
||||||
Move(const Field* field, const Pos &from, const Pos &to);
|
Move(const Field* field, const Pos &from, const Pos &to);
|
||||||
|
|
||||||
const Pos& from() const { return m_from; }
|
const Pos& from() const { return m_from; }
|
||||||
@ -80,6 +81,8 @@ namespace toruschess {
|
|||||||
/* player is included in m_prevFrom */
|
/* player is included in m_prevFrom */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool valid() const { return m_prevFrom != 0; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Pos m_from,m_to;
|
Pos m_from,m_to;
|
||||||
int m_prevFrom, m_prevTo;
|
int m_prevFrom, m_prevTo;
|
||||||
@ -98,16 +101,19 @@ namespace toruschess {
|
|||||||
int& place(const Pos &p) { return m_places[p.y()][p.x()]; }
|
int& place(const Pos &p) { return m_places[p.y()][p.x()]; }
|
||||||
|
|
||||||
QList<Move> validMoves(const Pos &from) const;
|
QList<Move> validMoves(const Pos &from) const;
|
||||||
bool validMove(const Move &m) const;
|
bool validMove(const Move &m) const; /* does not check for inCheck */
|
||||||
bool move(const Move &m);
|
bool move(const Move &m);
|
||||||
bool undo(const Move &m);
|
bool undo(const Move &m);
|
||||||
|
|
||||||
bool inCheck(Player player, const Pos &pking) const;
|
bool inCheck(Player player, const Pos &pking) const;
|
||||||
bool inCheck(Player player) const;
|
bool inCheck(Player player) const;
|
||||||
|
|
||||||
|
void move_unchecked(const Move &m);
|
||||||
|
void undo_unchecked(const Move &m);
|
||||||
private:
|
private:
|
||||||
QList<Move> simpleValidMoves(const Pos &from) const;
|
QList<Move> simpleValidMoves(const Pos &from) const;
|
||||||
|
|
||||||
|
Pos m_wking, m_bking;
|
||||||
int m_places[8][8];
|
int m_places[8][8];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -152,13 +152,13 @@
|
|||||||
<autokdesu>false</autokdesu>
|
<autokdesu>false</autokdesu>
|
||||||
<envvars/>
|
<envvars/>
|
||||||
<runarguments>
|
<runarguments>
|
||||||
<toruschess/>
|
<toruschess></toruschess>
|
||||||
</runarguments>
|
</runarguments>
|
||||||
<cwd>
|
<cwd>
|
||||||
<toruschess>/home/stefan/studium/7. semester/fapra_vis/src/toruschess/..</toruschess>
|
<toruschess>/home/stefan/studium/7. semester/fapra_vis/src/toruschess/..</toruschess>
|
||||||
</cwd>
|
</cwd>
|
||||||
<debugarguments>
|
<debugarguments>
|
||||||
<toruschess/>
|
<toruschess></toruschess>
|
||||||
</debugarguments>
|
</debugarguments>
|
||||||
</run>
|
</run>
|
||||||
<general>
|
<general>
|
||||||
|
Loading…
Reference in New Issue
Block a user