Skip to content

Commit

Permalink
Impl primary versions of each assign config option
Browse files Browse the repository at this point in the history
  • Loading branch information
grunt-lucas committed Oct 2, 2023
1 parent 636e259 commit f82208b
Show file tree
Hide file tree
Showing 10 changed files with 177 additions and 78 deletions.
3 changes: 0 additions & 3 deletions Todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,6 @@
+ if assign.cfg does not exist, warn the user and run the full assignment param search matrix
+ this will be a default-on warning
+ user can supply --cache-assign-config option to force porytiles to search for a valid assign param and save it to input folder
+ we will also need a way to specify config params for the paired primary set when compiling in secondary mode
+ it is possible (and quite likely) that you will have cases where the primary set needs different params
+ there should be primary versions of the options: --primary-assign-algo=<ALGO>, etc

+ `report` command that prints out various statistics
+ Number of tiles, metatiles, unique colors, etc
Expand Down
17 changes: 11 additions & 6 deletions include/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -805,15 +805,20 @@ struct CompilerConfig {
std::string defaultBehavior;

// Palette assignment algorithm configuration
AssignAlgorithm assignAlgorithm;
std::size_t exploredNodeCutoff;
std::size_t bestBranches;
bool smartPrune;
AssignAlgorithm primaryAssignAlgorithm;
std::size_t primaryExploredNodeCutoff;
std::size_t primaryBestBranches;
bool primarySmartPrune;
AssignAlgorithm secondaryAssignAlgorithm;
std::size_t secondaryExploredNodeCutoff;
std::size_t secondaryBestBranches;
bool secondarySmartPrune;

CompilerConfig()
: mode{}, transparencyColor{RGBA_MAGENTA}, tripleLayer{true}, defaultBehavior{"MB_NORMAL"},
assignAlgorithm{AssignAlgorithm::DEPTH_FIRST}, exploredNodeCutoff{2'000'000}, bestBranches{SIZE_MAX},
smartPrune{false}
primaryAssignAlgorithm{AssignAlgorithm::DEPTH_FIRST}, primaryExploredNodeCutoff{2'000'000},
primaryBestBranches{SIZE_MAX}, primarySmartPrune{false}, secondaryAssignAlgorithm{AssignAlgorithm::DEPTH_FIRST},
secondaryExploredNodeCutoff{2'000'000}, secondaryBestBranches{SIZE_MAX}, secondarySmartPrune{false}
{
}
};
Expand Down
72 changes: 56 additions & 16 deletions src/cli_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,31 +519,71 @@ static void parseSubcommandOptions(PtContext &ctx, int argc, char *const *argv)
case ASSIGN_EXPLORE_CUTOFF_VAL:
cutoffFactor = parseIntegralOption<std::size_t>(ctx.err, ASSIGN_EXPLORE_CUTOFF, optarg);
// FIXME : error if this factor is too large
ctx.compilerConfig.exploredNodeCutoff = cutoffFactor * EXPLORATION_CUTOFF_MULTIPLIER;
if (ctx.subcommand == Subcommand::COMPILE_PRIMARY) {
ctx.compilerConfig.primaryExploredNodeCutoff = cutoffFactor * EXPLORATION_CUTOFF_MULTIPLIER;
}
else if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
ctx.compilerConfig.secondaryExploredNodeCutoff = cutoffFactor * EXPLORATION_CUTOFF_MULTIPLIER;
}
break;
case ASSIGN_ALGO_VAL:
ctx.compilerConfig.assignAlgorithm = parseAssignAlgorithm(ctx.err, ASSIGN_ALGO, optarg);
if (ctx.subcommand == Subcommand::COMPILE_PRIMARY) {
ctx.compilerConfig.primaryAssignAlgorithm = parseAssignAlgorithm(ctx.err, ASSIGN_ALGO, optarg);
}
else if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
ctx.compilerConfig.secondaryAssignAlgorithm = parseAssignAlgorithm(ctx.err, ASSIGN_ALGO, optarg);
}
break;
case BEST_BRANCHES_VAL:
if (std::string{optarg} == "smart") {
ctx.compilerConfig.smartPrune = true;
if (ctx.subcommand == Subcommand::COMPILE_PRIMARY) {
if (std::string{optarg} == "smart") {
ctx.compilerConfig.primarySmartPrune = true;
}
else {
ctx.compilerConfig.primaryBestBranches = parseIntegralOption<std::size_t>(ctx.err, BEST_BRANCHES, optarg);
if (ctx.compilerConfig.primaryBestBranches == 0) {
fatalerror(ctx.err, fmt::format("option '{}' argument cannot be 0",
fmt::styled(BEST_BRANCHES, fmt::emphasis::bold)));
}
}
}
else {
ctx.compilerConfig.bestBranches = parseIntegralOption<std::size_t>(ctx.err, BEST_BRANCHES, optarg);
if (ctx.compilerConfig.bestBranches == 0) {
fatalerror(ctx.err,
fmt::format("option '{}' argument cannot be 0", fmt::styled(BEST_BRANCHES, fmt::emphasis::bold)));
else if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
if (std::string{optarg} == "smart") {
ctx.compilerConfig.secondarySmartPrune = true;
}
else {
ctx.compilerConfig.secondaryBestBranches = parseIntegralOption<std::size_t>(ctx.err, BEST_BRANCHES, optarg);
if (ctx.compilerConfig.secondaryBestBranches == 0) {
fatalerror(ctx.err, fmt::format("option '{}' argument cannot be 0",
fmt::styled(BEST_BRANCHES, fmt::emphasis::bold)));
}
}
}
break;
case PRIMARY_ASSIGN_EXPLORE_CUTOFF_VAL:
// TODO : impl primary version of option
if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
ctx.compilerConfig.primaryExploredNodeCutoff = cutoffFactor * EXPLORATION_CUTOFF_MULTIPLIER;
}
break;
case PRIMARY_ASSIGN_ALGO_VAL:
// TODO : impl primary version of option
if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
ctx.compilerConfig.primaryAssignAlgorithm = parseAssignAlgorithm(ctx.err, PRIMARY_ASSIGN_ALGO, optarg);
}
break;
case PRIMARY_BEST_BRANCHES_VAL:
// TODO : impl primary version of option
if (ctx.subcommand == Subcommand::COMPILE_SECONDARY) {
if (std::string{optarg} == "smart") {
ctx.compilerConfig.primarySmartPrune = true;
}
else {
ctx.compilerConfig.primaryBestBranches =
parseIntegralOption<std::size_t>(ctx.err, PRIMARY_BEST_BRANCHES, optarg);
if (ctx.compilerConfig.primaryBestBranches == 0) {
fatalerror(ctx.err, fmt::format("option '{}' argument cannot be 0",
fmt::styled(PRIMARY_BEST_BRANCHES, fmt::emphasis::bold)));
}
}
}
break;

