[fix] AI killer instinct, border toggle, and animation timing.
This commit is contained in:
+194
-158
@@ -5,11 +5,14 @@
|
||||
#include <WebServer.h>
|
||||
#include <Preferences.h>
|
||||
|
||||
#ifndef SHOW_BORDER
|
||||
#define SHOW_BORDER 1
|
||||
#endif
|
||||
|
||||
#define NUM_LEDS 64
|
||||
const int COLS = 7;
|
||||
const int ROWS = 6;
|
||||
|
||||
// --- Configuration & Globals ---
|
||||
CRGB leds[NUM_LEDS];
|
||||
Encoder myEnc(ENC_A, ENC_B);
|
||||
WebServer server(80);
|
||||
@@ -29,13 +32,14 @@ State gameState = MENU;
|
||||
|
||||
int8_t menuMode = 0;
|
||||
int8_t currentPlayer = 1;
|
||||
int8_t winnerPlayer = 0; // Tracks who actually won for the flashing effect
|
||||
int8_t winnerPlayer = 0;
|
||||
int8_t activeCol = 3;
|
||||
long oldEncPos = -999;
|
||||
uint32_t lastActivityTime = 0;
|
||||
uint32_t demoResetTimer = 0;
|
||||
bool isDemoOver = false;
|
||||
uint8_t demoPly = 4;
|
||||
bool abortAi = false;
|
||||
|
||||
uint8_t current_look_ahead;
|
||||
uint8_t current_brightness;
|
||||
@@ -52,22 +56,21 @@ void drawStaticUI();
|
||||
void renderBoard();
|
||||
int getFirstEmptyRow(int col);
|
||||
bool isBoardFull();
|
||||
int8_t scanBoard(); // Changed to return the winner ID
|
||||
void updateThinkingVisuals(int8_t p, int8_t col);
|
||||
int8_t scanBoard();
|
||||
void updateThinkingVisuals(int8_t playerColor, int8_t column);
|
||||
void animateDrop(int col, int player);
|
||||
void moveDiscToCol(int startCol, int targetCol, int player, int speed);
|
||||
int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiP, int8_t huP, int8_t rootCol);
|
||||
void performAiMove(int8_t aiP);
|
||||
int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiPlayer, int8_t humanPlayer, int8_t rootCol);
|
||||
void performAiMove(int8_t aiPlayer);
|
||||
void showMenu();
|
||||
int getDynamicPly();
|
||||
|
||||
// --- Utility & Rendering ---
|
||||
|
||||
int getIdx(int x, int y) { return (y * 8) + x; }
|
||||
|
||||
void drawStaticUI()
|
||||
{
|
||||
FastLED.clear();
|
||||
#if SHOW_BORDER == 1
|
||||
CRGB borderColor = CRGB::Blue;
|
||||
if (gameState == DEMO || gameState >= 2)
|
||||
{
|
||||
@@ -78,48 +81,57 @@ void drawStaticUI()
|
||||
leds[getIdx(x, 1)] = borderColor;
|
||||
for (int y = 1; y < 8; y++)
|
||||
leds[getIdx(7, y)] = borderColor;
|
||||
#endif
|
||||
}
|
||||
|
||||
void renderBoard()
|
||||
{
|
||||
drawStaticUI();
|
||||
for (int c = 0; c < COLS; c++)
|
||||
for (int column = 0; column < COLS; column++)
|
||||
{
|
||||
for (int r = 0; r < ROWS; r++)
|
||||
for (int row = 0; row < ROWS; row++)
|
||||
{
|
||||
if (board[c][r] == 1)
|
||||
leds[getIdx(c, 7 - r)] = CRGB::Yellow;
|
||||
if (board[c][r] == 2)
|
||||
leds[getIdx(c, 7 - r)] = CRGB::Red;
|
||||
if (board[column][row] == 1)
|
||||
leds[getIdx(column, 7 - row)] = CRGB::Yellow;
|
||||
if (board[column][row] == 2)
|
||||
leds[getIdx(column, 7 - row)] = CRGB::Red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getFirstEmptyRow(int col)
|
||||
{
|
||||
for (int r = 0; r < ROWS; r++)
|
||||
for (int row = 0; row < ROWS; row++)
|
||||
{
|
||||
if (board[col][r] == 0)
|
||||
return r;
|
||||
if (board[col][row] == 0)
|
||||
return row;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isBoardFull()
|
||||
{
|
||||
for (int column = 0; column < COLS; column++)
|
||||
{
|
||||
if (board[column][ROWS - 1] == 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int getDynamicPly()
|
||||
{
|
||||
if (!progressive_difficulty && gameState != DEMO)
|
||||
return current_look_ahead;
|
||||
int count = 0;
|
||||
for (int c = 0; c < COLS; c++)
|
||||
for (int r = 0; r < ROWS; r++)
|
||||
if (board[c][r] != 0)
|
||||
count++;
|
||||
return constrain(current_look_ahead + (count / 7), 1, 10);
|
||||
int occupiedCount = 0;
|
||||
for (int column = 0; column < COLS; column++)
|
||||
for (int row = 0; row < ROWS; row++)
|
||||
if (board[column][row] != 0)
|
||||
occupiedCount++;
|
||||
return constrain(current_look_ahead + (occupiedCount / 7), 1, 10);
|
||||
}
|
||||
|
||||
// --- Visuals & Animations ---
|
||||
|
||||
void updateThinkingVisuals(int8_t p, int8_t col)
|
||||
void updateThinkingVisuals(int8_t playerColor, int8_t column)
|
||||
{
|
||||
static uint32_t lastCycle = 0;
|
||||
if (millis() - lastCycle < 25)
|
||||
@@ -139,8 +151,8 @@ void updateThinkingVisuals(int8_t p, int8_t col)
|
||||
}
|
||||
for (int x = 0; x < COLS; x++)
|
||||
leds[getIdx(x, 0)] = CRGB::Black;
|
||||
CRGB aiColor = (p == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
leds[getIdx(col, 0)] = aiColor.nscale8(aiBrightness);
|
||||
CRGB aiColor = (playerColor == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
leds[getIdx(column, 0)] = aiColor.nscale8(aiBrightness);
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
@@ -149,12 +161,12 @@ void animateDrop(int col, int player)
|
||||
int targetRow = getFirstEmptyRow(col);
|
||||
if (targetRow == -1)
|
||||
return;
|
||||
for (int r = 5; r >= targetRow; r--)
|
||||
for (int row = 5; row >= targetRow; row--)
|
||||
{
|
||||
renderBoard();
|
||||
leds[getIdx(col, 7 - r)] = (player == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
leds[getIdx(col, 7 - row)] = (player == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
FastLED.show();
|
||||
delay(max(20, 80 - (5 - r) * 15));
|
||||
delay(max(20, 80 - (5 - row) * 15));
|
||||
}
|
||||
board[col][targetRow] = player;
|
||||
renderBoard();
|
||||
@@ -165,7 +177,7 @@ void moveDiscToCol(int startCol, int targetCol, int player, int speed)
|
||||
{
|
||||
int current = startCol;
|
||||
CRGB pColor = (player == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
while (current != targetCol)
|
||||
while (current != targetCol && !abortAi)
|
||||
{
|
||||
leds[getIdx(current, 0)] = CRGB::Black;
|
||||
current += (targetCol > current) ? 1 : -1;
|
||||
@@ -173,59 +185,52 @@ void moveDiscToCol(int startCol, int targetCol, int player, int speed)
|
||||
leds[getIdx(current, 0)] = pColor;
|
||||
FastLED.show();
|
||||
delay(speed);
|
||||
if (digitalRead(ENC_SW) == LOW)
|
||||
abortAi = true;
|
||||
}
|
||||
activeCol = targetCol;
|
||||
}
|
||||
|
||||
// --- AI Engine ---
|
||||
|
||||
bool isBoardFull()
|
||||
{
|
||||
for (int c = 0; c < COLS; c++)
|
||||
if (board[c][5] == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
int8_t scanBoard()
|
||||
{
|
||||
memset(winMask, 0, sizeof(winMask));
|
||||
auto check = [&](int column, int row, int columnOffset, int rowOffset)
|
||||
auto checkMatch = [&](int col, int row, int dCol, int dRow)
|
||||
{
|
||||
int8_t postion = board[column][row];
|
||||
if (postion != 0 && board[column + columnOffset][row + rowOffset] == postion && board[column + 2 * columnOffset][row + 2 * rowOffset] == postion && board[column + 3 * columnOffset][row + 3 * rowOffset] == postion)
|
||||
int8_t pAtPos = board[col][row];
|
||||
if (pAtPos != 0 && board[col + dCol][row + dRow] == pAtPos &&
|
||||
board[col + 2 * dCol][row + 2 * dRow] == pAtPos && board[col + 3 * dCol][row + 3 * dRow] == pAtPos)
|
||||
{
|
||||
for (int index = 0; index < 4; index++)
|
||||
winMask[getIdx(column + index * columnOffset, 7 - (row + index * rowOffset))] = true;
|
||||
return postion;
|
||||
for (int i = 0; i < 4; i++)
|
||||
winMask[getIdx(col + i * dCol, 7 - (row + i * dRow))] = true;
|
||||
return pAtPos;
|
||||
}
|
||||
return (int8_t)0;
|
||||
};
|
||||
for (int row = 0; row < 6; row++)
|
||||
for (int column = 0; column < 4; column++)
|
||||
for (int r = 0; r < 6; r++)
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int8_t res = check(column, row, 1, 0);
|
||||
int8_t res = checkMatch(c, r, 1, 0);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
for (int row = 0; row < 3; row++)
|
||||
for (int column = 0; column < 7; column++)
|
||||
for (int r = 0; r < 3; r++)
|
||||
for (int c = 0; c < 7; c++)
|
||||
{
|
||||
int8_t res = check(column, row, 0, 1);
|
||||
int8_t res = checkMatch(c, r, 0, 1);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
for (int row = 0; row < 3; row++)
|
||||
for (int column = 0; column < 4; column++)
|
||||
for (int r = 0; r < 3; r++)
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int8_t res = check(column, row, 1, 1);
|
||||
int8_t res = checkMatch(c, r, 1, 1);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
for (int row = 3; row < 6; row++)
|
||||
for (int column = 0; column < 4; column++)
|
||||
for (int r = 3; r < 6; r++)
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int8_t res = check(column, row, 1, -1);
|
||||
int8_t res = checkMatch(c, r, 1, -1);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
@@ -234,55 +239,69 @@ int8_t scanBoard()
|
||||
|
||||
int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiPlayer, int8_t humanPlayer, int8_t rootCol)
|
||||
{
|
||||
if (depth % 2 == 0)
|
||||
{
|
||||
if (digitalRead(ENC_SW) == LOW)
|
||||
{
|
||||
abortAi = true;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (depth >= current_look_ahead - 1)
|
||||
updateThinkingVisuals(aiPlayer, rootCol);
|
||||
else
|
||||
yield();
|
||||
if (abortAi)
|
||||
return 0;
|
||||
|
||||
// Check for wins within minimax
|
||||
int8_t win = scanBoard();
|
||||
if (win == aiPlayer)
|
||||
return 1000 + depth;
|
||||
if (win == humanPlayer)
|
||||
return -1000 - depth;
|
||||
int8_t winner = scanBoard();
|
||||
if (winner == aiPlayer)
|
||||
return 1000 + depth; // Win sooner is better
|
||||
if (winner == humanPlayer)
|
||||
return -1000 - depth; // Lose later is better
|
||||
if (depth == 0 || isBoardFull())
|
||||
return 0;
|
||||
|
||||
int order[] = {3, 2, 4, 1, 5, 0, 6};
|
||||
int best = isMax ? -2000 : 2000;
|
||||
for (int column : order)
|
||||
int colOrder[] = {3, 2, 4, 1, 5, 0, 6};
|
||||
int bestScore = isMax ? -10000 : 10000;
|
||||
|
||||
for (int column : colOrder)
|
||||
{
|
||||
if (abortAi)
|
||||
return 0;
|
||||
int row = getFirstEmptyRow(column);
|
||||
if (row != -1)
|
||||
{
|
||||
board[column][row] = isMax ? aiPlayer : humanPlayer;
|
||||
int val = minimax(depth - 1, alpha, beta, !isMax, aiPlayer, humanPlayer, (depth == current_look_ahead ? column : rootCol));
|
||||
int score = minimax(depth - 1, alpha, beta, !isMax, aiPlayer, humanPlayer, (depth == current_look_ahead ? column : rootCol));
|
||||
board[column][row] = 0;
|
||||
if (isMax)
|
||||
{
|
||||
best = max(best, val);
|
||||
alpha = max(alpha, best);
|
||||
bestScore = max(bestScore, score);
|
||||
alpha = max(alpha, bestScore);
|
||||
}
|
||||
else
|
||||
{
|
||||
best = min(best, val);
|
||||
beta = min(beta, best);
|
||||
bestScore = min(bestScore, score);
|
||||
beta = min(beta, bestScore);
|
||||
}
|
||||
if (beta <= alpha)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
return bestScore;
|
||||
}
|
||||
|
||||
void performAiMove(int8_t aiPlayer)
|
||||
{
|
||||
abortAi = false;
|
||||
int humanPlayer = (aiPlayer == 1) ? 2 : 1;
|
||||
int bestScore = -30000;
|
||||
int bestCol = 3;
|
||||
int originalPly = current_look_ahead;
|
||||
current_look_ahead = (gameState == DEMO) ? demoPly : getDynamicPly();
|
||||
|
||||
// PHASE 1: Immediate Win Check (OFFENSE)
|
||||
for (int column = 0; column < COLS; column++)
|
||||
{
|
||||
int row = getFirstEmptyRow(column);
|
||||
@@ -293,20 +312,34 @@ void performAiMove(int8_t aiPlayer)
|
||||
{
|
||||
board[column][row] = 0;
|
||||
bestCol = column;
|
||||
goto finalize;
|
||||
}
|
||||
board[column][row] = humanPlayer;
|
||||
if (current_look_ahead >= 2 && scanBoard() == humanPlayer)
|
||||
{
|
||||
board[column][row] = 0;
|
||||
bestCol = column;
|
||||
goto finalize;
|
||||
goto finalizeMove; // TAKE THE WIN IMMEDIATELY
|
||||
}
|
||||
board[column][row] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 2: Immediate Block Check (DEFENSE)
|
||||
for (int column = 0; column < COLS; column++)
|
||||
{
|
||||
int row = getFirstEmptyRow(column);
|
||||
if (row != -1)
|
||||
{
|
||||
board[column][row] = humanPlayer;
|
||||
if (scanBoard() == humanPlayer)
|
||||
{
|
||||
board[column][row] = 0;
|
||||
bestCol = column;
|
||||
goto finalizeMove; // MUST BLOCK
|
||||
}
|
||||
board[column][row] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// PHASE 3: Minimax Look-ahead
|
||||
for (int column : {3, 2, 4, 1, 5, 0, 6})
|
||||
{
|
||||
if (abortAi)
|
||||
goto finalizeMove;
|
||||
int row = getFirstEmptyRow(column);
|
||||
if (row != -1)
|
||||
{
|
||||
@@ -320,34 +353,29 @@ void performAiMove(int8_t aiPlayer)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((gameState == DEMO || blunder_enabled) && random(100) < 20)
|
||||
{
|
||||
int rCol = random(0, 7);
|
||||
if (getFirstEmptyRow(rCol) != -1)
|
||||
bestCol = rCol;
|
||||
}
|
||||
finalize:
|
||||
current_look_ahead = originalPly;
|
||||
moveDiscToCol(activeCol, bestCol, aiPlayer, 100);
|
||||
delay(450);
|
||||
animateDrop(bestCol, aiPlayer);
|
||||
}
|
||||
|
||||
// --- Web Portal ---
|
||||
if ((gameState == DEMO || blunder_enabled) && random(100) < 20 && !abortAi)
|
||||
{
|
||||
int randomColumn = random(0, 7);
|
||||
if (getFirstEmptyRow(randomColumn) != -1)
|
||||
bestCol = randomColumn;
|
||||
}
|
||||
|
||||
finalizeMove:
|
||||
current_look_ahead = originalPly;
|
||||
if (!abortAi)
|
||||
{
|
||||
moveDiscToCol(activeCol, bestCol, aiPlayer, 100);
|
||||
delay(450);
|
||||
animateDrop(bestCol, aiPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
void handleRoot()
|
||||
{
|
||||
String html = "<html><head><meta name='viewport' content='width=device-width, initial-scale=1'>"
|
||||
"<style>body{font-family:sans-serif;background:#121212;color:white;text-align:center;}"
|
||||
" .card{background:#222;padding:25px;border-radius:15px;display:inline-block;margin-top:20px;}"
|
||||
" input{width:100%;padding:10px;margin:10px 0;border-radius:5px;border:none;}</style></head><body>";
|
||||
html += "<h1>Connect 4 Admin</h1><div class='card'><form action='/save' method='POST'>";
|
||||
html += "Base AI Ply (1-10):<input type='number' name='ply' value='" + String(current_look_ahead) + "'>";
|
||||
html += "Brightness (5-255):<input type='number' name='br' value='" + String(current_brightness) + "'>";
|
||||
html += "Idle Timeout (Sec):<input type='number' name='idle' value='" + String(current_idle_timeout_ms / 1000) + "'>";
|
||||
html += "Enable Blunders: <input type='checkbox' name='blunder' " + String(blunder_enabled ? "checked" : "") + "><br>";
|
||||
html += "Evolution Mode: <input type='checkbox' name='evolve' " + String(progressive_difficulty ? "checked" : "") + "><br><br>";
|
||||
html += "<input type='submit' value='Save Settings' style='background:#28a745;color:white;font-weight:bold;'></form></div></body></html>";
|
||||
String html = "<html><head><meta name='viewport' content='width=device-width, initial-scale=1'><style>body{font-family:sans-serif;background:#121212;color:white;text-align:center;} .card{background:#222;padding:25px;border-radius:15px;display:inline-block;margin-top:20px;} input{width:100%;padding:10px;margin:10px 0;border-radius:5px;border:none;}</style></head><body><h1>Connect 4 Admin</h1><div class='card'><form action='/save' method='POST'>";
|
||||
html += "Base AI Ply:<input type='number' name='ply' value='" + String(current_look_ahead) + "'>Brightness:<input type='number' name='br' value='" + String(current_brightness) + "'>Idle Timeout (s):<input type='number' name='idle' value='" + String(current_idle_timeout_ms / 1000) + "'>";
|
||||
html += "Blunders: <input type='checkbox' name='blunder' " + String(blunder_enabled ? "checked" : "") + "><br>Evolution: <input type='checkbox' name='evolve' " + String(progressive_difficulty ? "checked" : "") + "><br><br><input type='submit' value='Save' style='background:#28a745;color:white;'></form></div></body></html>";
|
||||
server.send(200, "text/html", html);
|
||||
}
|
||||
|
||||
@@ -366,9 +394,8 @@ void handleSave()
|
||||
}
|
||||
if (server.hasArg("idle"))
|
||||
{
|
||||
uint32_t s = server.arg("idle").toInt();
|
||||
current_idle_timeout_ms = s * 1000;
|
||||
prefs.putUInt("idle", s);
|
||||
current_idle_timeout_ms = server.arg("idle").toInt() * 1000;
|
||||
prefs.putUInt("idle", current_idle_timeout_ms / 1000);
|
||||
}
|
||||
blunder_enabled = server.hasArg("blunder");
|
||||
prefs.putBool("blunder", blunder_enabled);
|
||||
@@ -382,19 +409,21 @@ void showMenu()
|
||||
{
|
||||
isDemoOver = false;
|
||||
FastLED.clear();
|
||||
#if SHOW_BORDER == 1
|
||||
for (int x = 0; x < 7; x++)
|
||||
leds[getIdx(x, 1)] = CRGB::Blue;
|
||||
for (int y = 1; y < 8; y++)
|
||||
leds[getIdx(7, y)] = CRGB::Blue;
|
||||
#endif
|
||||
if (menuMode < 2)
|
||||
{
|
||||
CRGB p1Col = (menuMode == 1) ? CRGB::Red : CRGB::Yellow;
|
||||
CRGB pCol = (menuMode == 1) ? CRGB::Red : CRGB::Yellow;
|
||||
for (int y = 3; y <= 6; y++)
|
||||
leds[getIdx(3, y)] = p1Col;
|
||||
leds[getIdx(2, 3)] = p1Col;
|
||||
leds[getIdx(4, 3)] = p1Col;
|
||||
leds[getIdx(2, 6)] = p1Col;
|
||||
leds[getIdx(4, 6)] = p1Col;
|
||||
leds[getIdx(3, y)] = pCol;
|
||||
leds[getIdx(2, 3)] = pCol;
|
||||
leds[getIdx(4, 3)] = pCol;
|
||||
leds[getIdx(2, 6)] = pCol;
|
||||
leds[getIdx(4, 6)] = pCol;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,7 +453,6 @@ void setup()
|
||||
current_idle_timeout_ms = prefs.getUInt("idle", 60) * 1000;
|
||||
blunder_enabled = prefs.getBool("blunder", false);
|
||||
progressive_difficulty = prefs.getBool("evolve", false);
|
||||
|
||||
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(current_brightness);
|
||||
pinMode(ENC_SW, INPUT_PULLUP);
|
||||
@@ -446,18 +474,21 @@ void loop()
|
||||
{
|
||||
if (gameState >= 2 || gameState == DEMO)
|
||||
{
|
||||
for (int index = 0; index < 10; index++)
|
||||
{
|
||||
fadeToBlackBy(leds, NUM_LEDS, 32);
|
||||
FastLED.show();
|
||||
delay(30);
|
||||
}
|
||||
delay(500);
|
||||
gameState = MENU;
|
||||
abortAi = true;
|
||||
memset(board, 0, sizeof(board));
|
||||
winnerPlayer = 0;
|
||||
demoResetTimer = 0;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
fadeToBlackBy(leds, NUM_LEDS, 40);
|
||||
FastLED.show();
|
||||
delay(20);
|
||||
}
|
||||
gameState = MENU;
|
||||
showMenu();
|
||||
lastActivityTime = millis();
|
||||
oldEncPos = newPos;
|
||||
lastActivityTime = millis();
|
||||
delay(300);
|
||||
return;
|
||||
}
|
||||
lastActivityTime = millis();
|
||||
@@ -503,12 +534,14 @@ void loop()
|
||||
{
|
||||
activeCol = (newPos % 7 + 7) % 7;
|
||||
oldEncPos = newPos;
|
||||
lastActivityTime = millis();
|
||||
}
|
||||
renderBoard();
|
||||
leds[getIdx(activeCol, 0)] = (currentPlayer == 1) ? CRGB::Yellow : CRGB::Red;
|
||||
FastLED.show();
|
||||
if (pressed)
|
||||
{
|
||||
lastActivityTime = millis();
|
||||
int row = getFirstEmptyRow(activeCol);
|
||||
if (row != -1)
|
||||
{
|
||||
@@ -528,18 +561,22 @@ void loop()
|
||||
{
|
||||
if (menuMode < 2)
|
||||
{
|
||||
int8_t aiPlayer = (menuMode == 0) ? 2 : 1;
|
||||
performAiMove(aiPlayer);
|
||||
winnerPlayer = scanBoard();
|
||||
if (winnerPlayer != 0)
|
||||
int8_t aiP = (menuMode == 0) ? 2 : 1;
|
||||
performAiMove(aiP);
|
||||
lastActivityTime = millis();
|
||||
if (!abortAi)
|
||||
{
|
||||
gameState = FINISHED_WIN;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else if (isBoardFull())
|
||||
{
|
||||
gameState = FINISHED_DRAW;
|
||||
demoResetTimer = millis();
|
||||
winnerPlayer = scanBoard();
|
||||
if (winnerPlayer != 0)
|
||||
{
|
||||
gameState = FINISHED_WIN;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else if (isBoardFull())
|
||||
{
|
||||
gameState = FINISHED_DRAW;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -557,24 +594,27 @@ void loop()
|
||||
FastLED.show();
|
||||
delay(600);
|
||||
performAiMove(currentPlayer);
|
||||
winnerPlayer = scanBoard();
|
||||
if (winnerPlayer != 0)
|
||||
if (!abortAi)
|
||||
{
|
||||
gameState = FINISHED_WIN;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else if (isBoardFull())
|
||||
{
|
||||
gameState = FINISHED_DRAW;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPlayer = (currentPlayer == 1) ? 2 : 1;
|
||||
winnerPlayer = scanBoard();
|
||||
if (winnerPlayer != 0)
|
||||
{
|
||||
gameState = FINISHED_WIN;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else if (isBoardFull())
|
||||
{
|
||||
gameState = FINISHED_DRAW;
|
||||
demoResetTimer = millis();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentPlayer = (currentPlayer == 1) ? 2 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // FINISHED state
|
||||
{
|
||||
static uint32_t lastFlash = 0;
|
||||
static bool toggle = true;
|
||||
if (millis() - lastFlash > 300)
|
||||
@@ -584,8 +624,10 @@ void loop()
|
||||
renderBoard();
|
||||
for (int i = 0; i < NUM_LEDS; i++)
|
||||
{
|
||||
#if SHOW_BORDER == 1
|
||||
if (leds[i] == CRGB::Blue)
|
||||
continue;
|
||||
#endif
|
||||
if (gameState == FINISHED_WIN)
|
||||
{
|
||||
if (winMask[i])
|
||||
@@ -608,11 +650,5 @@ void loop()
|
||||
demoResetTimer = 0;
|
||||
demoPly = random(3, 7);
|
||||
}
|
||||
if (pressed)
|
||||
{
|
||||
gameState = MENU;
|
||||
showMenu();
|
||||
delay(300);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user