Skip to content

Initial soh_ap transpiler - #1

Draft
mattman107 wants to merge 27 commits into
xxAtrain223:mainfrom
mattman107:soh_ap
Draft

Initial soh_ap transpiler#1
mattman107 wants to merge 27 commits into
xxAtrain223:mainfrom
mattman107:soh_ap

Conversation

@mattman107

@mattman107 mattman107 commented May 5, 2026

Copy link
Copy Markdown

Hello!

Been working on this on and off, and I finally have something I think (mostly) works.

Some of the biggest changes include:

  1. I wound up renaming the transpiler from ap to soh_ap. It makes it clearer what game it is for. (May have missed some places that needed to be changed over. Please lmk as I am bad at c++ and cmake stuff)
  2. Implemented barebones transpiler functions. I mostly followed along with your soh example here. Kept the structure as similar as possible while modifying any c++ output to python (or close to. Will need to have more discussions about this)
  3. This kinda goes hand and hand with Implement Enum Support #2, but I implemented the rls_match functionality. Again this was me following along with your example, but I did have to do some wacky python stuff in here, so I figured I would call it out.
  4. Added test files that were auto generated. For some reason I couldn't get the tests to succeed, so if you seen anything I clearly did wrong please lmk.

Now on to things that I noticed about what is currently auto generating that I am not exactly sure how we are going to solve.

  1. When calling functions in the lambdas of regions.gen.py a requirement we will need for Ship AP is that we pass the world (or bundle. This is tuple of stuff we crammed together to more easily pass things to all the functions). Not sure how we are going to modify for example is_child() and has(RG_OPEN_CHEST) to also pass our arbitrary junk. Maybe replace one of the parenthesis?
  2. AP events are required to have unique names per location. Ship doesn't require events to have any names. My first thought was smash together the region name and event type in a string.
  3. This implementation uses the (almost antiquated) AP boolean + lambda access rules. Ship AP is currently in the middle of transitioning to use AP's rule builder module. I'm not 100% sure how that would be done with this, as depending on the context the operator we may use for a comparison could change. For example when comparing rules together it would require us to use & for and, and | for or. But there are times when we may need to compare bools together. I'm not 100% sure how I would make this context known with the existing BinaryExpr.
  4. Pythons Enums require that the user specify what Enum they belong to. I'm not sure how the current implementation allows for that to occur, as it just knows that there is a keyword being used, not what Enum it might be apart of. For example, EnemyDistance in python and it being used

I think you have done a great job so far! Most of this stuff seems like wizardry to me, but it is incredible to see it come together. Please feel free to tear all of this apart because I won't lie, I barely know what I am doing.

edit: Also I think a valid answer to any of the weird and wacky things I said above could be "That would be crazy to try and add to this". I think a huge win for Ship AP would be if we could auto generate both the functions and the regions/locations/events/exits, but honestly even if we could just get one of them (preferably the regions/etc) I would be happy 😄

IQubic and others added 5 commits April 27, 2026 20:18
Get Basic output generating for soh_ap
Add SharedSpirit. May need special attention when creating the SharedSpirit functions.
Fixed function definitions not having default values for parameters.
@xxAtrain223

Copy link
Copy Markdown
Owner

Thanks for taking this on, this is great progress!
For the big changes:
1. It makes sense to rename ap to soh_ap, here's what I found that still needs to be renamed.
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/README.md?plain=1#L28
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/README.md?plain=1#L38
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/console/CMakeLists.txt#L12
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/console/CMakeLists.txt#L5
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/console/main.cpp#L27
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/console/tests/acceptance_ap_tests.cpp#L10
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/docs/BUILDING.md?plain=1#L141
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/CMakeLists.txt#L5
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/CMakeLists.txt#L7
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/CMakeLists.txt#L8
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/CMakeLists.txt#L16
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/tests/ap_tests.cpp#L34
https://github.com/mattman107/RandoLogicScript/blob/soh_ap/transpilers/soh_ap/tests/ap_tests.cpp#L35
3. Active means that the condition matched and it's now in fallthrough mode, so the condition doesn't need to checked. I think it can be added as an optional after args. You could probably also reuse the body results in a couple places.
4. It looks like you're hitting some non-determinism with the region extensions. I'll change it from std::unordered_multimap<std::string, const ExtendRegionDecl*> to std::map<std::string, std::vector<const ExtendRegionDecl*>>.

Things to solve:
1. You could always make bundle the first parameter or a kwarg and then pass it as the first parameter or as a kwarg.
2. Some events in SOH are defined in multiple regions, but it seems like your first thought would work.
3. Could you show a couple examples?
4. This might be where we need to be able to define enums, but the simplest thing you could do right now is something like this:

