Commit 52e670e3 authored by Dmitry Suzdalev's avatar Dmitry Suzdalev

Ported KReversi's Engine to use new classes.

It compiles, but this code is just unused yet.

Moved away from inheriting Engine from SuperEngine.
Idea behind SuperEngine was to introduce base class for several engines.
Well, currently there's only one.
And I think that it's better to actually create 
base-interface class when we have more than one engine at hands,
so we can look for what they have in common etc etc :).

Yesterday's problem with QGV's margin didn't go away... Oh, well

svn path=/branches/work/kreversi_rewrite/; revision=571980
parent 1bebafe0
......@@ -10,7 +10,7 @@ set(kreversi_SRCS
kreversigame.cpp
kreversiscene.cpp
kreversiview.cpp
#Engine.cpp
Engine.cpp
mainwindow.cpp
main.cpp )
......
......@@ -118,6 +118,7 @@
#include "Engine.h"
#include "kreversigame.h"
// ================================================================
......@@ -253,6 +254,37 @@ MoveAndValue::MoveAndValue(int x, int y, int value)
setXYV(x, y, value);
}
// ================================================================
// class Score
/* This class keeps track of the score for both colors. Such a score
* could be either the number of pieces, the score from the evaluation
* function or anything similar.
*/
class Score {
public:
Score();
uint score(ChipColor color) const { return m_score[color]; }
void set(ChipColor color, uint score) { m_score[color] = score; }
void inc(ChipColor color) { m_score[color]++; }
void dec(ChipColor color) { m_score[color]--; }
void add(ChipColor color, uint s) { m_score[color] += s; }
void sub(ChipColor color, uint s) { m_score[color] -= s; }
private:
uint m_score[2];
};
// helper function
static ChipColor opponentColorFor(ChipColor color)
{
if(color == NoColor)
return color;
else
return ( color == White ? Black : White );
}
// ================================================================
// The Engine itself
......@@ -264,39 +296,47 @@ const int Engine::ILLEGAL_VALUE = 8888888;
const int Engine::BC_WEIGHT = 3;
Engine::Engine(int st, int sd) : SuperEngine(st, sd)
Engine::Engine(int st, int sd)/* : SuperEngine(st, sd) */
: m_strength(st)
{
m_random.setSeed(sd);
SetupBcBoard();
SetupBits();
}
Engine::Engine(int st) : SuperEngine(st)
Engine::Engine(int st) //: SuperEngine(st)
: m_strength(st)
{
m_random.setSeed(0);
SetupBcBoard();
SetupBits();
}
Engine::Engine() : SuperEngine(1)
Engine::Engine()// : SuperEngine(1)
: m_strength(1)
{
m_random.setSeed(0);
SetupBcBoard();
SetupBits();
}
// keep GUI alive
/*
void Engine::yield()
{
qApp->processEvents();
}
*/
// Calculate the best move from the current position, and return it.
KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
{
Color color;
ChipColor color;
// A competitive game is one where we try our damnedest to make the
// best move. The opposite is a casual game where the engine might
......@@ -311,17 +351,17 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
m_exhaustive = false;
// Get the color to calculate the move for.
color = game->toMove();
if (color == Nobody)
return Move(Nobody, -1, -1);
color = game.currentPlayer();
if (color == NoColor)
return KReversiMove(NoColor, -1, -1);
// Figure out the current score
m_score.set(White, game->score(White));
m_score.set(Black, game->score(Black));
m_score->set(White, game.playerScore(White));
m_score->set(Black, game.playerScore(Black));
// Treat the first move as a special case (we can basically just
// pick a move at random).
if (m_score.score(White) + m_score.score(Black) == 4)
if (m_score->score(White) + m_score->score(Black) == 4)
return ComputeFirstMove(game);
// Let there be room for 3000 changes during the recursive search.
......@@ -332,16 +372,16 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
// the number of possible moves goes down, so we can search deeper
// without using more time.
m_depth = m_strength;
if (m_score.score(White) + m_score.score(Black) + m_depth + 3 >= 64)
m_depth = 64 - m_score.score(White) - m_score.score(Black);
else if (m_score.score(White) + m_score.score(Black) + m_depth + 4 >= 64)
if (m_score->score(White) + m_score->score(Black) + m_depth + 3 >= 64)
m_depth = 64 - m_score->score(White) - m_score->score(Black);
else if (m_score->score(White) + m_score->score(Black) + m_depth + 4 >= 64)
m_depth += 2;
else if (m_score.score(White) + m_score.score(Black) + m_depth + 5 >= 64)
else if (m_score->score(White) + m_score->score(Black) + m_depth + 5 >= 64)
m_depth++;
// If we are very close to the end, we can even make the search
// exhaustive.
if (m_score.score(White) + m_score.score(Black) + m_depth >= 64)
if (m_score->score(White) + m_score->score(Black) + m_depth >= 64)
m_exhaustive = true;
// The evaluation is a linear combination of the score (number of
......@@ -350,7 +390,7 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
// values and the later in the game the more we use the number of
// pieces.
m_coeff = 100 - (100*
(m_score.score(White) + m_score.score(Black)
(m_score->score(White) + m_score->score(Black)
+ m_depth - 4)) / 60;
// Initialize the board that we use for the search.
......@@ -358,9 +398,9 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
for (uint y = 0; y < 10; y++) {
if (1 <= x && x <= 8
&& 1 <= y && y <= 8)
m_board[x][y] = game->color(x, y);
m_board[x][y] = game.chipColorAt(x-1, y-1);
else
m_board[x][y] = Nobody;
m_board[x][y] = NoColor;
}
// Initialize a lot of stuff that we will use in the search.
......@@ -368,11 +408,11 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
// Initialize m_bc_score to the current bc score. This is kept
// up-to-date incrementally so that way we won't have to calculate
// it from scratch for each evaluation.
m_bc_score.set(White, CalcBcScore(White));
m_bc_score.set(Black, CalcBcScore(Black));
m_bc_score->set(White, CalcBcScore(White));
m_bc_score->set(Black, CalcBcScore(Black));
ULONG64 colorbits = ComputeOccupiedBits(color);
ULONG64 opponentbits = ComputeOccupiedBits(opponent(color));
ULONG64 opponentbits = ComputeOccupiedBits(opponentColorFor(color));
int maxval = -LARGEINT;
int max_x = 0;
......@@ -395,7 +435,7 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
for (int y = 1; y < 9; y++) {
// Don't bother with non-empty squares and squares that aren't
// neighbors to opponent pieces.
if (m_board[x][y] != Nobody
if (m_board[x][y] != NoColor
|| (m_neighbor_bits[x][y] & opponentbits) == null_bits)
continue;
......@@ -454,11 +494,11 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
// Return a suitable move.
if (interrupted())
return Move(Nobody, -1, -1);
return KReversiMove(NoColor, -1, -1);
else if (maxval != -LARGEINT)
return Move(color, max_x, max_y);
return KReversiMove(color, max_x, max_y);
else
return Move(Nobody, -1, -1);
return KReversiMove(NoColor, -1, -1);
}
......@@ -468,21 +508,21 @@ KReversiMove Engine::computeMove(const KReversiGame& game, bool competitive)
KReversiMove Engine::ComputeFirstMove(const KReversiGame& game)
{
int r;
Color color = game->toMove();
ChipColor color = game.currentPlayer();
r = m_random.getLong(4) + 1;
if (color == White) {
if (r == 1) return Move(color, 3, 5);
else if (r == 2) return Move(color, 4, 6);
else if (r == 3) return Move(color, 5, 3);
else return Move(color, 6, 4);
if (r == 1) return KReversiMove(color, 3, 5);
else if (r == 2) return KReversiMove(color, 4, 6);
else if (r == 3) return KReversiMove(color, 5, 3);
else return KReversiMove(color, 6, 4);
}
else {
if (r == 1) return Move(color, 3, 4);
else if (r == 2) return Move(color, 5, 6);
else if (r == 3) return Move(color, 4, 3);
else return Move(color, 6, 5);
if (r == 1) return KReversiMove(color, 3, 4);
else if (r == 2) return KReversiMove(color, 5, 6);
else if (r == 3) return KReversiMove(color, 4, 3);
else return KReversiMove(color, 6, 5);
}
}
......@@ -493,21 +533,21 @@ KReversiMove Engine::ComputeFirstMove(const KReversiGame& game)
// search.
//
int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
int Engine::ComputeMove2(int xplay, int yplay, ChipColor color, int level,
int cutoffval, ULONG64 colorbits,
ULONG64 opponentbits)
{
int number_of_turned = 0;
SquareStackEntry mse;
Color opponent = ::opponent(color);
ChipColor opponent = opponentColorFor(color);
m_nodes_searched++;
// Put the piece on the board and incrementally update scores and bitmaps.
m_board[xplay][yplay] = color;
colorbits |= m_coord_bit[xplay][yplay];
m_score.inc(color);
m_bc_score.add(color, m_bc_board[xplay][yplay]);
m_score->inc(color);
m_bc_score->add(color, m_bc_board[xplay][yplay]);
// Loop through all 8 directions and turn the pieces that can be turned.
for (int xinc = -1; xinc <= 1; xinc++)
......@@ -534,8 +574,8 @@ int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
m_squarestack.Push(x, y);
m_bc_score.add(color, m_bc_board[x][y]);
m_bc_score.sub(opponent, m_bc_board[x][y]);
m_bc_score->add(color, m_bc_board[x][y]);
m_bc_score->sub(opponent, m_bc_board[x][y]);
number_of_turned++;
}
}
......@@ -547,8 +587,8 @@ int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
if (number_of_turned > 0) {
// First adjust the number of pieces for each side.
m_score.add(color, number_of_turned);
m_score.sub(opponent, number_of_turned);
m_score->add(color, number_of_turned);
m_score->sub(opponent, number_of_turned);
// If we are at the bottom of the search, get the evaluation.
if (level >= m_depth)
......@@ -567,7 +607,7 @@ int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
if (retval == -LARGEINT) {
// No possible move for anybody => end of game:
int finalscore = m_score.score(color) - m_score.score(opponent);
int finalscore = m_score->score(color) - m_score->score(opponent);
if (m_exhaustive)
retval = finalscore;
......@@ -585,22 +625,22 @@ int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
}
}
m_score.add(opponent, number_of_turned);
m_score.sub(color, number_of_turned);
m_score->add(opponent, number_of_turned);
m_score->sub(color, number_of_turned);
}
// Undo the move. Start by unturning the turned pieces.
for (int i = number_of_turned; i > 0; i--) {
mse = m_squarestack.Pop();
m_bc_score.add(opponent, m_bc_board[mse.m_x][mse.m_y]);
m_bc_score.sub(color, m_bc_board[mse.m_x][mse.m_y]);
m_bc_score->add(opponent, m_bc_board[mse.m_x][mse.m_y]);
m_bc_score->sub(color, m_bc_board[mse.m_x][mse.m_y]);
m_board[mse.m_x][mse.m_y] = opponent;
}
// Now remove the new piece that we put down.
m_board[xplay][yplay] = Nobody;
m_score.sub(color, 1);
m_bc_score.sub(color, m_bc_board[xplay][yplay]);
m_board[xplay][yplay] = NoColor;
m_score->sub(color, 1);
m_bc_score->sub(color, m_bc_board[xplay][yplay]);
// Return a suitable value.
if (number_of_turned < 1 || interrupted())
......@@ -615,20 +655,21 @@ int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
// most valuable move, but not the move itself.
//
int Engine::TryAllMoves(Color opponent, int level, int cutoffval,
int Engine::TryAllMoves(ChipColor opponent, int level, int cutoffval,
ULONG64 opponentbits, ULONG64 colorbits)
{
int maxval = -LARGEINT;
// Keep GUI alive by calling the event loop.
yield();
//// FIXME dimsuz:
//yield();
ULONG64 null_bits;
null_bits = 0;
for (int x = 1; x < 9; x++) {
for (int y = 1; y < 9; y++) {
if (m_board[x][y] == Nobody
if (m_board[x][y] == NoColor
&& (m_neighbor_bits[x][y] & colorbits) != null_bits) {
int val = ComputeMove2(x, y, opponent, level+1, maxval, opponentbits,
colorbits);
......@@ -658,22 +699,22 @@ int Engine::TryAllMoves(Color opponent, int level, int cutoffval,
// using the board control values.
//
int Engine::EvaluatePosition(Color color)
int Engine::EvaluatePosition(ChipColor color)
{
int retval;
Color opponent = ::opponent(color);
ChipColor opponent = opponentColorFor(color);
int score_color = m_score.score(color);
int score_opponent = m_score.score(opponent);
int score_color = m_score->score(color);
int score_opponent = m_score->score(opponent);
if (m_exhaustive)
retval = score_color - score_opponent;
else {
retval = (100-m_coeff) *
(m_score.score(color) - m_score.score(opponent))
+ m_coeff * BC_WEIGHT * (m_bc_score.score(color)
- m_bc_score.score(opponent));
(m_score->score(color) - m_score->score(opponent))
+ m_coeff * BC_WEIGHT * (m_bc_score->score(color)
- m_bc_score->score(opponent));
}
return retval;
......@@ -756,7 +797,7 @@ void Engine::SetupBcBoard()
// Calculate the board control score.
//
int Engine::CalcBcScore(Color color)
int Engine::CalcBcScore(ChipColor color)
{
int sum = 0;
......@@ -772,7 +813,7 @@ int Engine::CalcBcScore(Color color)
// Calculate a bitmap of the occupied squares for a certain color.
//
ULONG64 Engine::ComputeOccupiedBits(Color color)
ULONG64 Engine::ComputeOccupiedBits(ChipColor color)
{
ULONG64 retval = 0;
......
......@@ -128,6 +128,7 @@
//#include <sys/times.h>
#include <QBitArray>
#include <QVector>
#include <krandomsequence.h>
#include "kreversiboard.h"
class KReversiGame;
......@@ -195,6 +196,7 @@ public:
int m_value;
};
class Score;
// The real beef of this program: the engine that finds good moves for
// the computer player.
......@@ -207,6 +209,9 @@ public:
KReversiMove computeMove(const KReversiGame& game, bool competitive);
void setInterrupt(bool intr) { m_interrupt = intr; }
bool interrupted() const { return m_interrupt; }
private:
KReversiMove ComputeFirstMove(const KReversiGame& game);
int ComputeMove2(int xplay, int yplay, ChipColor color, int level,
......@@ -233,8 +238,8 @@ private:
ChipColor m_board[10][10];
int m_bc_board[9][9];
Score m_score;
Score m_bc_score;
Score* m_score;
Score* m_bc_score;
SquareStack m_squarestack;
int m_depth;
......@@ -243,6 +248,10 @@ private:
bool m_exhaustive;
bool m_competitive;
uint m_strength;
KRandomSequence m_random;
bool m_interrupt;
ULONG64 m_coord_bit[9][9];
ULONG64 m_neighbor_bits[9][9];
};
......
......@@ -2,6 +2,14 @@
#include "kreversiboard.h"
static ChipColor opponentColorFor(ChipColor color)
{
if(color == NoColor)
return color;
else
return ( color == White ? Black : White );
}
KReversiBoard::KReversiBoard()
{
resetBoard();
......@@ -12,12 +20,30 @@ void KReversiBoard::resetBoard()
for(int r=0; r<8; ++r)
for(int c=0; c<8; ++c)
m_cells[r][c] = NoColor;
// initial pos
m_cells[3][3] = m_cells[4][4] = White;
m_cells[3][4] = m_cells[4][3] = Black;
m_score[White] = m_score[Black] = 2;
}
int KReversiBoard::playerScore( ChipColor player ) const
{
return m_score[player];
}
void KReversiBoard::setChipColor(int row, int col, ChipColor color)
{
Q_ASSERT( row < 8 && col < 8 );
// first: if the current cell already contains a chip of the opponent,
// we'll decrease opponents score
if( m_cells[row][col] != NoColor && m_cells[row][col] != color )
m_score[opponentColorFor(color)]--;
// and now replacing with chip of 'color'
m_cells[row][col] = color;
m_score[color]++;
kDebug() << "Score of White player: " << m_score[White] << endl;
kDebug() << "Score of Black player: " << m_score[Black] << endl;
}
ChipColor KReversiBoard::chipColorAt(int row, int col) const
......
#ifndef KREVERSI_BOARD_H
#define KREVERSI_BOARD_H
#include <QVector>
// noColor = empty
enum ChipColor { NoColor, White, Black };
enum ChipColor { White = 0, Black = 1, NoColor = 2 };
/**
* This class represents a reversi board.
......@@ -28,8 +26,19 @@ public:
* Retruns type of the chip in cell at (row, col)
*/
ChipColor chipColorAt(int row, int col) const;
/**
* Returns number of chips owned by this player
*/
int playerScore( ChipColor player ) const;
private:
/**
* The board itself
*/
ChipColor m_cells[8][8];
/**
* Score of each player
*/
int m_score[2];
};
struct KReversiMove
......
......@@ -3,7 +3,7 @@
#include <QObject>
class KReversiBoard;
#include "kreversiboard.h"
/**
* KReversiGame incapsulates all of the game logic.
......@@ -21,6 +21,9 @@ public:
* @returns the board so the callers can examine its current state
*/
const KReversiBoard& board() const;
ChipColor currentPlayer() const { return NoColor;/* FIXME dimsuz: implement */ }
int playerScore( ChipColor player ) const { return m_board->playerScore( player ); }
ChipColor chipColorAt( int row, int col ) const { return m_board->chipColorAt( row, col ); }
private:
KReversiBoard *m_board;
};
......
......@@ -11,28 +11,15 @@ const int CHIP_SIZE = 32;
KReversiView::KReversiView( QGraphicsScene* scene, QWidget *parent )
: QGraphicsView(scene, parent)
{
setRenderHints(QPainter::Antialiasing );
scene->setSceneRect( 0, 0, CHIP_SIZE*BOARD_SIZE, CHIP_SIZE*BOARD_SIZE );
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
//setFixedSize( sceneRect().toRect().size() );
}
void KReversiView::showEvent(QShowEvent*)
{
// NOTE. it seems that QGV initializes it's margin only when
// it has been shown. So we need to setSize here
// Rude. It would be much better to calc the sizes in constructor.
setFixedSize(sizeHint());
}
/*
QSize KReversiView::sizeHint() const
{
// QGraphicsView creates a 4px (as by time of writing)
// margin around the scene // and it's no way not to
// hardcode it here, rather then this:
// we map scene's (0,0) to view coord-system and then use
// (for example) x coordinate of resulting point.
// That'll be the margin
int margin = mapFromScene( 0, 0 ).x();
return QSize(scene()->width()+(margin*2), scene()->height()+margin*2);
return QSize( scene()->width()+8, scene()->height()+8 );
}
*/
......@@ -8,7 +8,6 @@ class KReversiView : public QGraphicsView
public:
KReversiView( QGraphicsScene* scene, QWidget *parent );
private:
virtual QSize sizeHint() const;
virtual void showEvent(QShowEvent*);
//virtual QSize sizeHint() const;
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment