Ai Speed

Source: src/ai_speed.c · 112 lines · 13 functions.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

valid_move() · source line 18
static bool valid_move(uint16_t delay_ms)
  1. Exit this functionreturn delay_ms >= AI_MOVE_DELAY_MIN_MS && delay_ms <= AI_MOVE_DELAY_MAX_MS && (delay_ms - AI_MOVE_DELAY_MIN_MS) % AI_MOVE_DELAY_STEP_MS == 0;
Read original function source
static bool valid_move(uint16_t delay_ms)
{
    return delay_ms >= AI_MOVE_DELAY_MIN_MS && delay_ms <= AI_MOVE_DELAY_MAX_MS &&
           (delay_ms - AI_MOVE_DELAY_MIN_MS) % AI_MOVE_DELAY_STEP_MS == 0;
}
initAITiming() · source line 24
bool initAITiming(void)

Calls into this documentation:

  1. Executeready = false;
  2. Executestorage_healthy = false;
  3. Executetiming = (ai_timing_record_t){1, AI_MOVE_DELAY_DEFAULT_MS, AI_RESTART_DELAY_DEFAULT_SECONDS, 0};
  4. Executeai_timing_record_t loaded = {0};
  5. Executebool found = false;
  6. Decision!game_storage_read("arcade_ai", "timing", &loaded, sizeof(loaded), &found)
    True
      1. Exit this functionreturn false;
    False

    Continue after this decision.

  7. Decisionfound
    True
      1. Decisionloaded.version != 1 || !valid_move(loaded.move_ms) || loaded.restart_seconds < AI_RESTART_DELAY_MIN_SECONDS || loaded.restart_seconds > AI_RESTART_DELAY_MAX_SECONDS
        True
          1. ExecuteESP_LOGE("ai_timing", "Invalid saved timing; using defaults without overwriting it");
          2. Exit this functionreturn false;
        False

        Continue after this decision.

      2. Executetiming = loaded;
    False

    Continue after this decision.

  8. Executeready = true;
  9. Executestorage_healthy = true;
  10. Exit this functionreturn true;
Read original function source
bool initAITiming(void)
{
    ready = false;
    storage_healthy = false;
    timing = (ai_timing_record_t){1, AI_MOVE_DELAY_DEFAULT_MS,
                                  AI_RESTART_DELAY_DEFAULT_SECONDS, 0};
    ai_timing_record_t loaded = {0};
    bool found = false;
    if (!game_storage_read("arcade_ai", "timing", &loaded, sizeof(loaded), &found)) {
        return false;
    }
    if (found) {
        if (loaded.version != 1 || !valid_move(loaded.move_ms) ||
            loaded.restart_seconds < AI_RESTART_DELAY_MIN_SECONDS ||
            loaded.restart_seconds > AI_RESTART_DELAY_MAX_SECONDS) {
            ESP_LOGE("ai_timing", "Invalid saved timing; using defaults without overwriting it");
            return false;
        }
        timing = loaded;
    }
    ready = true;
    storage_healthy = true;
    return true;
}
aiTimingStorageHealthy() · source line 49
bool aiTimingStorageHealthy(void)
  1. Exit this functionreturn ready && storage_healthy;
Read original function source
bool aiTimingStorageHealthy(void) { return ready && storage_healthy; }
getAIBaseMoveDelay() · source line 50
uint16_t getAIBaseMoveDelay(void)
  1. Exit this functionreturn timing.move_ms;
Read original function source
uint16_t getAIBaseMoveDelay(void) { return timing.move_ms; }
getAIRestartDelaySeconds() · source line 51
uint8_t getAIRestartDelaySeconds(void)
  1. Exit this functionreturn timing.restart_seconds;
Read original function source
uint8_t getAIRestartDelaySeconds(void) { return timing.restart_seconds; }
save_timing() · source line 53
static bool save_timing(ai_timing_record_t next)

Calls into this documentation:

  1. Decision!ready
    True
      1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executestorage_healthy = game_storage_write("arcade_ai", "timing", &next, sizeof(next));
  3. Decisionstorage_healthy
    True
      1. Executetiming = next;
    False

    Continue after this decision.

  4. Exit this functionreturn storage_healthy;
Read original function source
static bool save_timing(ai_timing_record_t next)
{
    if (!ready) {
        return false;
    }
    storage_healthy = game_storage_write("arcade_ai", "timing", &next, sizeof(next));
    if (storage_healthy) {
        timing = next;
    }
    return storage_healthy;
}
saveAIBaseMoveDelay() · source line 65
bool saveAIBaseMoveDelay(uint16_t delay_ms)

Calls into this documentation:

  1. Decision!valid_move(delay_ms)
    True
      1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executeai_timing_record_t next = timing;
  3. Executenext.move_ms = delay_ms;
  4. Exit this functionreturn save_timing(next);
Read original function source
bool saveAIBaseMoveDelay(uint16_t delay_ms)
{
    if (!valid_move(delay_ms)) {
        return false;
    }
    ai_timing_record_t next = timing;
    next.move_ms = delay_ms;
    return save_timing(next);
}
saveAIRestartDelaySeconds() · source line 75
bool saveAIRestartDelaySeconds(uint8_t seconds)

Calls into this documentation:

  1. Decisionseconds < AI_RESTART_DELAY_MIN_SECONDS || seconds > AI_RESTART_DELAY_MAX_SECONDS
    True
      1. Exit this functionreturn false;
    False

    Continue after this decision.

  2. Executeai_timing_record_t next = timing;
  3. Executenext.restart_seconds = seconds;
  4. Exit this functionreturn save_timing(next);
Read original function source
bool saveAIRestartDelaySeconds(uint8_t seconds)
{
    if (seconds < AI_RESTART_DELAY_MIN_SECONDS || seconds > AI_RESTART_DELAY_MAX_SECONDS) {
        return false;
    }
    ai_timing_record_t next = timing;
    next.restart_seconds = seconds;
    return save_timing(next);
}
aiSpeedDelay() · source line 85
uint16_t aiSpeedDelay(uint8_t index)
  1. Decisionindex >= AI_SPEED_COUNT
    True
      1. Executeindex = AI_SPEED_DEFAULT_INDEX;
    False

    Continue after this decision.

  2. Exit this functionreturn timing.move_ms / (1U << index);
Read original function source
uint16_t aiSpeedDelay(uint8_t index)
{
    if (index >= AI_SPEED_COUNT) {
        index = AI_SPEED_DEFAULT_INDEX;
    }
    return timing.move_ms / (1U << index);
}
aiRestartDelay() · source line 93
uint32_t aiRestartDelay(void)
  1. Exit this functionreturn (uint32_t)timing.restart_seconds * 1000U;
Read original function source
uint32_t aiRestartDelay(void) { return (uint32_t)timing.restart_seconds * 1000U; }
cycleAISpeed() · source line 95
void cycleAISpeed(uint8_t *index)
  1. Decisionindex != NULL
    True
      1. Execute*index = (*index + 1) % AI_SPEED_COUNT;
    False

    Continue after this decision.

Read original function source
void cycleAISpeed(uint8_t *index)
{
    if (index != NULL) {
        *index = (*index + 1) % AI_SPEED_COUNT;
    }
}
aiSpeedShortText() · source line 102
const char *aiSpeedShortText(uint8_t index)
  1. Executestatic const char *labels[] = {"X1", "X2", "X4", "X8"};
  2. Exit this functionreturn labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
Read original function source
const char *aiSpeedShortText(uint8_t index)
{
    static const char *labels[] = {"X1", "X2", "X4", "X8"};
    return labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
}
aiSpeedText() · source line 108
const char *aiSpeedText(uint8_t index)
  1. Executestatic const char *labels[] = {"AI SPEED X1", "AI SPEED X2", "AI SPEED X4", "AI SPEED X8"};
  2. Exit this functionreturn labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
