Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions .github/workflows/code-health.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
name: C++ Code Health
# Stub: the linter (devtools/code_health) and its self-tests live on the fork's agents branch.
# This checks that branch out next to the code and runs it against the code checkout.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
inputs:
base-ref:
description: Revision to compare with (e.g. origin/main); empty = whole tree
required: false

permissions:
contents: read
Expand All @@ -14,22 +21,33 @@ jobs:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: actions/checkout@v7
with:
repository: dayfine/Arduino-Source
ref: agents
path: .agents-tools
- uses: actions/setup-python@v6
with:
python-version: '3.11'
cache: pip
cache-dependency-path: devtools/code_health/requirements.txt
- run: python -m pip install -r devtools/code_health/requirements.txt
cache-dependency-path: .agents-tools/devtools/code_health/requirements.txt
- run: python -m pip install -r .agents-tools/devtools/code_health/requirements.txt
- name: Linter self-tests
working-directory: .agents-tools
env:
CODE_HEALTH_REPO: ${{ github.workspace }}
run: python -m unittest discover -s devtools/code_health/tests -v
- name: Check changed C++ files
working-directory: .agents-tools
env:
PR_BASE: ${{ github.event.pull_request.base.sha }}
INPUT_BASE: ${{ inputs.base-ref }}
PUSH_BASE: ${{ github.event.before }}
CODE: ${{ github.workspace }}
shell: bash
run: |
base="${PR_BASE:-$PUSH_BASE}"
base="${PR_BASE:-${INPUT_BASE:-$PUSH_BASE}}"
if [[ -z "$base" || "$base" == 0000000000000000000000000000000000000000 ]]; then
base=$(git hash-object -w -t tree --stdin </dev/null)
base=$(git -C "$CODE" hash-object -w -t tree --stdin </dev/null)
fi
python -m devtools.code_health --base-ref "$base" --github
python -m devtools.code_health --root "$CODE" --base-ref "$base" --github
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@



