scummvm/engines/groovie/cell.cpp
Eugene Sandulenko 3f6159a968 Proper implementation of microscope puzzle.
svn-id: r42634
2009-07-20 22:15:37 +00:00

799 lines
18 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
#include "groovie/cell.h"
namespace Groovie {
CellGame::CellGame() {
_startX = _startY = _endX = _endY = 255;
_stack_index = _boardStackPtr = 0;
_flag4 = false;
_flag2 = false;
_coeff3 = 0;
_moveCount = 0;
}
byte CellGame::getStartX() {
if (_startX > BOARDSIZE) {
warning ("CellGame::getStartX: not calculated yet (%d)!", _startX);
return 0;
} else {
return _startX;
}
}
byte CellGame::getStartY() {
if (_startY > BOARDSIZE) {
warning ("CellGame::getStartY: not calculated yet (%d)!", _startY);
return 6;
} else {
return _startY;
}
}
byte CellGame::getEndX() {
if (_endX > BOARDSIZE) {
warning ("CellGame::getEndX: not calculated yet (%d)!", _endX);
return 1;
} else {
return _endX;
}
}
byte CellGame::getEndY() {
if (_endY > BOARDSIZE) {
warning ("CellGame::getEndY: not calculated yet (%d)!", _endY);
return 6;
} else {
return _endY;
}
}
CellGame::~CellGame() {
}
const int8 possibleMoves[][9] = {
{ 1, 7, 8, -1 },
{ 0, 2, 7, 8, 9, -1 },
{ 1, 3, 8, 9, 10, -1 },
{ 2, 4, 9, 10, 11, -1 },
{ 3, 5, 10, 11, 12, -1 },
{ 4, 6, 11, 12, 13, -1 }, // 5
{ 5, 12, 13, -1 },
{ 0, 1, 8, 14, 15, -1 },
{ 0, 1, 2, 7, 9, 14, 15, 16, -1 },
{ 1, 2, 3, 8, 10, 15, 16, 17, -1 },
{ 2, 3, 4, 9, 11, 16, 17, 18, -1 }, // 10
{ 3, 4, 5, 10, 12, 17, 18, 19, -1 },
{ 4, 5, 6, 11, 13, 18, 19, 20, -1 },
{ 5, 6, 12, 19, 20, -1 },
{ 7, 8, 15, 21, 22, -1 },
{ 7, 8, 9, 14, 16, 21, 22, 23, -1 }, // 15
{ 8, 9, 10, 15, 17, 22, 23, 24, -1 },
{ 9, 10, 11, 16, 18, 23, 24, 25, -1 },
{ 10, 11, 12, 17, 19, 24, 25, 26, -1 },
{ 11, 12, 13, 18, 20, 25, 26, 27, -1 },
{ 12, 13, 19, 26, 27, -1 }, // 20
{ 14, 15, 22, 28, 29, -1 },
{ 14, 15, 16, 21, 23, 28, 29, 30, -1 },
{ 15, 16, 17, 22, 24, 29, 30, 31, -1 },
{ 16, 17, 18, 23, 25, 30, 31, 32, -1 },
{ 17, 18, 19, 24, 26, 31, 32, 33, -1 }, // 25
{ 18, 19, 20, 25, 27, 32, 33, 34, -1 },
{ 19, 20, 26, 33, 34, -1 },
{ 21, 22, 29, 35, 36, -1 },
{ 21, 22, 23, 28, 30, 35, 36, 37, -1 },
{ 22, 23, 24, 29, 31, 36, 37, 38, -1 }, // 30
{ 23, 24, 25, 30, 32, 37, 38, 39, -1 },
{ 24, 25, 26, 31, 33, 38, 39, 40, -1 },
{ 25, 26, 27, 32, 34, 39, 40, 41, -1 },
{ 26, 27, 33, 40, 41, -1 },
{ 28, 29, 36, 42, 43, -1 }, // 35
{ 28, 29, 30, 35, 37, 42, 43, 44, -1 },
{ 29, 30, 31, 36, 38, 43, 44, 45, -1 },
{ 30, 31, 32, 37, 39, 44, 45, 46, -1 },
{ 31, 32, 33, 38, 40, 45, 46, 47, -1 },
{ 32, 33, 34, 39, 41, 46, 47, 48, -1 }, // 40
{ 33, 34, 40, 47, 48, -1 },
{ 35, 36, 43, -1 },
{ 35, 36, 37, 42, 44, -1 },
{ 36, 37, 38, 43, 45, -1 },
{ 37, 38, 39, 44, 46, -1 }, // 45
{ 38, 39, 40, 45, 47, -1 },
{ 39, 40, 41, 46, 48, -1 },
{ 40, 41, 47, -1 }
};
const int8 strategy2[][17] = {
{ 2, 9, 14, 15, 16, -1 },
{ 3, 10, 14, 15, 16, 17, -1 },
{ 0, 4, 7, 11, 14, 15, 16, 17, 18, -1 },
{ 1, 5, 8, 12, 15, 16, 17, 18, 19, -1 },
{ 2, 6, 9, 13, 16, 17, 18, 19, 20, -1 },
{ 3, 10, 17, 18, 19, 20, -1 }, // 5
{ 4, 11, 18, 19, 20, -1 },
{ 2, 9, 16, 21, 22, 23, -1 },
{ 3, 10, 17, 21, 22, 23, 24, -1 },
{ 0, 4, 7, 11, 14, 18, 21, 22, 23, 24, 25, -1 },
{ 1, 5, 8, 12, 15, 19, 22, 23, 24, 25, 26, -1 }, // 10
{ 2, 6, 9, 13, 16, 20, 23, 24, 25, 26, 27, -1 },
{ 3, 10, 17, 24, 25, 26, 27, -1 },
{ 4, 11, 18, 25, 26, 27, -1 },
{ 0, 1, 2, 9, 16, 23, 28, 29, 30, -1 },
{ 0, 1, 2, 3, 10, 17, 24, 28, 29, 30, 31, -1 }, // 15
{ 0, 1, 2, 3, 4, 7, 11, 14, 18, 21, 25, 28, 29, 30, 31, 32, -1 },
{ 1, 2, 3, 4, 5, 8, 12, 15, 19, 22, 26, 29, 30, 31, 32, 33, -1 },
{ 2, 3, 4, 5, 6, 9, 13, 16, 20, 23, 27, 30, 31, 32, 33, 34, -1 },
{ 3, 4, 5, 6, 10, 17, 24, 31, 32, 33, 34, -1 },
{ 4, 5, 6, 11, 18, 25, 32, 33, 34, -1 }, // 20
{ 7, 8, 9, 16, 23, 30, 35, 36, 37, -1 },
{ 7, 8, 9, 10, 17, 24, 31, 35, 36, 37, 38, -1 },
{ 7, 8, 9, 10, 11, 14, 18, 21, 25, 28, 32, 35, 36, 37, 38, 39, -1 },
{ 8, 9, 10, 11, 12, 15, 19, 22, 26, 29, 33, 36, 37, 38, 39, 40, -1 },
{ 9, 10, 11, 12, 13, 16, 20, 23, 27, 30, 34, 37, 38, 39, 40, 41, -1 }, // 25
{ 10, 11, 12, 13, 17, 24, 31, 38, 39, 40, 41, -1 },
{ 11, 12, 13, 18, 25, 32, 39, 40, 41, -1 },
{ 14, 15, 16, 23, 30, 37, 42, 43, 44, -1 },
{ 14, 15, 16, 17, 24, 31, 38, 42, 43, 44, 45, -1 },
{ 14, 15, 16, 17, 18, 21, 25, 28, 32, 35, 39, 42, 43, 44, 45, 46, -1 }, // 30
{ 15, 16, 17, 18, 19, 22, 26, 29, 33, 36, 40, 43, 44, 45, 46, 47, -1 },
{ 16, 17, 18, 19, 20, 23, 27, 30, 34, 37, 41, 44, 45, 46, 47, 48, -1 },
{ 17, 18, 19, 20, 24, 31, 38, 45, 46, 47, 48, -1 },
{ 18, 19, 20, 25, 32, 39, 46, 47, 48, -1 },
{ 21, 22, 23, 30, 37, 44, -1 }, // 35
{ 21, 22, 23, 24, 31, 38, 45, -1 },
{ 21, 22, 23, 24, 25, 28, 32, 35, 39, 42, 46, -1 },
{ 22, 23, 24, 25, 26, 29, 33, 36, 40, 43, 47, -1 },
{ 23, 24, 25, 26, 27, 30, 34, 37, 41, 44, 48, -1 },
{ 24, 25, 26, 27, 31, 38, 45, -1 }, // 40
{ 25, 26, 27, 32, 39, 46, -1 },
{ 28, 29, 30, 37, 44, -1 },
{ 28, 29, 30, 31, 38, 45, -1 },
{ 28, 29, 30, 31, 32, 35, 39, 42, 46, -1 },
{ 29, 30, 31, 32, 33, 36, 40, 43, 47, -1 }, // 45
{ 30, 31, 32, 33, 34, 37, 41, 44, 48, -1 },
{ 31, 32, 33, 34, 38, 45, -1 },
{ 32, 33, 34, 39, 46, -1 }
};
void CellGame::copyToTempBoard() {
for (int i = 0; i < 53; ++i) {
_tempBoard[i] = _board[i];
}
}
void CellGame::copyFromTempBoard() {
for (int i = 0; i < 53; ++i) {
_board[i] = _tempBoard[i];
}
}
void CellGame::copyToShadowBoard() {
_board[53] = 0;
_board[55] = 1;
_board[56] = 0;
for (int i = 0; i < 49; ++i) {
_shadowBoard[i] = _board[i];
}
}
void CellGame::pushBoard() {
assert(_boardStackPtr < 57 * 9);
for (int i = 0; i < 57; ++i)
_boardStack[_boardStackPtr + i] = _board[i];
_boardStackPtr += 57;
}
void CellGame::popBoard() {
assert(_boardStackPtr > 0);
_boardStackPtr -= 57;
for (int i = 0; i < 57; ++i) {
_board[i] = _boardStack[_boardStackPtr + i];
}
}
void CellGame::pushShadowBoard() {
assert(_boardStackPtr < 57 * 9);
for (int i = 0; i < 57; ++i)
_boardStack[_boardStackPtr + i] = _shadowBoard[i];
_boardStackPtr += 57;
}
void CellGame::popShadowBoard() {
assert(_boardStackPtr > 0);
_boardStackPtr -= 57;
for (int i = 0; i < 57; ++i) {
_shadowBoard[i] = _boardStack[_boardStackPtr + i];
}
}
void CellGame::clearMoves() {
_stack_startXY[0] = _board[53];
_stack_endXY[0] = _board[54];
_stack_pass[0] = _board[55];
_stack_index = 1;
}
void CellGame::pushMove() {
_stack_startXY[_stack_index] = _board[53];
_stack_endXY[_stack_index] = _board[54];
_stack_pass[_stack_index] = _board[55];
_stack_index++;
}
void CellGame::resetMove() {
_board[53] = 0;
_board[54] = 0;
_board[55] = 0;
}
void CellGame::takeCells(uint16 whereTo, int8 color) {
int cellN;
const int8 *str;
str = possibleMoves[whereTo];
while (1) {
cellN = *str++;
if (cellN < 0)
break;
if (_tempBoard[cellN] > 0) {
--_tempBoard[_tempBoard[cellN] + 48];
_tempBoard[cellN] = color;
++_tempBoard[color + 48];
}
}
}
void CellGame::countAllCells() {
_board[49] = 0;
_board[50] = 0;
_board[51] = 0;
_board[52] = 0;
for (int i = 0; i < 49; i++) {
switch (_board[i]) {
case 1: // CELL_BLUE
_board[49]++;
break;
case 2: // CELL_GREEN
_board[50]++;
break;
case 3:
_board[51]++;
break;
case 4:
_board[52]++;
break;
}
}
}
int CellGame::countCellsOnTempBoard(int8 color) {
const int8 *str;
int res = 0;
int i;
for (i = 0; i < 49; i++)
_boardSum[i] = 0;
for (i = 0; i < 49; i++) {
if (_tempBoard[i] == color) {
for (str = possibleMoves[i]; *str > 0; str++) {
if (!_tempBoard[*str])
++_boardSum[*str];
}
}
}
for (i = 0; i < 49; i++)
res += _boardSum[i];
return res;
}
bool CellGame::canMoveFunc1(int8 color) {
const int8 *str;
if (_board[55] == 1) {
for (; _board[53] < 49; _board[53]++) {
if (_shadowBoard[_board[53]] == color) {
str = &possibleMoves[_board[53]][_board[56]];
for (;_board[56] < 8; _board[56]++) {
_board[54] = *str++;
if (_board[54] < 0)
break;
if (!_shadowBoard[_board[54]]) {
_shadowBoard[_board[54]] = -1;
++_board[56];
return true;
}
}
_board[56] = 0;
}
}
_board[53] = 0;
_board[55] = 2;
_board[56] = 0;
}
if (_board[55] == 2) {
for (; _board[53] < 49; _board[53]++) {
if (_shadowBoard[_board[53]] == color) {
str = &strategy2[_board[53]][_board[56]];
for (;_board[56] < 16; _board[56]++) {
_board[54] = *str++;
if (_board[54] < 0)
break;
if (!_board[_board[54]]) {
++_board[56];
return true;
}
}
_board[56] = 0;
}
}
}
return false;
}
bool CellGame::canMoveFunc3(int8 color) {
const int8 *str;
if (_board[55] == 1) {
for (; _board[53] < 49; _board[53]++) {
if (_shadowBoard[_board[53]] == color) {
str = &possibleMoves[_board[53]][_board[56]];
for (;_board[56] < 8; _board[56]++) {
_board[54] = *str++;
if (_board[54] < 0)
break;
if (!_shadowBoard[_board[54]]) {
_shadowBoard[_board[54]] = -1;
++_board[56];
return true;
}
}
_board[56] = 0;
}
}
_board[53] = 0;
_board[55] = 2;
_board[56] = 0;
for (int i = 0; i < 49; ++i)
_shadowBoard[i] = _board[i];
}
if (_board[55] == 2) {
for (; _board[53] < 49; _board[53]++) {
if (_shadowBoard[_board[53]] == color) {
str = &strategy2[_board[53]][_board[56]];
for (;_board[56] < 16; _board[56]++) {
_board[54] = *str++;
if (_board[54] < 0)
break;
if (!_shadowBoard[_board[54]]) {
_shadowBoard[_board[54]] = -1;
++_board[56];
return true;
}
}
_board[56] = 0;
}
}
}
return false;
}
bool CellGame::canMoveFunc2(int8 color) {
const int8 *str;
while (1) {
while (_board[_board[54]]) {
++_board[54];
if (_board[54] >= 49)
return false;
}
if (!_board[55]) {
str = possibleMoves[_board[54]];
while (1) {
_board[53] = *str++;
if (_board[53] < 0)
break;
if (_board[_board[53]] == color) {
_board[55] = 1;
return true;
}
}
_board[55] = 1;
}
if (_board[55] == 1) {
_board[55] = 2;
_board[56] = 0;
}
if (_board[55] == 2) {
str = &strategy2[_board[54]][_board[56]];
for (; _board[56] < 16; _board[56]++) {
_board[53] = *str++;
if (_board[53] < 0)
break;
if (_board[_board[53]] == color) {
++_board[56];
return true;
}
}
++_board[54];
_board[55] = 0;
if (_board[54] >= 49)
break;
}
}
return false;
}
void CellGame::makeMove(int8 color) {
copyToTempBoard();
_tempBoard[_board[54]] = color;
++_tempBoard[color + 48];
if (_board[55] == 2) {
_tempBoard[_board[53]] = 0;
--_tempBoard[color + 48];
}
takeCells(_board[54], color);
}
int CellGame::getBoardWeight(int8 color1, int8 color2) {
int8 celln;
const int8 *str;
byte cellCnt[8];
str = possibleMoves[_board[54]];
cellCnt[1] = _board[49];
cellCnt[2] = _board[50];
cellCnt[3] = _board[51];
cellCnt[4] = _board[52];
if (_board[55] != 2)
++cellCnt[color2];
celln = *str++;
celln = _board[celln];
if (celln > 0) {
--cellCnt[celln];
++cellCnt[color2];
}
celln = *str++;
celln = _board[celln];
if (celln > 0) {
--cellCnt[celln];
++cellCnt[color2];
}
celln = *str++;
celln = _board[celln];
if (celln > 0) {
--cellCnt[celln];
++cellCnt[color2];
}
while (1) {
celln = *str++;
if (celln < 0)
break;
celln = _board[celln];
if (celln > 0) {
--cellCnt[celln];
++cellCnt[color2];
}
}
return _coeff3 + 2 * (2 * cellCnt[color1] - cellCnt[1] - cellCnt[2] - cellCnt[3] - cellCnt[4]);
}
void CellGame::chooseBestMove(int8 color) {
int moveIndex = 0;
int curWeight;
int bestWeight;
if (_flag2) {
bestWeight = 32767;
for (int i = 0; i < _stack_index; ++i) {
_board[53] = _stack_startXY[i];
_board[54] = _stack_endXY[i];
_board[55] = _stack_pass[i];
makeMove(color);
curWeight = countCellsOnTempBoard(color);
if (curWeight <= bestWeight) {
if (curWeight < bestWeight)
moveIndex = 0;
bestWeight = curWeight;
_stack_startXY[moveIndex] = _board[53];
_stack_endXY[moveIndex] = _board[54];
_stack_pass[moveIndex++] = _board[55];
}
}
_stack_index = moveIndex;
}
_startX = _stack_startXY[0] % 7;
_startY = _stack_startXY[0] / 7;
_endX = _stack_endXY[0] % 7;
_endY = _stack_endXY[0] / 7;
}
int8 CellGame::calcBestWeight(int8 color1, int8 color2, uint16 depth, int bestWeight) {
int8 res;
int8 curColor;
bool canMove;
int type;
uint16 i;
int8 currBoardWeight;
int8 weight;
pushBoard();
copyFromTempBoard();
curColor = color2;
for (i = 0;; ++i) {
if (i >= 4) {
res = _coeff3 + 2 * (2 * _board[color1 + 48] - _board[49] - _board[50] - _board[51] - _board[52]);
popBoard();
return res;
}
++curColor;
if (curColor > 4)
curColor = 1;
if (_board[curColor + 48]) {
if (_board[curColor + 48] >= 49 - _board[49] - _board[50] - _board[51] - _board[52]) {
resetMove();
canMove = canMoveFunc2(curColor);
type = 1;
} else {
copyToShadowBoard();
if (depth == 1) {
canMove = canMoveFunc3(curColor);
type = 3;
} else {
canMove = canMoveFunc1(curColor);
type = 2;
}
}
if (canMove)
break;
}
}
if (_flag1) {
popBoard();
return bestWeight + 1;
}
depth -= 1;
if (depth) {
makeMove(curColor);
if (type == 1) {
res = calcBestWeight(color1, curColor, depth, bestWeight);
} else {
pushShadowBoard();
res = calcBestWeight(color1, curColor, depth, bestWeight);
popShadowBoard();
}
} else {
res = getBoardWeight(color1, curColor);
}
if (res < bestWeight && color1 != curColor || _flag4) {
popBoard();
return res;
}
currBoardWeight = _coeff3 + 2 * (2 * _board[color1 + 48] - _board[49] - _board[50] - _board[51] - _board[52]);
while (1) {
if (type == 1) {
canMove = canMoveFunc2(curColor);
} else if (type == 2) {
canMove = canMoveFunc1(curColor);
} else {
canMove = canMoveFunc3(curColor);
}
if (!canMove)
break;
if (_flag1) {
popBoard();
return bestWeight + 1;
}
if (_board[55] == 2) {
if (getBoardWeight(color1, curColor) == currBoardWeight)
continue;
}
if (!depth) {
weight = getBoardWeight(color1, curColor);
if (type == 1) {
if (_board[55] == 2)
_board[56] = 16;
}
} else {
makeMove(curColor);
if (type != 1) {
pushShadowBoard();
weight = calcBestWeight(color1, curColor, depth, bestWeight);
popShadowBoard();
} else {
weight = calcBestWeight(color1, curColor, depth, bestWeight);
}
}
if ((weight < res && color1 != curColor) || (weight > res && color1 == curColor))
res = weight;
if ((res < bestWeight && color1 != curColor) || _flag4)
break;
}
popBoard();
return res;
}
int16 CellGame::doGame(int8 color, int depth) {
bool canMove;
int type;
int8 currBoardWeight;
int8 w1;
int8 w2;
countAllCells();
if (_board[color + 48] >= 49 - _board[49] - _board[50] - _board[51] - _board[52]) {
resetMove();
canMove = canMoveFunc2(color);
type = true;
} else {
copyToShadowBoard();
canMove = canMoveFunc1(color);
type = false;
}
if (canMove) {
if (_board[color + 48] - _board[49] - _board[50] - _board[51] - _board[52] == 0)
depth = 0;
_coeff3 = 0;
if (_board[55] == 1)
_coeff3 = 1;
clearMoves();
if (depth) {
makeMove(color);
_flag4 = false;
if (type) {
w2 = calcBestWeight(color, color, depth, -127);
} else {
pushShadowBoard();
w2 = calcBestWeight(color, color, depth, -127);
popShadowBoard();
}
} else {
w2 = getBoardWeight(color, color);
}
currBoardWeight = 2 * (2 * _board[color + 48] - _board[49] - _board[50] - _board[51] - _board[52]);
while (1) {
if (type)
canMove = canMoveFunc2(color);
else
canMove = canMoveFunc1(color);
if (!canMove)
break;
if (_flag1)
break;
_coeff3 = 0;
if (_board[55] == 2) {
if (getBoardWeight(color, color) == currBoardWeight)
continue;
}
if (_board[55] == 1)
_coeff3 = 1;
if (depth) {
makeMove(color);
_flag4 = false;
if (type) {
w1 = calcBestWeight(color, color, depth, w2);
} else {
pushShadowBoard();
w1 = calcBestWeight(color, color, depth, w2);
popShadowBoard();
}
} else {
w1 = getBoardWeight(color, color);
}
if (w1 == w2)
pushMove();
if (w1 > w2) {
clearMoves();
w2 = w1;
}
}
chooseBestMove(color);
return 1;
}
return 0;
}
const int8 depths[] = { 1, 1, 1, 2, 1, 1, 2, 2, 1, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 3, 3 };
int16 CellGame::calcMove(int8 color, uint16 depth) {
int result;
_flag1 = false;
++_moveCount;
if (depth) {
if (depth == 1) {
_flag2 = true;
result = doGame(color, 0);
} else {
int newDepth;
newDepth = depths[3 * (depth - 2) + _moveCount % 3];
_flag2 = true;
if (newDepth >= 20) {
assert(0); // This branch is not implemented
} else {
result = doGame(color, newDepth);
}
}
} else {
_flag2 = false;
result = doGame(color, depth);
}
return result;
}
int CellGame::playStauf(byte color, uint16 depth, byte *scriptBoard) {
int i;
for (i = 0; i < 49; i++, scriptBoard++) {
_board[i] = 0;
if (*scriptBoard == 50)
_board[i] = 1;
if (*scriptBoard == 66)
_board[i] = 2;
}
for (i = 49; i < 57; i++)
_board[i] = 0;
return calcMove(color, depth);
}
} // End of Groovie namespace