Read original function source
const char *aiSpeedText(uint8_t index)
{
    static const char *labels[] = {"AI SPEED X1", "AI SPEED X2", "AI SPEED X4", "AI SPEED X8"};
    return labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "ai_speed.h"
#include "game_storage.h"
#include "esp_log.h"

typedef struct {
    uint32_t version;
    uint16_t move_ms;
    uint8_t restart_seconds;
    uint8_t reserved;
} ai_timing_record_t;

_Static_assert(sizeof(ai_timing_record_t) == 8, "AI timing record layout changed");
static ai_timing_record_t timing = {1, AI_MOVE_DELAY_DEFAULT_MS,
                                    AI_RESTART_DELAY_DEFAULT_SECONDS, 0};
static bool ready = false;
static bool storage_healthy = false;

static bool valid_move(uint16_t delay_ms)
{
    return delay_ms >= AI_MOVE_DELAY_MIN_MS && delay_ms <= AI_MOVE_DELAY_MAX_MS &&
           (delay_ms - AI_MOVE_DELAY_MIN_MS) % AI_MOVE_DELAY_STEP_MS == 0;
}

bool initAITiming(void)
{
    ready = false;
    storage_healthy = false;
    timing = (ai_timing_record_t){1, AI_MOVE_DELAY_DEFAULT_MS,
                                  AI_RESTART_DELAY_DEFAULT_SECONDS, 0};
    ai_timing_record_t loaded = {0};
    bool found = false;
    if (!game_storage_read("arcade_ai", "timing", &loaded, sizeof(loaded), &found)) {
        return false;
    }
    if (found) {
        if (loaded.version != 1 || !valid_move(loaded.move_ms) ||
            loaded.restart_seconds < AI_RESTART_DELAY_MIN_SECONDS ||
            loaded.restart_seconds > AI_RESTART_DELAY_MAX_SECONDS) {
            ESP_LOGE("ai_timing", "Invalid saved timing; using defaults without overwriting it");
            return false;
        }
        timing = loaded;
    }
    ready = true;
    storage_healthy = true;
    return true;
}

bool aiTimingStorageHealthy(void) { return ready && storage_healthy; }
uint16_t getAIBaseMoveDelay(void) { return timing.move_ms; }
uint8_t getAIRestartDelaySeconds(void) { return timing.restart_seconds; }

static bool save_timing(ai_timing_record_t next)
{
    if (!ready) {
        return false;
    }
    storage_healthy = game_storage_write("arcade_ai", "timing", &next, sizeof(next));
    if (storage_healthy) {
        timing = next;
    }
    return storage_healthy;
}

bool saveAIBaseMoveDelay(uint16_t delay_ms)
{
    if (!valid_move(delay_ms)) {
        return false;
    }
    ai_timing_record_t next = timing;
    next.move_ms = delay_ms;
    return save_timing(next);
}

bool saveAIRestartDelaySeconds(uint8_t seconds)
{
    if (seconds < AI_RESTART_DELAY_MIN_SECONDS || seconds > AI_RESTART_DELAY_MAX_SECONDS) {
        return false;
    }
    ai_timing_record_t next = timing;
    next.restart_seconds = seconds;
    return save_timing(next);
}

uint16_t aiSpeedDelay(uint8_t index)
{
    if (index >= AI_SPEED_COUNT) {
        index = AI_SPEED_DEFAULT_INDEX;
    }
    return timing.move_ms / (1U << index);
}

uint32_t aiRestartDelay(void) { return (uint32_t)timing.restart_seconds * 1000U; }

void cycleAISpeed(uint8_t *index)
{
    if (index != NULL) {
        *index = (*index + 1) % AI_SPEED_COUNT;
    }
}

const char *aiSpeedShortText(uint8_t index)
{
    static const char *labels[] = {"X1", "X2", "X4", "X8"};
    return labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
}

const char *aiSpeedText(uint8_t index)
{
    static const char *labels[] = {"AI SPEED X1", "AI SPEED X2", "AI SPEED X4", "AI SPEED X8"};
    return labels[index < AI_SPEED_COUNT ? index : AI_SPEED_DEFAULT_INDEX];
}