### Install Dependencies Source: https://github.com/steve1316/uma-android-automation/blob/master/scripts/imageDetection/README.md Install the necessary Python dependencies for the image detection utility. Ensure you are in the repository root. ```bash pip install -r scripts/imageDetection/requirements.txt ``` -------------------------------- ### Install Frontend Dependencies Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Run this command in the project's root directory to install all necessary frontend dependencies using Yarn. ```bash yarn install ``` -------------------------------- ### Compile and Install Android Build Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Use this command to compile the application and install it directly onto your Android device for testing purposes. ```bash yarn android ``` -------------------------------- ### Install Python Dependencies Source: https://github.com/steve1316/uma-android-automation/blob/master/scripts/data-scraper/README.md Installs the necessary Python packages listed in the requirements file. Ensure you are in the repository root directory before running. ```bash pip install -r scripts/data-scraper/requirements.txt ``` -------------------------------- ### Start Expo Metro Server Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Execute this command to start the Metro HTTP server for the React Native frontend. This is the recommended way to run the development environment. ```bash yarn start ``` ```bash npx expo start ``` -------------------------------- ### Get Race Grade Label Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Maps backend grade strings (e.g., 'G1', 'PRE_OP') to short labels for display in grade badges. Provides specific abbreviations for 'PRE_OP', 'FINALE', 'MAIDEN', and 'DEBUT'. ```javascript /** * Maps a backend grade string (e.g. "G1", "PRE_OP", "MAIDEN") to the short label * shown inside the colored grade badge. Defaults to the input when unknown. * @param {string} grade */ function calGradeLabel(grade) { if (!grade) return "" if (grade === "PRE_OP" || grade === "PRE-OP") return "Pre" if (grade === "FINALE") return "Fin" if (grade === "MAIDEN") return "Mdn" if (grade === "DEBUT") return "Dbt" return grade } ``` -------------------------------- ### Change Phone Resolution to 1080p via ADB Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Execute these ADB commands from a computer to set the device resolution to 1080p and DPI to 240. Ensure ADB is installed and your device is connected. ```bash adb shell wm size 1080x1920 ``` ```bash adb shell wm density 240 ``` -------------------------------- ### Initialization Functions Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Calls functions to initialize font size, toolbar, and connect to the application when the page loads. ```javascript initFontSize() initToolbar() renderCalendar(null) connect() ``` -------------------------------- ### Start or Reset Refresh Timer Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Starts or resets an interval to update timer text. Clears any existing interval before setting a new one. ```javascript RefreshTimer() // Start or reset the interval to update the timer text. if (refreshTimerInterval) clearInterval(refreshTimerInterval) refreshTimerInterval = setInterval(updateRefreshTimer, 5000) ``` -------------------------------- ### Test and Visualize with test.py Source: https://github.com/steve1316/uma-android-automation/blob/master/yolo/README.md Launch the interactive visualization tool to test the model's performance on full-size images. It performs localized ROI inference, re-maps detections, and provides an interactive UI. The latest `best.pt` is auto-loaded from the `runs/` directory. ```powershell python test.py ``` -------------------------------- ### Generate Release APK Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Run this command to generate a release-ready APK file for the Android application. ```bash yarn build ``` -------------------------------- ### Training Analysis Pipeline Steps Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Outlines the process for analyzing training options, including stat gain detection, caching, and filtering based on failure chance. ```text 1. Iterate all 5 stats: For each stat, the bot clicks the corresponding training tab button. 2. Stat gain detection per training: - Main stat gain and sub-stat gains (detected via template matching or optionally YOLO — see below) - Failure chance percentage - Relationship bar colors (blue, green, orange) for support card characters present - Rainbow count (number of rainbow indicators) - Skill hints available 3. Results are cached for the current turn in `cachedAnalysisResults` to avoid re-reading if the training screen is visited multiple times. 4. Filtering: Trainings exceeding the maximum failure chance threshold (default 20%) are excluded, unless risky training mode or Good-Luck Charm overrides are active. ``` -------------------------------- ### Load and Render Log Files List Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Fetches the list of log files from the server, caches the response, and renders them in a list. Displays a status message during loading and handles errors. ```javascript async function loadLogFiles() { const listEl = document.getElementById("logFilesList") const emptyEl = document.getElementById("logFilesEmpty") const statusEl = document.getElementById("logFilesStatus") const countEl = document.getElementById("logFilesCount") const totalEl = document.getElementById("logFilesTotalSize") statusEl.textContent = "Loading..." try { const res = await fetch("/logs/files") if (!res.ok) throw new Error(`HTTP ${res.status}`) const data = await res.json() const files = Array.isArray(data.files) ? data.files : [] logFilesCache = files countEl.textContent = String(data.count || files.length) totalEl.textContent = formatFileSize(data.totalSize || 0) statusEl.textContent = "" listEl.innerHTML = "" if (files.length === 0) { emptyEl.style.display = "block" } else { emptyEl.style.display = "none" for (const file of files) { listEl.appendChild(renderLogFileRow(file)) } } } catch (err) { statusEl.textContent = "Failed to load log files." console.error("loadLogFiles failed:", err) } } ``` -------------------------------- ### Generate Training Data with crop_dataset.py Source: https://github.com/steve1316/uma-android-automation/blob/master/yolo/README.md Use this script to process raw screenshots into specialized crops for training the YOLO model. Ensure your raw screenshots are in the `images_original/` directory. ```powershell python crop_dataset.py ``` -------------------------------- ### Run Image Detection Script Source: https://github.com/steve1316/uma-android-automation/blob/master/scripts/imageDetection/README.md Execute the image detection script from the repository root. The script resolves paths relative to its own location. ```bash python scripts/imageDetection/imageDetection.py ``` -------------------------------- ### Initialize Toolbar Collapse State Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Loads the toolbar collapse state from local storage on initialization. Adds the 'collapsed' class if the toolbar was previously collapsed. ```javascript function initToolbar() { const isCollapsed = localStorage.getItem("toolbarCollapsed") === "true" if (isCollapsed) { document.getElementById("toolbar").classList.add("collapsed") } } ``` -------------------------------- ### Run Data Scraper Source: https://github.com/steve1316/uma-android-automation/blob/master/src/data/README.md Execute the Python script to refresh the game data JSON files from the repository root. ```bash python scripts/data-scraper/main.py ``` -------------------------------- ### Open Log File for Inline Viewing Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Opens a modal to display a log file inline. Files larger than LOG_FILE_PREVIEW_BYTE_LIMIT show a placeholder to prevent browser freezing. Requires elements with ids 'logFileModal', 'logFileModalTitle', 'logFileModalBody', and 'logFileModalDownload'. ```javascript async function openLogFile(name) { const modal = document.getElementById("logFileModal") const titleEl = document.getElementById("logFileModalTitle") const bodyEl = document.getElementById("logFileModalBody") const downloadEl = document.getElementById("logFileModalDownload") titleEl.textContent = name downloadEl.href ``` -------------------------------- ### Format Codebase Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Use this command to format both TypeScript/TSX files with Prettier and Kotlin files with Ktlint, ensuring code consistency. ```bash yarn format ``` -------------------------------- ### Train YOLO Model with yolo.py Source: https://github.com/steve1316/uma-android-automation/blob/master/yolo/README.md Run the main training pipeline. This script automatically utilizes settings defined in `config.py`, including image size and region definitions. ```powershell python yolo.py ``` -------------------------------- ### Format Kotlin Files Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Execute this command to format only Kotlin files using Ktlint, adhering to the settings defined in android/.editorconfig. ```bash yarn format:kt ``` -------------------------------- ### UMA Bot Main Processing Loop Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md The main loop calls `process()` on each tick to determine the current screen and dispatch to the appropriate handler. Dialogs are always checked first. If no known screen is detected, the bot taps to progress. ```mermaid flowchart TD Start["process() called"] --> Dialogs{"Any dialogs detected?"} Dialogs -->|Yes| HandleDialog["Handle dialog (close, confirm, etc.)"] HandleDialog --> Return["Return null (continue loop)"] Dialogs -->|No| MainScreen{"On the Main Screen?"} MainScreen -->|Yes| HandleMain["handleMainScreen() (full turn logic)"] HandleMain --> Return MainScreen -->|No| TrainingEvent{"Training Event screen?"} TrainingEvent -->|Yes| HandleEvent["handleTrainingEvent() (select reward option)"] HandleEvent --> Return TrainingEvent -->|No| MandatoryRace{"Mandatory Race Prep screen?"} MandatoryRace -->|Yes| HandleMandatory["handleRaceEvents() (enter mandatory race)"] HandleMandatory --> Return MandatoryRace -->|No| RacingScreen{"Already on Racing screen?"} RacingScreen -->|Yes| HandleRace["handleStandaloneRace() (complete the race)"] HandleRace --> Return RacingScreen -->|No| EndScreen{"Career End screen?"} EndScreen -->|Yes| FinalUpdate["Purchase career-end skills Read final fan count Log final stats"] FinalUpdate --> Complete["Return Success (bot stops)"] EndScreen -->|No| CampaignSpecific{"Campaign-specific condition?"} CampaignSpecific -->|Yes| HandleCampaign["Handle scenario logic (e.g. Unity Cup race)"] HandleCampaign --> Return CampaignSpecific -->|No| Inheritance{"Inheritance event?"} Inheritance -->|Yes| HandleInherit["Accept inheritance"] HandleInherit --> Return Inheritance -->|No| Misc["Perform misc checks or tap to progress"] Misc --> Return ``` -------------------------------- ### WebSocket Connection and Event Handlers Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Establishes a WebSocket connection and sets up handlers for open, message, close, and error events. It manages reconnection attempts and UI status updates. ```javascript function connect() { if (reconnectAttempts > 0) { setStatus("connecting", "Reconnecting in 5...") } else { setStatus("connecting", "Connecting...") } // Reset sync state on fresh connection. isSyncing = true syncingBuffer = [] const protocol = location.protocol === "https:" ? "wss:" : "ws:" const wsUrl = protocol + "//" + location.host ws = new WebSocket(wsUrl) ws.onopen = function () { if (reconnectInterval) clearInterval(reconnectInterval) setStatus("connected") reconnectAttempts = 0 footerInfo.textContent = "Connected to " + location.host // Clear the display on a fresh connection. This removes logs from // a previous run, and prevents duplicate logs from being appended // when the server resyncs the history buffer over this new connection. clearDisplay() } ws.onmessage = function (event) { if (event.data === "HISTORY_DONE") { // History sync is finished. Process all buffered messages. isSyncing = false appendBatchMessages(syncingBuffer) syncingBuffer = [] processAllHistoryForDashboard() } else if (event.data === "CMD:CLEAR") { // Received a signal from the server to clear the display for a new run. clearDisplay() } else if (event.data.startsWith("HB:")) { // Received a batch of historical logs as a JSON array. try { var msgs = JSON.parse(event.data.substring(3)) if (isSyncing) { syncingBuffer.push.apply(syncingBuffer, msgs) } else { appendBatchMessages(msgs) } } catch (e) { console.error("Error parsing history batch:", e) } } else if (event.data.startsWith("CAL:")) { // Received a Race History calendar snapshot. try { renderCalendar(JSON.parse(event.data.substring(4))) } catch (e) { console.error("Error parsing calendar snapshot:", e) } } else if (event.data.startsWith("SRS_STATE:")) { // Smart Race Solver enabled flag pushed by Racing.kt at run start. Toggles // the .srs-disabled class on the panel so it disappears when SRS is off. setSmartRaceSolverEnabled(event.data.substring(10) === "true") } else { // Process individual log messages, images or unknown formats. try { var data = JSON.parse(event.data) if (data.type === "image") { handleIncomingImage(data) } else if (data.type === "image_batch_done") { handleImageBatchDone() } else { if (isSyncing) { syncingBuffer.push(data) } else { appendLogMessage(data) } } } catch (e) { // Fallback for non-JSON messages if any. console.warn("Received non-JSON message:", event.data) } } } ws.onclose = function () { setStatus("disconnected") scheduleReconnect() } ws.onerror = function () { setStatus("disconnected") } } ``` -------------------------------- ### Initialize Font Size from Preference Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Initializes the font size by retrieving it from localStorage. If no stored preference is found or if it's invalid, it uses a default value and updates the UI. ```javascript function initFontSize() { const storedSize = localStorage.getItem("uaa_log_font_size") if (storedSize !== null) { const parsedSize = parseFloat(storedSize) if (!isNaN(parsedSize)) { currentFontSize = parsedSize } } updateFontSize(currentFontSize) } ``` -------------------------------- ### UMA Bot Main Screen Turn Orchestration Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md When on the Main Screen, `handleMainScreen()` orchestrates the turn by calling various functions for state updates, checks, and action execution. It includes scenario-specific hooks for entry and updates. ```mermaid sequenceDiagram participant Bot as Campaign participant OCR as OCR Engine participant State as Game State Bot->>Bot: onBeforeMainScreenUpdate() Note over Bot: Scenario hook (e.g. Trackblazer shop check) Bot->>OCR: Read date string from screen OCR-->>Bot: "Classic Year Early Feb" Bot->>State: updateDate() → turn 27 Note over Bot: Date changed → reset daily flags par 9 parallel OCR threads (10s timeout) Bot->>OCR: Read Speed stat Bot->>OCR: Read Stamina stat Bot->>OCR: Read Power stat Bot->>OCR: Read Guts stat Bot->>OCR: Read Wit stat Bot->>OCR: Read Skill Points Bot->>OCR: Read Mood Bot->>OCR: Read Energy Bot->>OCR: Check racing requirements end Bot->>Bot: Update aptitudes (first time only) Bot->>Bot: Update fan count (if needed) Bot->>Bot: performGlobalChecks() Bot->>Bot: onMainScreenEntry() Note over Bot: Scenario hook (e.g. Trackblazer item usage) Bot->>Bot: decideNextAction() Bot->>Bot: executeAction() ``` -------------------------------- ### Download Logs from Server Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Fetches logs from the server endpoint. If the server download fails, it falls back to reconstructing logs from available message data. ```javascript function downloadLogs() { const traineeName = document.getElementById("dashboardName").textContent.trim() const datePart = new Date().toISOString().slice(0, 19).replace(/[:T]/g, "-") // Prefer the server-side download for full, non-truncated logs. fetch("/logs/download") .then((response) => { if (!response.ok) throw new Error("Server download failed") return response.blob() }) .then((blob) => { const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url // Use a descriptive filename. if (traineeName && traineeName !== "-") { a.download = `${traineeName}_${datePart}.txt` } else { a.download = `log @ ${datePart}.txt` } document.body.appendChild(a) a.click() document.body.removeChild(a) URL.revokeObjectURL(url) }) .catch((err) => { console.error("Download error:", err) // Fallback: reconstruct raw log lines from pre-parsed objects. const content = allMessages.map((m) => { var line = "" if (m.parsed.newline) line += m.parsed.newline if (m.parsed.timestamp) line += m.parsed.timestamp + " " if (m.parsed.level) line += "[" + m.parsed.level + "] " line += m.parsed.message return line }).join("\n") const blob = new Blob([content], { type: "text/plain" }) const url = URL.createObjectURL(blob) const a = document.createElement("a") a.href = url a.download = `log @ ${datePart}.txt` a.click() URL.revokeObjectURL(url) }) } ``` -------------------------------- ### Show Training Breakdown Tooltip Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Displays a tooltip detailing the breakdown of training actions, including counts and levels for stats like Speed, Stamina, Power, Guts, and Wit. ```javascript /** * Shows a specific tooltip for the training action counter. */ function showTrainingTooltip(event) { let content = '
' const stats = ["Speed", "Stamina", "Power", "Guts", "Wit"] stats.forEach(stat => { const count = trainingBreakdown[stat] || 0 const level = trainingLevels[stat] const levelText = level != null ? ` (Lvl ${level})` : "" content += `
${stat} ${count}${levelText}
` }) content += "
" showTooltip(event, "Training Breakdown", content) } ``` -------------------------------- ### Ask the Docs Chatbot Flow Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Illustrates the data flow for the Ask the Docs chatbot, from user question to final answer mode. It shows the retrieval process, model generation, and verification steps. ```mermaid graph TD A[question] --> B(JS) Chat → bridge → searchDocs(q, 4) B --> C{DocIndex top-k by cosine similarity} C --> D{ChatOrchestrator.expandSection() reassembles full sections} D --> E{no active model ─────────────────────────► retrieveOnly (verbatim)} D --> F{llama.rn generates an answer} F --> G{output == "NOT_IN_DOCS" ─────────────────► retrieveOnly (verbatim)} F --> H{groundingVerifier.overlap()} H --> I{overlap ≥ SUMMARY_THRESHOLD (0.3) ───────► generated (cite)} H --> J{overlap < SUMMARY_THRESHOLD ────────────► verifierFallback (verbatim) ``` -------------------------------- ### MILP Solver Backend Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md The MILP solver builds an ExpressionsBasedModel using ojAlgo to mirror a GLPK formulation. It defines decision variables for race vs train, specific race choices, completion indicators, and consecutive race indicators. ```kotlin SmartRaceSolver.solve(state) ``` -------------------------------- ### Unity Cup Opponent Selection Logic Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Visualizes the decision flow for selecting an opponent in the Unity Cup. The bot analyzes double circle icons to determine favorable matchups. ```mermaid stateDiagram-v2 [*] --> TapOpponent1 TapOpponent1 --> AnalyzePredictions1: Confirmation dialog opens AnalyzePredictions1 --> RaceConfirmed: â AnalyzePredictions1 --> TapOpponent2: < 3 double circles TapOpponent2 --> AnalyzePredictions2: Confirmation dialog opens AnalyzePredictions2 --> RaceConfirmed: â AnalyzePredictions2 --> TapOpponent3: < 3 double circles TapOpponent3 --> AnalyzePredictions3: Confirmation dialog opens AnalyzePredictions3 --> RaceConfirmed: â AnalyzePredictions3 --> ForceFallback: < 3 double circles ForceFallback --> RaceConfirmed: Force select Opponent 2 RaceConfirmed --> [*] ``` -------------------------------- ### Initialize Tooltip Element Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Creates and appends a hidden tooltip div to the document body. This element will be used to display custom tooltips. ```javascript // ============================================== // Tooltip UI // ============================================== const tooltip = document.createElement("div") tooltip.id = "tooltip" tooltip.className = "custom-tooltip" document.body.appendChild(tooltip) ``` -------------------------------- ### Switch View Tabs Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Manages tab switching between 'logs', 'images', and 'logFiles' views. It updates button classes and view visibility. It also includes logic for lazy-loading log files and scrolling to the bottom for the logs view. ```javascript function switchTab(tabID) { // Update buttons. document.getElementById("logsTabBtn").classList.toggle("active", tabID === "logs") document.getElementById("imagesTabBtn").classList.toggle("active", tabID === "images") document.getElementById("logFilesTabBtn").classList.toggle("active", tabID === "logFiles") // Update views. document.getElementById("logsView").classList.toggle("active", tabID === "logs") document.getElementById("imagesView").classList.toggle("active", tabID === "images") document.getElementById("logFilesView").classList.toggle("active", tabID === "logFiles") if (tabID === "logs" && autoScroll) { scrollToBottom() } // Lazy-load the log files index on first activation; user can hit Refresh for re-fetches. if (tabID === "logFiles" && !window.__logFilesLoaded) { loadLogFiles() window.__logFilesLoaded = true } } ``` -------------------------------- ### Open Log File Function Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Fetches and displays the content of a log file in a modal. Handles large files by prompting download instead of inline preview. Includes error handling for fetch requests. ```javascript async function openLogFile(name) { const downloadEl = document.getElementById("logFileModalDownloadLink") const bodyEl = document.getElementById("logFileModalBody") const modal = document.getElementById("logFileModal") downloadEl.href = `/logs/files/${encodeURIComponent(name)}?download=1` downloadEl.setAttribute("download", name) bodyEl.textContent = "Loading..." modal.classList.add("visible") const cached = logFilesCache.find((f) => f.name === name) if (cached && cached.size > LOG_FILE_PREVIEW_BYTE_LIMIT) { bodyEl.textContent = `File is ${formatFileSize(cached.size)} - too large to preview inline. Use the Download button above to save it.` return } try { const res = await fetch(`/logs/files/${encodeURIComponent(name)}`) if (!res.ok) throw new Error(`HTTP ${res.status}`) bodyEl.textContent = await res.text() } catch (err) { bodyEl.textContent = `Failed to load log file: ${err.message}` console.error("openLogFile failed:", err) } } ``` -------------------------------- ### Solver Architecture Diagram Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Visual representation of the solver's components and their data flow, including settings, bridge, integration, solvers, and bot interaction. ```mermaid flowchart LR Settings["SmartRaceSolverSettings (TS)"] -->|SolverConfigSnapshot| Bridge["SmartRaceSolverModule (RN bridge)"] Bridge --> Integration["SmartRaceSolverIntegration"] Integration --> Solver["SmartRaceSolver.solve()"] Solver -->|exact| MILP["MilpSolver (ojAlgo)"] Solver -.->|fallback| Heuristic["Heuristic (beam search)"] Integration --> History["RaceHistory + EpithetTracker"] Bot["Racing.kt / Trackblazer.kt"] -->|peek / mark / commit| Integration Integration --> LogStream["LogStreamServer (Race History calendar)"] ``` -------------------------------- ### Revert Phone Resolution to Original Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Use these commands within the aShell You app to reset the device resolution and density to their original settings. ```bash wm size reset && wm density reset ``` -------------------------------- ### Render Single Log File Row Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Builds an HTML element for a single log file row, including name, timestamp, size, and a download link. Attaches a click listener to open the file inline. ```javascript function renderLogFileRow(file) { const row = document.createElement("div") row.className = "log-file-row" row.title = "Click to view" const name = document.createElement("span") name.className = "log-file-name" name.textContent = file.name const modified = document.createElement("span") modified.className = "log-file-meta" modified.textContent = formatLogFileTimestamp(file.modified) const size = document.createElement("span") size.className = "log-file-meta" size.textContent = formatFileSize(file.size) const download = document.createElement("a") download.className = "log-file-download" download.href = `/logs/files/${encodeURIComponent(file.name)}?download=1` download.setAttribute("download", file.name) download.textContent = "Download" download.addEventListener("click", (e) => e.stopPropagation()) row.appendChild(name) row.appendChild(modified) row.appendChild(size) row.appendChild(download) row.addEventListener("click", () => openLogFile(file.name)) return row } ``` -------------------------------- ### Change Phone Resolution to 1080p Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Use these commands within the aShell You app to set the device resolution to 1080p. This method is faster and more accurate but only works when downscaling. ```bash wm size 1080x1920 && wm density 240 ``` -------------------------------- ### Show Energy Recovery Tooltip Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Displays a tooltip showing the energy recovery breakdown by type. It dynamically generates HTML based on the energyBreakdown object. ```javascript function showEnergyTooltip(event) { let content = '
' const types = ["Rest", "Date", "Summer"] types.forEach(type => { const count = energyBreakdown[type] || 0 content += `
${type} ${count}
` }) content += "
" showTooltip(event, "Energy Recovery Breakdown", content) } ``` -------------------------------- ### Race History Calendar Rendering Logic Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Contains logic for rendering the Race History calendar from a snapshot provided by the bot. It manages the latest calendar snapshot and results indexed by turn number for efficient hover-tooltips. ```javascript // Latest snapshot kept for the hover-tooltip lookup. Decisions and results are // indexed by turn number so showRaceCellTooltip is O(1) per hover. var latestCalendarSnapshot = null var latestResultsByTurn = {} /** * Renders the Race History calendar from a snapshot pushed by the bot. * Snapshot shape: { currentTurn:int, decisions:{turn->{type,raceKey?,name?,grade?,raceTrack?,terrain?,distanceType?,distanceMeter ``` -------------------------------- ### Commit Pending Race in Racing.kt Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Detects the first-place screen and commits the pending race. The plan is locked in on a win, and replanned on a loss to account for dead epithets. ```kotlin commitPendingRace(won = firstPlace) ``` -------------------------------- ### Handle Trainee Updates Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Handles updates to trainee stats and aptitudes using the pre-parsed 'trainee' object from the log data. It processes category and data fields to update the trainee's information. ```javascript function handleTraineeUpdates(parsed, stateTarget) { const trainee = parsed.trainee if (!trainee) return const category = trainee.category const data = trainee.data if (stateTarget) { if (category === "Name") { stateTarget.name = data.tri ``` -------------------------------- ### Format TypeScript/TSX Files Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Run this command to format only TypeScript and TSX files using Prettier. ```bash yarn format:tsx ``` -------------------------------- ### UMA Android Automation Architecture Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md This diagram illustrates the core components and relationships within the UMA Android automation app's architecture, showing how different campaign types and systems interact. ```mermaid classDiagram class Campaign { +process() TaskResult? +handleMainScreen() Boolean +decideNextAction() MainScreenAction +executeAction() Boolean } Campaign <|-- UraFinale Campaign <|-- UnityCup Campaign <|-- Trackblazer Campaign *-- Racing Campaign *-- Training Campaign *-- TrainingEvent Campaign *-- SkillPlan Campaign *-- Trainee ``` -------------------------------- ### YOLOv8 Nano Model for Stat Detection Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Configuration details for using a YOLOv8 nano model for detecting stat gain digits. This method is an alternative to template matching and requires specific ONNX Runtime settings. ```text When `enableYoloStatDetection` is enabled, stat gain digits are detected using a YOLOv8 nano model (`best.onnx`) instead of template matching. The model is trained to detect 11 classes (digits 0–9 and the '+' symbol) in small 130x50 pixel crop regions for each stat. It runs via ONNX Runtime with a confidence threshold of 0.8 and IoU threshold of 0.45 for NMS. The `YoloDetector` is loaded once as a singleton and kept in memory. Both detection methods coexist — the setting controls which one is used at runtime. The YOLO training pipeline and model export tools live in the [yolo/](yolo/) directory. ``` -------------------------------- ### Training Event Detection Icon Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md This icon is used to detect the training event screen. Accurate detection is the first step for the bot to process and decide on the rewards offered during training events. ```Java IconTrainingEventHorseshoe ``` -------------------------------- ### Copy All Logs to Clipboard Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Copies the content of all currently stored log messages to the clipboard. Provides visual feedback on the button. ```javascript function copyAllLogs() { var text = allMessages .map(function (m) { var line = "" if (m.parsed.timestamp) line += m.parsed.timestamp + " " if (m.parsed.level) line += "[" + m.parsed.level + "] " line += m.parsed.message return m.parsed.newline + line }) .join("\n") navigator.clipboard.writeText(text).then(function () { // Briefly flash the button to confirm. var btn = event.target.closest(".toolbar-btn") if (btn) { var original = btn.textContent btn.textContent = "✓ Copied!" setTimeout(function () { btn.innerHTML = "📋 Copy" }, 1500) } }) } ``` -------------------------------- ### Beam Search Heuristic Backend Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md The beam search heuristic is used as a fallback when the MILP model is infeasible. It expands beams, scores children, and prunes to a default beam width. ```kotlin DEFAULT_BEAM_WIDTH = 32 ``` -------------------------------- ### Render Race History Calendar Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Renders the race history calendar grid based on provided snapshot data. It handles empty states and populates the calendar with cells representing turns, decisions, and results. ```javascript function renderCalendar(snapshot) { snapshot = snapshot || { currentTurn: 1, decisions: {}, results: [] } if (typeof snapshot !== "object") return latestCalendarSnapshot = snapshot latestResultsByTurn = {} var resultsForIndex = Array.isArray(snapshot.results) ? snapshot.results : [] for (var ri = 0; ri < resultsForIndex.length; ri++) { var rr = resultsForIndex[ri] if (rr && typeof rr.turn === "number") latestResultsByTurn[rr.turn] = rr } var container = document.getElementById("raceHistoryYears") var badgeEl = document.getElementById("raceHistoryTurnBadge") if (!container || !badgeEl) return var hasRealSnapshot = typeof snapshot.currentTurn === "number" && ( Object.keys(snapshot.decisions || {}).length > 0 || (snapshot.results || []).length > 0 ) var currentTurn = typeof snapshot.currentTurn === "number" ? snapshot.currentTurn : 1 badgeEl.textContent = hasRealSnapshot ? ("Turn " + currentTurn + " / 72") : "Turn -" // Index results by turn for O(1) lookup during cell rendering. var resultsByTurn = {} var results = Array.isArray(snapshot.results) ? snapshot.results : [] for (var i = 0; i < results.length; i++) { var r = results[i] if (r && typeof r.turn === "number") resultsByTurn[r.turn] = r } var decisions = snapshot.decisions && typeof snapshot.decisions === "object" ? snapshot.decisions : {} var html = "" for (var y = 0; y < CAL_YEAR_LABELS.length; y++) { var year = CAL_YEAR_LABELS[y] html += '
' html += '
' + year.name + '
' for (var row = 0; row < 6; row++) { html += '
' for (var col = 0; col < 4; col++) { var offset = row * 4 + col var turn = year.startTurn + offset html += renderCalendarCell(turn, offset, decisions[String(turn)], resultsByTurn[turn]) } html += '
' } html += '
' } container.innerHTML = html } ``` -------------------------------- ### Revert Phone Resolution Changes via ADB Source: https://github.com/steve1316/uma-android-automation/blob/master/README.md Execute these ADB commands from a computer to reset the device resolution and density to their original settings. ```bash adb shell wm size reset ``` ```bash adb shell wm density reset ``` -------------------------------- ### Handle Incoming Image Data Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Creates and appends an image card to the image grid for each incoming image data object. Updates the image count and status. Sets up an onclick handler to open the image in a modal. ```javascript function handleIncomingImage(data) { const grid = document.getElementById("imageGrid") const card = document.createElement("div") card.className = "image-card" const img = document.createElement("img") img.src = "data:image/jpeg;base64," + data.data img.alt = data.name const info = document.createElement("div") info.className = "image-info" info.textContent = data.name card.appendChild(info) card.appendChild(img) card.onclick = function() { openModal(img.src) } grid.appendChild(card) receivedImagesCount++ document.getElementById("imageCount").textContent = receivedImagesCount document.getElementById("imageStatus").textContent = "Receiving images..." } ``` -------------------------------- ### Show Simple Action Counter Tooltip Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Displays a tooltip for action counters, showing the total number of actions performed. It extracts the count from an element with the class 'stat-value'. ```javascript /** * Shows a simple title/count tooltip for other action counters. */ function showSimpleTooltip(event, title) { const count = event.currentTarget.querySelector(".stat-value").textContent const content = `
Total actions ${count}
` showTooltip(event, title, content) } ``` -------------------------------- ### Show Race History Tooltip Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Displays a tooltip summarizing race history by grade. It lists counts for recognized grades (Finale, G1-G3, OP, etc.) and includes an 'Other' category for unrecognized grades. ```javascript /** * Shows a specific tooltip for the race action counter. */ function showRaceTooltip(event) { let content = '
' const grades = ["Finale", "G1", "G2", "G3", "OP", "Pre-OP", "Maiden", "Mandatory", "Standalone"] let found = false grades.forEach(grade => { const count = raceBreakdown[grade] || 0 if (count > 0) { found = true content += `
${grade} ${count}
` } }) // Show "Other" if there are any unrecognized grades. let otherCount = 0 for (const [grade, count] of Object.entries(raceBreakdown)) { if (!grades.includes(grade) && count > 0) { otherCount += count } } if (otherCount > 0) { found = true content += `
Other ${otherCount}
` } // If no races found, display a message if (!found) { content += `
No races recorded
` } content += "
" showTooltip(event, "Race History", content) } ``` -------------------------------- ### Trackblazer Scenario Decision Flow Source: https://github.com/steve1316/uma-android-automation/blob/master/HOW_IT_WORKS.md Illustrates the custom decision-making process for the Trackblazer scenario, including checks for season, energy, and irregular training. ```mermaid flowchart TD Start["Trackblazer decideNextAction()"] --> Summer{"Is it Summer?"} Summer -->|Yes| TRAIN["â TRAIN (Summer training)"] Summer -->|No| Finale{"Finale turns 73-75?"} Finale -->|Yes| TRAIN2["â TRAIN (Finale training)"] Finale -->|No| EnergyGuard{"Energy 3+ consecutive races?"} EnergyGuard -->|Yes| REST["â REST (avoid -30 stat penalty)"] EnergyGuard -->|No| Irregular{"Irregular Training enabled + not checked?"} Irregular -->|Yes| EvalTraining["Open training screen Analyze all 5 trainings"] EvalTraining --> ValidFound{"High-value training found?"} ValidFound -->|Yes| TRAIN3["â TRAIN (irregular training)"] ValidFound -->|No| BackOut["Close training screen Mark as checked"] BackOut --> BaseDecision["super.decideNextAction() (base priority waterfall)"] Irregular -->|No| BaseDecision ``` -------------------------------- ### Handle Energy and Mood Updates Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Updates the energy and mood state based on `energyInfo` from parsed data. Can update a target state object or the dashboard DOM. ```javascript function handleEnergyInfo(parsed, stateTarget) { const info = parsed.energyInfo if (!info) return if (stateTarget) { stateTarget.energy = info.to + "%" if (info.moodTo) stateTarget.mood = info.moodTo } else { document.getElementById("dashboardEnergy").textContent = info.to + "%" if (info.moodTo) updateMoodBadge(info.moodTo) } } ``` -------------------------------- ### Show Custom Tooltip Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Displays a custom tooltip with a title and content at the event's current target. The tooltip's visibility and position are updated. ```javascript /** * Shows the custom tooltip with the given content. */ function showTooltip(event, title, content) { const target = event.currentTarget let html = `
${title}
` html += `
${content}
` tooltip.innerHTML = html tooltip.classList.add("visible") updateTooltipPosition(event) } ``` -------------------------------- ### Open Image Enlargement Modal Source: https://github.com/steve1316/uma-android-automation/blob/master/android/app/src/main/assets/log_viewer.html Opens a modal to display an image in a larger size. Requires an HTML element with id 'imageModal' and 'enlargedImage'. ```javascript function openModal(src) { const modal = document.getElementById("imageModal") const modalImg = document.getElementById("enlargedImage") modal.classList.add("visible") modalImg.src = src } ```