Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
bb02248
Merge nav/shared (#114) at 5e3150565 into bdsp/mvp-v3
dayfine Nov 1, 2026
a00a13f
Merge bdsp/maps-rederive (#111) into bdsp/mvp-v3
dayfine Nov 1, 2026
64afa5f
Merge bdsp/segment00-planned-walks (#112) into bdsp/mvp-v3
dayfine Nov 1, 2026
c919d80
BDSP AutoStory: segments 00 and 01 as tables of event checkpoints
dayfine Nov 1, 2026
d12a317
BDSP AutoStory: the story maps as JSON files in the shared map schema
dayfine Nov 1, 2026
c2a53bf
BDSP AutoStory: the segment tables as row lists under the code-health…
dayfine Nov 1, 2026
9eca24b
BDSP AutoStory: copy Jubilife's grid for its east corner, for gcc -Wd…
dayfine Nov 1, 2026
cd3d0b3
BDSP AutoStory 00.04: into Sandgem by the road, then the town plan to…
dayfine Nov 1, 2026
bba98e9
BDSP AutoStory: Route 202's catch patch is three grass rows, with tre…
dayfine Nov 1, 2026
bf23b77
BDSP AutoStory 00.04b: train from where the catch's save stands, into…
dayfine Nov 1, 2026
cb14b8f
BDSP AutoStory: the training area keeps only standable tiles
dayfine Nov 1, 2026
d374993
BDSP AutoStory 00.04: tap west below Sandgem's Center door before lin…
dayfine Nov 1, 2026
9d2cf61
BDSP AutoStory 00.04: turn west before the taps below Sandgem's Cente…
dayfine Nov 1, 2026
4a74ffc
BDSP AutoStory 00.04: shorter comment on the taps, under the file-len…
dayfine Nov 1, 2026
59d88d9
BDSP AutoStory: indoors() holds under the evening tint; out the door …
dayfine Nov 1, 2026
d8afcee
BDSP AutoStory 00.04: stop training when the party faints and the gam…
dayfine Nov 1, 2026
50edfb0
BDSP AutoStory tests: plan into Sandgem from every tile of the traini…
dayfine Nov 1, 2026
aaacf4a
Merge bdsp/segment00-planned-walks (#112, with #111 and nav/shared 70…
dayfine Nov 1, 2026
0d8a9d0
BDSP AutoStory tests: the grid tests live in #111, the indoors test i…
dayfine Nov 1, 2026
96f583a
Merge bdsp/segment00-planned-walks (#112, #111: maps as JSON) into bd…
dayfine Nov 1, 2026
c68a631
BDSP AutoStory: out of a door needs the door's fade; log every look
dayfine Nov 1, 2026
82fec35
BDSP AutoStory 00.04: Potions from the Poke Mart lady on Route 201, t…
dayfine Nov 1, 2026
284c120
Merge bdsp/segment00-planned-walks (#112, #111: committed JSON maps b…
dayfine Nov 1, 2026
dbad84f
BDSP AutoStory: no map files from Packages at start; the built-in map…
dayfine Nov 1, 2026
179c4fb
Merge bdsp/segment00-planned-walks (#112, #111: the two-walk Sandgem …
dayfine Nov 1, 2026
b08c418
BDSP AutoStory 00.04: post-catch rows 00.04b/c/d, each from a save; e…
dayfine Nov 1, 2026
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
4 changes: 4 additions & 0 deletions SerialPrograms/Source/PokemonBDSP/PokemonBDSP_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "Programs/AutoStory/PokemonBDSP_AutoStory_MapGrid_Tests.h"
#include "Programs/AutoStory/PokemonBDSP_AutoStory_Tests.h"
#include "Programs/AutoStory/PokemonBDSP_AutoStory_Segment00_Tests.h"
#include "Programs/AutoStory/PokemonBDSP_AutoStory_StoryTables_Tests.h"
#include "Programs/AutoStory/PokemonBDSP_AutoStory_Indoors_Tests.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
Expand Down Expand Up @@ -294,6 +296,8 @@ void add_tests(UnitTestDatabase& database){
add_tests_AutoStory_MapGrids(database);
add_tests_AutoStory(database);
add_tests_AutoStory_Segment00(database);
add_tests_AutoStory_StoryTables(database);
add_tests_AutoStory_Indoors(database);
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
/* BDSP AutoStory Indoors Tests
*
* From: https://github.com/PokemonAutomation/
*
*/

#include <optional>
#include <string>
#include <vector>
#include "Common/Cpp/TestRunners/UnitTestDatabase.h"
#include "CommonFramework/ImageTypes/ImageRGB32.h"
#include "Pokemon/Navigation/Pokemon_MapSet.h"
#include "Pokemon/Navigation/Pokemon_TileGrid.h"
#include "Data/PokemonBDSP_AutoStory_SandgemTown.h"
#include "Data/PokemonBDSP_AutoStory_StoryMaps.h"
#include "StorySegments/PokemonBDSP_AutoStory_SandgemCenterEvents.h"
#include "Utils/PokemonBDSP_AutoStory_PokemonCenter.h"
#include "Utils/PokemonBDSP_AutoStory_RouteWalk.h"
#include "PokemonBDSP_AutoStory_Indoors_Tests.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonBDSP{


namespace{

using Pokemon::Direction;
using Pokemon::MapSet;
using Pokemon::TilePos;

// indoors() decides when leave_door_south() is out: r257 walked on south
// past Sandgem's road into a house after leaving the Center, because the
// evening tint made the outside as warm as a room. Synthetic screens: the
// evening outside (warm all over), a room (warm, black along the bottom),
// a fade (black all over), and warm with a thin dark edge (a shadow).
struct IndoorsCase{
std::string name;
uint32_t fill;
double dark_bottom; // how much of the screen's height, from the bottom, is black
bool indoors;
};

class Test_IndoorsAnyLight : public UnitTest{
public:
Test_IndoorsAnyLight()
: UnitTest("PokemonBDSP::AutoStory - indoors() under the evening tint")
{}
virtual UnitTestResult run(Logger&, CancellableScope&) const override{
const std::vector<IndoorsCase> cases{
{.name = "evening outside, orange all over", .fill = 0xffe0a040, .dark_bottom = 0, .indoors = false},
{.name = "a room: orange, black below its floor", .fill = 0xffe0a040, .dark_bottom = 0.10, .indoors = true},
{.name = "a fade: black all over", .fill = 0xff000000, .dark_bottom = 0, .indoors = false},
{.name = "orange with a thin shadow at the bottom", .fill = 0xffe0a040, .dark_bottom = 0.01, .indoors = false},
{.name = "daytime grass", .fill = 0xff60b040, .dark_bottom = 0.10, .indoors = false},
};
for (const IndoorsCase& item : cases){
ImageRGB32 screen(1920, 1080);
const size_t dark_from = (size_t)(1080 * (1 - item.dark_bottom));
for (size_t y = 0; y < 1080; y++){
for (size_t x = 0; x < 1920; x++){
screen.pixel(x, y) = y >= dark_from ? 0xff000000 : item.fill;
}
}
if (indoors(screen) != item.indoors){
return item.name + (item.indoors ? ": read as outside." : ": read as inside.");
}
}
return true;
}
};

// door_fade_look(): out of a door is a dark look, then a bright one that is
// not a room. r259 turned round in Sandgem's lit Center, read "not a room"
// once at full brightness, and walked east inside it as if outside.
struct DoorLook{
double brightness;
bool room;
};
struct DoorCase{
std::string name;
std::vector<DoorLook> looks;
// The look (1-based) that reads out, or 0 for none.
size_t out_at;
};

class Test_DoorFade : public UnitTest{
public:
Test_DoorFade()
: UnitTest("PokemonBDSP::AutoStory - out of a door needs its fade")
{}
virtual UnitTestResult run(Logger&, CancellableScope&) const override{
const std::vector<DoorCase> cases{
{.name = "r259: a bright look in the Center that misses the room", .looks = {{458, true}, {440, false}, {457, true}}, .out_at = 0},
{.name = "the fade, then the town", .looks = {{458, true}, {300, true}, {60, false}, {310, false}}, .out_at = 4},
{.name = "the fade, then the room again", .looks = {{458, true}, {60, false}, {420, true}}, .out_at = 0},
{.name = "the fade, still dim", .looks = {{60, false}, {150, false}, {320, false}}, .out_at = 3},
};
for (const DoorCase& item : cases){
DoorFadeWatch watch;
size_t out_at = 0;
for (size_t i = 0; i < item.looks.size() && out_at == 0; i++){
if (door_fade_look(watch, item.looks[i].brightness, item.looks[i].room)){
out_at = i + 1;
}
}
if (out_at != item.out_at){
return item.name + ": out at look " + std::to_string(out_at) + ", expected " + std::to_string(item.out_at) + ".";
}
}
return true;
}
};

// 00.04c's Potions: the Mart lady's walk (after_catch_potions() and the Mart lady checkpoint)
// is walks until stopped, so on the grid each leg ends at its wall: from
// a tile to three below the Center's door, a tap east and north to the
// Center's front, west to the Lab's sign, a row south and west to
// her grass's edge, north to the trees on her row (she stands east of
// there), back a row south and east to Route 201's sign, a row south and
// east to the trees east of the Mart, and north to the front row's end.
struct LadyLeg{
Direction direction;
size_t tiles; // 0: walk until the wall
TilePos end; // world tile
std::string name;
};

class Test_MartLadyWalk : public UnitTest{
public:
Test_MartLadyWalk()
: UnitTest("PokemonBDSP::AutoStory - 00.04: the walk to the Poke Mart lady and back")
{}
virtual UnitTestResult run(Logger& logger, CancellableScope&) const override{

Check warning on line 134 in SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_Indoors_Tests.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonBDSP::Test_MartLadyWalk::run( Logger & logger , CancellableScope &) const: function_lines=26 (advisory above 25, limit 50)
const MapSet& maps = story_maps();
const auto sandgem = [&](const TilePos& pos){ return maps.to_world(MAP_SANDGEM_TOWN, pos); };
const auto route201 = [&](const TilePos& pos){ return maps.to_world(MAP_ROUTE_201, pos); };
const std::vector<LadyLeg> legs{
{Direction::EAST, 1, sandgem({18, 0}), "a tap east under the Center's wall"},
{Direction::NORTH, 0, sandgem({18, 11}), "north to the Center's front"},
{Direction::WEST, 0, east_of_lab_sign(), "west to the Lab's sign"},
{Direction::SOUTH, 1, sandgem({6, 12}), "a row south onto the path"},
{Direction::WEST, 0, route201({42, 5}), "west to her grass's edge"},
{Direction::NORTH, 0, route201({42, 3}), "north to the trees, on her row"},
{Direction::SOUTH, 1, route201({42, 4}), "a row south onto the signs' row"},
{Direction::EAST, 0, route201_sign_west(), "east to Route 201's sign"},
{Direction::SOUTH, 1, route201({54, 5}), "a row south onto the path"},
{Direction::EAST, 0, sandgem({29, 12}), "east to the trees east of the Mart"},
{Direction::NORTH, 0, sandgem(SandgemTown::FRONT_ROW_EAST_END), "north to the front row's end"},
};
// Out of the Center's door we stand a tile to three below it (r261).
for (int below = 1; below <= 3; below++){
const std::string error = follow(maps, legs, sandgem({17, 10 + below}), logger);
if (!error.empty()){
return "From " + std::to_string(below) + " below the door: " + error;
}
}
return true;
}

private:
static std::string follow(const MapSet& maps, const std::vector<LadyLeg>& legs, TilePos at, Logger& logger){
for (const LadyLeg& leg : legs){
if (leg.tiles == 0){
at = Pokemon::walk_to_wall(maps.world(), at, leg.direction).stop;
}else{
const std::optional<TilePos> next = maps.world().step(at, leg.direction);
if (!next){
return leg.name + ": blocked at " + maps.describe(at) + ".";
}
at = *next;
}
// The tap east lands on whichever row we stood on.
if (&leg != &legs.front() && !(at == leg.end)){
return leg.name + ": ends at " + maps.describe(at) + ", not " + maps.describe(leg.end) + ".";
}
logger.log(leg.name + ": " + maps.describe(at));
}
return "";
}
};

}


void add_tests_AutoStory_Indoors(UnitTestDatabase& database){
database.add<Test_IndoorsAnyLight>();
database.add<Test_DoorFade>();
database.add<Test_MartLadyWalk>();
}


}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* BDSP AutoStory Indoors Tests
*
* From: https://github.com/PokemonAutomation/
*
*/

#ifndef PokemonAutomation_PokemonBDSP_AutoStory_Indoors_Tests_H
#define PokemonAutomation_PokemonBDSP_AutoStory_Indoors_Tests_H

#include "Common/Cpp/TestRunners/UnitTest.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonBDSP{



// indoors() on synthetic screens under any light: the evening outside,
// a room, a fade, a thin shadow, grass.
void add_tests_AutoStory_Indoors(UnitTestDatabase& database);



}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/* BDSP AutoStory Story Tables Tests
*
* From: https://github.com/PokemonAutomation/
*
*/

#include <algorithm>
#include <format>
#include <functional>
#include <string>
#include <vector>
#include "Common/Cpp/TestRunners/UnitTestDatabase.h"
#include "StorySegments/PokemonBDSP_AutoStory_Segment_00.h"
#include "StorySegments/PokemonBDSP_AutoStory_Segment_01.h"
#include "Utils/PokemonBDSP_AutoStory_StoryTables.h"
#include "PokemonBDSP_AutoStory_StoryTables_Tests.h"

namespace PokemonAutomation{
namespace NintendoSwitch{
namespace PokemonBDSP{


namespace{

// Builds tables without a console; nothing in them runs.
class DescribeOnlyRunner : public StoryStepRunner{
public:
virtual bool saved_at_start(const std::string&) const override{
return false;
}
virtual Pokemon::Outcome save_at_start(const std::string&) override{
return Pokemon::outcome_failed("not_run");
}
virtual Pokemon::Outcome run_step(const std::string&, StoryFailure, const StoryStep&) override{
return Pokemon::outcome_failed("not_run");
}
};

struct TableCase{
std::string name;
std::function<Pokemon::Segment(StoryStepRunner&)> make;
std::string segment_id;
std::vector<std::string> checkpoint_ids;
// Rows whose first event is not the save (they neither save nor retry).
std::vector<std::string> unsaved;
};

const std::vector<TableCase>& table_cases(){
static const std::vector<TableCase> cases{
{
.name = "Segment 00",
.make = make_segment_00,
.segment_id = "00",
.checkpoint_ids = {"00.00", "00.01", "00.02", "00.03", "00.04", "00.04b", "00.04c", "00.04d", "00.05"},
.unsaved = {"00.00"},
},
{
.name = "Segment 01",
.make = make_segment_01,
.segment_id = "01",
.checkpoint_ids = {"01.01", "01.02", "01.03", "01.04", "01.05", "01.06", "01.07", "01.08", "01.09", "01.10", "01.11"},
.unsaved = {},
},
};
return cases;
}

// An empty string when `table` matches `expected`; else what differs.
std::string check_table(Pokemon::Segment& table, const TableCase& expected){

Check warning on line 69 in SerialPrograms/Source/PokemonBDSP/Programs/AutoStory/PokemonBDSP_AutoStory_StoryTables_Tests.cpp

View workflow job for this annotation

GitHub Actions / code-health

Code health (advisory)

PokemonAutomation::NintendoSwitch::PokemonBDSP::check_table( Pokemon :: Segment & table , const TableCase & expected): function_lines=36 (advisory above 25, limit 50)
if (table.id != expected.segment_id){
return std::format("{}: id is \"{}\", expected \"{}\".", expected.name, table.id, expected.segment_id);
}
std::vector<std::string> ids;
for (const Pokemon::CheckpointSpec& spec : table.checkpoints){
ids.push_back(spec.id);
}
if (ids != expected.checkpoint_ids){
std::string read;
for (const std::string& id : ids){
read += " " + id;
}
return std::format("{}: {} rows,{}; expected {}.", expected.name, ids.size(), read, expected.checkpoint_ids.size());
}
for (Pokemon::CheckpointSpec& spec : table.checkpoints){
Pokemon::Checkpoint checkpoint = spec.build();
if (checkpoint.id != spec.id){
return std::format("{}: row {} builds checkpoint \"{}\".", expected.name, spec.id, checkpoint.id);
}
if (checkpoint.events.empty()){
return std::format("{}: {} has no events.", expected.name, spec.id);
}
const bool unsaved = std::find(expected.unsaved.begin(), expected.unsaved.end(), spec.id) != expected.unsaved.end();
const bool saves_first = checkpoint.events[0].name == "save";
if (saves_first == unsaved){
return std::format("{}: {}'s first event is \"{}\", expected {}.",
expected.name, spec.id, checkpoint.events[0].name, unsaved ? "a step" : "the save");
}
const std::vector<std::string> problems = Pokemon::lint_checkpoint(checkpoint);
if (!problems.empty()){
return std::format("{}: {} lint: {}", expected.name, spec.id, problems[0]);
}
}
return "";
}

class Test_StoryTables : public UnitTest{
public:
Test_StoryTables()
: UnitTest("PokemonBDSP::AutoStory - Story tables: rows, events and describe()")
{}
virtual UnitTestResult run(Logger& logger, CancellableScope&) const override{
for (const TableCase& expected : table_cases()){
DescribeOnlyRunner runner;
Pokemon::Segment table = expected.make(runner);
const std::string problem = check_table(table, expected);
if (!problem.empty()){
return problem;
}
const std::string text = Pokemon::describe(table);
for (const std::string& id : expected.checkpoint_ids){
if (text.find(id) == std::string::npos){
return std::format("{}: describe() does not name {}:\n{}", expected.name, id, text);
}
}
logger.log(text);
}
return true;
}
};

}


void add_tests_AutoStory_StoryTables(UnitTestDatabase& database){
database.add<Test_StoryTables>();
}


}
}
}
Loading
Loading