847 lines
28 KiB
C++
847 lines
28 KiB
C++
/*********************************************************************
|
|
Mill.cpp
|
|
Copyright (c) Thomas Weber. All rights reserved.
|
|
Copyright (C) 2021 The Sanmill developers (see AUTHORS file)
|
|
Licensed under the GPLv3 License.
|
|
https://github.com/madweasel/Muehle
|
|
\*********************************************************************/
|
|
|
|
#include "mill.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: Mill()
|
|
// Desc: Mill class constructor
|
|
//-----------------------------------------------------------------------------
|
|
Mill::Mill()
|
|
{
|
|
srand((unsigned)time(nullptr));
|
|
|
|
moveLogFrom = nullptr;
|
|
moveLogTo = nullptr;
|
|
playerOneAI = nullptr;
|
|
playerTwoAI = nullptr;
|
|
movesDone = 0;
|
|
|
|
field.createBoard();
|
|
initialField.createBoard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: ~Mill()
|
|
// Desc: Mill class destructor
|
|
//-----------------------------------------------------------------------------
|
|
Mill::~Mill()
|
|
{
|
|
exit();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: deleteArrays()
|
|
// Desc: Deletes all arrays the Mill class has created.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::exit()
|
|
{
|
|
SAFE_DELETE_ARRAY(moveLogFrom);
|
|
SAFE_DELETE_ARRAY(moveLogTo);
|
|
|
|
field.deleteBoard();
|
|
initialField.deleteBoard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: beginNewGame()
|
|
// Desc: Reinitializes the Mill object.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::beginNewGame(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer)
|
|
{
|
|
// free mem
|
|
exit();
|
|
|
|
// create arrays
|
|
field.createBoard();
|
|
initialField.createBoard();
|
|
|
|
// calc beginning player
|
|
if (currentPlayer == field.playerOne || currentPlayer == field.playerTwo) {
|
|
beginningPlayer = currentPlayer;
|
|
} else {
|
|
beginningPlayer = (rand() % 2) ? field.playerOne : field.playerTwo;
|
|
}
|
|
|
|
field.curPlayer->id = beginningPlayer;
|
|
field.oppPlayer->id = (field.curPlayer->id == field.playerTwo) ? field.playerOne : field.playerTwo;
|
|
|
|
winner = 0;
|
|
movesDone = 0;
|
|
playerOneAI = firstPlayerAI;
|
|
playerTwoAI = secondPlayerAI;
|
|
moveLogFrom = new unsigned int[MAX_NUM_MOVES];
|
|
moveLogTo = new unsigned int[MAX_NUM_MOVES];
|
|
|
|
// remember initialField
|
|
field.copyBoard(&initialField);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: startSettingPhase()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::startSettingPhase(MillAI *firstPlayerAI, MillAI *secondPlayerAI, int currentPlayer, bool settingPhase)
|
|
{
|
|
beginNewGame(firstPlayerAI, secondPlayerAI, currentPlayer);
|
|
|
|
field.settingPhase = settingPhase;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setUpCalcPossibleMoves()
|
|
// Desc: Calculates and set the number of possible moves for the passed player considering the game state stored in the 'board' variable.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::setUpCalcPossibleMoves(Player *player)
|
|
{
|
|
// locals
|
|
unsigned int i, j, k, movingDirection;
|
|
|
|
for (player->numPossibleMoves = 0, i = 0; i < fieldStruct::size; i++) {
|
|
for (j = 0; j < fieldStruct::size; j++) {
|
|
|
|
// is stone from player ?
|
|
if (field.board[i] != player->id)
|
|
continue;
|
|
|
|
// is destination free ?
|
|
if (field.board[j] != field.squareIsFree)
|
|
continue;
|
|
|
|
// when current player has only 3 stones he is allowed to spring his stone
|
|
if (player->numStones > 3 || field.settingPhase) {
|
|
|
|
// determine moving direction
|
|
for (k = 0, movingDirection = 4; k < 4; k++)
|
|
if (field.connectedSquare[i][k] == j)
|
|
movingDirection = k;
|
|
|
|
// are both squares connected ?
|
|
if (movingDirection == 4)
|
|
continue;
|
|
}
|
|
|
|
// everything is ok
|
|
player->numPossibleMoves++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setUpSetWarningAndMill()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::setUpSetWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour)
|
|
{
|
|
// locals
|
|
int rowOwner = field.board[stone];
|
|
|
|
// mill closed ?
|
|
if (rowOwner != field.squareIsFree && field.board[firstNeighbour] == rowOwner && field.board[secondNeighbour] == rowOwner) {
|
|
|
|
field.stonePartOfMill[stone]++;
|
|
field.stonePartOfMill[firstNeighbour]++;
|
|
field.stonePartOfMill[secondNeighbour]++;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: putPiece()
|
|
// Desc: Put a stone onto the board during the setting phase.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::putPiece(unsigned int pos, int player)
|
|
{
|
|
// locals
|
|
unsigned int i;
|
|
unsigned int numberOfMillsCurrentPlayer = 0, numberOfMillsOpponentPlayer = 0;
|
|
Player *myPlayer = (player == field.curPlayer->id) ? field.curPlayer : field.oppPlayer;
|
|
|
|
// check parameters
|
|
if (player != fieldStruct::playerOne && player != fieldStruct::playerTwo)
|
|
return false;
|
|
if (pos >= fieldStruct::size)
|
|
return false;
|
|
if (field.board[pos] != field.squareIsFree)
|
|
return false;
|
|
|
|
// set stone
|
|
field.board[pos] = player;
|
|
myPlayer->numStones++;
|
|
field.stonesSet++;
|
|
|
|
// setting phase finished ?
|
|
if (field.stonesSet == 18)
|
|
field.settingPhase = false;
|
|
|
|
// calc possible moves
|
|
setUpCalcPossibleMoves(field.curPlayer);
|
|
setUpCalcPossibleMoves(field.oppPlayer);
|
|
|
|
// zero
|
|
for (i = 0; i < fieldStruct::size; i++)
|
|
field.stonePartOfMill[i] = 0;
|
|
|
|
// go in every direction
|
|
for (i = 0; i < fieldStruct::size; i++) {
|
|
setUpSetWarningAndMill(i, field.neighbour[i][0][0], field.neighbour[i][0][1]);
|
|
setUpSetWarningAndMill(i, field.neighbour[i][1][0], field.neighbour[i][1][1]);
|
|
}
|
|
|
|
// since every mill was detected 3 times
|
|
for (i = 0; i < fieldStruct::size; i++)
|
|
field.stonePartOfMill[i] /= 3;
|
|
|
|
// count completed mills
|
|
for (i = 0; i < fieldStruct::size; i++) {
|
|
if (field.board[i] == field.curPlayer->id)
|
|
numberOfMillsCurrentPlayer += field.stonePartOfMill[i];
|
|
else
|
|
numberOfMillsOpponentPlayer += field.stonePartOfMill[i];
|
|
}
|
|
numberOfMillsCurrentPlayer /= 3;
|
|
numberOfMillsOpponentPlayer /= 3;
|
|
|
|
// stonesSet & numStonesMissing
|
|
if (field.settingPhase) {
|
|
// ... This calculation is not correct! It is possible that some mills did not cause a stone removal.
|
|
field.curPlayer->numStonesMissing = numberOfMillsOpponentPlayer;
|
|
field.oppPlayer->numStonesMissing = numberOfMillsCurrentPlayer - field.stoneMustBeRemoved;
|
|
field.stonesSet = field.curPlayer->numStones + field.oppPlayer->numStones + field.curPlayer->numStonesMissing + field.oppPlayer->numStonesMissing;
|
|
} else {
|
|
field.stonesSet = 18;
|
|
field.curPlayer->numStonesMissing = 9 - field.curPlayer->numStones;
|
|
field.oppPlayer->numStonesMissing = 9 - field.oppPlayer->numStones;
|
|
}
|
|
|
|
// when opponent is unable to move than current player has won
|
|
if ((!field.curPlayer->numPossibleMoves) && (!field.settingPhase) && (!field.stoneMustBeRemoved) && (field.curPlayer->numStones > 3))
|
|
winner = field.oppPlayer->id;
|
|
else if ((field.curPlayer->numStones < 3) && (!field.settingPhase))
|
|
winner = field.oppPlayer->id;
|
|
else if ((field.oppPlayer->numStones < 3) && (!field.settingPhase))
|
|
winner = field.curPlayer->id;
|
|
else
|
|
winner = 0;
|
|
|
|
// everything is ok
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: settingPhaseHasFinished()
|
|
// Desc: This function has to be called when the setting phase has finished.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::settingPhaseHasFinished()
|
|
{
|
|
// remember initialField
|
|
field.copyBoard(&initialField);
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: getField()
|
|
// Desc: Copy the current board state into the array 'pField'.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::getField(int *pField)
|
|
{
|
|
unsigned int index;
|
|
|
|
// if no log is available than no game is in progress and board is invalid
|
|
if (moveLogFrom == nullptr)
|
|
return false;
|
|
|
|
for (index = 0; index < field.size; index++) {
|
|
if (field.warnings[index] != field.noWarning)
|
|
pField[index] = (int)field.warnings[index];
|
|
else
|
|
pField[index] = field.board[index];
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: getLog()
|
|
// Desc: Copy the whole history of moves into the passed arrays, which must be of size [MAX_NUM_MOVES].
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::getLog(unsigned int &numMovesDone, unsigned int *from, unsigned int *to)
|
|
{
|
|
unsigned int index;
|
|
|
|
numMovesDone = movesDone;
|
|
|
|
for (index = 0; index < movesDone; index++) {
|
|
from[index] = moveLogFrom[index];
|
|
to[index] = moveLogTo[index];
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setNextPlayer()
|
|
// Desc: Current player and opponent player are switched in the board struct.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::setNextPlayer()
|
|
{
|
|
Player *tmpPlayer;
|
|
|
|
tmpPlayer = field.curPlayer;
|
|
field.curPlayer = field.oppPlayer;
|
|
field.oppPlayer = tmpPlayer;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: isCurrentPlayerHuman()
|
|
// Desc: Returns true if the current player is not assigned to an AI.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::isCurrentPlayerHuman()
|
|
{
|
|
if (field.curPlayer->id == field.playerOne)
|
|
return (playerOneAI == nullptr) ? true : false;
|
|
else
|
|
return (playerTwoAI == nullptr) ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: isOpponentPlayerHuman()
|
|
// Desc: Returns true if the opponent player is not assigned to an AI.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::isOpponentPlayerHuman()
|
|
{
|
|
if (field.oppPlayer->id == field.playerOne)
|
|
return (playerOneAI == nullptr) ? true : false;
|
|
else
|
|
return (playerTwoAI == nullptr) ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setAI()
|
|
// Desc: Assigns an AI to a player.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::setAI(int player, MillAI *AI)
|
|
{
|
|
if (player == field.playerOne) {
|
|
playerOneAI = AI;
|
|
}
|
|
if (player == field.playerTwo) {
|
|
playerTwoAI = AI;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: getChoiceOfSpecialAI()
|
|
// Desc: Returns the move the passed AI would do.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::getChoiceOfSpecialAI(MillAI *AI, unsigned int *pushFrom, unsigned int *pushTo)
|
|
{
|
|
fieldStruct theField;
|
|
*pushFrom = field.size;
|
|
*pushTo = field.size;
|
|
theField.createBoard();
|
|
field.copyBoard(&theField);
|
|
if (AI != nullptr && (field.settingPhase || field.curPlayer->numPossibleMoves > 0) && winner == 0)
|
|
AI->play(&theField, pushFrom, pushTo);
|
|
theField.deleteBoard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: getComputersChoice()
|
|
// Desc: Returns the move the AI of the current player would do.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::getComputersChoice(unsigned int *pushFrom, unsigned int *pushTo)
|
|
{
|
|
fieldStruct theField;
|
|
*pushFrom = field.size;
|
|
*pushTo = field.size;
|
|
theField.createBoard();
|
|
field.copyBoard(&theField);
|
|
|
|
if ((field.settingPhase || field.curPlayer->numPossibleMoves > 0) && winner == 0) {
|
|
if (field.curPlayer->id == field.playerOne) {
|
|
if (playerOneAI != nullptr)
|
|
playerOneAI->play(&theField, pushFrom, pushTo);
|
|
} else {
|
|
if (playerTwoAI != nullptr)
|
|
playerTwoAI->play(&theField, pushFrom, pushTo);
|
|
}
|
|
}
|
|
|
|
theField.deleteBoard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: isNormalMovePossible()
|
|
// Desc: 'Normal' in this context means, by moving the stone along a connection without jumping.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::isNormalMovePossible(unsigned int from, unsigned int to, Player *player)
|
|
{
|
|
// locals
|
|
unsigned int movingDirection, i;
|
|
|
|
// parameter ok ?
|
|
if (from >= field.size)
|
|
return false;
|
|
if (to >= field.size)
|
|
return false;
|
|
|
|
// is stone from player ?
|
|
if (field.board[from] != player->id)
|
|
return false;
|
|
|
|
// is destination free ?
|
|
if (field.board[to] != field.squareIsFree)
|
|
return false;
|
|
|
|
// when current player has only 3 stones he is allowed to spring his stone
|
|
if (player->numStones > 3 || field.settingPhase) {
|
|
|
|
// determine moving direction
|
|
for (i = 0, movingDirection = 4; i < 4; i++)
|
|
if (field.connectedSquare[from][i] == to)
|
|
movingDirection = i;
|
|
|
|
// are both squares connected ?
|
|
if (movingDirection == 4)
|
|
return false;
|
|
}
|
|
|
|
// everything is ok
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: calcPossibleMoves()
|
|
// Desc: ...
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::calcPossibleMoves(Player *player)
|
|
{
|
|
// locals
|
|
unsigned int i, j;
|
|
|
|
// zero
|
|
for (i = 0; i < MAX_NUM_POS_MOVES; i++)
|
|
player->posTo[i] = field.size;
|
|
for (i = 0; i < MAX_NUM_POS_MOVES; i++)
|
|
player->posFrom[i] = field.size;
|
|
|
|
// calc
|
|
for (player->numPossibleMoves = 0, i = 0; i < field.size; i++) {
|
|
for (j = 0; j < field.size; j++) {
|
|
if (isNormalMovePossible(i, j, player)) {
|
|
player->posFrom[player->numPossibleMoves] = i;
|
|
player->posTo[player->numPossibleMoves] = j;
|
|
player->numPossibleMoves++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// stoneMoveAble
|
|
for (i = 0; i < field.size; i++) {
|
|
for (j = 0; j < 4; j++) {
|
|
if (field.board[i] == player->id)
|
|
field.stoneMoveAble[i][j] = isNormalMovePossible(i, field.connectedSquare[i][j], player);
|
|
else
|
|
field.stoneMoveAble[i][j] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setWarningAndMill()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::setWarningAndMill(unsigned int stone, unsigned int firstNeighbour, unsigned int secondNeighbour, bool isNewStone)
|
|
{
|
|
// locals
|
|
int rowOwner = field.board[stone];
|
|
unsigned int rowOwnerWarning = (rowOwner == field.playerOne) ? field.playerOneWarning : field.playerTwoWarning;
|
|
|
|
// mill closed ?
|
|
if (rowOwner != field.squareIsFree && field.board[firstNeighbour] == rowOwner && field.board[secondNeighbour] == rowOwner) {
|
|
|
|
field.stonePartOfMill[stone]++;
|
|
field.stonePartOfMill[firstNeighbour]++;
|
|
field.stonePartOfMill[secondNeighbour]++;
|
|
if (isNewStone)
|
|
field.stoneMustBeRemoved = 1;
|
|
}
|
|
|
|
//warning ?
|
|
if (rowOwner != field.squareIsFree && field.board[firstNeighbour] == field.squareIsFree && field.board[secondNeighbour] == rowOwner)
|
|
field.warnings[firstNeighbour] |= rowOwnerWarning;
|
|
if (rowOwner != field.squareIsFree && field.board[secondNeighbour] == field.squareIsFree && field.board[firstNeighbour] == rowOwner)
|
|
field.warnings[secondNeighbour] |= rowOwnerWarning;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: updateMillsAndWarnings()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::updateMillsAndWarnings(unsigned int newStone)
|
|
{
|
|
// locals
|
|
unsigned int i;
|
|
bool atLeastOneStoneRemoveAble;
|
|
|
|
// zero
|
|
for (i = 0; i < field.size; i++)
|
|
field.stonePartOfMill[i] = 0;
|
|
|
|
for (i = 0; i < field.size; i++)
|
|
field.warnings[i] = field.noWarning;
|
|
|
|
field.stoneMustBeRemoved = 0;
|
|
|
|
// go in every direction
|
|
for (i = 0; i < field.size; i++) {
|
|
|
|
setWarningAndMill(i, field.neighbour[i][0][0], field.neighbour[i][0][1], i == newStone);
|
|
setWarningAndMill(i, field.neighbour[i][1][0], field.neighbour[i][1][1], i == newStone);
|
|
}
|
|
|
|
// since every mill was detected 3 times
|
|
for (i = 0; i < field.size; i++)
|
|
field.stonePartOfMill[i] /= 3;
|
|
|
|
// no stone must be removed if each belongs to a mill
|
|
for (atLeastOneStoneRemoveAble = false, i = 0; i < field.size; i++)
|
|
if (field.stonePartOfMill[i] == 0 && field.board[i] == field.oppPlayer->id)
|
|
atLeastOneStoneRemoveAble = true;
|
|
if (!atLeastOneStoneRemoveAble)
|
|
field.stoneMustBeRemoved = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: doMove()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::doMove(unsigned int pushFrom, unsigned int pushTo)
|
|
{
|
|
// avoid index override
|
|
if (movesDone >= MAX_NUM_MOVES)
|
|
return false;
|
|
|
|
// is game still running ?
|
|
if (winner)
|
|
return false;
|
|
|
|
// handle the remove of a stone
|
|
if (field.stoneMustBeRemoved) {
|
|
|
|
// parameter ok ?
|
|
if (pushFrom >= field.size)
|
|
return false;
|
|
|
|
// is it stone from the opponent ?
|
|
if (field.board[pushFrom] != field.oppPlayer->id)
|
|
return false;
|
|
|
|
// is stone not part of mill?
|
|
if (field.stonePartOfMill[pushFrom])
|
|
return false;
|
|
|
|
// remove stone
|
|
moveLogFrom[movesDone] = pushFrom;
|
|
moveLogTo[movesDone] = field.size;
|
|
field.board[pushFrom] = field.squareIsFree;
|
|
field.oppPlayer->numStonesMissing++;
|
|
field.oppPlayer->numStones--;
|
|
field.stoneMustBeRemoved--;
|
|
movesDone++;
|
|
|
|
// is the game finished ?
|
|
if ((field.oppPlayer->numStones < 3) && (!field.settingPhase))
|
|
winner = field.curPlayer->id;
|
|
|
|
// update warnings & mills
|
|
updateMillsAndWarnings(field.size);
|
|
|
|
// calc possibilities
|
|
calcPossibleMoves(field.curPlayer);
|
|
calcPossibleMoves(field.oppPlayer);
|
|
|
|
// is opponent unable to move ?
|
|
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase)
|
|
winner = field.curPlayer->id;
|
|
|
|
// next player
|
|
if (!field.stoneMustBeRemoved)
|
|
setNextPlayer();
|
|
|
|
// everything is ok
|
|
return true;
|
|
|
|
// handle setting phase
|
|
} else if (field.settingPhase) {
|
|
|
|
// parameter ok ?
|
|
if (pushTo >= field.size)
|
|
return false;
|
|
|
|
// is destination free ?
|
|
if (field.board[pushTo] != field.squareIsFree)
|
|
return false;
|
|
|
|
// set stone
|
|
moveLogFrom[movesDone] = field.size;
|
|
moveLogTo[movesDone] = pushTo;
|
|
field.board[pushTo] = field.curPlayer->id;
|
|
field.curPlayer->numStones++;
|
|
field.stonesSet++;
|
|
movesDone++;
|
|
|
|
// update warnings & mills
|
|
updateMillsAndWarnings(pushTo);
|
|
|
|
// calc possibilities
|
|
calcPossibleMoves(field.curPlayer);
|
|
calcPossibleMoves(field.oppPlayer);
|
|
|
|
// setting phase finished ?
|
|
if (field.stonesSet == 18)
|
|
field.settingPhase = false;
|
|
|
|
// is opponent unable to move ?
|
|
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase)
|
|
winner = field.curPlayer->id;
|
|
|
|
// next player
|
|
if (!field.stoneMustBeRemoved)
|
|
setNextPlayer();
|
|
|
|
// everything is ok
|
|
return true;
|
|
|
|
// normal move
|
|
} else {
|
|
|
|
// is move possible ?
|
|
if (!isNormalMovePossible(pushFrom, pushTo, field.curPlayer))
|
|
return false;
|
|
|
|
// move stone
|
|
moveLogFrom[movesDone] = pushFrom;
|
|
moveLogTo[movesDone] = pushTo;
|
|
field.board[pushFrom] = field.squareIsFree;
|
|
field.board[pushTo] = field.curPlayer->id;
|
|
movesDone++;
|
|
|
|
// update warnings & mills
|
|
updateMillsAndWarnings(pushTo);
|
|
|
|
// calc possibilities
|
|
calcPossibleMoves(field.curPlayer);
|
|
calcPossibleMoves(field.oppPlayer);
|
|
|
|
// is opponent unable to move ?
|
|
if (field.oppPlayer->numPossibleMoves == 0 && !field.settingPhase)
|
|
winner = field.curPlayer->id;
|
|
|
|
// next player
|
|
if (!field.stoneMustBeRemoved)
|
|
setNextPlayer();
|
|
|
|
// everything is ok
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: setCurrentGameState()
|
|
// Desc: Set an arbitrary game state as the current one.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::setCurrentGameState(fieldStruct *curState)
|
|
{
|
|
curState->copyBoard(&field);
|
|
|
|
winner = 0;
|
|
movesDone = 0;
|
|
|
|
if ((field.curPlayer->numStones < 3) && (!field.settingPhase))
|
|
winner = field.oppPlayer->id;
|
|
|
|
if ((field.oppPlayer->numStones < 3) && (!field.settingPhase))
|
|
winner = field.curPlayer->id;
|
|
|
|
if ((field.curPlayer->numPossibleMoves == 0) && (!field.settingPhase))
|
|
winner = field.oppPlayer->id;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: compareWithField()
|
|
// Desc: Compares the current 'board' variable with the passed one. 'stoneMoveAble[]' is ignored.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::compareWithField(fieldStruct *compareField)
|
|
{
|
|
unsigned int i, j;
|
|
bool ret = true;
|
|
|
|
if (!comparePlayers(field.curPlayer, compareField->curPlayer)) {
|
|
cout << "error - curPlayer differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (!comparePlayers(field.oppPlayer, compareField->oppPlayer)) {
|
|
cout << "error - oppPlayer differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (field.stonesSet != compareField->stonesSet) {
|
|
cout << "error - stonesSet differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (field.settingPhase != compareField->settingPhase) {
|
|
cout << "error - settingPhase differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (field.stoneMustBeRemoved != compareField->stoneMustBeRemoved) {
|
|
cout << "error - stoneMustBeRemoved differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
for (i = 0; i < field.size; i++) {
|
|
if (field.board[i] != compareField->board[i]) {
|
|
cout << "error - board[] differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (field.warnings[i] != compareField->warnings[i]) {
|
|
cout << "error - warnings[] differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (field.stonePartOfMill[i] != compareField->stonePartOfMill[i]) {
|
|
cout << "error - stonePart[] differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
if (field.connectedSquare[i][j] != compareField->connectedSquare[i][j]) {
|
|
cout << "error - connectedSquare[] differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
// if (board.stoneMoveAble[i][j] != compareField->stoneMoveAble[i][j]) { cout << "error - stoneMoveAble differs!" << endl; ret = false; }
|
|
|
|
if (field.neighbour[i][j / 2][j % 2] != compareField->neighbour[i][j / 2][j % 2]) {
|
|
cout << "error - neighbour differs!" << endl;
|
|
ret = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: comparePlayers()
|
|
// Desc: Compares the two passed players and returns false if they differ.
|
|
//-----------------------------------------------------------------------------
|
|
bool Mill::comparePlayers(Player *playerA, Player *playerB)
|
|
{
|
|
// unsigned int i;
|
|
bool ret = true;
|
|
|
|
if (playerA->numStonesMissing != playerB->numStonesMissing) {
|
|
cout << "error - numStonesMissing differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (playerA->numStones != playerB->numStones) {
|
|
cout << "error - numStones differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (playerA->id != playerB->id) {
|
|
cout << "error - id differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (playerA->warning != playerB->warning) {
|
|
cout << "error - warning differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
if (playerA->numPossibleMoves != playerB->numPossibleMoves) {
|
|
cout << "error - numPossibleMoves differs!" << endl;
|
|
ret = false;
|
|
}
|
|
|
|
// for (i=0; i<MAX_NUM_POS_MOVES; i++) if (playerA->posFrom[i] = playerB->posFrom[i]) return false;
|
|
// for (i=0; i<MAX_NUM_POS_MOVES; i++) if (playerA->posTo [i] = playerB->posTo [i]) return false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: printBoard()
|
|
// Desc: Calls the printBoard() function of the current board.
|
|
// Prints the current game state on the screen.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::printBoard()
|
|
{
|
|
field.printBoard();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: undoMove()
|
|
// Desc: Sets the initial board as the current one and apply all (minus one) moves from the move history.
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::undoMove(void)
|
|
{
|
|
// locals
|
|
unsigned int *moveLogFrom_bak = new unsigned int[movesDone];
|
|
unsigned int *moveLogTo_bak = new unsigned int[movesDone];
|
|
unsigned int movesDone_bak = movesDone;
|
|
unsigned int i;
|
|
|
|
// at least one move must be done
|
|
if (movesDone) {
|
|
|
|
// make backup of log
|
|
for (i = 0; i < movesDone; i++) {
|
|
moveLogFrom_bak[i] = moveLogFrom[i];
|
|
moveLogTo_bak[i] = moveLogTo[i];
|
|
}
|
|
|
|
// reset
|
|
initialField.copyBoard(&field);
|
|
winner = 0;
|
|
movesDone = 0;
|
|
|
|
// and play again
|
|
for (i = 0; i < movesDone_bak - 1; i++) {
|
|
doMove(moveLogFrom_bak[i], moveLogTo_bak[i]);
|
|
}
|
|
}
|
|
|
|
// free mem
|
|
delete[] moveLogFrom_bak;
|
|
delete[] moveLogTo_bak;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Name: calcNumberOfRestingStones()
|
|
// Desc:
|
|
//-----------------------------------------------------------------------------
|
|
void Mill::calcNumberOfRestingStones(int &numWhiteStonesResting, int &numBlackStonesResting)
|
|
{
|
|
if (getCurrentPlayer() == fieldStruct::playerTwo) {
|
|
numWhiteStonesResting = fieldStruct::numStonesPerPlayer - field.curPlayer->numStonesMissing - field.curPlayer->numStones;
|
|
numBlackStonesResting = fieldStruct::numStonesPerPlayer - field.oppPlayer->numStonesMissing - field.oppPlayer->numStones;
|
|
} else {
|
|
numWhiteStonesResting = fieldStruct::numStonesPerPlayer - field.oppPlayer->numStonesMissing - field.oppPlayer->numStones;
|
|
numBlackStonesResting = fieldStruct::numStonesPerPlayer - field.curPlayer->numStonesMissing - field.curPlayer->numStones;
|
|
}
|
|
} |