Skip to content

Commit

Permalink
Implement undoMove()
Browse files Browse the repository at this point in the history
This is much more efficient than using copyPosition, which is
important in the minimax algorithm where speed is essential.

Also, if we want to start using the libgameai later, this makes it
easier to port kjumpingcube.
  • Loading branch information
ingwal committed Feb 7, 2014
1 parent 42397f4 commit 59e27e8
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 9 deletions.
71 changes: 68 additions & 3 deletions ai_box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,44 @@ void AI_Box::resizeBox (int side)
createBox (side);
}

bool AI_Box::doMove (Player player, int index, QList<int> * steps)

// ----------------------------------------------------------------
// Moves


bool AI_Box::doMove (Player player, int index, MoveUndodata * undodata, QList<int> * steps)
{
// Check for move legality.
Player oldOwner = m_owners[index];
if ((oldOwner != player) && (oldOwner != Nobody)) {
qDebug() << "ILLEGAL MOVE: player" << player << "old" << oldOwner <<
"at" << index/m_side << index%m_side;
qDebug() << "ILLEGAL MOVE: player" << player << "old" << oldOwner
<< "at" << index/m_side << index%m_side;

return false; // The move is not valid.
}

if (undodata) {
undodata->oldCubesToWin[Nobody] = m_cubesToWin[Nobody];
undodata->oldCubesToWin[One] = m_cubesToWin[One];
undodata->oldCubesToWin[Two] = m_cubesToWin[Two];
}

// Bitfield to mark saved cube indices.
quint64 savedCubes[(maxSide * maxSide - 1) / 64 + 1];
for (int i = 0; i < (maxSide * maxSide - 1) / 64 + 1; ++i)
savedCubes[i] = 0ULL;

// Save old values of changed cubes (owner + value) into the
// MoveUndodata to be restored by undoMove().
int saveCubes = 0;
if (undodata) {
undodata->changedCubes[saveCubes++] = ((index << 8)
| (m_owners[index] << 4)
| (m_values[index] << 0));
savedCubes[index / 64] |= 1ULL << (index % 64);

}

m_stackPtr = -1;
m_owners[index] = player; // Take ownership if not already owned.
if (m_maxValues [index] == m_values [index]++) { // Increase the cube.
Expand All @@ -106,6 +135,7 @@ bool AI_Box::doMove (Player player, int index, QList<int> * steps)
while (m_stackPtr >= 0) {
// Pop the stack and decrease an overloaded cube.
index = m_stack [m_stackPtr--];

// fprintf (stderr, " Expand at %d, value %d\n", index, m_values[index]);
m_values[index] = m_values[index] - m_maxValues[index];

Expand All @@ -121,6 +151,13 @@ bool AI_Box::doMove (Player player, int index, QList<int> * steps)
if ((indexN = m_neighbors [offset + nb]) < 0)
continue; // No neighbor on this side.

if (undodata && !(savedCubes[indexN / 64] & (1ULL << (indexN % 64)))) {
undodata->changedCubes[saveCubes++] = ((indexN << 8)
| (m_owners[indexN] << 4)
| (m_values[indexN] << 0));
savedCubes[indexN / 64] |= 1ULL << (indexN % 64);
}

// Increase the neighbor and take over ownership.
oldOwner = m_owners[indexN];
m_owners[indexN] = player;
Expand All @@ -147,17 +184,45 @@ bool AI_Box::doMove (Player player, int index, QList<int> * steps)
if (steps) {
steps->append (0);
}

// Mark the end of changed cubes in the undodata.
if (undodata) {
undodata->changedCubes[saveCubes++] = 0xffff;
}

// printBox();
// fprintf (stderr, "PLAYER WON\n");
return true;
}
// printBox();
} // End while()

if (undodata) {
undodata->changedCubes[saveCubes++] = 0xffff;
}

// printBox();
return false;
}

void AI_Box::undoMove (MoveUndodata * undodata)
{
m_cubesToWin[Nobody] = undodata->oldCubesToWin[Nobody];
m_cubesToWin[One] = undodata->oldCubesToWin[One];
m_cubesToWin[Two] = undodata->oldCubesToWin[Two];

for (int i = 0; undodata->changedCubes[i] != 0xffff; ++i) {
int index = (undodata->changedCubes[i] >> 8) & 0xff;
m_owners[index] = Player((undodata->changedCubes[i] >> 4) & 0xf);
m_values[index] = (undodata->changedCubes[i] >> 0) & 0xf;
}
}


// ----------------------------------------------------------------
// Game history


void AI_Box::copyPosition (Player player, bool isAI, int index)
{
#if AILog > 4
Expand Down
13 changes: 12 additions & 1 deletion ai_box.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,18 @@ class AI_Box : public QObject
}
}

bool doMove (Player player, int index, QList<int> * steps = 0);
// This struct is passed to doMove() and is used to store
// everything that is needed by undoMove() to actually undo it.
struct MoveUndodata {
Player oldPlayer; // The player previously to move in the position
int oldCubesToWin[3];
quint16 changedCubes[maxSide * maxSide]; // 8 bits index, 4 bits owner and 4 bits value
// end with 0xffff
};

bool doMove (Player player, int index,
MoveUndodata * undodata = 0, QList<int> * steps = 0);
void undoMove (MoveUndodata * undodata);
#if AILog > 0
void printBox();
#endif
Expand Down
9 changes: 5 additions & 4 deletions ai_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,9 @@ Move AI_Main::tryMoves (Player player, int level)
<< "val" << cubesToMove[n].val;
#endif

copyPosition (player, true, 0);
bool won = doMove (player, cubesToMove[n].index);
MoveUndodata undodata;
bool won = doMove (player, cubesToMove[n].index, &undodata);

#if AILog > 2
n_simulate++;
#endif
Expand All @@ -298,7 +299,7 @@ Move AI_Main::tryMoves (Player player, int level)
<< "move" << cubesToMove[n].index/m_side
<< cubesToMove[n].index%m_side;
#endif
undoPosition (player);
undoMove(&undodata);
break;
}
else if (level >= m_maxLevel) {
Expand Down Expand Up @@ -344,7 +345,7 @@ Move AI_Main::tryMoves (Player player, int level)
#endif
}
Player p = player;
undoPosition (player);
undoMove(&undodata);
if (p != player) qDebug() << "ERROR: PLAYER CHANGED: from" << p <<
"to" << player;
if (m_stopped) {
Expand Down
2 changes: 1 addition & 1 deletion game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ void Game::doMove (int index)
emit setAction (UNDO, true); // Update Undo and Redo actions.
emit setAction (REDO, false);
m_steps->clear();
bool won = m_box->doMove (m_currentPlayer, index, m_steps);
bool won = m_box->doMove (m_currentPlayer, index, 0, m_steps);
#if AILog > 1
qDebug() << "GAME WON?" << won << "STEPS" << (* m_steps);
// m_box->printBox();
Expand Down

0 comments on commit 59e27e8

Please sign in to comment.