[update] Improve heuristic.
This commit is contained in:
+22
-5
@@ -1,6 +1,6 @@
|
||||
/* ============================================================
|
||||
* Connect Four — Browser Edition
|
||||
* A single-file game: AI (minimax + alpha-beta), demo mode,
|
||||
* A single-file game: AI (minimax + alpha-beta + heuristic), demo mode,
|
||||
* game log (localStorage), blunder mode, idle timeout.
|
||||
*
|
||||
* Include this script in an HTML page that has:
|
||||
@@ -168,6 +168,7 @@ function scanBoard(b) {
|
||||
|
||||
function evaluateBoard(b, aiP, huP) {
|
||||
let score = 0;
|
||||
let aiThreats = 0, huThreats = 0;
|
||||
|
||||
// Center column bonus
|
||||
for (let r = 0; r < ROWS; r++) {
|
||||
@@ -177,16 +178,27 @@ function evaluateBoard(b, aiP, huP) {
|
||||
|
||||
// Score a window of 4 cells by piece counts
|
||||
function scoreWindow(c, r, dc, dr) {
|
||||
let ai = 0, hu = 0;
|
||||
let ai = 0, hu = 0, emptyC = -1, emptyR = -1;
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const v = b[c + i * dc][r + i * dr];
|
||||
const cc = c + i * dc;
|
||||
const rr = r + i * dr;
|
||||
const v = b[cc][rr];
|
||||
if (v === aiP) ai++;
|
||||
else if (v === huP) hu++;
|
||||
else { emptyC = cc; emptyR = rr; }
|
||||
}
|
||||
if (ai > 0 && hu > 0) return 0;
|
||||
if (ai === 3) return 50;
|
||||
if (ai === 3) {
|
||||
aiThreats++;
|
||||
const playable = emptyR === 0 || b[emptyC][emptyR - 1] !== 0;
|
||||
return playable ? 100 : 40;
|
||||
}
|
||||
if (ai === 2) return 5;
|
||||
if (hu === 3) return -50;
|
||||
if (hu === 3) {
|
||||
huThreats++;
|
||||
const playable = emptyR === 0 || b[emptyC][emptyR - 1] !== 0;
|
||||
return playable ? -100 : -40;
|
||||
}
|
||||
if (hu === 2) return -5;
|
||||
return 0;
|
||||
}
|
||||
@@ -208,6 +220,10 @@ function evaluateBoard(b, aiP, huP) {
|
||||
for (let c = 0; c <= COLS - 4; c++)
|
||||
score += scoreWindow(c, r, 1, -1);
|
||||
|
||||
// Fork bonus: multiple threats are disproportionately dangerous
|
||||
if (aiThreats >= 2) score += 200;
|
||||
if (huThreats >= 2) score -= 200;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@@ -324,6 +340,7 @@ function checkGameEnd() {
|
||||
|
||||
if (gameState !== State.DEMO) {
|
||||
games = logGame(games, gameMenuMode, gameLevel, won ? w : 0, currentMoves);
|
||||
console.log(`Game: ${currentMoves} → ${won ? playerName(w) + " wins" : "Draw"}`);
|
||||
}
|
||||
gameState = won ? State.FINISHED_WIN : State.FINISHED_DRAW;
|
||||
demoResetTimer = performance.now() / 1000;
|
||||
|
||||
+29
-4
@@ -189,25 +189,50 @@ int8_t scanBoardB(const int8_t b[][ROWS])
|
||||
int evaluateBoardB(const int8_t b[][ROWS], int8_t aiP, int8_t huP)
|
||||
{
|
||||
int score = 0;
|
||||
int aiThreats = 0, huThreats = 0;
|
||||
|
||||
// Center column bonus
|
||||
for (int r = 0; r < ROWS; r++) {
|
||||
if (b[3][r] == aiP) score += 3;
|
||||
else if (b[3][r] == huP) score -= 3;
|
||||
}
|
||||
|
||||
// Score a window of 4 cells — playability-aware for 3-in-a-row
|
||||
auto sw = [&](int c, int r, int dc, int dr) -> int {
|
||||
int ai = 0, hu = 0;
|
||||
int emptyC = -1, emptyR = -1;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int8_t v = b[c + i*dc][r + i*dr];
|
||||
if (v == aiP) ai++; else if (v == huP) hu++;
|
||||
int cc = c + i * dc, rr = r + i * dr;
|
||||
int8_t v = b[cc][rr];
|
||||
if (v == aiP) ai++;
|
||||
else if (v == huP) hu++;
|
||||
else { emptyC = cc; emptyR = rr; }
|
||||
}
|
||||
if (ai && hu) return 0;
|
||||
if (ai == 3) return 50; if (ai == 2) return 5;
|
||||
if (hu == 3) return -50; if (hu == 2) return -5;
|
||||
if (ai == 3) {
|
||||
aiThreats++;
|
||||
bool playable = (emptyR == 0) || (b[emptyC][emptyR - 1] != 0);
|
||||
return playable ? 100 : 40;
|
||||
}
|
||||
if (ai == 2) return 5;
|
||||
if (hu == 3) {
|
||||
huThreats++;
|
||||
bool playable = (emptyR == 0) || (b[emptyC][emptyR - 1] != 0);
|
||||
return playable ? -100 : -40;
|
||||
}
|
||||
if (hu == 2) return -5;
|
||||
return 0;
|
||||
};
|
||||
|
||||
for (int r = 0; r < 6; r++) for (int c = 0; c < 4; c++) score += sw(c,r,1,0);
|
||||
for (int r = 0; r < 3; r++) for (int c = 0; c < 7; c++) score += sw(c,r,0,1);
|
||||
for (int r = 0; r < 3; r++) for (int c = 0; c < 4; c++) score += sw(c,r,1,1);
|
||||
for (int r = 3; r < 6; r++) for (int c = 0; c < 4; c++) score += sw(c,r,1,-1);
|
||||
|
||||
// Fork bonus: multiple simultaneous threats are disproportionately dangerous
|
||||
if (aiThreats >= 2) score += 200;
|
||||
if (huThreats >= 2) score -= 200;
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user