// Fieldmap override options
Expand Down Expand Up @@ -900,9 +940,10 @@ static void parseSubcommandOptions(PtContext &ctx, int argc, char *const *argv)
ctx.err.setAllWarnings(WarningMode::OFF);
}

if (ctx.compilerConfig.smartPrune && ctx.compilerConfig.bestBranches > 0) {
fatalerror(ctx.err, fmt::format("found two conflicting configs for `{}' option", BEST_BRANCHES));
}
// TODO : should we fail here, or warn the user, or do nothing and just prioritize one over the other?
// if (ctx.compilerConfig.smartPrune && ctx.compilerConfig.bestBranches > 0) {
// fatalerror(ctx.err, fmt::format("found two conflicting configs for `{}' option", BEST_BRANCHES));
// }

/*
* Apply and validate the fieldmap configuration parameters
Expand Down Expand Up @@ -951,7 +992,6 @@ static void parseSubcommandOptions(PtContext &ctx, int argc, char *const *argv)
die(ctx.err, "Errors generated during command line parsing. Decompilation terminated.");
}
}

} // namespace porytiles

TEST_CASE("parseCompile should work as expected with all command lines")
Expand Down
37 changes: 22 additions & 15 deletions src/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -718,11 +718,17 @@ std::unique_ptr<CompiledTileset> compile(PtContext &ctx, const DecompiledTileset
AssignState initialState = {tmpHardwarePalettes, unassignedNormPalettes.size()};
ctx.compilerContext.exploredNodeCounter = 0;
AssignResult assignResult = AssignResult::NO_SOLUTION_POSSIBLE;
if (ctx.compilerConfig.assignAlgorithm == AssignAlgorithm::DEPTH_FIRST) {
AssignAlgorithm assignAlgorithm = ctx.compilerConfig.mode == CompilerMode::PRIMARY
? ctx.compilerConfig.primaryAssignAlgorithm
: ctx.compilerConfig.secondaryAssignAlgorithm;
std::size_t exploredNodeCutoff = ctx.compilerConfig.mode == CompilerMode::PRIMARY
? ctx.compilerConfig.primaryExploredNodeCutoff
: ctx.compilerConfig.secondaryExploredNodeCutoff;
if (assignAlgorithm == AssignAlgorithm::DEPTH_FIRST) {
assignResult =
assignDepthFirst(ctx, initialState, assignedPalsSolution, primaryPaletteColorSets, unassignedNormPalettes);
}
else if (ctx.compilerConfig.assignAlgorithm == AssignAlgorithm::BREADTH_FIRST) {
else if (assignAlgorithm == AssignAlgorithm::BREADTH_FIRST) {
assignResult =
assignBreadthFirst(ctx, initialState, assignedPalsSolution, primaryPaletteColorSets, unassignedNormPalettes);
}
Expand All @@ -739,11 +745,11 @@ std::unique_ptr<CompiledTileset> compile(PtContext &ctx, const DecompiledTileset
fatalerror_noPossiblePaletteAssignment(ctx.err, ctx.compilerSrcPaths, ctx.compilerConfig.mode);
}
else if (assignResult == AssignResult::EXPLORE_CUTOFF_REACHED) {
fatalerror_assignExploreCutoffReached(ctx.err, ctx.compilerSrcPaths, ctx.compilerConfig.mode,
ctx.compilerConfig.assignAlgorithm, ctx.compilerConfig.exploredNodeCutoff);
fatalerror_assignExploreCutoffReached(ctx.err, ctx.compilerSrcPaths, ctx.compilerConfig.mode, assignAlgorithm,
exploredNodeCutoff);
}
pt_logln(ctx, stderr, "{} assigned all NormalizedPalettes successfully after {} iterations",
assignAlgorithmString(ctx.compilerConfig.assignAlgorithm), ctx.compilerContext.exploredNodeCounter);
assignAlgorithmString(assignAlgorithm), ctx.compilerContext.exploredNodeCounter);

/*
* Copy the assignments into the compiled palettes. In a future version we will support sibling tiles (tile sharing)
Expand Down Expand Up @@ -1398,7 +1404,7 @@ TEST_CASE("assign should correctly assign all normalized palettes or fail if imp
porytiles::PtContext ctx{};
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.fieldmapConfig.numPalettesInPrimary = SOLUTION_SIZE;
ctx.compilerConfig.exploredNodeCutoff = 20;
ctx.compilerConfig.primaryExploredNodeCutoff = 20;

REQUIRE(std::filesystem::exists("res/tests/2x2_pattern_2.png"));
png::image<png::rgba_pixel> png1{"res/tests/2x2_pattern_2.png"};
Expand Down Expand Up @@ -1435,7 +1441,7 @@ TEST_CASE("assign should correctly assign all normalized palettes or fail if imp
porytiles::PtContext ctx{};
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.fieldmapConfig.numPalettesInPrimary = SOLUTION_SIZE;
ctx.compilerConfig.exploredNodeCutoff = 200;
ctx.compilerConfig.primaryExploredNodeCutoff = 200;

REQUIRE(std::filesystem::exists("res/tests/compile_raw_set_1/set.png"));
png::image<png::rgba_pixel> png1{"res/tests/compile_raw_set_1/set.png"};
Expand Down Expand Up @@ -1472,9 +1478,9 @@ TEST_CASE("makeTile should create the expected GBATile from the given Normalized
ctx.compilerConfig.transparencyColor = porytiles::RGBA_MAGENTA;
ctx.fieldmapConfig.numPalettesInPrimary = 2;
ctx.fieldmapConfig.numTilesInPrimary = 4;
ctx.compilerConfig.exploredNodeCutoff = 5;
ctx.compilerConfig.primaryExploredNodeCutoff = 5;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/2x2_pattern_2.png"));
png::image<png::rgba_pixel> png1{"res/tests/2x2_pattern_2.png"};
Expand Down Expand Up @@ -1527,9 +1533,9 @@ TEST_CASE("compile simple example should perform as expected")
porytiles::PtContext ctx{};
ctx.fieldmapConfig.numPalettesInPrimary = 2;
ctx.fieldmapConfig.numTilesInPrimary = 4;
ctx.compilerConfig.exploredNodeCutoff = 5;
ctx.compilerConfig.primaryExploredNodeCutoff = 5;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/2x2_pattern_2.png"));
png::image<png::rgba_pixel> png1{"res/tests/2x2_pattern_2.png"};
Expand Down Expand Up @@ -1603,7 +1609,7 @@ TEST_CASE("compile function should fill out primary CompiledTileset struct with
ctx.fieldmapConfig.numPalettesInPrimary = 3;
ctx.fieldmapConfig.numPalettesTotal = 6;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_3/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_3/primary/middle.png"));
Expand Down Expand Up @@ -1741,7 +1747,7 @@ TEST_CASE("compile function should fill out secondary CompiledTileset struct wit
ctx.fieldmapConfig.numPalettesInPrimary = 3;
ctx.fieldmapConfig.numPalettesTotal = 6;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_3/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_3/primary/middle.png"));
Expand Down Expand Up @@ -1902,7 +1908,7 @@ TEST_CASE("compile function should correctly compile primary set with animated t
ctx.fieldmapConfig.numPalettesInPrimary = 3;
ctx.fieldmapConfig.numPalettesTotal = 6;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_1/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_1/primary/middle.png"));
Expand Down Expand Up @@ -2113,7 +2119,8 @@ TEST_CASE("compile function should correctly compile secondary set with animated
ctx.fieldmapConfig.numPalettesInPrimary = 3;
ctx.fieldmapConfig.numPalettesTotal = 6;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_1/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_1/primary/middle.png"));
Expand Down
3 changes: 2 additions & 1 deletion src/decompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ TEST_CASE("decompile should decompile a basic tileset")
ctx.fieldmapConfig.numPalettesInPrimary = 6;
ctx.fieldmapConfig.numPalettesTotal = 13;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_2/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_2/primary/middle.png"));
Expand Down
6 changes: 4 additions & 2 deletions src/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,8 @@ TEST_CASE("drive should emit all expected files for anim_metatiles_2 primary set
ctx.output.path = parentDir;
ctx.subcommand = porytiles::Subcommand::COMPILE_PRIMARY;
ctx.err.printErrors = false;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_2/primary"));
ctx.compilerSrcPaths.primarySourcePath = "res/tests/anim_metatiles_2/primary";
Expand Down Expand Up @@ -965,7 +966,8 @@ TEST_CASE("drive should emit all expected files for anim_metatiles_2 secondary s
ctx.output.path = parentDir;
ctx.subcommand = porytiles::Subcommand::COMPILE_SECONDARY;
ctx.err.printErrors = false;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/anim_metatiles_2/primary"));
ctx.compilerSrcPaths.primarySourcePath = "res/tests/anim_metatiles_2/primary";
Expand Down
12 changes: 8 additions & 4 deletions src/emitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ TEST_CASE("emitTilesPng should emit the expected tiles.png file")
std::filesystem::path parentDir = porytiles::createTmpdir();
ctx.subcommand = porytiles::Subcommand::COMPILE_PRIMARY;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_2/primary/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_2/primary/middle.png"));
Expand Down Expand Up @@ -497,7 +498,8 @@ TEST_CASE("emitMetatilesBin should emit metatiles.bin as expected based on setti
ctx.output.path = parentDir;
ctx.subcommand = porytiles::Subcommand::COMPILE_PRIMARY;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_1/bottom.png"));
REQUIRE(std::filesystem::exists("res/tests/simple_metatiles_1/middle.png"));
Expand Down Expand Up @@ -564,7 +566,8 @@ TEST_CASE("emitAttributes should correctly emit metatile attributes")
ctx.subcommand = porytiles::Subcommand::COMPILE_PRIMARY;
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.err.printErrors = false;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

std::unordered_map<std::string, std::uint8_t> behaviorMap = {
{"MB_NORMAL", 0x00}, {"MB_TALL_GRASS", 0x02}, {"MB_PUDDLE", 0x16}};
Expand Down Expand Up @@ -648,7 +651,8 @@ TEST_CASE("emitAttributes should correctly emit metatile attributes")
ctx.compilerConfig.mode = porytiles::CompilerMode::PRIMARY;
ctx.compilerConfig.tripleLayer = false;
ctx.err.printErrors = false;
ctx.compilerConfig.assignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.primaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;
ctx.compilerConfig.secondaryAssignAlgorithm = porytiles::AssignAlgorithm::DEPTH_FIRST;

std::unordered_map<std::string, std::uint8_t> behaviorMap = {
{"MB_NORMAL", 0x00}, {"MB_TALL_GRASS", 0x02}, {"MB_PUDDLE", 0x16}};
Expand Down
Loading

0 comments on commit f82208b

Please sign in to comment.