Guidance for AI coding agents working in this repository.
For how to write a new automation program, see also CodingAgentContext/AutomationProgramPatterns.md.
This is the Pokémon Automation Computer Control (CC) program. It is an open-source application that automates Pokémon (and other) games on Nintendo Switch using microcontroller controller hardware, computer vision, OCR, and machine learning.
Common/— shared code.Common/Cppis Qt-free standard C++ (the foundation of the project; keep it Qt-free except documented seams).Common/Qtholds Qt-specific helpers.SerialPrograms/— the application:SerialPrograms/Source(code),SerialPrograms/cmake(CMake modules),SerialPrograms/bin(build output).Packages/— not part of this repo: the separatePokemonAutomation/Packagesrepo (downloadable resources inPackages/DownloadedResources, firmware, tooling). CMake clones it intoPackages/automatically at configure time if it is missing (clone_packages).3rdParty/,3rdParty-Core/— vendored dependencies (compiled as external includes; MSVC builds use/external:W0).CodingAgentContext/— agent-facing notes on writing new automation programs.- Test images/fixtures live in a separate repo:
PokemonAutomation/CommandLineTests. At test time it is checked out next to the program;GlobalAutoPathsdetects theCommandLineTests/(orUnitTestResources/) folder and loads test files relative to it. - Documentation lives in a separate repo:
PokemonAutomation/pokemonautomation.github.io(MkDocs, published at https://pokemonautomation.github.io).docs/Developer/is the developer guide (build setup, button commands, image matching, parallel routines, tests).docs/Programs/<Game>/<Program>.mdis each program's user page; the.htmlpath in a program's descriptor (e.g.Programs/NintendoSwitch/PreventSleep.html) names that file. Clone it next to this repo to read it locally, check it before writing new guidance here, and update a program's page there when a change alters what its users do.
The codebase splits into programs (individual automation programs) and infra
(everything else: CommonFramework, CommonTools, Common/Cpp, Controllers, ...).
Programs live in:
SerialPrograms/Source/VideoGames/SerialPrograms/Source/Pokemon/(not yet moved intoVideoGames/)SerialPrograms/Source/PokemonBDSP/(not yet moved intoVideoGames/)- Console-generic (non-game-specific) programs under
SerialPrograms/Source/NintendoSwitch/: itsInference/,Options/,Programs/, andDevPrograms/subfolders are programs; the rest of that folder (Commands/,Controllers/,Framework/, console state/handles, ...) is infra.
Infra has a much higher quality standard than programs because of its large blast radius, and it requires strict human review. New AI-generated code must meet that bar; filler and low-quality "AI slop" are not acceptable.
The long-term goal is to reduce the Qt footprint in preparation for a future migration away from Qt.
- Qt is forbidden in program code (the three program folders above).
- Qt is strongly discouraged in non-UI infra. Use it only when the alternative is a lot of new code or would pull in a library the project doesn't already use.
- Qt is generally unavoidable for UI code (widgets, panels, windows).
Direct use of std::filesystem is banned: its native encoding is ambiguous (UTF-8 bytes
on Linux, UTF-16 on Windows), while this project defines char*/std::string as UTF-8.
Use the wrappers in Common/Cpp/Filesystem/ instead: Filesystem:: functions, Path,
FileIO, file_to_string(). Rationale is documented in the header comment of
Common/Cpp/Filesystem/FilePath.h.
Qt > 6.8.3 on Windows can hang when raw std::threads coexist with Qt event loops
("thread adoption" bug: QTBUG-131892, see project issue #570; see
Common/Cpp/Concurrency/Qt6.9ThreadBugWorkaround.h). Therefore:
- Don't use
std::threadmanually, and don't useCommon/Cpp/Concurrency/Threaddirectly either:Threadleaks the thread (a consequence of the Qt adoption-bug workaround), so only the thread pool implementations are allowed to use it. Pools reuse their worker threads, which keeps the leaked-thread count down to the peak number of concurrent tasks (the pools still leak all of their threads on exit). Run all background work on the app-level pools inSerialPrograms/Source/CommonFramework/Tools/GlobalThreadPools.h. - Don't use
std::mutex/std::condition_variabledirectly. Use thePokemonAutomation::Mutex/ConditionVariablewrappers inCommon/Cpp/Concurrency/. std::asyncis banned (it may spawn a new thread under the hood).
Some legacy files still use std::thread/std::mutex directly; don't add new uses, and
migrate a file to the wrappers when you're already touching it and it's straightforward.
- UI state lives in a session (or
UiState) class — e.g.PanelSession(a program instance),VideoOverlaySession, or theUiState<...>option classes. These hold all program data and logic and are fully thread-safe/asynchronous. - The actual UI lives in
Widgetclasses that attach to the session. The session always outlives the UI. Keep state in the session; widgets never own program state. - The indirection is
Common/Cpp/UiWrapper.h: state classes inheritUiState<T>and the UI is produced by a factory registered inUiState<T>::m_ui_factory(called viamake_ui_component()). This lets state compile and run with no UI at all (headless) and keeps the door open for migrating to a different UI library.
- CMake >= 3.24, C++23. Dependencies: Qt 6.10.2 (with
qtmultimedia+qtserialport), OpenCV 4, ONNX Runtime, Tesseract. Linux CI builds with Ninja; Windows with MSVC. - Configure/build from
SerialPrograms/with the CMake presets inSerialPrograms/CMakePresets.json(Ninja, C++23, automoc on), e.g.cmake --preset defaultthencmake --build --preset default; platform-specific setup is documented inSerialPrograms/BuildInstructions/. -DPA_CORE_ONLY=ONbuilds only the GUI-free core (no Qt/OpenCV) — a quick way to compile-checkCommon/Cppchanges.- New source files must be added to the explicit list in
SerialPrograms/cmake/SourceFiles.cmake; they are not picked up automatically. - MSVC builds pass
/W4 /WX— every compiler warning is an error (clang-cl, GCC, and clang also build in CI with strict flags). - No tab characters in source (CI has a "Check for Tabs" job).
- Run the test suite by launching the built binary with
--command-line-test-modefromSerialPrograms/bin(no GUI). Requirements (see the test steps in.github/workflows/cpp-ci-serial-programs-base.ymlandSerialPrograms/Source/Tests/CommandLineTests.h):PokemonAutomation/CommandLineTestschecked out next to the program (test images).PaddleOCRDetectionresources unzipped fromPackages/DownloadedResources/into the program's downloaded-resources folder (SerialPrograms/bin/DownloadedResources/on Windows/Linux;~/Library/Application Support/SerialPrograms/DownloadedResourceson macOS).- Headless Linux:
QT_QPA_PLATFORM=offscreenandTESSDATA_PREFIX=<bin>/Resources/Tesseract.
- Test framework: test classes derive
UnitTest(Common/Cpp/TestRunners/UnitTest.h) and are collected via per-moduleadd_tests(UnitTestDatabase&)— seeSerialPrograms/Source/Tests/CommandLineTests.h.
char*/std::stringare UTF-8. UseCommon/Cpp/Strings/(StringTools,Unicode) for text processing.- File headers:
/* Title \n * \n * From: https://github.com/PokemonAutomation/ \n * \n */; include guards arePokemonAutomation_<Name>_H. - Line endings: the codebase is overwhelmingly CRLF (there is no
.gitattributesnormalization;* -text). Preserve each file's existing line endings when editing — do not re-normalize a file to LF, it produces a whole-file diff. - Errors/cancellation: use
CommonFramework/Exceptions/(OperationFailedException,ProgramFinishedException, ... — typically viathrow_and_log) andCommon/Cpp/CancellableScope.h(CancellableScope::throw_if_cancelled()in loops). - File paths: use the macros from
CommonFramework/GlobalAutoPaths.h(RESOURCE_PATH(),USER_FILE_PATH(),DOWNLOADED_RESOURCE_PATH(), ...) rather than hardcoding locations.
- Put the opening brace on the same line as
if,else if,for,while, orswitch, with no space between the closing)and{:if (condition){, notif (condition) {. Write}else if (condition){and}else{without spaces aroundelse's braces. - Always use braces and multiline bodies for conditionals and loops, even for a single
statement. Do not write
if (condition) return;orif (condition){ return; }. Put the body on its own indented line and the closing brace on its own line (except when followed byelse/else if). The same rule applies to one-statement loop bodies. - Follow these rules in new or edited control flow even when nearby legacy code differs. Avoid reformatting unrelated code.
if (condition){
do_something();
}else if (other_condition){
do_something_else();
}else{
handle_default();
}
for (size_t i = 0; i < count; i++){
process(i);
}