[fix] Interrupt demo when button is pressed.

This commit is contained in:
2026-03-10 08:29:13 +01:00
parent 0aa1802cce
commit c73d6e3686
+173 -166
View File
@@ -13,6 +13,7 @@
const int COLS = 7; const int COLS = 7;
const int ROWS = 6; const int ROWS = 6;
// --- Global Variables ---
CRGB leds[NUM_LEDS]; CRGB leds[NUM_LEDS];
Encoder myEnc(ENC_A, ENC_B); Encoder myEnc(ENC_A, ENC_B);
WebServer server(80); WebServer server(80);
@@ -24,6 +25,7 @@ enum State
{ {
MENU, MENU,
PLAYING, PLAYING,
AI_TURN,
FINISHED_WIN, FINISHED_WIN,
FINISHED_DRAW, FINISHED_DRAW,
DEMO DEMO
@@ -37,34 +39,37 @@ int8_t activeCol = 3;
long oldEncPos = -999; long oldEncPos = -999;
uint32_t lastActivityTime = 0; uint32_t lastActivityTime = 0;
uint32_t demoResetTimer = 0; uint32_t demoResetTimer = 0;
bool isDemoOver = false; uint32_t globalInputCooldown = 0;
uint8_t demoPly = 4; uint8_t demoPly = 4; // FIXED: Restored global declaration
bool abortAi = false; bool abortAi = false;
bool lastButtonState = HIGH;
bool buttonBlocked = false;
uint8_t current_look_ahead; uint8_t current_look_ahead = 6;
uint8_t current_brightness; uint8_t current_brightness = 30;
uint32_t current_idle_timeout_ms; uint32_t current_idle_timeout_ms = 60000;
bool blunder_enabled = false; bool blunder_enabled = false;
bool progressive_difficulty = false; bool progressive_difficulty = false;
uint8_t aiBrightness = 0; uint8_t aiBrightness = 0;
bool aiFadeUp = true; bool aiFadeUp = true;
// --- Function Prototypes --- // --- Prototypes ---
int getIdx(int x, int y); int getIdx(int x, int y);
void drawStaticUI(); void drawStaticUI();
void renderBoard(); void renderBoard();
int getFirstEmptyRow(int col); int getFirstEmptyRow(int col);
bool isBoardFull(); bool isBoardFull();
int8_t scanBoard(); int8_t scanBoard();
void updateThinkingVisuals(int8_t playerColor, int8_t column); void updateThinkingVisuals(int8_t pColor, int8_t column);
void animateDrop(int col, int player); void animateDrop(int col, int player);
void moveDiscToCol(int startCol, int targetCol, int player, int speed); void moveDiscToCol(int startCol, int targetCol, int player, int speed);
int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiPlayer, int8_t humanPlayer, int8_t rootCol); int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiP, int8_t huP, int8_t rootCol);
void performAiMove(int8_t aiPlayer); void performAiMove(int8_t aiP);
void showMenu(); void showMenu();
int getDynamicPly(); int getDynamicPly();
// --- Functions ---
int getIdx(int x, int y) { return (y * 8) + x; } int getIdx(int x, int y) { return (y * 8) + x; }
void drawStaticUI() void drawStaticUI()
@@ -72,7 +77,7 @@ void drawStaticUI()
FastLED.clear(); FastLED.clear();
#if SHOW_BORDER == 1 #if SHOW_BORDER == 1
CRGB borderColor = CRGB::Blue; CRGB borderColor = CRGB::Blue;
if (gameState == DEMO || gameState >= 2) if (gameState == DEMO || gameState >= 3)
{ {
uint8_t glow = beat8(15); uint8_t glow = beat8(15);
borderColor = blend(CRGB::Blue, CRGB::White, glow / 4); borderColor = blend(CRGB::Blue, CRGB::White, glow / 4);
@@ -87,35 +92,31 @@ void drawStaticUI()
void renderBoard() void renderBoard()
{ {
drawStaticUI(); drawStaticUI();
for (int column = 0; column < COLS; column++) for (int c = 0; c < COLS; c++)
{ {
for (int row = 0; row < ROWS; row++) for (int r = 0; r < ROWS; r++)
{ {
if (board[column][row] == 1) if (board[c][r] == 1)
leds[getIdx(column, 7 - row)] = CRGB::Yellow; leds[getIdx(c, 7 - r)] = CRGB::Yellow;
if (board[column][row] == 2) if (board[c][r] == 2)
leds[getIdx(column, 7 - row)] = CRGB::Red; leds[getIdx(c, 7 - r)] = CRGB::Red;
} }
} }
} }
int getFirstEmptyRow(int col) int getFirstEmptyRow(int col)
{ {
for (int row = 0; row < ROWS; row++) for (int r = 0; r < ROWS; r++)
{ if (board[col][r] == 0)
if (board[col][row] == 0) return r;
return row;
}
return -1; return -1;
} }
bool isBoardFull() bool isBoardFull()
{ {
for (int column = 0; column < COLS; column++) for (int c = 0; c < COLS; c++)
{ if (board[c][ROWS - 1] == 0)
if (board[column][ROWS - 1] == 0)
return false; return false;
}
return true; return true;
} }
@@ -123,36 +124,40 @@ int getDynamicPly()
{ {
if (!progressive_difficulty && gameState != DEMO) if (!progressive_difficulty && gameState != DEMO)
return current_look_ahead; return current_look_ahead;
int occupiedCount = 0; int count = 0;
for (int column = 0; column < COLS; column++) for (int c = 0; c < COLS; c++)
for (int row = 0; row < ROWS; row++) for (int r = 0; r < ROWS; r++)
if (board[column][row] != 0) if (board[c][r] != 0)
occupiedCount++; count++;
return constrain(current_look_ahead + (occupiedCount / 7), 1, 10); return constrain(current_look_ahead + (count / 7), 1, 10);
} }
void updateThinkingVisuals(int8_t playerColor, int8_t column) void updateThinkingVisuals(int8_t pColor, int8_t column)
{ {
static uint32_t lastCycle = 0; static uint32_t lastCycle = 0;
if (millis() - lastCycle < 25) if (millis() - lastCycle < 20)
return; return;
lastCycle = millis(); lastCycle = millis();
if (aiFadeUp) if (aiFadeUp)
{ {
aiBrightness += 15; aiBrightness += 25;
if (aiBrightness >= 240) if (aiBrightness >= 230)
aiFadeUp = false; aiFadeUp = false;
} }
else else
{ {
aiBrightness -= 15; aiBrightness -= 25;
if (aiBrightness <= 15) if (aiBrightness <= 25)
aiFadeUp = true; aiFadeUp = true;
} }
for (int x = 0; x < COLS; x++) for (int x = 0; x < COLS; x++)
leds[getIdx(x, 0)] = CRGB::Black; leds[getIdx(x, 0)] = CRGB::Black;
CRGB aiColor = (playerColor == 1) ? CRGB::Yellow : CRGB::Red;
leds[getIdx(column, 0)] = aiColor.nscale8(aiBrightness); // FIXED: Explicit color initialization to avoid nscale8 compiler error
CRGB aiColor = (pColor == 1) ? CRGB(CRGB::Yellow) : CRGB(CRGB::Red);
aiColor.nscale8(aiBrightness);
leds[getIdx(column, 0)] = aiColor;
FastLED.show(); FastLED.show();
} }
@@ -161,32 +166,33 @@ void animateDrop(int col, int player)
int targetRow = getFirstEmptyRow(col); int targetRow = getFirstEmptyRow(col);
if (targetRow == -1) if (targetRow == -1)
return; return;
for (int row = 5; row >= targetRow; row--) for (int r = 5; r >= targetRow; r--)
{ {
renderBoard(); renderBoard();
leds[getIdx(col, 7 - row)] = (player == 1) ? CRGB::Yellow : CRGB::Red; leds[getIdx(col, 7 - r)] = (player == 1) ? CRGB::Yellow : CRGB::Red;
FastLED.show(); FastLED.show();
delay(max(20, 80 - (5 - row) * 15)); delay(max(10, 60 - (5 - r) * 10));
} }
board[col][targetRow] = player; board[col][targetRow] = player;
renderBoard();
FastLED.show();
} }
void moveDiscToCol(int startCol, int targetCol, int player, int speed) void moveDiscToCol(int startCol, int targetCol, int player, int speed)
{ {
int current = startCol; int current = startCol;
CRGB pColor = (player == 1) ? CRGB::Yellow : CRGB::Red; CRGB colr = (player == 1) ? CRGB::Yellow : CRGB::Red;
while (current != targetCol && !abortAi) while (current != targetCol && !abortAi)
{ {
if (gameState == DEMO && digitalRead(ENC_SW) == LOW)
{
abortAi = true;
break;
}
leds[getIdx(current, 0)] = CRGB::Black; leds[getIdx(current, 0)] = CRGB::Black;
current += (targetCol > current) ? 1 : -1; current += (targetCol > current) ? 1 : -1;
renderBoard(); renderBoard();
leds[getIdx(current, 0)] = pColor; leds[getIdx(current, 0)] = colr;
FastLED.show(); FastLED.show();
delay(speed); delay(speed);
if (digitalRead(ENC_SW) == LOW)
abortAi = true;
} }
activeCol = targetCol; activeCol = targetCol;
} }
@@ -194,180 +200,168 @@ void moveDiscToCol(int startCol, int targetCol, int player, int speed)
int8_t scanBoard() int8_t scanBoard()
{ {
memset(winMask, 0, sizeof(winMask)); memset(winMask, 0, sizeof(winMask));
auto checkMatch = [&](int col, int row, int dCol, int dRow) auto check = [&](int c, int r, int dc, int dr)
{ {
int8_t pAtPos = board[col][row]; int8_t p = board[c][r];
if (pAtPos != 0 && board[col + dCol][row + dRow] == pAtPos && if (p != 0 && board[c + dc][r + dr] == p && board[c + 2 * dc][r + 2 * dr] == p && board[c + 3 * dc][r + 3 * dr] == p)
board[col + 2 * dCol][row + 2 * dRow] == pAtPos && board[col + 3 * dCol][row + 3 * dRow] == pAtPos)
{ {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
winMask[getIdx(col + i * dCol, 7 - (row + i * dRow))] = true; winMask[getIdx(c + i * dc, 7 - (r + i * dr))] = true;
return pAtPos; return p;
} }
return (int8_t)0; return (int8_t)0;
}; };
for (int r = 0; r < 6; r++) for (int r = 0; r < 6; r++)
for (int c = 0; c < 4; c++) for (int c = 0; c < 4; c++)
{ {
int8_t res = checkMatch(c, r, 1, 0); int8_t res = check(c, r, 1, 0);
if (res) if (res)
return res; return res;
} }
for (int r = 0; r < 3; r++) for (int r = 0; r < 3; r++)
for (int c = 0; c < 7; c++) for (int c = 0; c < 7; c++)
{ {
int8_t res = checkMatch(c, r, 0, 1); int8_t res = check(c, r, 0, 1);
if (res) if (res)
return res; return res;
} }
for (int r = 0; r < 3; r++) for (int r = 0; r < 3; r++)
for (int c = 0; c < 4; c++) for (int c = 0; c < 4; c++)
{ {
int8_t res = checkMatch(c, r, 1, 1); int8_t res = check(c, r, 1, 1);
if (res) if (res)
return res; return res;
} }
for (int r = 3; r < 6; r++) for (int r = 3; r < 6; r++)
for (int c = 0; c < 4; c++) for (int c = 0; c < 4; c++)
{ {
int8_t res = checkMatch(c, r, 1, -1); int8_t res = check(c, r, 1, -1);
if (res) if (res)
return res; return res;
} }
return 0; return 0;
} }
int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiPlayer, int8_t humanPlayer, int8_t rootCol) int minimax(int depth, int alpha, int beta, bool isMax, int8_t aiP, int8_t huP, int8_t rootCol)
{ {
if (depth % 2 == 0) if (gameState == DEMO && digitalRead(ENC_SW) == LOW)
{
if (digitalRead(ENC_SW) == LOW)
{ {
abortAi = true; abortAi = true;
return 0; return 0;
} }
}
if (depth >= current_look_ahead - 1) if (depth >= current_look_ahead - 1)
updateThinkingVisuals(aiPlayer, rootCol); updateThinkingVisuals(aiP, rootCol);
else else
yield(); yield();
if (abortAi) if (abortAi)
return 0; return 0;
int8_t winner = scanBoard(); int8_t win = scanBoard();
if (winner == aiPlayer) if (win == aiP)
return 1000 + depth; // Win sooner is better return 1000 + depth;
if (winner == humanPlayer) if (win == huP)
return -1000 - depth; // Lose later is better return -1000 - depth;
if (depth == 0 || isBoardFull()) if (depth == 0 || isBoardFull())
return 0; return 0;
int colOrder[] = {3, 2, 4, 1, 5, 0, 6}; int colOrder[] = {3, 2, 4, 1, 5, 0, 6};
int bestScore = isMax ? -10000 : 10000; int best = isMax ? -10000 : 10000;
for (int c : colOrder)
for (int column : colOrder)
{ {
if (abortAi) if (abortAi)
return 0; return 0;
int row = getFirstEmptyRow(column); int r = getFirstEmptyRow(c);
if (row != -1) if (r != -1)
{ {
board[column][row] = isMax ? aiPlayer : humanPlayer; board[c][r] = isMax ? aiP : huP;
int score = minimax(depth - 1, alpha, beta, !isMax, aiPlayer, humanPlayer, (depth == current_look_ahead ? column : rootCol)); int score = minimax(depth - 1, alpha, beta, !isMax, aiP, huP, (depth == current_look_ahead ? c : rootCol));
board[column][row] = 0; board[c][r] = 0;
if (isMax) if (isMax)
{ {
bestScore = max(bestScore, score); if (score > best)
alpha = max(alpha, bestScore); best = score;
if (best > alpha)
alpha = best;
} }
else else
{ {
bestScore = min(bestScore, score); if (score < best)
beta = min(beta, bestScore); best = score;
if (best < beta)
beta = best;
} }
if (beta <= alpha) if (beta <= alpha)
break; break;
} }
} }
return bestScore; return best;
} }
void performAiMove(int8_t aiPlayer) void performAiMove(int8_t aiP)
{ {
abortAi = false; abortAi = false;
int humanPlayer = (aiPlayer == 1) ? 2 : 1; int huP = (aiP == 1) ? 2 : 1;
int bestScore = -30000; int bestScore = -30000;
int bestCol = 3; int bestCol = 3;
int originalPly = current_look_ahead; int originalPly = current_look_ahead;
current_look_ahead = (gameState == DEMO) ? demoPly : getDynamicPly(); current_look_ahead = (gameState == DEMO) ? demoPly : getDynamicPly();
// PHASE 1: Immediate Win Check (OFFENSE) for (int c = 0; c < COLS; c++)
for (int column = 0; column < COLS; column++)
{ {
int row = getFirstEmptyRow(column); if (gameState == DEMO && digitalRead(ENC_SW) == LOW)
if (row != -1)
{ {
board[column][row] = aiPlayer; abortAi = true;
if (scanBoard() == aiPlayer) goto finalizeMove;
{
board[column][row] = 0;
bestCol = column;
goto finalizeMove; // TAKE THE WIN IMMEDIATELY
} }
board[column][row] = 0; int r = getFirstEmptyRow(c);
if (r != -1)
{
board[c][r] = aiP;
if (scanBoard() == aiP)
{
board[c][r] = 0;
bestCol = c;
goto finalizeMove;
}
board[c][r] = huP;
if (scanBoard() == huP)
{
board[c][r] = 0;
bestCol = c;
goto finalizeMove;
}
board[c][r] = 0;
} }
} }
for (int c : {3, 2, 4, 1, 5, 0, 6})
// 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) if (abortAi)
goto finalizeMove; goto finalizeMove;
int row = getFirstEmptyRow(column); int r = getFirstEmptyRow(c);
if (row != -1) if (r != -1)
{ {
board[column][row] = aiPlayer; board[c][r] = aiP;
int score = minimax(current_look_ahead, -30000, 30000, false, aiPlayer, humanPlayer, column); int score = minimax(current_look_ahead, -30000, 30000, false, aiP, huP, c);
board[column][row] = 0; board[c][r] = 0;
if (score > bestScore) if (score > bestScore)
{ {
bestScore = score; bestScore = score;
bestCol = column; bestCol = c;
} }
} }
} }
if ((gameState == DEMO || blunder_enabled) && random(100) < 20 && !abortAi)
{
int randomColumn = random(0, 7);
if (getFirstEmptyRow(randomColumn) != -1)
bestCol = randomColumn;
}
finalizeMove: finalizeMove:
current_look_ahead = originalPly; current_look_ahead = originalPly;
if (!abortAi) if (!abortAi)
{ {
moveDiscToCol(activeCol, bestCol, aiPlayer, 100); moveDiscToCol(activeCol, bestCol, aiP, 80);
delay(450); if (!abortAi)
animateDrop(bestCol, aiPlayer); {
delay(150);
animateDrop(bestCol, aiP);
}
} }
} }
@@ -375,7 +369,7 @@ 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><h1>Connect 4 Admin</h1><div class='card'><form action='/save' method='POST'>"; 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 += "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>"; 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 Settings' style='background:#28a745;color:white;'></form></div></body></html>";
server.send(200, "text/html", html); server.send(200, "text/html", html);
} }
@@ -407,7 +401,6 @@ void handleSave()
void showMenu() void showMenu()
{ {
isDemoOver = false;
FastLED.clear(); FastLED.clear();
#if SHOW_BORDER == 1 #if SHOW_BORDER == 1
for (int x = 0; x < 7; x++) for (int x = 0; x < 7; x++)
@@ -415,9 +408,9 @@ void showMenu()
for (int y = 1; y < 8; y++) for (int y = 1; y < 8; y++)
leds[getIdx(7, y)] = CRGB::Blue; leds[getIdx(7, y)] = CRGB::Blue;
#endif #endif
CRGB pCol = (menuMode == 1) ? CRGB::Red : CRGB::Yellow;
if (menuMode < 2) if (menuMode < 2)
{ {
CRGB pCol = (menuMode == 1) ? CRGB::Red : CRGB::Yellow;
for (int y = 3; y <= 6; y++) for (int y = 3; y <= 6; y++)
leds[getIdx(3, y)] = pCol; leds[getIdx(3, y)] = pCol;
leds[getIdx(2, 3)] = pCol; leds[getIdx(2, 3)] = pCol;
@@ -446,17 +439,16 @@ void showMenu()
void setup() void setup()
{ {
Serial.begin(115200);
prefs.begin("c4-game", false); prefs.begin("c4-game", false);
current_look_ahead = prefs.getUChar("ply", 8); current_look_ahead = prefs.getUChar("ply", 8);
current_brightness = prefs.getUChar("br", 25); current_brightness = prefs.getUChar("br", 25);
current_idle_timeout_ms = prefs.getUInt("idle", 60) * 1000; current_idle_timeout_ms = prefs.getUInt("idle", 60) * 1000;
blunder_enabled = prefs.getBool("blunder", false); blunder_enabled = prefs.getBool("blunder", false);
progressive_difficulty = prefs.getBool("evolve", false); progressive_difficulty = prefs.getBool("evolve", false);
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.addLeds<WS2812B, 4, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(current_brightness); FastLED.setBrightness(current_brightness);
pinMode(ENC_SW, INPUT_PULLUP); pinMode(2, INPUT_PULLUP);
WiFi.softAP("Connect4-Config", WIFI_PASSWORD); WiFi.softAP("Connect4-Config", "12345678");
server.on("/", handleRoot); server.on("/", handleRoot);
server.on("/save", HTTP_POST, handleSave); server.on("/save", HTTP_POST, handleSave);
server.begin(); server.begin();
@@ -467,44 +459,47 @@ void setup()
void loop() void loop()
{ {
server.handleClient(); server.handleClient();
long newPos = myEnc.read() / SENSITIVITY; long newPos = myEnc.read() / 2;
bool pressed = (digitalRead(ENC_SW) == LOW); bool currentButton = digitalRead(2);
if (newPos != oldEncPos || (pressed && (millis() - lastActivityTime > 500))) bool pressed = false;
if (currentButton == LOW && lastButtonState == HIGH)
{ {
if (gameState >= 2 || gameState == DEMO) if (millis() > globalInputCooldown)
pressed = true;
}
lastButtonState = currentButton;
if ((newPos != oldEncPos || pressed) && (gameState >= 3 || gameState == DEMO))
{ {
abortAi = true; abortAi = true;
memset(board, 0, sizeof(board)); memset(board, 0, sizeof(board));
winnerPlayer = 0; winnerPlayer = 0;
demoResetTimer = 0;
for (int i = 0; i < 10; i++) for (int i = 0; i < 10; i++)
{ {
fadeToBlackBy(leds, NUM_LEDS, 40); fadeToBlackBy(leds, NUM_LEDS, 50);
FastLED.show(); FastLED.show();
delay(20); delay(15);
} }
gameState = MENU; gameState = MENU;
showMenu(); showMenu();
oldEncPos = newPos; oldEncPos = newPos;
lastActivityTime = millis(); lastActivityTime = millis();
delay(300); globalInputCooldown = millis() + 600;
return; return;
} }
lastActivityTime = millis();
}
uint32_t activeLimit = (gameState == PLAYING) ? (current_idle_timeout_ms * 2) : current_idle_timeout_ms; if (gameState != DEMO && (gameState < 3) && (millis() - lastActivityTime > current_idle_timeout_ms))
if (gameState != DEMO && (gameState < 2) && (millis() - lastActivityTime > activeLimit))
{ {
gameState = DEMO; gameState = DEMO;
memset(board, 0, sizeof(board)); memset(board, 0, sizeof(board));
currentPlayer = 1; currentPlayer = 1;
demoPly = random(3, 7);
return; return;
} }
if (gameState == MENU) if (gameState == MENU)
{
if (millis() > globalInputCooldown)
{ {
if (newPos != oldEncPos) if (newPos != oldEncPos)
{ {
@@ -515,17 +510,18 @@ void loop()
if (pressed) if (pressed)
{ {
memset(board, 0, sizeof(board)); memset(board, 0, sizeof(board));
gameState = PLAYING;
if (menuMode == 1) if (menuMode == 1)
{ {
performAiMove(1); currentPlayer = 1;
currentPlayer = 2; gameState = AI_TURN;
} }
else else
{ {
currentPlayer = 1; currentPlayer = 1;
gameState = PLAYING;
}
globalInputCooldown = millis() + 400;
} }
delay(300);
} }
} }
else if (gameState == PLAYING) else if (gameState == PLAYING)
@@ -541,7 +537,6 @@ void loop()
FastLED.show(); FastLED.show();
if (pressed) if (pressed)
{ {
lastActivityTime = millis();
int row = getFirstEmptyRow(activeCol); int row = getFirstEmptyRow(activeCol);
if (row != -1) if (row != -1)
{ {
@@ -560,6 +555,19 @@ void loop()
else else
{ {
if (menuMode < 2) if (menuMode < 2)
{
gameState = AI_TURN;
}
else
{
currentPlayer = (currentPlayer == 1) ? 2 : 1;
}
}
lastActivityTime = millis();
}
}
}
else if (gameState == AI_TURN)
{ {
int8_t aiP = (menuMode == 0) ? 2 : 1; int8_t aiP = (menuMode == 0) ? 2 : 1;
performAiMove(aiP); performAiMove(aiP);
@@ -577,14 +585,10 @@ void loop()
gameState = FINISHED_DRAW; gameState = FINISHED_DRAW;
demoResetTimer = millis(); demoResetTimer = millis();
} }
}
}
else else
{ {
currentPlayer = (currentPlayer == 1) ? 2 : 1; gameState = PLAYING;
} currentPlayer = (aiP == 1) ? 2 : 1;
}
delay(300);
} }
} }
} }
@@ -592,7 +596,7 @@ void loop()
{ {
renderBoard(); renderBoard();
FastLED.show(); FastLED.show();
delay(600); delay(300);
performAiMove(currentPlayer); performAiMove(currentPlayer);
if (!abortAi) if (!abortAi)
{ {
@@ -633,7 +637,11 @@ void loop()
if (winMask[i]) if (winMask[i])
leds[i] = toggle ? (winnerPlayer == 1 ? CRGB::Yellow : CRGB::Red) : CRGB::Black; leds[i] = toggle ? (winnerPlayer == 1 ? CRGB::Yellow : CRGB::Red) : CRGB::Black;
else else
leds[i].nscale8(60); {
CRGB c = leds[i];
c.nscale8(60);
leds[i] = c;
}
} }
else if (gameState == FINISHED_DRAW) else if (gameState == FINISHED_DRAW)
{ {
@@ -648,7 +656,6 @@ void loop()
memset(board, 0, sizeof(board)); memset(board, 0, sizeof(board));
gameState = DEMO; gameState = DEMO;
demoResetTimer = 0; demoResetTimer = 0;
demoPly = random(3, 7);
} }
} }
} }