Commit d1ec9049 authored by Inge Wallin's avatar Inge Wallin

Extract all Reversi logic into it's own file.

This makes the rest of the engine a lot simpler.

This patch doesn't fully integrate the new logic but runs it in
parallel with the old one with plenty of checks to see that they are
equivalent.  And it seems that they are.
parent b58a3ea9
......@@ -23,6 +23,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/libkdegames/highscore )
########### next target ###############
set(kreversi_SRCS
reversi.cpp
colorscheme.cpp
kreversigame.cpp
kreversiview.cpp
......
......@@ -241,13 +241,13 @@ public:
m_score[Black] = 0;
}
uint score(ChipColor color) const { return m_score[color]; }
uint score(Color 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; }
void set(Color color, uint score) { m_score[color] = score; }
void inc(Color color) { m_score[color]++; }
void dec(Color color) { m_score[color]--; }
void add(Color color, uint s) { m_score[color] += s; }
void sub(Color color, uint s) { m_score[color] -= s; }
private:
uint m_score[2];
......@@ -314,7 +314,7 @@ void Engine::yield()
// Calculate the best move from the current position, and return it.
KReversiPos Engine::computeMove(const KReversiGame& game, bool competitive)
KReversiPos Engine::computeMove(const KReversiGame& game, Color color, bool competitive)
{
if (m_computingMove) {
//kDebug() << "I'm already computing move! Yours KReversi Engine.";
......@@ -322,8 +322,6 @@ KReversiPos Engine::computeMove(const KReversiGame& game, bool competitive)
}
m_computingMove = true;
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
// make "a mistake". The idea behind this is not to scare away
......@@ -336,8 +334,7 @@ KReversiPos Engine::computeMove(const KReversiGame& game, bool competitive)
// but that case is determined further down.
m_exhaustive = false;
// Get the color to calculate the move for.
color = game.currentPlayer();
// Check the color to get the move for.
if (color == NoColor) {
m_computingMove = false;
return KReversiPos();
......@@ -497,7 +494,7 @@ KReversiPos Engine::computeMove(const KReversiGame& game, bool competitive)
KReversiPos Engine::ComputeFirstMove(const KReversiGame& game)
{
int r;
ChipColor color = game.currentPlayer();
Color color = game.currentPlayer();
r = m_random.getLong(4) + 1;
......@@ -522,13 +519,13 @@ KReversiPos Engine::ComputeFirstMove(const KReversiGame& game)
// search.
//
int Engine::ComputeMove2(int xplay, int yplay, ChipColor color, int level,
int Engine::ComputeMove2(int xplay, int yplay, Color color, int level,
int cutoffval, quint64 colorbits,
quint64 opponentbits)
{
int number_of_turned = 0;
SquareStackEntry mse;
ChipColor opponent = opponentColorFor(color);
Color opponent = opponentColorFor(color);
m_nodesSearched++;
......@@ -646,7 +643,7 @@ int Engine::ComputeMove2(int xplay, int yplay, ChipColor color, int level,
// most valuable move, but not the move itself.
//
int Engine::TryAllMoves(ChipColor opponent, int level, int cutoffval,
int Engine::TryAllMoves(Color opponent, int level, int cutoffval,
quint64 opponentbits, quint64 colorbits)
{
int maxval = -LARGEINT;
......@@ -688,11 +685,11 @@ int Engine::TryAllMoves(ChipColor opponent, int level, int cutoffval,
// using the board control values.
//
int Engine::EvaluatePosition(ChipColor color)
int Engine::EvaluatePosition(Color color)
{
int retval;
ChipColor opponent = opponentColorFor(color);
Color opponent = opponentColorFor(color);
int score_color = m_score->score(color);
int score_opponent = m_score->score(opponent);
......@@ -782,7 +779,7 @@ void Engine::SetupBcBoard()
// Calculate the board control score.
//
int Engine::CalcBcScore(ChipColor color)
int Engine::CalcBcScore(Color color)
{
int sum = 0;
......@@ -798,7 +795,7 @@ int Engine::CalcBcScore(ChipColor color)
// Calculate a bitmap of the occupied squares for a certain color.
//
quint64 Engine::ComputeOccupiedBits(ChipColor color)
quint64 Engine::ComputeOccupiedBits(Color color)
{
quint64 retval = 0;
......
......@@ -128,16 +128,8 @@
// KReversi
#include "commondefs.h"
class KReversiGame;
static ChipColor opponentColorFor(ChipColor color)
{
if (color == NoColor)
return color;
else
return (color == White ? Black : White);
}
class KReversiGame;
// SquareStackEntry and SquareStack are used during search to keep
......@@ -203,7 +195,7 @@ class Engine
~Engine();
KReversiPos computeMove(const KReversiGame& game, bool competitive);
KReversiPos computeMove(const KReversiGame& game, Color color, bool competitive);
bool isThinking() const { return m_computingMove; }
void setInterrupt(bool intr) { m_interrupt = intr; }
......@@ -213,24 +205,24 @@ class Engine
uint strength() const { return m_strength; }
private:
KReversiPos ComputeFirstMove(const KReversiGame& game);
int ComputeMove2(int xplay, int yplay, ChipColor color, int level,
int ComputeMove2(int xplay, int yplay, Color color, int level,
int utoffval,
quint64 colorbits, quint64 opponentbits);
int TryAllMoves(ChipColor opponent, int level, int cutoffval,
int TryAllMoves(Color opponent, int level, int cutoffval,
quint64 opponentbits, quint64 colorbits);
int EvaluatePosition(ChipColor color);
int EvaluatePosition(Color color);
void SetupBcBoard();
void SetupBits();
int CalcBcScore(ChipColor color);
quint64 ComputeOccupiedBits(ChipColor color);
int CalcBcScore(Color color);
quint64 ComputeOccupiedBits(Color color);
void yield();
private:
ChipColor m_board[10][10];
Color m_board[10][10];
int m_bc_board[9][9];
Score* m_score;
Score* m_bc_score;
......
......@@ -24,21 +24,17 @@
#ifndef COMMONDEFS_H
#define COMMONDEFS_H
// noColor = empty
#include "reversi.h"
enum ChipColor {
White = 0,
Black = 1,
NoColor = 2
};
using namespace Reversi;
struct KReversiPos
{
KReversiPos( ChipColor col = NoColor, int r = -1, int c = -1 )
KReversiPos(Color col = NoColor, int r = -1, int c = -1 )
: color(col), row(r), col(c) { }
ChipColor color;
Color color;
int row;
int col;
......
......@@ -65,7 +65,7 @@ void KReversiGame::makePlayerMove(int row, int col, bool demoMode)
if (!demoMode)
move = KReversiPos(m_playerColor, row, col);
else {
move = m_engine->computeMove(*this, true);
move = m_engine->computeMove(*this, m_playerColor, true);
if (!move.isValid())
return;
}
......@@ -93,6 +93,7 @@ void KReversiGame::startNextTurn(bool demoMode)
// No Computer move possible and not in demo mode
//kDebug() << "Computer can't move!";
m_curPlayer = m_playerColor;
m_position.setToMove(m_playerColor);
emit computerCantMove();
}
}
......@@ -120,7 +121,7 @@ void KReversiGame::makeComputerMove()
m_curPlayer = m_computerColor;
// FIXME dimsuz: m_competitive. Read from config.
// (also there's computeMove in getHint)
KReversiPos move = m_engine->computeMove(*this, true);
KReversiPos move = m_engine->computeMove(*this, m_computerColor, true);
if (!move.isValid())
return;
......@@ -157,19 +158,37 @@ int KReversiGame::undo()
// Yes, I like long descriptions in comments ;).
KReversiPos move = lastUndo.takeFirst();
setChipColor(NoColor, move.row, move.col);
setColor(NoColor, move.row, move.col);
// and change back the color of the rest chips
foreach (const KReversiPos &pos, lastUndo) {
ChipColor opponentColor = opponentColorFor(m_cells[pos.row][pos.col]);
setChipColor(opponentColor, pos.row, pos.col);
Color opponentColor = opponentColorFor(m_cells[pos.row][pos.col]);
setColor(opponentColor, pos.row, pos.col);
}
lastUndo.clear();
// ----------------------------------------------------------------
// new code. The old code will be disabled when we trust the new code enough
Move move2 = m_newUndoStack.pop();
if (move2.color != move.color
|| move2.row != move.row
|| move2.col != move.col) {
kDebug() << "Moves not the same in undo: "
<< move.color << move.row << move.col << " "
<< move2.color << move2.row << move2.col;
}
if (!m_position.undoMove(move2, true)) {
kDebug() << "UNDO FAILED!";
}
checkBoard();
// ----------------------------------------------------------------
movesUndone++;
if (move.color == m_playerColor)
break; //we've undone all computer + one player moves
}
m_curPlayer = m_playerColor;
......@@ -186,7 +205,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
{
changedChips.clear();
setChipColor(move.color, move.row, move.col);
setColor(move.color, move.row, move.col);
// the first one is the move itself
changedChips.append(move);
......@@ -196,7 +215,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row-1; r >= 0; --r) {
if (m_cells[r][move.col] == move.color)
break;
setChipColor(move.color, r, move.col);
setColor(move.color, r, move.col);
changedChips.append(KReversiPos(move.color, r, move.col));
}
}
......@@ -204,7 +223,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row+1; r < 8; ++r) {
if (m_cells[r][move.col] == move.color)
break;
setChipColor(move.color, r, move.col);
setColor(move.color, r, move.col);
changedChips.append(KReversiPos(move.color, r, move.col));
}
}
......@@ -212,7 +231,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int c=move.col-1; c >= 0; --c) {
if (m_cells[move.row][c] == move.color)
break;
setChipColor(move.color, move.row, c);
setColor(move.color, move.row, c);
changedChips.append(KReversiPos(move.color, move.row, c));
}
}
......@@ -220,7 +239,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int c=move.col+1; c < 8; ++c) {
if (m_cells[move.row][c] == move.color)
break;
setChipColor(move.color, move.row, c);
setColor(move.color, move.row, c);
changedChips.append(KReversiPos(move.color, move.row, c));
}
}
......@@ -228,7 +247,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row-1, c=move.col-1; r>=0 && c >= 0; --r, --c) {
if (m_cells[r][c] == move.color)
break;
setChipColor(move.color, r, c);
setColor(move.color, r, c);
changedChips.append(KReversiPos(move.color, r, c));
}
}
......@@ -236,7 +255,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row-1, c=move.col+1; r>=0 && c < 8; --r, ++c) {
if (m_cells[r][c] == move.color)
break;
setChipColor(move.color, r, c);
setColor(move.color, r, c);
changedChips.append(KReversiPos(move.color, r, c));
}
}
......@@ -244,7 +263,7 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row+1, c=move.col-1; r < 8 && c >= 0; ++r, --c) {
if (m_cells[r][c] == move.color)
break;
setChipColor(move.color, r, c);
setColor(move.color, r, c);
changedChips.append(KReversiPos(move.color, r, c));
}
}
......@@ -252,13 +271,21 @@ void KReversiGame::makeMove(const KReversiPos& move, PosList &changedChips)
for (int r=move.row+1, c=move.col+1; r < 8 && c < 8; ++r, ++c) {
if (m_cells[r][c] == move.color)
break;
setChipColor(move.color, r, c);
setColor(move.color, r, c);
changedChips.append(KReversiPos(move.color, r, c));
}
}
m_curPlayer = (m_curPlayer == White ? Black : White);
//kDebug() << "Current player changed to" << (m_curPlayer == White ? "White" : "Black");
// ----------------------------------------------------------------
// new code
Move move2(move.color, move.row, move.col);
m_position.makeMove(move2);
m_newUndoStack.push(move2);
checkBoard();
}
PosList KReversiGame::changedChips() const
......@@ -297,12 +324,12 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
//
// Well, I wrote this description from my head, now lets produce some code for that ;)
ChipColor opColor = opponentColorFor(move.color);
Color opColor = opponentColorFor(move.color);
int opponentChipsNum = 0;
bool foundPlayerColor = false;
if (dir == Up) {
for (int row=move.row-1; row >= 0; --row) {
ChipColor color = m_cells[row][move.col];
Color color = m_cells[row][move.col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -319,7 +346,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == Down) {
for (int row=move.row+1; row < 8; ++row) {
ChipColor color = m_cells[row][move.col];
Color color = m_cells[row][move.col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -336,7 +363,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == Left) {
for (int col=move.col-1; col >= 0; --col) {
ChipColor color = m_cells[move.row][col];
Color color = m_cells[move.row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -353,7 +380,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == Right) {
for (int col=move.col+1; col < 8; ++col) {
ChipColor color = m_cells[move.row][col];
Color color = m_cells[move.row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -370,7 +397,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == UpLeft) {
for (int row=move.row-1, col=move.col-1; row >= 0 && col >= 0; --row, --col) {
ChipColor color = m_cells[row][col];
Color color = m_cells[row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -387,7 +414,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == UpRight) {
for (int row=move.row-1, col=move.col+1; row >= 0 && col < 8; --row, ++col) {
ChipColor color = m_cells[row][col];
Color color = m_cells[row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -404,7 +431,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == DownLeft) {
for (int row=move.row+1, col=move.col-1; row < 8 && col >= 0; ++row, --col) {
ChipColor color = m_cells[row][col];
Color color = m_cells[row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -421,7 +448,7 @@ bool KReversiGame::hasChunk(Direction dir, const KReversiPos& move) const
}
else if (dir == DownRight) {
for (int row=move.row+1, col=move.col+1; row < 8 && col < 8; ++row, ++col) {
ChipColor color = m_cells[row][col];
Color color = m_cells[row][col];
if (color == opColor) {
opponentChipsNum++;
}
......@@ -495,7 +522,7 @@ void KReversiGame::setComputerSkill(int skill)
KReversiPos KReversiGame::getHint() const
{
// FIXME dimsuz: don't use true, use m_competitive
return m_engine->computeMove(*this, true);
return m_engine->computeMove(*this, m_playerColor, true);
}
KReversiPos KReversiGame::getLastMove() const
......@@ -520,12 +547,13 @@ PosList KReversiGame::possibleMoves() const
return l;
}
int KReversiGame::playerScore(ChipColor player) const
int KReversiGame::playerScore(Color player) const
{
return m_score[player];
//return m_score[player];
return m_position.playerScore(player);
}
void KReversiGame::setChipColor(ChipColor color, int row, int col)
void KReversiGame::setColor(Color color, int row, int col)
{
Q_ASSERT(row < 8 && col < 8);
// first: if the current cell already contains a chip of the opponent,
......@@ -549,10 +577,47 @@ void KReversiGame::setChipColor(ChipColor color, int row, int col)
//kDebug() << "Score of Black player:" << m_score[Black];
}
ChipColor KReversiGame::chipColorAt(int row, int col) const
Color KReversiGame::chipColorAt(int row, int col) const
{
Q_ASSERT(row < 8 && col < 8);
return m_cells[row][col];
}
void KReversiGame::checkBoard()
{
bool equal = true;
for (int r = 0; r < 8; ++r) {
for (int c = 0; c < 8; ++c) {
if (m_cells[r][c] != m_position.colorAt(r, c)) {
equal = false;
}
}
}
if (m_score[Black] != m_position.playerScore(Black)
|| m_score[White] != m_position.playerScore(White))
equal = false;
if (!equal) {
kDebug() << "BOARDS NOT EQUAL:";
kDebug() << " OLD " << " " << " NEW ";
for (int r = 0; r < 8; ++r) {
QString oldBoard;
QString newBoard;
for (int c = 0; c < 8; ++c) {
oldBoard.append("*o_"[m_cells[r][c]]);
}
for (int c = 0; c < 8; ++c) {
newBoard.append("*o_"[m_position.colorAt(r, c)]);
}
kDebug() << oldBoard << " " << newBoard;
}
kDebug() << m_score[Black] << m_score[White] << " "
<< m_position.playerScore(Black) << m_position.playerScore(White);
}
}
#include "kreversigame.moc"
......@@ -30,9 +30,12 @@
// KReversi
#include "commondefs.h"
#include "reversi.h"
class Engine;
using namespace Reversi;
/**
* KReversiGame incapsulates all of the game logic.
* Whenever the board state changes it emits corresponding signals.
......@@ -114,16 +117,17 @@ public:
/**
* @return a color of the current player
*/
ChipColor currentPlayer() const { return m_curPlayer; }
//Color currentPlayer() const { return m_curPlayer; }
Color currentPlayer() const { return m_position.toMove(); }
/**
* @return score (number of chips) of the player
*/
int playerScore(ChipColor player) const;
int playerScore(Color player) const;
/**
* @return color of the chip at position [row, col]
*/
ChipColor chipColorAt(int row, int col) const;
Color chipColorAt(int row, int col) const;
/**
* @return if undo is possible
*/
......@@ -139,7 +143,8 @@ public:
/**
* @return true, if it's computer's turn now
*/
bool isComputersTurn() const { return m_curPlayer == m_computerColor; }
//bool isComputersTurn() const { return m_curPlayer == m_computerColor; }
bool isComputersTurn() const { return m_position.toMove() == m_computerColor; }
/**
* @return a list of chips which were changed during last move.
* First of them will be the move itself, and the rest - chips which
......@@ -183,11 +188,11 @@ private:
/**
* Sets the type of chip at (row,col)
*/
void setChipColor(ChipColor type, int row, int col);
void setColor(Color type, int row, int col);
/**
* The board itself
*/
ChipColor m_cells[8][8];
Color m_cells[8][8];
/**
* Score of each player
*/
......@@ -195,15 +200,15 @@ private:
/**
* Color of the current player
*/
ChipColor m_curPlayer;
Color m_curPlayer;
/**
* The color of the human played chips
*/
ChipColor m_playerColor;
Color m_playerColor;
/**
* The color of the computer played chips
*/
ChipColor m_computerColor;
Color m_computerColor;
/**
* Our AI
......@@ -216,6 +221,19 @@ private:
* @see m_changedChips
*/
QStack<PosList> m_undoStack;
// ----------------------------------------------------------------
// New code
/**
* The current position.
*/
Position m_position;
QStack<Move> m_newUndoStack;
// Checks if the board in m_cells is equal to the board in m_position
void checkBoard();
};
#endif
/*******************************************************************
*
* Copyright 2013 Inge Wallin <inge@lysator.liu.se>
* Copyright 2006 Dmitry Suzdalev <dimsuz@gmail.com>
*
* This file is part of the KDE project "KReversi"
*
* KReversi 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, or (at your option)
* any later version.
*
* KReversi 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 KReversi; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
********************************************************************/
// KReversi
#include "reversi.h"