Skip to content

Smart Rewrite

Clement Julia edited this page Aug 10, 2021 · 4 revisions

We want to create a smart rule rewrite system leveraging Rascal's rewrite capabilities, so we want to do something smart using cases and rewrite.

Example: [ > Player | Crate ] -> [ > Player | > Crate ]

becomes

case layer(moving("player"), stationary("crate")) => layer(moving("player"), moving("crate"))

This is the theory, but the practice is much more complex, how can we generate this? Do we use metaprogramming to write out all possible cases? Not to mention a dozen of other implementation questions, what is movement, how do we define where the player is going?

What about the elipsis? Do we generate every possible case? increpare calls a compiler that does it like that dumb (and they probably know what they're talking about). We would need to generate something like that for every possible vertical and horizontal height and since they can stretch forever that could be problematic.

The first problem to solve is that matches on the left side are repeated on the right side

So if we have a property to match A or B and it matches A, then the rewrite rule using that same property (A or B) on the right side should know that we matched A. So maybe we should use regular expressions instead, not as nifty but might be more logical.

The issue remains of movement, if we simply have a bunch of regex stuff, how do we determine movement?

Regex idea [ Player | Crate ] -> [ Crate | Player ] becomes

//W: Wall, P: Player, C: Crate
line = resolve_line(#.PC..#); //becomes Wall . "Player Crate . . Wall"
/player crate/i := line

With ellipsis [ LavaGun | ... | Mortal ] -> [ LavaGun | ... | Corpse ]

// mortal = player or beast
/lavagun .* (player|beast)/i := line

but then what is movement? Do we use the same system as with the sound events, so

[ > Player | Crate ] -> [ > Player | > Crate ]

becomes

/moving_player crate/ := line

This is all theoretical, I haven't fully worked out how exactly the replacing would work in this case. if we label the arguments can we do that? Above example becomes

right = ("match0": "moving_player moving_crate")
if (/<match0:moving_player crate>/ := line) replaceFirst(line, match0, right["match0"]);

And then there is also the questions of horizontal/vertical matching, is it cheating if I just rotate the level?

Basically, I want to avoid creating a system exactly like PuzzleScript because, while it works, it is also complex if not complicated.

TL;DR: I have a lot of questions and very few answers...

PS. Funnily enough it seems that Puzzlescript's current implementation also makes use of meta programming, writing custom functions for rule matches: https://github.com/increpare/PuzzleScript/blob/2ba5ea9d699b9023cd1adc82dc21b33d70e755d7/src/js/engine.js#L1295

I am confusion

Update

I have finally had time to talk with Riemer and we have come up with a solution. I had previously caught a brief glimpse of a rascal concept called list patterns and we think it may be the solution to our problem. This allows us to pattern match a list of pixel.

[ >  Player | Crate ] -> [  >  Player | > Crate  ]

[*before, \moving_player("player", legend0, direction0), object("crate", legend1), *after]
->
before + [moving_player("player", legend0, direction0), moving_object("crate", legend1, direction0)] + after

The rule above matches to

[transparent("trans", "."), transparent("trans", "."), moving_player("player", "p", "left"), object("crate", "c"), transparent("trans", ".")]
Clone this wiki locally