UnitTestDatabase make_UNIT_TESTS_ALL(){

Check warning on line 54 in SerialPrograms/Source/ComputerPrograms/ComputerPrograms_UnitTestRunner.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::ComputerPrograms::make_UNIT_TESTS_ALL(): function_lines=38 (advisory above 25, limit 50)
UnitTestDatabase ret;

add_tests_QtEventThreadPool(ret, GlobalThreadPools::computation_normal());
Expand All @@ -70,6 +70,7 @@
Pokemon::add_tests_AutoStorySelect(ret);
Pokemon::add_tests_LegPlanner(ret);
Pokemon::add_tests_LegWalker(ret);
Pokemon::add_tests_WallRecovery(ret);
Pokemon::add_tests_MapData(ret);
Pokemon::add_tests_RouteLog(ret);
Pokemon::add_tests_StoryEvents(ret);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ namespace Pokemon{

void add_tests_LegPlanner(UnitTestDatabase& database);
void add_tests_LegWalker(UnitTestDatabase& database);
// Register shared wall-convergence policy tests.
void add_tests_WallRecovery(UnitTestDatabase& database);
void add_tests_MapData(UnitTestDatabase& database);
void add_tests_RouteLog(UnitTestDatabase& database);

Expand Down
134 changes: 134 additions & 0 deletions SerialPrograms/Source/Pokemon/Navigation/Pokemon_WallRecovery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/* Wall Recovery
*
* From: https://github.com/PokemonAutomation/
*
* Find wall anchors without choosing one uncertain starting position.
* A hold means keeping one movement direction pressed until a wall stops
* the player. Its descriptor belongs to the game; it is not a duration or
* a count of taps. Results predict convergence, not confirmed execution.
*
* MovementPolicy defines Hypothesis, Hold and Position, and static operations:
* Position position(const Hypothesis&);
* optional<WallRecoveryStep<Hypothesis, Hold>> hold(context, positions, direction);
* int tiles(const Hold&);
* The policy type selects game rules at compile time. Runtime Context data
* (map, walking mode, etc.) is passed separately; no policy instance or global
* map is needed. hold() receives Context and a const vector of hypotheses,
* rejecting any direction unsafe for any hypothesis, including NPCs, warps
* and unknown edges. Hypotheses are copyable; Hypothesis/Position support ==.
*/

#ifndef PokemonAutomation_Pokemon_WallRecovery_H
#define PokemonAutomation_Pokemon_WallRecovery_H

#include <algorithm>
#include <optional>
#include <span>
#include <vector>
#include "Pokemon_Grid.h"

namespace PokemonAutomation{
namespace Pokemon{

// Bound the number of directional wall holds in a recovery plan.
struct WallRecoveryLimits{
size_t max_holds = 3;
};

// One safe directional hold and its resulting hypotheses, supplied by the policy.
template <typename Hypothesis, typename Hold>
struct WallRecoveryStep{
std::vector<Hypothesis> positions;
Hold hold;
};

// A sequence of safe holds that brings every hypothesis to the same position.
template <typename Position, typename Hold>
struct WallAnchor{
std::vector<Hold> holds;
Position at;
};

// Return minimum-hold anchors, ordered by held tiles, then ALL_DIRECTIONS.
// Empty input has no anchor; already-converged input needs no holds even at
// max_holds=0. The borrowed span is not retained: search branches own their
// hypothesis vectors, including any distinct object-world alternatives.
template <typename MovementPolicy, typename Context>
std::vector<WallAnchor<typename MovementPolicy::Position, typename MovementPolicy::Hold>> find_wall_anchors(
std::span<const typename MovementPolicy::Hypothesis> positions, const Context& context,
const WallRecoveryLimits& limits = WallRecoveryLimits()
);

namespace WallRecoveryInternal{

// Whether a nonempty belief set represents one physical position.
template <typename MovementPolicy>
bool converged(std::span<const typename MovementPolicy::Hypothesis> positions){
return !positions.empty() && std::all_of(positions.begin(), positions.end(), [&](const auto& position){
return MovementPolicy::position(position) == MovementPolicy::position(positions.front());
});
}

// Explore exactly depth more holds, retaining only common safe anchors.
template <typename MovementPolicy, typename Context>
void search(
const std::vector<typename MovementPolicy::Hypothesis>& positions, const Context& context,
size_t depth, std::vector<typename MovementPolicy::Hold>& holds,
std::vector<WallAnchor<typename MovementPolicy::Position, typename MovementPolicy::Hold>>& found
){
for (Direction direction : ALL_DIRECTIONS){
auto next = MovementPolicy::hold(context, positions, direction);
if (!next || next->positions.empty() || next->positions == positions || MovementPolicy::tiles(next->hold) == 0){
continue;
}
holds.push_back(next->hold);
if (converged<MovementPolicy>(next->positions)){
if (depth == 1){
found.push_back({holds, MovementPolicy::position(next->positions.front())});
}
}else if (depth > 1){
search<MovementPolicy>(next->positions, context, depth - 1, holds, found);
}
holds.pop_back();
}
}

// Sum held-tile distances for stable ordering among equal-length plans.
template <typename MovementPolicy>
int total_tiles(std::span<const typename MovementPolicy::Hold> holds){
int total = 0;
for (const auto& hold : holds){
total += MovementPolicy::tiles(hold);
}
return total;
}

} // namespace WallRecoveryInternal

template <typename MovementPolicy, typename Context>
std::vector<WallAnchor<typename MovementPolicy::Position, typename MovementPolicy::Hold>> find_wall_anchors(
std::span<const typename MovementPolicy::Hypothesis> positions, const Context& context,
const WallRecoveryLimits& limits
){
using Anchor = WallAnchor<typename MovementPolicy::Position, typename MovementPolicy::Hold>;
if (WallRecoveryInternal::converged<MovementPolicy>(positions)){
return {Anchor{{}, MovementPolicy::position(positions.front())}};
}
std::vector<Anchor> found;
if (positions.empty()){
return found;
}
const std::vector<typename MovementPolicy::Hypothesis> starts(positions.begin(), positions.end());
for (size_t depth = 1; found.empty() && depth <= limits.max_holds; depth++){
std::vector<typename MovementPolicy::Hold> holds;
WallRecoveryInternal::search<MovementPolicy>(starts, context, depth, holds, found);
std::stable_sort(found.begin(), found.end(), [&](const Anchor& a, const Anchor& b){
return WallRecoveryInternal::total_tiles<MovementPolicy>(a.holds) < WallRecoveryInternal::total_tiles<MovementPolicy>(b.holds);
});
}
return found;
}

} // namespace Pokemon
} // namespace PokemonAutomation
#endif
Loading
Loading