1759 lines
56 KiB
C++
1759 lines
56 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "bagel/hodjnpodj/hnplibs/stdafx.h"
|
|
#include "bagel/hodjnpodj/mankala/mnk.h"
|
|
#include "bagel/hodjnpodj/hnplibs/gamedll.h"
|
|
#include "bagel/boflib/misc.h"
|
|
|
|
namespace Bagel {
|
|
namespace HodjNPodj {
|
|
namespace Mankala {
|
|
|
|
extern LPGAMESTRUCT pGameParams; // declared in mnk.cpp.
|
|
|
|
bool gbTurnSoundsOff; // used by mnkui.cpp too.
|
|
///DEFS mnk.h
|
|
|
|
static inline void DebugBreak() {
|
|
}
|
|
|
|
//* CMnk::InitMankala -- initialize a new game of Mankala
|
|
bool CMnk::InitMankala()
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::InitMankala) ;
|
|
int iError = 0 ; // error code
|
|
int iPlayer, iPit ; // loop variables
|
|
CMove * xpcMove = &m_cCurrentMove ; // pointer to current
|
|
// position move object
|
|
CPit * xpcPit ; // pointer to pit object
|
|
|
|
xpcMove->Zero() ; // clear all data fields in current move/pos object
|
|
xpcMove->m_bRealMove = true ; // but make this one the real one
|
|
// m_iPlayer = m_iNumberMoves = 0 ;
|
|
for (iPlayer = 0 ; iPlayer < NUMPLAYERS ; ++iPlayer)
|
|
for (iPit = -2 ; iPit < NUMPITS ; ++iPit) {
|
|
xpcPit = m_xpcPits[iPlayer][iPit + 2] ;
|
|
// point to pit object
|
|
if (xpcPit->m_iPlayer != iPlayer ||
|
|
xpcPit->m_iPit != iPit)
|
|
// validity checking
|
|
{
|
|
//iError = 100 ;
|
|
//goto cleanup ;
|
|
}
|
|
// xpcPit->m_iPlayer = iPlayer ;
|
|
// xpcPit->m_iPit = iPit ;
|
|
xpcPit->m_iNumStones =
|
|
xpcMove->m_iNumStones[iPlayer][iPit + 2] =
|
|
(iPit < 0) ? 0 // no stones in home/hand
|
|
: m_iStartStones ;
|
|
}
|
|
|
|
/*cleanup */
|
|
|
|
JXELEAVE(CMnk::InitMankala) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::Move -- make a move
|
|
bool CMnk::Move(CPit * xpcSowPit, CMove * xpcMove)
|
|
// xpcSowPit -- ptr to pit object whose stones are being sowed
|
|
// xpcMove -- move object for position to start from; if nullptr,
|
|
// then use the move object for current position on
|
|
// the board, and make the actual moves on the board
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::Move) ;
|
|
|
|
int iError = 0 ; // error code
|
|
int iNumStones ; // number of stones to be sowed
|
|
int iStone ; // loop variable
|
|
bool bStonesFound,
|
|
bOtherStonesFound ; // flag: stones found, game not over
|
|
int iTemp ; // temp variable for swapping players
|
|
int iPlayer = xpcSowPit->m_iPlayer, iPit = xpcSowPit->m_iPit ;
|
|
int iOtherPlayer = OTHERPLAYER(iPlayer) ; // other player (0 or 1)
|
|
int iTempPlayer;
|
|
CPit * xpcHandPit = m_xpcPits[iPlayer][HANDINDEX + 2] ; // hand pit
|
|
CPit * xpcHomePit = m_xpcPits[iPlayer][HOMEINDEX + 2] ; // home bin
|
|
CPit * xpcPit ; // target pit
|
|
// bool B;
|
|
MSG msg;
|
|
|
|
//_asm int 3; //to track GPf
|
|
if (!xpcMove) // if no starting position specified
|
|
xpcMove = &m_cCurrentMove ; // then it's a board move
|
|
|
|
iNumStones = xpcMove->m_iNumStones
|
|
[xpcMove->m_iPlayer][xpcSowPit->m_iPit + 2] ;
|
|
// get # stones being moved
|
|
if (iNumStones <= 0) { // if no stones
|
|
iError = 100 ; // attempt to sow an empty pit
|
|
goto cleanup ;
|
|
}
|
|
|
|
xpcMove->m_xpcPit = xpcSowPit ; // save ptr to pit being sowed
|
|
xpcMove->m_bFreeTurn = xpcMove->m_bCapture = false ; //reset FreeTurn, Capture indicators.
|
|
|
|
if (xpcMove->m_bRealMove && m_bDumpMoves)
|
|
DumpPosition(xpcMove) ;
|
|
|
|
|
|
|
|
/* move the stones from the bin to the hand */
|
|
|
|
if (pGameParams->bSoundEffectsEnabled) sndPlaySound(PICKUP, SND_ASYNC);
|
|
|
|
for (iStone = iNumStones ; iStone-- > 0 ;)
|
|
|
|
if (MoveStone(xpcMove, xpcSowPit, xpcHandPit)) {
|
|
iError = 101 ; // internal error moving stone
|
|
goto cleanup ;
|
|
}
|
|
|
|
/*
|
|
move stones from the hands to the individual pits
|
|
*/
|
|
for (iStone = iNumStones ; iStone-- > 0 ;) {
|
|
if (iPit < 0 || (iPit == 0 && iPlayer !=
|
|
xpcMove->m_xpcPit->m_iPlayer))
|
|
// if last iteration placed a stone in my home bin
|
|
// or in my opponent's last pit
|
|
iPit = NUMPITS - 1, iTemp = iPlayer, iPlayer = iOtherPlayer, iOtherPlayer = iTemp ;
|
|
// new pit is last pit on other side -- swap
|
|
// player and other player
|
|
else
|
|
--iPit ; // otherwise, just decrement to get pit #
|
|
|
|
xpcPit = m_xpcPits[iPlayer][iPit + 2] ; // pt to target pit
|
|
|
|
if (pGameParams->bSoundEffectsEnabled) sndPlaySound(ONESHELL, SND_ASYNC);
|
|
|
|
if (MoveStone(xpcMove, xpcHandPit, xpcPit))
|
|
// move stone from hand to target pit, test for error
|
|
{
|
|
iError = 102 ;
|
|
goto cleanup ;
|
|
}
|
|
}
|
|
|
|
if (iPit < 0) // if my last move was into my home bin
|
|
xpcMove->m_bFreeTurn = true ; // I get a free turn
|
|
|
|
else if (iPlayer == xpcMove->m_xpcPit->m_iPlayer &&
|
|
xpcMove->m_iNumStones[iPlayer][iPit + 2] == 1) {
|
|
// if the last stone went into a previously empty pit
|
|
|
|
if (pGameParams->bSoundEffectsEnabled) sndPlaySound(PICKUP, SND_ASYNC);
|
|
|
|
xpcMove->m_bCapture = true ; // we have a capture
|
|
xpcMove->m_iCapturePit = NUMPITS - 1 - iPit ;
|
|
// number of opponent's pit being captured
|
|
xpcPit = m_xpcPits[iOtherPlayer][xpcMove->m_iCapturePit + 2] ;
|
|
// point to pit object for captured pit
|
|
|
|
iNumStones = xpcMove->m_iNumStones[iOtherPlayer][xpcMove->m_iCapturePit + 2] ;
|
|
|
|
while (iNumStones-- > 0) // count stones
|
|
MoveStone(xpcMove, xpcPit, xpcHomePit) ;
|
|
// move stone from captured pit to my home bin pit
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!xpcMove->m_bFreeTurn) // if there's no free turn
|
|
xpcMove->m_iPlayer = OTHERPLAYER(xpcMove->m_iPlayer) ;
|
|
// then other player's turn
|
|
iPlayer = xpcMove->m_iPlayer ;
|
|
iOtherPlayer = OTHERPLAYER(iPlayer) ;
|
|
|
|
|
|
// check whether there are any moves to be played now -- if not,
|
|
// the game is over
|
|
bStonesFound = false ; // no stones found yet
|
|
bOtherStonesFound = false;
|
|
for (iPit = 0 ; iPit < NUMPITS ; iPit++) {
|
|
bStonesFound = bStonesFound || (xpcMove->m_iNumStones[iPlayer][iPit + 2] > 0) ;
|
|
|
|
bOtherStonesFound = bOtherStonesFound || xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][iPit + 2];
|
|
}
|
|
|
|
|
|
while (MFC::PeekMessage(&msg, nullptr, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE)); //flush out pending mouse clicks
|
|
|
|
// test for stones in this pit
|
|
if (pGameParams->bSoundEffectsEnabled && !gbTurnSoundsOff && bStonesFound && bOtherStonesFound) {
|
|
if (MFC::PeekMessage(&msg, nullptr, MM_MCINOTIFY, MM_MCINOTIFY, PM_REMOVE)) {
|
|
MFC::TranslateMessage(&msg);
|
|
MFC::DispatchMessage(&msg);
|
|
}
|
|
|
|
if (!xpcMove->m_bFreeTurn)
|
|
sndPlaySound(iPlayer ? IGO3 : YOUGO3, SND_SYNC);
|
|
else
|
|
sndPlaySound(iPlayer ? IGOAGAIN : GOAGAIN, SND_SYNC);
|
|
|
|
|
|
}
|
|
//#endif
|
|
|
|
|
|
if ((m_bGameOver = (!bStonesFound || !bOtherStonesFound))) { // game is over if
|
|
// no stones found -- tally the results
|
|
/*
|
|
depending upon who has no stones left,
|
|
for each of the other player's pits, transfer the stones
|
|
to the corresponding home bin
|
|
*/
|
|
iTempPlayer = !bStonesFound ? iOtherPlayer : iPlayer;
|
|
xpcHomePit = m_xpcPits[ iTempPlayer][-1 + 2] ; // home bin
|
|
for (iPit = 0 ; iPit < NUMPITS ; iPit++) {
|
|
xpcPit = m_xpcPits[iTempPlayer][iPit + 2] ;
|
|
iNumStones = xpcMove->m_iNumStones[iTempPlayer][iPit + 2] ;
|
|
// get number of stones
|
|
while (iNumStones-- > 0) // count stones
|
|
MoveStone(xpcMove,
|
|
(CPitWnd *)xpcPit, (CPitWnd *)xpcHomePit) ;
|
|
// move stone from pit to my home bin
|
|
}
|
|
} // ... winner is the one with more stones in home bin
|
|
|
|
|
|
|
|
|
|
if (xpcMove->m_bRealMove && m_bDumpMoves)
|
|
DumpPosition(xpcMove) ;
|
|
|
|
|
|
#define DECENT_CODE 0
|
|
#if DECENT_CODE //"THE FOLLOWING SECTION OF CODE IS RUINED"...(nish)
|
|
CMove* xpcStoreMove;
|
|
int iMoveIndex;
|
|
|
|
if (xpcMove->m_bRealMove) { // if it's real move
|
|
iMoveIndex = m_iNumberMoves++ ; // get move index, incr count
|
|
xpcStoreMove = m_cMoveList + iMoveIndex ;
|
|
// point to correct slot
|
|
xpcStoreMove->Copy(xpcMove) ; // copy
|
|
}
|
|
#endif
|
|
|
|
cleanup:
|
|
JXELEAVE(CMnk::Move) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
|
|
//* CMnk::MoveStone -- move one stone for move
|
|
bool CMnk::MoveStone(CMove * xpcMove,
|
|
CPit * xpcFromPit, CPit * xpcToPit)
|
|
// xpcMove -- position/move object in which move takes place
|
|
// xpcFromPit -- source pit (where stone comes from)
|
|
// xpcToPit -- target pit (where stone goes)
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::MoveStone) ;
|
|
int iError = 0 ; // error code
|
|
|
|
--xpcMove->m_iNumStones[xpcFromPit->m_iPlayer]
|
|
[xpcFromPit->m_iPit + 2] ;
|
|
|
|
if (xpcMove->m_bRealMove) // if this is a real move
|
|
((CMnkWindow *)this)->MoveStoneDisplay((CPitWnd *)xpcFromPit,
|
|
(CPitWnd *)xpcToPit) ;
|
|
// move display of stone from one pit to another
|
|
++xpcMove->m_iNumStones[xpcToPit->m_iPlayer][xpcToPit->m_iPit + 2] ;
|
|
/*
|
|
MSG msg;
|
|
if(MFC::PeekMessage(&msg,nullptr,MM_MCINOTIFY, MM_MCINOTIFY,PM_REMOVE)){
|
|
MFC::TranslateMessage(&msg);
|
|
MFC::DispatchMessage(&msg);
|
|
}
|
|
*/
|
|
// cleanup:
|
|
|
|
JXELEAVE(CMnk::MoveStone) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::InitData -- initialize data class object
|
|
bool CMnk::InitData(bool bInit)
|
|
// bInit -- if false, release data
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::InitData) ;
|
|
int iError = 0 ; // error code
|
|
// long lTableSize ; // size of lookup table
|
|
long lK ; // loop variable
|
|
int iJ ; // loop variable
|
|
byte *hpcTab; // table pointer for initialization
|
|
byte *xpcFive; // ptr to Five structure
|
|
struct FIVE stFive = {TABLEUNDEF, // 0
|
|
TABLEUNDEF >> 2, TABLEUNDEF & 3, TABLEUNDEF, // 1-2
|
|
TABLEUNDEF >> 4, TABLEUNDEF & 15, // 3
|
|
TABLEUNDEF >> 1, TABLEUNDEF & 1, TABLEUNDEF, // 4-5
|
|
TABLEUNDEF >> 3, TABLEUNDEF & 7, TABLEUNDEF
|
|
} ; // 6-7
|
|
|
|
if (bInit) { // if we're initializing
|
|
if (m_iTableStones > MAXTABLESTONES) // parm set too high
|
|
m_iTableStones = MAXTABLESTONES ;
|
|
|
|
if (!(m_lpCMnkData = new FAR CMnkData)) {
|
|
iError = 100 ; // can't allocate CMnkData
|
|
goto cleanup ;
|
|
}
|
|
if ((iError = CountConfigurations()))
|
|
goto cleanup ;
|
|
|
|
m_lNumConfigs = m_lpCMnkData->m_NA[m_iTableStones][TOTALPITS] ;
|
|
|
|
// m_lTableSize = (MAXCONFIGS/8 + 1)*5 ; // table length in bytes
|
|
m_lTableSize = (m_lNumConfigs / 8 + 2) * 5 ; // table length in bytes
|
|
|
|
if (!(m_lpCMnkData->m_hBestWin = GlobalAlloc(0, m_lTableSize)))
|
|
// allocate global handle and test
|
|
{
|
|
iError = 110 ; // Windows GlobalAlloc failure
|
|
goto cleanup ;
|
|
}
|
|
if (!(m_lpCMnkData->m_hpcBestWin
|
|
= (byte *)GlobalLock(m_lpCMnkData->m_hBestWin))) {
|
|
iError = 111 ; // Windows GlobalLock failure
|
|
goto cleanup ;
|
|
}
|
|
|
|
#if 0
|
|
if (!(m_lpCMnkData->m_hpcBestWin =
|
|
new byte[m_lTableSize + 8])) {
|
|
iError = 101 ; // can't allocate best win array
|
|
goto cleanup ;
|
|
}
|
|
#endif
|
|
|
|
if (m_bInitData) { // if we're initializing data tables
|
|
// init best win table to all undefined values
|
|
for (lK = m_lTableSize / sizeof(struct FIVE),
|
|
hpcTab = m_lpCMnkData->m_hpcBestWin ; lK-- > 0 ;)
|
|
for (iJ = sizeof(struct FIVE),
|
|
xpcFive = (byte *)&stFive ; iJ-- > 0 ;)
|
|
*hpcTab++ = *xpcFive++ ;
|
|
|
|
PopulateTable() ; // populate the best win table
|
|
WriteTableFile() ; // write best win table to disk
|
|
} else { // initialized data is already available
|
|
//ReadTableFile() ; // read best win table from disk
|
|
}
|
|
}
|
|
|
|
else { // we're releasing table
|
|
if (m_lpCMnkData) { // if data table locked
|
|
GlobalUnlock(m_lpCMnkData->m_hBestWin) ; // unlock it
|
|
m_lpCMnkData = nullptr ; // clear pointer
|
|
}
|
|
if (m_lpCMnkData->m_hBestWin) // if allocated
|
|
GlobalFree(m_lpCMnkData->m_hBestWin),
|
|
m_lpCMnkData->m_hBestWin = nullptr ;
|
|
// free it
|
|
#if 0
|
|
if (m_lpCMnkData) { // if data table allocated
|
|
if (m_lpCMnkData->m_hpcBestWin)
|
|
// if there's a best win table
|
|
delete m_lpCMnkData->m_hpcBestWin,
|
|
m_lpCMnkData->m_hpcBestWin = nullptr ;
|
|
delete m_lpCMnkData ;
|
|
m_lpCMnkData = nullptr ;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//delete m_lpCMnkData ;
|
|
//m_lpCMnkData = nullptr ;
|
|
|
|
cleanup:
|
|
|
|
JXELEAVE(CMnk::InitData) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::CountConfigurations -- set up Configurations table
|
|
bool CMnk::CountConfigurations()
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::CountConfigurations) ;
|
|
int iError = 0 ; // error code
|
|
int iStone, iPit, iK ; // loop variables
|
|
|
|
for (iStone = 0 ; iStone <= m_iTableStones ; ++iStone)
|
|
m_lpCMnkData->m_NX[iStone][1] = 1 ; // if there's only one
|
|
// pit, then there's only one configuration
|
|
|
|
for (iPit = 2 ; iPit <= TOTALPITS ; ++iPit)
|
|
for (iStone = 0 ; iStone <= m_iTableStones ; ++iStone) {
|
|
m_lpCMnkData->m_NX[iStone][iPit] = 0 ;
|
|
for (iK = 0 ; iK <= iStone ; ++iK)
|
|
m_lpCMnkData->m_NX[iStone][iPit] +=
|
|
m_lpCMnkData->m_NX[iK][iPit - 1] ;
|
|
}
|
|
|
|
for (iPit = 1 ; iPit <= TOTALPITS ; ++iPit)
|
|
for (iStone = 0 ; iStone <= m_iTableStones ; ++iStone) {
|
|
m_lpCMnkData->m_NA[iStone][iPit] = 0 ;
|
|
for (iK = 0 ; iK <= iStone ; ++iK)
|
|
m_lpCMnkData->m_NA[iStone][iPit] +=
|
|
m_lpCMnkData->m_NX[iK][iPit] ;
|
|
}
|
|
|
|
// cleanup:
|
|
|
|
JXELEAVE(CMnk::CountConfigurations) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::PopulateTable -- compute values for best win table
|
|
bool CMnk::PopulateTable() {
|
|
JXENTER(CMnk::PopulateTable) ;
|
|
int iError = 0 ; // error code
|
|
uint lConfigIndex ; // loop variable
|
|
CMove cMove ; // move structure
|
|
|
|
m_iCurrentMaxDepth = 3 ; // minimax depth is 3 for table populate
|
|
m_iCurrentCapDepth = 0 ; // capture depth is 0
|
|
|
|
for (lConfigIndex = 1 ; lConfigIndex < MAXCONFIGS - 1 ; ++lConfigIndex) {
|
|
cMove.Zero() ; // clear move/position object
|
|
cMove.m_lConfigIndex = lConfigIndex ; // store into move object
|
|
if (UnmapConfiguration(&cMove)) // translate configuration index
|
|
// into a configuration, test for error
|
|
{
|
|
iError = 100 ; // UnmapConfiguration error
|
|
goto cleanup ;
|
|
}
|
|
if (Minimax(&cMove)) { // find move, and test for error
|
|
iError = 101 ; // Minimax error
|
|
goto cleanup ;
|
|
}
|
|
if (SetBestWinCount(&cMove)) {
|
|
iError = 102 ; // SetBestWinCount error
|
|
goto cleanup ;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (iError)
|
|
iError += 100 ;
|
|
|
|
JXELEAVE(CMnk::PopulateTable) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
bool CMnk::WriteTableFile() {
|
|
int iError = 0; // error code
|
|
JXENTER(CMnk::WriteTableFile);
|
|
|
|
Common::strcpy_s(m_lpCMnkData->m_cFileHeader.m_szText,
|
|
"Mankala data file, version 1.0, "
|
|
"May, 1994, by John J. Xenakis");
|
|
m_lpCMnkData->m_cFileHeader.m_iHeaderSize = sizeof(CFileHeader) ;
|
|
m_lpCMnkData->m_cFileHeader.m_iVersion = 100 ;
|
|
m_lpCMnkData->m_cFileHeader.m_iTableStones = m_iTableStones ;
|
|
m_lpCMnkData->m_cFileHeader.m_lTableSize = m_lTableSize ;
|
|
|
|
Common::WriteStream *ws = g_system->getSavefileManager()->openForSaving("mankala.dat");
|
|
if (ws) {
|
|
Common::Serializer s(nullptr, ws);
|
|
m_lpCMnkData->m_cFileHeader.sync(s);
|
|
ws->finalize();
|
|
} else {
|
|
// Can't create file
|
|
iError = 100;
|
|
}
|
|
|
|
delete ws;
|
|
|
|
JXELEAVE(CMnk::WriteTableFile) ;
|
|
RETURN iError != 0;
|
|
}
|
|
|
|
bool CMnk::ReadTableFile() {
|
|
JXENTER(CMnk::ReadTableFile) ;
|
|
int iError = 0 ; // error code
|
|
|
|
Common::SeekableReadStream *rs = g_system->getSavefileManager()->openForLoading("mankala.dat");
|
|
if (rs) {
|
|
Common::Serializer s(rs, nullptr);
|
|
m_lpCMnkData->m_cFileHeader.sync(s);
|
|
|
|
if (m_lpCMnkData->m_cFileHeader.m_iVersion != 100) {
|
|
iError = 102; // invalid file header;
|
|
} else {
|
|
if (m_iTableStones > m_lpCMnkData->m_cFileHeader.m_iTableStones)
|
|
m_iTableStones = m_lpCMnkData->m_cFileHeader.m_iTableStones;
|
|
|
|
if (m_lTableSize > m_lpCMnkData->m_cFileHeader.m_lTableSize)
|
|
m_lTableSize = m_lpCMnkData->m_cFileHeader.m_lTableSize;
|
|
|
|
//lTotalCount = m_lTableSize;
|
|
// total number of bytes to be read
|
|
//hpData = m_lpCMnkData->m_hpcBestWin; // point to beginning of table
|
|
}
|
|
} else {
|
|
iError = 100; // can't open file
|
|
}
|
|
|
|
delete rs;
|
|
|
|
JXELEAVE(CMnk::ReadTableFile);
|
|
RETURN(iError != 0);
|
|
}
|
|
|
|
//* CMnk::MapConfiguration -- map a configuration to its integer index,
|
|
// store configuration index into Move object
|
|
bool CMnk::MapConfiguration(CMove * xpcMove)
|
|
// xpcMove -- CMove object containing the configuration
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::MapConfiguration) ;
|
|
int iError = 0 ; // error code
|
|
long lConfigIndex = 0 ; // return value, configuration index
|
|
int iStones ; // total number of stones
|
|
int iPlayer = xpcMove->m_iPlayer ;
|
|
int iOtherPlayer = OTHERPLAYER(iPlayer) ;
|
|
int iPit, iPitCount; // #pit, #stones in pit ;
|
|
int iK ; // loop variable
|
|
|
|
CountStones(xpcMove) ; // set m_iTotalStones
|
|
iStones = xpcMove->m_iTotalStones ;
|
|
|
|
if (iStones > m_iTableStones) // if number of stones
|
|
// exceeds the number we're supporting
|
|
lConfigIndex = -1 ;
|
|
else {
|
|
iPit = TOTALPITS ; // start from pit 12
|
|
iPitCount = -1 ; // force fetch of # stones in pit
|
|
lConfigIndex = (iStones == 0) ? 0 :
|
|
m_lpCMnkData->m_NA[iStones - 1][TOTALPITS] ;
|
|
// start from end of table as of previous # stones
|
|
|
|
for (iK = 1 ; iK <= iStones ; ++iK) {
|
|
while (iPitCount <= 0) { // find a stone
|
|
if (iPitCount == 0 && --iPit < 1) {
|
|
iError = 100 ; // logic error in algorithm
|
|
goto cleanup ;
|
|
}
|
|
|
|
// set iPitCount to the number of stones in iPit
|
|
if (iPit <= NUMPITS) // pit for current player
|
|
iPitCount = xpcMove->m_iNumStones[iPlayer]
|
|
[iPit - 1 + 2] ;
|
|
else // pit for other player
|
|
iPitCount = xpcMove->m_iNumStones
|
|
[iOtherPlayer]
|
|
[iPit - (NUMPITS + 1) + 2] ;
|
|
}
|
|
|
|
lConfigIndex += m_lpCMnkData->m_NX[iStones - iK + 1]
|
|
[iPit - 1] ;
|
|
--iPitCount ; // one less stone to count in this pit
|
|
}
|
|
++lConfigIndex ; // index begins at 1
|
|
}
|
|
|
|
cleanup:
|
|
xpcMove->m_lConfigIndex = lConfigIndex ; // store result
|
|
|
|
JXELEAVE(CMnk::MapConfiguration) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::UnmapConfiguration -- map configuration index back
|
|
// to configuration
|
|
bool CMnk::UnmapConfiguration(CMove * xpcMove)
|
|
// xpcMove -- CMove object containing the configuration / index
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::UnmapConfiguration) ;
|
|
int iError = 0 ; // error code
|
|
long lConfigIndex = xpcMove->m_lConfigIndex - 1 ; // config index
|
|
long lConfigSave = lConfigIndex + 1 ; // save for debugging
|
|
int iPlayer = xpcMove->m_iPlayer ;
|
|
int iOtherPlayer = OTHERPLAYER(iPlayer) ;
|
|
int iStones ; // total number of stones
|
|
int iPit, iPitCur ; // loop variable
|
|
int iK ; // loop variable
|
|
long lValue = 0L, lValueNew ; // table values
|
|
char szDebugStr[200] ; // debugging string
|
|
|
|
if (lConfigIndex < 0 || (uint)lConfigIndex >= MAXCONFIGS) {
|
|
iError = 100 ; // config index is out of range
|
|
goto cleanup ;
|
|
}
|
|
|
|
// find number of stones in configuration
|
|
// xpcMove->m_iTotalStones = 0 ;
|
|
for (iStones = 0 ; iStones <= m_iTableStones
|
|
&& (lValueNew = m_lpCMnkData->m_NA[iStones][TOTALPITS])
|
|
<= lConfigIndex ; ++iStones, lValue = lValueNew)
|
|
; // null loop body
|
|
|
|
xpcMove->m_iTotalStones = iStones ;
|
|
|
|
if (iStones > m_iTableStones) {
|
|
iError = 101 ; // can't compute number of stones
|
|
goto cleanup ;
|
|
}
|
|
|
|
lConfigIndex -= lValue ;
|
|
|
|
for (iK = xpcMove->m_iTotalStones ; iK >= 1 ; --iK) {
|
|
// find which pit stone # iK is in
|
|
iPit = 1 ;
|
|
for (iPitCur = 1, lValue = 0L ; iPitCur <= TOTALPITS
|
|
&& (lValueNew = m_lpCMnkData->m_NX[iK][iPitCur])
|
|
<= lConfigIndex ;
|
|
lValue = lValueNew, ++iPitCur) ;
|
|
iPit = iPitCur ;
|
|
|
|
// if (iPit > TOTALPITS)
|
|
// {
|
|
// iError = 120 + iK ; // can't compute pit #
|
|
// goto cleanup ;
|
|
// }
|
|
|
|
lConfigIndex -= lValue ;
|
|
|
|
// // ***** debugging
|
|
// Common::sprintf_s(szDebugStr, "lConfig=%ld, #stones=%d, pit %d = %d\n",
|
|
// lConfigSave, iStones, iK, iPit) ;
|
|
// debug(szDebugStr) ;
|
|
|
|
// increment stone count for appropriate pit
|
|
if (iPit <= NUMPITS) // pit for current player
|
|
++xpcMove->m_iNumStones[iPlayer][iPit - 1 + 2] ;
|
|
else // pit for other player
|
|
++xpcMove->m_iNumStones[iOtherPlayer]
|
|
[iPit - (NUMPITS + 1) + 2] ;
|
|
|
|
}
|
|
|
|
// *** debugging
|
|
MapConfiguration(xpcMove) ;
|
|
if (lConfigSave != xpcMove->m_lConfigIndex) { // if unmap/map failed
|
|
Common::sprintf_s(szDebugStr, "Config %ld changed to %ld.\n",
|
|
lConfigSave, xpcMove->m_lConfigIndex) ;
|
|
debug("%s", szDebugStr) ;
|
|
DebugBreak();
|
|
}
|
|
|
|
cleanup:
|
|
|
|
JXELEAVE(CMnk::UnmapConfiguration) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::SearchMove -- search for best move
|
|
bool CMnk::SearchMove(CMove * xpcMove, int &iMove)
|
|
// xpcMove -- pointer to move/position to find move for
|
|
// iMove (output) -- recommended pit number
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::SearchMove) ;
|
|
|
|
int iError = 0 ; // error code
|
|
//bool bDone = false ; // flag: evaluations done
|
|
//int iNumMoves = 0 ; // number of legal moves
|
|
int iNumStones ; // number of stones in pit
|
|
// bool bFree[NUMPITS], bCapture[NUMPITS] ;
|
|
// int iValue[NUMPITS] ; // value of each pit
|
|
int iPit ; // loop variable
|
|
//int iLastPit = -1 ; // for finding lowest/highest # pit
|
|
int iMaxValue = BESTWINUNDEF, iNumberAtMax = 0,
|
|
tmpVal, maxtmpVal ;
|
|
// computing maximum value
|
|
// CPit * xpcPit ; // pit being processed
|
|
int iPlayer = xpcMove->m_iPlayer ; // person on the move
|
|
// CMove * xpcMove = &m_cCurrentMove ; // current move/position object
|
|
bool bStonesFound;
|
|
|
|
m_iCurrentMaxDepth = m_iMaxDepth[iPlayer] ; // minimax depth
|
|
m_iCurrentCapDepth = m_iCapDepth[iPlayer] ; // capture depth
|
|
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit)
|
|
xpcMove->m_iValues[iPit] = BESTWINUNDEF ; // values undefined
|
|
|
|
switch (m_eLevel[iPlayer]) // switch based on type of
|
|
// move generation algorithm
|
|
{
|
|
case LEV_RANDOM: // handle these individually below
|
|
break;
|
|
|
|
case LEV_LOWEST:
|
|
StaticEvaluation(xpcMove);
|
|
// fall through
|
|
case LEV_HIGHEST:
|
|
AggressiveStaticEvaluation(xpcMove);
|
|
break;
|
|
|
|
case LEV_EVAL: // evaluate statically
|
|
DefensiveStaticEvaluation(xpcMove) ;
|
|
break ;
|
|
|
|
case LEV_MINIMAX:
|
|
TreeAlgo(xpcMove);
|
|
//Minimax(xpcMove) ; // use minimax algorithm
|
|
break ;
|
|
|
|
default:
|
|
iError = 100 ; // invalid m_eLevel value
|
|
goto cleanup ;
|
|
}
|
|
|
|
for (iPit = 0, bStonesFound = false ; iPit < NUMPITS ; ++iPit) {
|
|
iNumStones = xpcMove->m_iNumStones[iPlayer][iPit + 2];
|
|
bStonesFound = bStonesFound || iNumStones;
|
|
|
|
if ((iNumStones > 0))
|
|
// get # stones, test
|
|
// if this is a possible move
|
|
switch (m_eLevel[iPlayer]) // switch based on type of
|
|
// move generation algorithm
|
|
{
|
|
case LEV_RANDOM:
|
|
// Random choice
|
|
xpcMove->m_iValues[iPit] = 1 ;
|
|
break ;
|
|
case LEV_LOWEST:
|
|
case LEV_HIGHEST:
|
|
case LEV_EVAL: // these were handled above
|
|
case LEV_MINIMAX:
|
|
break ;
|
|
|
|
default:
|
|
iError = 101 ; // invalid m_eLevel value
|
|
goto cleanup ;
|
|
}
|
|
}
|
|
/*
|
|
if(!bStonesFound){
|
|
iPit=-3; //-3 is a good choice because iPit=-1, iPit=-2 represent the hand and home bins.
|
|
return(false); //let the while loop in mnkui.cpp that calls SearchMove continue, so that moving stones is automatic.
|
|
}
|
|
*/
|
|
|
|
// loop thru pits, find those with maximum value
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit)
|
|
if (xpcMove->m_iValues[iPit] > iMaxValue) // new max
|
|
iMaxValue = xpcMove->m_iValues[iPit], iNumberAtMax = 1 ;
|
|
else if (xpcMove->m_iValues[iPit] == iMaxValue)
|
|
++iNumberAtMax ;
|
|
|
|
if (iMaxValue <= BESTWINUNDEF) // if no move found (or there
|
|
// are no legal moves)
|
|
{
|
|
iError = 110 ; // no move found
|
|
goto cleanup ;
|
|
}
|
|
|
|
|
|
for (maxtmpVal = BESTWINUNDEF + 1, iPit = 0; iPit < NUMPITS ; ++iPit) {
|
|
if (xpcMove->m_iValues[iPit] == iMaxValue)
|
|
/*
|
|
Among those with equal iValues, Select the least range pit of 'em all
|
|
*/
|
|
if ((tmpVal = iPit - xpcMove->m_iNumStones[xpcMove->m_iPlayer][iPit + 2]) > maxtmpVal) {
|
|
iMove = iPit;
|
|
maxtmpVal = tmpVal;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
JXELEAVE(CMnk::SearchMove) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::Minimax -- find best move from supplied configuration
|
|
bool CMnk::Minimax(CMove * xpcMove, int iDepth)
|
|
// xpcMove -- CMove object containing the configuration
|
|
// iDepth -- minimax tree depth so far
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::Minimax) ;
|
|
|
|
int iError = 0 ; // error code
|
|
int iPlayer = xpcMove->m_iPlayer ; // player moving
|
|
CMove cMoveBase, cMove ; // six possible moves
|
|
int iLegalMoves = 0 ; // number of legal moves
|
|
CPit * xpcSowPit ; // ptr to CPit object being sowed
|
|
int iValue, iMaxValue = BESTWINUNDEF ; // move value for this move
|
|
int iBestMove = -1 ; // best move
|
|
bool bDone ; // flag: processing this move is done
|
|
int iPit, iP ; // loop variable
|
|
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit)
|
|
xpcMove->m_iValues[iPit] = BESTWINUNDEF ; // values undefined
|
|
|
|
memcpy(&cMoveBase, xpcMove, sizeof(CMove)) ;
|
|
// form a base CMove object
|
|
cMoveBase.m_iNumStones[0][HOMEINDEX + 2]
|
|
= cMoveBase.m_iNumStones[1][HOMEINDEX + 2] = 0 ;
|
|
// clear both players' home bins
|
|
cMoveBase.m_bRealMove = false ;
|
|
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit) {
|
|
bDone = false ; // not done yet
|
|
iValue = BESTWINUNDEF ; // no value yet
|
|
if (xpcMove->m_iNumStones[iPlayer][iPit + 2] == 0)
|
|
// if this pit has no stones in it
|
|
bDone = true ; // nothing to do
|
|
if (!bDone) {
|
|
++iLegalMoves ; // increment legal move count
|
|
memcpy(&cMove, &cMoveBase, sizeof(CMove)) ;
|
|
// copy the new CMove object from base object
|
|
|
|
xpcSowPit = m_xpcPits[iPlayer][iPit + 2] ;
|
|
Move(xpcSowPit, &cMove) ; // make move
|
|
// sow pit # iPit, and change iPlayer
|
|
CountStones(&cMove) ; // set m_iTotalStones
|
|
|
|
if (cMove.m_iTotalStones <= m_iTableStones)
|
|
// if we have the value of this in the
|
|
// best win table
|
|
{
|
|
MapConfiguration(&cMove) ; // map stone configuration
|
|
// to index in best win table
|
|
GetBestWinCount(&cMove) ;
|
|
iValue = cMove.m_iBestWinValue ;
|
|
// get value of this move
|
|
if (iValue > BESTWINUNDEF) // value was in table
|
|
bDone = true ;
|
|
}
|
|
if (!bDone && (iDepth < m_iCurrentMaxDepth ||
|
|
(iDepth < m_iCurrentMaxDepth + m_iCurrentCapDepth
|
|
&& (cMove.m_bCapture || cMove.m_bFreeTurn)))) {
|
|
Minimax(&cMove, iDepth + 1) ;
|
|
iValue = cMove.m_iBestWinValue ;
|
|
bDone = true ;
|
|
}
|
|
|
|
if (!bDone) {
|
|
iValue = 0 ;
|
|
for (iP = 0 ; iP < NUMPITS ; ++iP)
|
|
iValue += (cMove.m_iNumStones[cMove.m_iPlayer][iP + 2]
|
|
- cMove.m_iNumStones
|
|
[OTHERPLAYER(cMove.m_iPlayer)][iP + 2]) / 2 ;
|
|
}
|
|
|
|
iValue += cMove.m_iNumStones[cMove.m_iPlayer][HOMEINDEX + 2]
|
|
- cMove.m_iNumStones[OTHERPLAYER(cMove.m_iPlayer)]
|
|
[HOMEINDEX + 2] ;
|
|
|
|
if (cMove.m_iPlayer != iPlayer && iValue > BESTWINUNDEF)
|
|
iValue = -iValue ; // reverse value if other player
|
|
|
|
if (iMaxValue < iValue) // new max?
|
|
iMaxValue = iValue, iBestMove = iPit ;
|
|
}
|
|
xpcMove->m_iValues[iPit] = iValue ; // save val for this move
|
|
}
|
|
|
|
if ((xpcMove->m_iNumMoves = iLegalMoves) == 0)
|
|
// if there were no legal moves
|
|
iMaxValue = - xpcMove->m_iTotalStones ;
|
|
// if there is no legal move, then the value of this
|
|
// position is minus the number of stones on the
|
|
// other side
|
|
|
|
// ... by inserting test for iLegalMoves > 1 here, can provide other
|
|
// patterns -- eg, occasionally choosing less good move
|
|
|
|
xpcMove->m_iBestWinValue = iMaxValue ; // store best value
|
|
xpcMove->m_iBestMove = iBestMove ; // and best move
|
|
|
|
// cleanup:
|
|
JXELEAVE(CMnk::Minimax) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
|
|
//* CMnk::StaticEvaluation -- determine static value of a position
|
|
bool CMnk::StaticEvaluation(CMove * xpcMove)
|
|
// xpcMove -- position to be evaluated
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::StaticEvaluation) ;
|
|
|
|
int iError = 0 ; // error code
|
|
int iNumMoves = 0 ; // number of legal moves
|
|
int iNumStones ; // number of stones in pit
|
|
bool bFree[NUMPITS], bCapture[NUMPITS] ;
|
|
int iPit ; // loop variable
|
|
//int iMaxValue = BESTWINUNDEF ; // computing maximum value
|
|
//CPit * xpcPit ; // pit being processed
|
|
int iPlayer = xpcMove->m_iPlayer ; // current player
|
|
|
|
xpcMove->m_bHasCapture = xpcMove->m_bHasFree = false ;
|
|
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit) {
|
|
xpcMove->m_iValues[iPit] = BESTWINUNDEF ; // no value yet
|
|
// xpcPit = m_xpcPits[iPlayer][iPit + 2] ; // pt to pit object
|
|
if ((iNumStones = xpcMove->m_iNumStones[iPlayer][iPit + 2]))
|
|
// get number of stones and test
|
|
++iNumMoves ; // it's a legal move
|
|
|
|
bFree[iPit] = (iNumStones &&
|
|
(iNumStones % (2 * NUMPITS + 1) == iPit + 1)) ;
|
|
// test whether this pit yields a free turn
|
|
|
|
if (iNumStones == 2 * NUMPITS + 1)
|
|
bCapture[iPit] = true ;
|
|
|
|
else if (iNumStones && iNumStones <= iPit)
|
|
bCapture[iPit] = (xpcMove->m_iNumStones[iPlayer]
|
|
[iPit - iNumStones + 2] == 0) ;
|
|
|
|
else if (iNumStones > iPit + NUMPITS && iNumStones <= 2 * NUMPITS)
|
|
bCapture[iPit] = (xpcMove->m_iNumStones[iPlayer]
|
|
[iPit - iNumStones + NUMPITS + 2] == 0) ;
|
|
|
|
else
|
|
bCapture[iPit] = false ;
|
|
|
|
if (bFree[iPit])
|
|
xpcMove->m_bHasFree = true ;
|
|
|
|
if (bCapture[iPit])
|
|
xpcMove->m_bHasCapture = true ;
|
|
|
|
// the following formula is really just a guess
|
|
xpcMove->m_iValues[iPit] = iNumStones
|
|
+ (bCapture[iPit] ? 5 : 0) + (bFree[iPit] ? 5 : 0) ;
|
|
}
|
|
|
|
xpcMove->m_iNumMoves = iNumMoves ; // store # legal moves
|
|
|
|
// cleanup:
|
|
JXELEAVE(CMnk::StaticEvaluation) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
|
|
//* CMnk::AggressiveStaticEvaluation -- determine static value of a position
|
|
bool CMnk::AggressiveStaticEvaluation(CMove * xpcMove)
|
|
// xpcMove -- position to be evaluated
|
|
// returns: true if error, false otherwise
|
|
|
|
/*
|
|
For Simplicity: defensive moves are discussed from pt of view of crab
|
|
*/
|
|
{
|
|
JXENTER(CMnk::StaticEvaluation) ;
|
|
|
|
int iError = 0 ; // error code
|
|
int iNumMoves = 0 ; // number of legal moves
|
|
int iNumStones ; // number of stones in pit
|
|
int iFree,
|
|
iCapture,
|
|
maxCapture = 0;
|
|
//int maxFree=0;
|
|
int iPit,
|
|
iCapturePit = 0,
|
|
iFreePit = 0;
|
|
//int iMaxValue = BESTWINUNDEF ; // computing maximum value
|
|
int iPlayer = xpcMove->m_iPlayer ; // current player
|
|
|
|
bool bAFreePitExists,
|
|
bACaptureExists,
|
|
bDoFreePitFirst;
|
|
//CPit * xpcPit ; // pit being processed
|
|
|
|
|
|
/*INITIALIZE*/
|
|
xpcMove->m_bHasCapture = xpcMove->m_bHasFree = false ;
|
|
bDoFreePitFirst = false;
|
|
bAFreePitExists = false;
|
|
bACaptureExists = false;
|
|
maxCapture = 0;
|
|
|
|
for (iPit = 0, bAFreePitExists = false, bACaptureExists = false ; iPit < NUMPITS ; ++iPit) {
|
|
xpcMove->m_iValues[iPit] = BESTWINUNDEF ; // no value yet
|
|
//xpcPit = m_xpcPits[iPlayer][iPit + 2] ; // pt to pit object
|
|
|
|
|
|
if ((iNumStones = xpcMove->m_iNumStones[iPlayer][iPit + 2]))
|
|
// get number of stones and test
|
|
++iNumMoves ; // it's a legal move
|
|
|
|
|
|
iFree = (iNumStones && (iNumStones % (2 * NUMPITS + 1) == iPit + 1)) ? 1 : 0;
|
|
// test whether this pit yields a free turn
|
|
|
|
if (iNumStones == 2 * NUMPITS + 1)
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][NUMPITS + 1 - iPit];
|
|
|
|
else if (iNumStones && (iNumStones <= iPit) && !xpcMove->m_iNumStones[iPlayer][iPit - iNumStones + 2])
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][NUMPITS + 1 - iPit + iNumStones] ;
|
|
|
|
else if (iNumStones > iPit + NUMPITS && (iNumStones <= 2 * NUMPITS) && !xpcMove->m_iNumStones[iPlayer][2 * NUMPITS + 3 + iPit - iNumStones])
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][iNumStones - iPit - NUMPITS];
|
|
|
|
else
|
|
iCapture = 0;
|
|
|
|
if (iFree) {
|
|
xpcMove->m_bHasFree = true ;
|
|
if (!bAFreePitExists) iFreePit = iPit; //so that you start w/ the closest free Pit (ie. iPit with the lowest index),
|
|
if (iNumStones <= NUMPITS) bAFreePitExists = true; //unless there's a "WRAP AROUND" in which case the farthest pit will be selected.
|
|
}
|
|
|
|
/* determine which pit yields the max capture */
|
|
if (iCapture) {
|
|
xpcMove->m_bHasCapture = true ;
|
|
if (iCapture > maxCapture) {
|
|
maxCapture = iCapture;
|
|
iCapturePit = iPit;
|
|
}
|
|
bACaptureExists = true;
|
|
}
|
|
}//end- for
|
|
|
|
/* Determine whether the FreePit "interferes" with the CapturePit */
|
|
if (bACaptureExists && bAFreePitExists) {
|
|
if ((xpcMove->m_iNumStones[iPlayer][iFreePit + 2] < NUMPITS - 1) && //i.e. if iFreePit <=3
|
|
(xpcMove->m_iNumStones[iPlayer][iCapturePit + 2] < NUMPITS + 1) && //i.e. No Wrap Around Capture
|
|
((iCapturePit - xpcMove->m_iNumStones[iPlayer][iCapturePit + 2]) > iFreePit)) { //i.e. the landing pit in the capture case, ...
|
|
// is farther than the freePit.
|
|
bDoFreePitFirst = true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (((maxCapture < 4) && bAFreePitExists) || bDoFreePitFirst)
|
|
xpcMove->m_iValues[iFreePit] = 5;
|
|
else if (bACaptureExists)
|
|
xpcMove->m_iValues[iCapturePit] = 5;
|
|
else for (signed char i = 0; i < NUMPITS; xpcMove->m_iValues[i] = (xpcMove->m_iNumStones[iPlayer][i + 2]) ? 5 : 0, i++);
|
|
|
|
xpcMove->m_iNumMoves = iNumMoves ; // store # legal moves
|
|
|
|
|
|
// cleanup:
|
|
JXELEAVE(CMnk::AggressiveStaticEvaluation) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::StaticEvaluation -- determine static value of a position
|
|
bool CMnk::DefensiveStaticEvaluation(CMove * xpcMove)
|
|
// xpcMove -- position to be evaluated
|
|
// returns: true if error, false otherwise
|
|
|
|
/*
|
|
For Simplicity: defensive moves are discussed from pt of view of crab
|
|
*/
|
|
{
|
|
JXENTER(CMnk::DefensiveStaticEvaluation) ;
|
|
|
|
int iError = 0 ; // error code
|
|
int iNumMoves = 0 ; // number of legal moves
|
|
int iNumStones, // number of stones in pit
|
|
iOtherNumStones, //number of stones in opponent's pits.
|
|
iMaxStones,
|
|
StonesGained,
|
|
StonesSaved,
|
|
TotalBenefit,
|
|
maxTotalBenefit;
|
|
int OtherPlayer;
|
|
int iFree,
|
|
iCapture,
|
|
maxCapture,
|
|
// maxFree,
|
|
maxThreat,
|
|
minThreat;
|
|
int iPit = 0,
|
|
iCapturePit = 0,
|
|
iFreePit = 0,
|
|
iMaxThreatPit = 0,
|
|
iCurrentPitUnderThreat = 0,
|
|
iSaved = 0,
|
|
iStartWithThisCapture = 0;
|
|
//int iMaxValue = BESTWINUNDEF ; // computing maximum value
|
|
int iPlayer = xpcMove->m_iPlayer ; // current player
|
|
int ThreatCount, //how many pits of HUMAN are aggressive.
|
|
iEmptyPitCount,
|
|
iOtherEmptyPitCount;
|
|
int iPitUnderThreat[NUMPITS]; //List of pits of CRAB under threat.
|
|
int iEvasionNecessary[NUMPITS]; //Amount of threat to a pit on this side.
|
|
int iOtherOffensivePit[NUMPITS]; //which pit a given pit on the opposite side is offensive to.
|
|
int jYieldsACapture[NUMPITS]; //the pit on the opposite side that can be captured by a given pit.
|
|
|
|
int ii,
|
|
u,
|
|
j,
|
|
k,
|
|
kk,
|
|
tmpValues,
|
|
tmpCount;
|
|
|
|
bool bAFreePitExists,
|
|
bACaptureExists,
|
|
bAThreatExists,
|
|
bDoFreePitFirst,
|
|
bEndGamePlay,
|
|
bNearClustered,
|
|
bConclusive,
|
|
bSaveable,
|
|
bUnSaveable;
|
|
// CPit * xpcPit ; // pit being processed
|
|
|
|
|
|
/*INITIALIZE*/
|
|
xpcMove->m_bHasCapture = xpcMove->m_bHasFree = false ;
|
|
bAThreatExists = false;
|
|
bDoFreePitFirst = false;
|
|
bAFreePitExists = false;
|
|
bACaptureExists = false;
|
|
bEndGamePlay = false;
|
|
bConclusive = false;
|
|
bSaveable = false;
|
|
bUnSaveable = false;
|
|
tmpCount = 0;
|
|
maxCapture = 0;
|
|
maxThreat = 0;
|
|
minThreat = 0;
|
|
ThreatCount = 0;
|
|
iMaxStones = 0;
|
|
// maxFree = 0;
|
|
maxTotalBenefit = 0;
|
|
bNearClustered = false;
|
|
|
|
memset((void *)iEvasionNecessary, 0x0, NUMPITS * sizeof(int));
|
|
memset((void *)iPitUnderThreat, -1, NUMPITS * sizeof(int));
|
|
memset((void *)iOtherOffensivePit, -1, NUMPITS * sizeof(int));
|
|
|
|
|
|
/*
|
|
note that in SOME OF THE following sections of code
|
|
the prefix "Other" may refer to the computer_crab.
|
|
Codewise though the terms are perfectly interchangeable.
|
|
*/
|
|
|
|
/*determine if this is an EndOfGame Play move */
|
|
for (ii = 0, iEmptyPitCount = 0, iOtherEmptyPitCount = 0; ii < NUMPITS; ii++) {
|
|
if (!xpcMove->m_iNumStones[0][ii + 2])
|
|
iEmptyPitCount++;
|
|
|
|
if (!xpcMove->m_iNumStones[1][ii + 2])
|
|
iOtherEmptyPitCount++;
|
|
}
|
|
|
|
if (((iEmptyPitCount > 2) && (iOtherEmptyPitCount > 2)) || (bNearClustered && ((iEmptyPitCount > 1) || (iOtherEmptyPitCount > 1))))
|
|
bEndGamePlay = true;
|
|
|
|
|
|
/*
|
|
SET UP DEFENSIVE MOVES, DETERMINE OFFENDING CRAB_PITS, and
|
|
MY PITS UNDER ATTACK
|
|
*/
|
|
for (OtherPlayer = OTHERPLAYER(iPlayer), j = 0; j < NUMPITS; j++) {
|
|
iOtherNumStones = xpcMove->m_iNumStones[OtherPlayer][j + 2]; /*Stones in the j-th HUMAN pit*/
|
|
|
|
/*
|
|
if there are 13 stones in the HUMAN'S pit then the (5-j)th CRAB'S pit is threatened
|
|
*/
|
|
if (iOtherNumStones == 2 * NUMPITS + 1)
|
|
iCurrentPitUnderThreat = NUMPITS - j - 1;
|
|
|
|
else if (iOtherNumStones && (iOtherNumStones <= j) && !xpcMove->m_iNumStones[OtherPlayer][j - iOtherNumStones + 2]) {
|
|
iCurrentPitUnderThreat = NUMPITS - 1 - j + iOtherNumStones;
|
|
}
|
|
|
|
else if ((iOtherNumStones > j + NUMPITS) && (iOtherNumStones <= 2 * NUMPITS)
|
|
&& (!xpcMove->m_iNumStones[OtherPlayer][2 * NUMPITS + 3 + j - iOtherNumStones]))
|
|
iCurrentPitUnderThreat = iOtherNumStones - j - NUMPITS - 2;
|
|
else
|
|
iCurrentPitUnderThreat = -1;
|
|
|
|
/* then check to see if there are stones in iCurrentPitUnderThreat at all */
|
|
if ((iCurrentPitUnderThreat != -1) && !xpcMove->m_iNumStones[iPlayer][iCurrentPitUnderThreat + 2])
|
|
iCurrentPitUnderThreat = -1;
|
|
|
|
/* if there are stones in the pit under threat then set flags */
|
|
if (iCurrentPitUnderThreat != -1) {
|
|
bAThreatExists = true;
|
|
iPitUnderThreat[ThreatCount] = iCurrentPitUnderThreat;
|
|
iEvasionNecessary[iCurrentPitUnderThreat] = xpcMove->m_iNumStones[iPlayer][iCurrentPitUnderThreat + 2]; //the extent of threat to a given CRAB pit
|
|
iOtherOffensivePit[j] = iCurrentPitUnderThreat; // the j th HUMAN pit is offensive to iCurrentPitUnderThreat CRAB pit.
|
|
ThreatCount++; //the total # of CRAB pits under threat.
|
|
}
|
|
}//end for -j
|
|
|
|
|
|
|
|
/*
|
|
Find the pit least threatened and the one most threatened
|
|
*/
|
|
for (ii = 0; ii < NUMPITS; ii++) {
|
|
if (maxThreat < iEvasionNecessary[ii]) {
|
|
maxThreat = iEvasionNecessary[ii];
|
|
iMaxThreatPit = ii;
|
|
}
|
|
minThreat = min(minThreat, iEvasionNecessary[ii]);
|
|
}
|
|
|
|
|
|
|
|
for (iPit = 0, bAFreePitExists = false, bACaptureExists = false ; iPit < NUMPITS ; ++iPit) {
|
|
xpcMove->m_iValues[iPit] = BESTWINUNDEF ; // no value yet
|
|
//xpcPit = m_xpcPits[iPlayer][iPit + 2] ; // pt to pit object
|
|
|
|
|
|
if ((iNumStones = xpcMove->m_iNumStones[iPlayer][iPit + 2]))
|
|
// get number of stones and test
|
|
++iNumMoves ; // it's a legal move
|
|
|
|
/*
|
|
determine the max # of stones across all pits on one side
|
|
*/
|
|
if (iNumStones > iMaxStones) iMaxStones = iNumStones;
|
|
|
|
/*
|
|
test whether this pit yields a free turn
|
|
*/
|
|
iFree = (iNumStones && (iNumStones % (2 * NUMPITS + 1) == iPit + 1)) ? 1 : 0;
|
|
|
|
|
|
/*
|
|
Test for a Capture and the amount of Capture.
|
|
There are 3 ways you can execute a capture:
|
|
if you have 13 stones in a pit, then you definitely capture all stones in the opposite pit +1 (which is your own stone).
|
|
if you have <13 stones in a pit and you don't wrap around the other side, then you might yield a capture.
|
|
if you have <13 stones in a pit then you never cross the point you started from and you might yield a capture.
|
|
|
|
If You happen to cross your starting point as in a loop (if you have >13 stones) you will never land up w/ a capture.
|
|
*/
|
|
|
|
if (iNumStones == 2 * NUMPITS + 1) {
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][NUMPITS + 1 - iPit];
|
|
jYieldsACapture[iPit] = NUMPITS - 1 - iPit;
|
|
} else if (iNumStones && (iNumStones <= iPit) && !xpcMove->m_iNumStones[iPlayer][iPit - iNumStones + 2]) {
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][NUMPITS + 1 - iPit + iNumStones] ;
|
|
jYieldsACapture[iPit] = NUMPITS - 1 - iPit + iNumStones;
|
|
} else if (iNumStones > iPit + NUMPITS && (iNumStones <= 2 * NUMPITS) && !xpcMove->m_iNumStones[iPlayer][2 * NUMPITS + 3 + iPit - iNumStones]) {
|
|
iCapture = xpcMove->m_iNumStones[OTHERPLAYER(iPlayer)][iNumStones - iPit - NUMPITS];
|
|
jYieldsACapture[iPit] = iNumStones - iPit - NUMPITS - 2;
|
|
} else {
|
|
iCapture = 0;
|
|
jYieldsACapture[iPit] = -1;
|
|
}
|
|
|
|
/*
|
|
(OTHER THINGS BEING THE SAME), if there're more than one FreePit
|
|
then start from the one closest to the home bin
|
|
*/
|
|
if (iFree) {
|
|
xpcMove->m_bHasFree = true ;
|
|
if (!bAFreePitExists) iFreePit = iPit; //so that you start w/ the closest free Pit (ie. iPit with the lowest index),
|
|
if (iNumStones <= NUMPITS) bAFreePitExists = true; //unless there's a "WRAP AROUND" in which case the farthest pit will be selected.
|
|
}
|
|
|
|
/*
|
|
Determine which pit yields the max capture,
|
|
(OTHER THINGS BEING THE SAME), start with this pit
|
|
*/
|
|
if (iCapture) {
|
|
xpcMove->m_bHasCapture = true ;
|
|
if (iCapture > maxCapture) {
|
|
maxCapture = iCapture;
|
|
iCapturePit = iPit;
|
|
}
|
|
bACaptureExists = true;
|
|
}
|
|
|
|
}//end- for iPit
|
|
|
|
/*
|
|
Determine whether the FreePit "interferes" with the CapturePit.
|
|
There's NO interference if doing the free pit first keeps the capture intact.
|
|
*/
|
|
|
|
|
|
if (bACaptureExists && bAFreePitExists) {
|
|
if ((xpcMove->m_iNumStones[iPlayer][iFreePit + 2] < NUMPITS - 1) && //i.e. if iFreePit <=3
|
|
(xpcMove->m_iNumStones[iPlayer][iCapturePit + 2] < NUMPITS + 1) && //i.e. No Wrap Around Capture
|
|
((iCapturePit - xpcMove->m_iNumStones[iPlayer][iCapturePit + 2]) > iFreePit)) { //i.e. the landing pit in the capture case, ...
|
|
// is farther than the freePit.
|
|
bDoFreePitFirst = true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Use End Game Play Strategy if more than 2 stones are empty on each side.
|
|
In this case ALWAYS start with the pit which has stones falling furthest away
|
|
from home bin. if this pit is a free pit avoid it, unless
|
|
by doing otherwise you have to squander more than 3 stones to the
|
|
opposite side then go for the free pit.
|
|
*/
|
|
|
|
if (bEndGamePlay) {
|
|
if (maxCapture > 3) {
|
|
xpcMove->m_iValues[iCapturePit] = 5;
|
|
} else if (bAThreatExists) {
|
|
xpcMove->m_iValues[iMaxThreatPit] = 5;
|
|
} else {
|
|
for (u = 0; u < NUMPITS; u++) {
|
|
if (!xpcMove->m_iNumStones[iPlayer][u + 2]) continue; //proceed in the current loop only if there are stones.
|
|
|
|
tmpValues = u + 1 - xpcMove->m_iNumStones[iPlayer][u + 2]; //highest value to pit w/ stones falling furthest away from home bin.
|
|
if (tmpValues <= 0) //u is a free pit or has stones falling on the opp. side.
|
|
tmpValues = BESTWINUNDEF + 1; //the lowest possible; add 1 to distinguish with pits without stones.
|
|
xpcMove->m_iValues[u] = tmpValues;
|
|
}//end for u.
|
|
}//end else
|
|
|
|
goto cleanup;
|
|
}//end if bEndGamePlay
|
|
|
|
/*
|
|
if there's no end of game play: (HIGHEST PRIORITY enumerated first)
|
|
1) if the DoFreePitFirst flag is high then do the Free Pit first
|
|
2) if there's a threat more than the max capture do the threat first.
|
|
3) if the smallest capture's more than the highest threat do the capture first.
|
|
4) if you can't execute 2) or 3) then do the threat if it's more than 3 first, else do the capture.
|
|
5) if your capture is less than 4 stones and you have a free pit too, do the free pit.
|
|
6) if you can't conclude from 1)-5) assign equal values to all.
|
|
|
|
This is the overall condensed strategy. The actual implementation is simpler
|
|
if you construct a binary conditions table (TCF for threat, capture, free) and
|
|
then implement subconditions if two or more of these flags are high, according to the above
|
|
rules.
|
|
*/
|
|
|
|
//T C F stands for....(threatExists, CaptureExists, FreePitExists)
|
|
if (!bAThreatExists && !bACaptureExists && bAFreePitExists) { // 0 0 1
|
|
xpcMove->m_iValues[iFreePit] = 5;
|
|
bConclusive = true;
|
|
}
|
|
|
|
if (!bAThreatExists && bACaptureExists && !bAFreePitExists) { // 0 1 0
|
|
xpcMove->m_iValues[iCapturePit] = 5;
|
|
bConclusive = true;
|
|
}
|
|
|
|
if (!bAThreatExists && bACaptureExists && bAFreePitExists) { // 0 1 1
|
|
if (bDoFreePitFirst || (maxCapture < 3)) //If the capture is less than 3 or if there's a "non-interfering"
|
|
xpcMove->m_iValues[iFreePit] = 5; //free pit then do free pit first.
|
|
else
|
|
xpcMove->m_iValues[iCapturePit] = 5;
|
|
|
|
bConclusive = true;
|
|
}
|
|
|
|
|
|
if (bAThreatExists && !bAFreePitExists && !bACaptureExists) { //1 0 0
|
|
xpcMove->m_iValues[iMaxThreatPit] = 5;
|
|
bConclusive = true;
|
|
}
|
|
|
|
if (bAThreatExists && !bACaptureExists && bAFreePitExists) { //1 0 1
|
|
xpcMove->m_iValues[iFreePit] = 5;
|
|
bConclusive = true; // if no capture do free pit ---> no contest from Threat.
|
|
}
|
|
if (bAThreatExists && bACaptureExists) {
|
|
if (!bAFreePitExists || (bAFreePitExists && !bDoFreePitFirst)) { //1 1 0 && 1 1 1
|
|
if (maxCapture >= maxThreat) {
|
|
xpcMove->m_iValues[iCapturePit] = 5;
|
|
} else {
|
|
/*
|
|
see if you can capture the offending pit anyway :
|
|
if you have more than CapturePit Stones (ie. a wrap around capture).
|
|
you take care of the threat because you fill up
|
|
pits on the opp.side. If this condition is true and maxThreat>5
|
|
then go for the capture. If that condition is true and but maxThreat<=5
|
|
then go for the threat.
|
|
|
|
if IT'S not a wrap around capture, and the offending pits can be
|
|
captured (i.e. look at all capture pits and the pit numbers captured
|
|
from opp. side), then see if you have more than one offending pit for
|
|
the same threat, in which case go for the bigger offending pit capture...
|
|
|
|
*/
|
|
if (xpcMove->m_iNumStones[iPlayer][iCapturePit] > iCapturePit) {
|
|
if (maxThreat > 4) xpcMove->m_iValues[iCapturePit] = 5;
|
|
else xpcMove->m_iValues[iMaxThreatPit] = 5;
|
|
} else {
|
|
|
|
/*
|
|
determine if an offensive pit is capturable. If atleast one pit offensive
|
|
to the maxThreatPit is uncapturable then go for the threat pit instead of the
|
|
capture.
|
|
*/
|
|
for (kk = 0, tmpCount = 0, bUnSaveable = false; kk < ThreatCount; kk++) {
|
|
if (iPitUnderThreat[kk] == iMaxThreatPit) tmpCount++;
|
|
if (tmpCount > 1) {
|
|
bUnSaveable = true;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
go thru every pit and see if it can capture a pit.
|
|
If it can't go to next pit.
|
|
else see if the captured pit is offensive to one of the other pits.
|
|
If not go to next pit.
|
|
if yes, find out the number of stones gained (captured) and the number of
|
|
stones saved (stones no longer threatened because of capture(s))
|
|
*/
|
|
|
|
for (ii = 0, bSaveable = false; !bUnSaveable && ii < NUMPITS; ii++) { // ii for this side.
|
|
if (jYieldsACapture[ii] == -1) continue; //skip the following lines if ii does not yield a capture.
|
|
if ((iSaved = iOtherOffensivePit[jYieldsACapture[ii]]) == -1) { //iSaved lies on this side.
|
|
continue; //the captured pit is not offensive.
|
|
} else {
|
|
bSaveable = true;
|
|
StonesSaved = xpcMove->m_iNumStones[iPlayer][2 + iSaved]; // no. of stones saved via capture.
|
|
StonesGained = xpcMove->m_iNumStones[OtherPlayer][2 + jYieldsACapture[ii]]; //no. of stones gained via capture.
|
|
TotalBenefit = StonesGained + StonesSaved;
|
|
if (maxTotalBenefit < TotalBenefit) {
|
|
maxTotalBenefit = TotalBenefit;
|
|
iStartWithThisCapture = ii;
|
|
}
|
|
} //end if iSaved
|
|
}//end for ii.
|
|
|
|
if (bSaveable && !bUnSaveable)
|
|
xpcMove->m_iValues[iStartWithThisCapture] = 5; //optimum capture
|
|
else
|
|
xpcMove->m_iValues[iMaxThreatPit] = 5; //threat pit can't be saved.
|
|
|
|
}//endif xpcMove->...
|
|
}//endif maxCapture>=maxThreat
|
|
} else { //if bdoFreePitFirst
|
|
xpcMove->m_iValues[iFreePit] = 5;
|
|
}
|
|
bConclusive = true;
|
|
} //end if (bAThreatExists&&baCaptureExists)
|
|
|
|
if ((!bAThreatExists && !bACaptureExists && !bAFreePitExists) || !bConclusive) {
|
|
for (k = 0; k < NUMPITS; k++) {
|
|
xpcMove->m_iValues[k] = xpcMove->m_iNumStones[iPlayer][k + 2] ? 5 : 0; //assign equal values to all.
|
|
}
|
|
}//end 0 0 0
|
|
|
|
xpcMove->m_iNumMoves = iNumMoves ; // store # legal moves
|
|
|
|
|
|
cleanup:
|
|
JXELEAVE(CMnk::DefensiveStaticEvaluation) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
/*
|
|
|
|
*/
|
|
|
|
bool CMnk::TreeAlgo(CMove *xpcMove) {
|
|
return true;
|
|
}
|
|
|
|
//* CMnk::CountStones -- count total stones in configuration
|
|
bool CMnk::CountStones(CMove * xpcMove)
|
|
// xpcMove -- CMove object containing the configuration
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::CountStones) ;
|
|
int iError = 0 ; // error code
|
|
//long lConfigIndex = 0 ; // return value, configuration index
|
|
int iStones ; // total number of stones
|
|
int iPlayer, iPit ; // loop variables
|
|
|
|
for (iStones = 0, iPlayer = 0 ; iPlayer < NUMPLAYERS ; ++iPlayer)
|
|
for (iPit = 0 ; iPit < NUMPITS ; ++iPit)
|
|
iStones += xpcMove->m_iNumStones[iPlayer][iPit + 2] ;
|
|
// count total number of stones
|
|
xpcMove->m_iTotalStones = iStones ; // store it
|
|
|
|
// cleanup:
|
|
|
|
JXELEAVE(CMnk::CountStones) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::GetBestWinCount -- get position value in best win table
|
|
bool CMnk::GetBestWinCount(CMove * xpcMove)
|
|
// xpcMove -- CMove object for position to be evaluated
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::GetBestWinCount) ;
|
|
int iError = 0 ; // error code
|
|
uint lIndex = xpcMove->m_lConfigIndex;
|
|
int iValue = 0; // value from table
|
|
struct FIVE * hpFive ; // ptr to structure of 8 5-bit values
|
|
|
|
if (/*lIndex < 0 ||*/ lIndex >= MAXCONFIGS) {
|
|
iError = 100 ; // index out of range
|
|
goto cleanup ;
|
|
}
|
|
|
|
hpFive = (struct FIVE *)(m_lpCMnkData->m_hpcBestWin
|
|
+ (lIndex / 8) * 5) ;
|
|
// point to group of eight numbers
|
|
|
|
switch (lIndex % 8) {
|
|
case 0:
|
|
iValue = hpFive->v0 ;
|
|
break ;
|
|
|
|
case 1:
|
|
// iValue = hpFive->v1 ;
|
|
iValue = (hpFive->v1a << 2) + (hpFive->v1b) ;
|
|
break ;
|
|
|
|
case 2:
|
|
iValue = hpFive->v2 ;
|
|
break ;
|
|
|
|
case 3:
|
|
// iValue = hpFive->v3 ;
|
|
iValue = (hpFive->v3a << 4) + (hpFive->v3b) ;
|
|
break ;
|
|
|
|
case 4:
|
|
// iValue = hpFive->v4 ;
|
|
iValue = (hpFive->v4a << 1) + (hpFive->v4b) ;
|
|
break ;
|
|
|
|
case 5:
|
|
iValue = hpFive->v5 ;
|
|
break ;
|
|
|
|
case 6:
|
|
// iValue = hpFive->v6 ;
|
|
iValue = (hpFive->v6a << 3) + (hpFive->v6b) ;
|
|
break ;
|
|
|
|
case 7:
|
|
iValue = hpFive->v7 ;
|
|
break ;
|
|
}
|
|
|
|
if (iValue == TABLEUNDEF) // undefined or unspecified
|
|
iValue = BESTWINUNDEF ;
|
|
|
|
else if (iValue > 15) // negative number
|
|
iValue -= 32 ;
|
|
|
|
xpcMove->m_iBestWinValue = iValue ;
|
|
//
|
|
//
|
|
// xpcMove->m_iBestWinValue = (iValue == TABLEUNDEF) ?
|
|
// BESTWINUNDEF : iValue ; // store value
|
|
|
|
cleanup:
|
|
|
|
JXELEAVE(CMnk::GetBestWinCount) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::SetBestWinCount -- set value in best win table
|
|
bool CMnk::SetBestWinCount(CMove * xpcMove)
|
|
// xpcMove -- pointer to CMove object where value is to be set
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::SetBestWinCount) ;
|
|
int iError = 0 ; // error code
|
|
struct FIVE * hpFive ; // ptr to structure of 8 5-bit values
|
|
uint lIndex = xpcMove->m_lConfigIndex;
|
|
int iValue = xpcMove->m_iBestWinValue ; // value from table
|
|
bool bTest = false ; // debugging test
|
|
|
|
if (/*lIndex < 0 ||*/ lIndex >= MAXCONFIGS) {
|
|
iError = 100 ; // index out of range
|
|
goto cleanup ;
|
|
}
|
|
|
|
hpFive = (struct FIVE *)(m_lpCMnkData->m_hpcBestWin
|
|
+ (lIndex / 8) * 5) ;
|
|
// point to group of eight numbers
|
|
|
|
switch (lIndex % 8) {
|
|
case 0:
|
|
hpFive->v0 = iValue ;
|
|
break ;
|
|
|
|
case 1:
|
|
// hpFive->v1 = iValue ;
|
|
hpFive->v1a = iValue >> 2 ;
|
|
hpFive->v1b = iValue ;
|
|
break ;
|
|
|
|
case 2:
|
|
hpFive->v2 = iValue ;
|
|
break ;
|
|
|
|
case 3:
|
|
// hpFive->v3 = iValue ;
|
|
hpFive->v3a = iValue >> 4 ;
|
|
hpFive->v3b = iValue ;
|
|
break ;
|
|
|
|
case 4:
|
|
// hpFive->v4 = iValue ;
|
|
hpFive->v4a = iValue >> 1 ;
|
|
hpFive->v4b = iValue ;
|
|
break ;
|
|
|
|
case 5:
|
|
hpFive->v5 = iValue ;
|
|
break ;
|
|
|
|
case 6:
|
|
// hpFive->v6 = iValue ;
|
|
hpFive->v6a = iValue >> 3 ;
|
|
hpFive->v6b = iValue ;
|
|
break ;
|
|
|
|
case 7:
|
|
hpFive->v7 = iValue ;
|
|
break ;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (m_bDumpPopulate && (lIndex % 10000 == 0
|
|
|| (bTest = iValue > xpcMove->m_iTotalStones
|
|
|| -iValue > xpcMove->m_iTotalStones))) {
|
|
DumpPosition(xpcMove) ;
|
|
if (bTest) {
|
|
DumpBestWinTable() ;
|
|
DebugBreak() ;
|
|
}
|
|
DoPendingEvents() ;
|
|
}
|
|
|
|
//#define THRESHHOLD 20000L
|
|
//#define BUFSIZE 10000
|
|
//
|
|
// if (lIndex >= THRESHHOLD)
|
|
// {
|
|
// static byte FAR cBuffer[BUFSIZE] ;
|
|
//
|
|
// if (lIndex == THRESHHOLD)
|
|
// {
|
|
// _fmemcpy(cBuffer, (char *)m_lpCMnkData->m_hpcBestWin, BUFSIZE) ;
|
|
// DumpBestWinTable() ;
|
|
// }
|
|
///// else if (lIndex >= 104856 // error detected at 104858
|
|
// else if (_fmemcmp(cBuffer, (char *)m_lpCMnkData->m_hpcBestWin,
|
|
// BUFSIZE)) // if first BUFSIZE bytes changed
|
|
// {
|
|
// DumpPosition(xpcMove) ;
|
|
// DumpBestWinTable() ;
|
|
// DebugBreak() ;
|
|
// }
|
|
// }
|
|
|
|
|
|
JXELEAVE(CMnk::SetBestWinCount) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::DumpPosition -- dump contents of CMove object
|
|
bool CMnk::DumpPosition(CMove * xpcMove)
|
|
// xpcMove -- CMove object containing the configuration
|
|
// returns: true if error, false otherwise
|
|
{
|
|
JXENTER(CMnk::DumpPosition) ;
|
|
int iError = 0 ; // error code
|
|
|
|
if (xpcMove->m_bRealMove)
|
|
debugN("(REAL) ");
|
|
|
|
debug("P%d Cfg=%ld w/%d stones, %d moves, sow=%d, "
|
|
"best %d for %d.",
|
|
xpcMove->m_iPlayer, xpcMove->m_lConfigIndex,
|
|
xpcMove->m_iTotalStones, xpcMove->m_iNumMoves,
|
|
(xpcMove->m_xpcPit ? xpcMove->m_xpcPit->m_iPit : -1),
|
|
xpcMove->m_iBestMove, xpcMove->m_iBestWinValue);
|
|
|
|
|
|
debug(" [%3d] %2d %2d %2d %2d %2d %2d H=%d",
|
|
xpcMove->m_iNumStones[1][HOMEINDEX + 2],
|
|
xpcMove->m_iNumStones[1][2], xpcMove->m_iNumStones[1][3],
|
|
xpcMove->m_iNumStones[1][4], xpcMove->m_iNumStones[1][5],
|
|
xpcMove->m_iNumStones[1][6], xpcMove->m_iNumStones[1][7],
|
|
xpcMove->m_iNumStones[1][HANDINDEX + 2]);
|
|
|
|
debug(" %2d %2d %2d %2d %2d %2d [%3d] H=%d\n",
|
|
xpcMove->m_iNumStones[0][7], xpcMove->m_iNumStones[0][6],
|
|
xpcMove->m_iNumStones[0][5], xpcMove->m_iNumStones[0][4],
|
|
xpcMove->m_iNumStones[0][3], xpcMove->m_iNumStones[0][2],
|
|
xpcMove->m_iNumStones[0][HOMEINDEX + 2],
|
|
xpcMove->m_iNumStones[0][HANDINDEX + 2]);
|
|
DoPendingEvents() ;
|
|
|
|
// cleanup:
|
|
|
|
JXELEAVE(CMnk::DumpPosition) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
//* CMnk::DumpBestWinTable -- dump fields of best win table
|
|
bool CMnk::DumpBestWinTable(long lLow,
|
|
long lHigh) {
|
|
// lLow -- low end of configuration range
|
|
// lHigh -- high end of configuration range
|
|
// returns: true if error, false otherwise
|
|
JXENTER(CMnk::DumpBestWinTable) ;
|
|
int iError = 0 ; // error code
|
|
CMove cMove ; // dummy move structure
|
|
long lEol ; // config index at end of line
|
|
|
|
for (lEol = lLow + 23 ; lEol <= lHigh + 23 ; lEol += 24) {
|
|
if (lEol > lHigh)
|
|
lEol = lHigh ;
|
|
debugN("Table[%ld-%ld]:", lLow, lEol) ;
|
|
|
|
while (lLow <= lEol) { // loop thru values
|
|
cMove.m_lConfigIndex = lLow++ ;
|
|
GetBestWinCount(&cMove) ;
|
|
if (cMove.m_iBestWinValue == BESTWINUNDEF)
|
|
debugN(" U") ;
|
|
else
|
|
debugN(" %d", cMove.m_iBestWinValue) ;
|
|
}
|
|
debugN("\n") ;
|
|
}
|
|
debugN("\n") ;
|
|
|
|
// cleanup:
|
|
|
|
JXELEAVE(CMnk::DumpBestWinTable) ;
|
|
RETURN(iError != 0) ;
|
|
}
|
|
|
|
} // namespace Mankala
|
|
} // namespace HodjNPodj
|
|
} // namespace Bagel
|