std::string SohApTranspiler::GenerateExpression(const rls::ast::Identifier& node) const {
    auto type = project.getType(&node);
    if (!type.has_value()) {
        return node.name;
    }
    switch (type.value()) {
        case rls::ast::Type::Distance:
            return "EnemyDistance." + node.name;
        default:
            return node.name;
    }
}

@xxAtrain223 xxAtrain223 self-assigned this May 9, 2026
@mattman107

Copy link
Copy Markdown
Author

Awesome! Thank you for the feedback. I just fixed the other places that needed to be renamed in my local repo. I'll work on implementing some of this. Going to need some time though 😅

@xxAtrain223

Copy link
Copy Markdown
Owner

I changed ExtendRegionDecls from a multimap to a map of (sorted) vectors in 6ef81c0. Merge that in, regenerate the ap_soh examples and run AcceptanceAp.ExamplesRlsMatchesGolden, hopefully that will give you the same output and passes the test.

@mattman107

Copy link
Copy Markdown
Author

I think the most recent changes should fix how I was using active in rls_match.

Made the bundle the first parameter in function signatures. Still not sure how to pass that down into subsequent function calls.

std::string SohApTranspiler::GenerateExpression(const rls::ast::Identifier& node) const {
auto type = project.getType(&node);
if (!type.has_value()) {
return node.name;
}
switch (type.value()) {
case rls::ast::Type::Distance:
return "EnemyDistance." + node.name;
default:
return node.name;
}
}

This works in some cases, but not all. As an example:

This works because we needed to define the items we need to use:

def call_gossip_fairy_except_suns(bundle) -> bool:
    return can_use(RandomizerGet.RG_ZELDAS_LULLABY) or can_use(RandomizerGet.RG_EPONAS_SONG) or can_use(RandomizerGet.RG_SONG_OF_TIME)

This example doesn't work though. Trying to use the e parameter, but since it is now being referred to the RandomizerEnemy.e it thinks that is something in the RandomizerEnemy enum:

def can_get_drop(bundle, e: RandomizerEnemy, distance: EnemyDistance = EnemyDistance.ED_CLOSE, above_link: bool = False) -> bool:
    return can_kill(RandomizerEnemy.e, EnemyDistance.distance, True, 1, False, False) and (distance_to_int(EnemyDistance.distance) <= distance_to_int(EnemyDistance.ED_MASTER_SWORD_JUMPSLASH) or rls_match((lambda e: e == RE_GOLD_SKULLTULA), (lambda: _can_get_drop_gold_skulltula(EnemyDistance.distance)), False, (lambda e: e == RE_KEESE or e == RE_FIRE_KEESE or e == RE_GUAY), (lambda: True), False, (lambda: true), (lambda: above_link or distance_to_int(EnemyDistance.distance) <= distance_to_int(EnemyDistance.ED_BOOMERANG) and can_use(RandomizerGet.RG_BOOMERANG)), False))

@xxAtrain223

Copy link
Copy Markdown
Owner

Ok, we should be able to distinguish the two now. https://github.com/xxAtrain223/RandoLogicScript/compare/fae58bb0668a25d9f546c5ee3347fb7e2c52de82..dbbd70f69a833640207126df5f3856e9bfe2f866#diff-9ba7a3ed12ed289f9433d9fb999b958866e19bcdf44e829e35ae45ce577e1bd1

rls::ast::Type::Setting might be the reason to add enum and extern enum to the language.

@mattman107

mattman107 commented May 14, 2026

Copy link
Copy Markdown
Author

Sounds good to me. I'll implement this asap.

Just a list of things I need to do so I remember:

  1. Add the enum values
  2. Create list of event location
  3. Properly handle event names (likely region name + event type)
  4. Determine if I should keep bools, or go straight to rules for this first implementation

mattman107 and others added 13 commits May 14, 2026 18:12
Add enum types to access rules
Add event locations
Brainstorm settings
add locations enum
- Disable function creation for now
- Update enum names to match upstream Ship AP
- Fix duplicated values in enum output
- Add enum value method for fixing names in python
Add OptionFilter unit tests
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix up remaining OptionFilter issues i could think of.
Add more unit tests.
Make rls_match work with RB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
mattman107 and others added 4 commits July 12, 2026 12:40
…nal rules

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Key renderEnumValue/pythonTypeName on the RLS enum name instead of
ast::Type, and lower MemberExpr, StringLiteral and ListExpr.

Generate normal RLS enums into enums.gen.py as IntEnums; extern enums are
glob patterns with no members, so the world still supplies those. Declared
enums render under their own class name, so WaterLevel no longer folds
into Events.

Also fix the console rejecting `-t soh_ap` (the guard checked "ap" while
the dispatch checked "soh_ap") and a missing <algorithm> include in the
sema tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants