Live data from Hacker News

Prolog Basics Explained with Pokémon

unplannedobsolescence.com

51–60 of 64 posts

Re: Prolog Basics Explained with Pokémon

#51

Love this use case, makes me want to implement something similar for Magic the Gathering. I love using scryfall, but I think a more cli first approach with descriptive rules would suffice much better for brewing in eternal formats like Commander with ever growing card pools. I mostly work off of keyword search.

Your comment makes me want to do this for Yu-Gi-Oh!

Re: Prolog Basics Explained with Pokémon

#52

If this is your article, you have a typo in learns_priority/3, "move_priority #> 0" should be "P #> 0".

Thank you!

Since you're here, I have a small question: Why use pokemon showdowns API directly and not https://pokeapi.co/ ?

Re: Prolog Basics Explained with Pokémon

#53

> " Don't be bothered with by the fact that the solutions end with "or false" here. It's a function of how the search algorithms work; the solver looked for more solutions, then failed. I'll admit, I don't totally understand why it only sometimes does this, but it's expected. " I think this is explained in The Power of Prolog[1] that the answers coming from Prolog are not printing text to a terminal, they are valid P…

Eh, no, admittedly this is a bit confusing but the ";" at the top-level is not the same as the ;/2 disjunction operator. At the top-level the ";" is just a switch to say "give me more". In most Prolog systems you can also press space for the same thing.

Whether a query will end with "false" or "true" really depends entirely on the query, and the predicates it's calling. So it depends on how a predicate is called. You just have to analyse the predicate in the good, old-fashioned way of eyballing it and maybe running it a couple of times to see why it fails or succeeds, and why it first succeeds and then fails like above.

This is really all down to determinism, which is to say, whether a predicate leaves behind a choice point or not. In this particular case, what's going on can be revealed by looking at the trace of the query. For the following, I loaded the Pokemon database from https://github.com/alexpetros/prologdex/blob/main/src/dex/po...) into SWI-Prolog and traced it like this:

  [trace] 21 ?- type(Pokemon, water), type(Pokemon, ice).
     Call: (15) dex:type(_2236, water) ? creep
     Exit: (15) dex:type(squirtle, water) ? creep
     Call: (15) dex:type(squirtle, ice) ? creep
     Fail: (15) dex:type(squirtle, ice) ? creep
     Redo: (15) dex:type(_2236, water) ? creep
     Exit: (15) dex:type(wartortle, water) ? creep
     Call: (15) dex:type(wartortle, ice) ? creep
     Fail: (15) dex:type(wartortle, ice) ? abort
  % Execution Aborted
Now, if you squint real hard you'll see that the Prolog engine first unifies the variable 'Pokemon' with the name of a Pokemon, "squirtle". That happens to be the first pokemon in the database that has "water" as the type. Basically the engine looks for a type/2 fact where the second argument is "water" and finds type('squirtle', 'water') first.

In the second call... I mean this one:

     Call: (15) dex:type(squirtle, ice) ? creep
The engine looks for a fact that would satisfy the second goal in the top-level query. This is the top-level query:

  [trace] 21 ?- type(Pokemon, water), type(Pokemon, ice).
And this is the second goal in it:

  type(Pokemon, ice)
Since "Pokemon" is already bound to "squirtle", the engine tries to satisfy the fact

  type(squirtle, ice)
It can't, so it fails. And then it repeats with the second water pokemon in the list, which is wartotrtle.

What happens when the engine finds a pokemon that is both water and ice? The first one in the database is dewogong, at which point the tracer goes like this:

     Redo: (15) dex:type(_92, water) ? creep
     Exit: (15) dex:type(dewgong, water) ? creep
     Call: (15) dex:type(dewgong, ice) ? creep
     Exit: (15) dex:type(dewgong, ice) ? creep
  Pokemon = dewgong .
So this time both goals in the top-level query are satisfiable and the engine returns the name of the value of "Pokemon" that makes the query true (well, satisfiable).

At that point the "." means I pressed "enter" and that's basically saying "no more thank you". I was getting a little impatient. If I had pressed ";" (or space) instead, we'd have a repeat of the previous pattern with successive failures until another water and ice pokemon was found. The next one in the database is cloyster, and so it goes.

That back-and-forth between success and failure goes on until we run out of pokemon. At some point the engine can't unify either goal of the top-level query with a fact in the database so the final result is "false"; because after succeeding a bunch of times, it can't succeed no more.

Honestly this is a fair question to have and I had to trace the query to be able to give a fair account of it so the advice to take it as "expected" is not that bad. Prolog is doing something mind-numbingly repetitive on the background and it's hard to follow it, so sometimes the best thing is to just run your program and see what it does, instead of trying to intuit it.

Re: Prolog Basics Explained with Pokémon

#54
post #27

> " Don't be bothered with by the fact that the solutions end with "or false" here. It's a function of how the search algorithms work; the solver looked for more solutions, then failed. I'll admit, I don't totally understand why it only sometimes does this, but it's expected. " I think this is explained in The Power of Prolog[1] that the answers coming from Prolog are not printing text to a terminal, they are valid P…

This embodies why I don't like Prolog. Prolog's philosophy is that you should just write the predicates without thinking about how the engine works. But as soon as you do something actually complicated, you realize that the different optimization modes of the engine give different results, and shortly after that you'll find yourself in the "exhaustively try every possible combination until we get one that satisfies t…

[deleted]

Re: Prolog Basics Explained with Pokémon

#55
post #5

Was initially nonplussed, but toward the end I realized the choice of pokemon for an example actually works out well for showing how prologue can solve problems. I’m now a bit curious about trying it out somewhere.

All examples shown in the article can be ran with Datalog too (with stratified negation and arithmetic comparison), which has a clearer execution model and looks almost identical to Prolog. Prolog underneath is doing backtracking, while Datalog is finding a least fixed point of derived relations where iterating on data won't produce more relations, and is akind to SQL (but usually stronger because of recursion).

Ahem. "Prolog underneath" is doing SLD-Resolution implemented as a Depth-First Search with backtracking. Saying it's "doing backtracking" is really fudging quite a bit.

Datalog, instead, is "underneath" implementing a TP-Operator, a procedure that finds the fix-point of a Datalog program which happens to be the same as its Least Herbrand Model, which is what SLD-Resolution also finds, except that SLD-Resolution allows functions and does not guarantee termination, like Datalog does. The big advantage of a TP Operator is the termination guarantees and it can be implemented so that it's efficient, but it's still limited, and the fact that there are many different flavours of Datalog (with or without stratified negation, arithmetic, lists etc) is testament to the difficulty of improving on Prolog's efficiency without breaking its soundness or completeness. Or, like I always say, "sound, complete, efficient: choose two".

Re: Prolog Basics Explained with Pokémon

#56
post #42

Love this use case, makes me want to implement something similar for Magic the Gathering. I love using scryfall, but I think a more cli first approach with descriptive rules would suffice much better for brewing in eternal formats like Commander with ever growing card pools. I mostly work off of keyword search.

This might be your jam. I should revisit this, most of it is unimplemented. But the parsing part was interesting, since MTG has such a codified language for explaining the rules. My idea here was to take a card's rules text from the official API and parse it into smth that can be used in the game, so you could keep up with new sets with implementation. The weird rule-breaking edge cases will always fail, but a large…

Hey, that's cool! I had the same idea too:

https://github.com/stassa/Gleemin

Although the code is nigh-on unreadable now ^_^@

>> The weird rule-breaking edge cases will always fail, but a large set of design space can fit, I think.

Well, in that case you just update the parser or the rules engine. That's precisely how M:tG Arena works and they 've managed to keep up with new sets at a pace never before seen in official M:tG engines. There was a time when writing an M:tG parser was considered impossible [1] and M:tG engines, both official and community ones, each had their own little DSL that they had to manually translate ability text into which could take months especially if the engine also had to be updated to use the new rules in a set. Eventually someone in Wizards realised this is just dumb and they did it the way you go about it above.

______________

[1] Source: my conversations with the folks behind Forge and other community-made M:tG engines.

Re: Prolog Basics Explained with Pokémon

#57
post #42

Earlier quoted context omitted.

This might be your jam. I should revisit this, most of it is unimplemented. But the parsing part was interesting, since MTG has such a codified language for explaining the rules. My idea here was to take a card's rules text from the official API and parse it into smth that can be used in the game, so you could keep up with new sets with implementation. The weird rule-breaking edge cases will always fail, but a large…

Hey, that's cool! I had the same idea too: https://github.com/stassa/Gleemin Although the code is nigh-on unreadable now ^_^@ >> The weird rule-breaking edge cases will always fail, but a large set of design space can fit, I think. Well, in that case you just update the parser or the rules engine. That's precisely how M:tG Arena works and they 've managed to keep up with new sets at a pace never before seen in offici…

Wow that is cool, thanks for that!

Re: Prolog Basics Explained with Pokémon

#58
post #42

Love this use case, makes me want to implement something similar for Magic the Gathering. I love using scryfall, but I think a more cli first approach with descriptive rules would suffice much better for brewing in eternal formats like Commander with ever growing card pools. I mostly work off of keyword search.

This might be your jam. I should revisit this, most of it is unimplemented. But the parsing part was interesting, since MTG has such a codified language for explaining the rules. My idea here was to take a card's rules text from the official API and parse it into smth that can be used in the game, so you could keep up with new sets with implementation. The weird rule-breaking edge cases will always fail, but a large…

Somewhat related, check out https://github.com/wordbots/wordbots-parser, a digital card game where you write the cards and the engine parses them to determine what they do. It's fun to mess around with.

Re: Prolog Basics Explained with Pokémon

#59
post #27

> " Don't be bothered with by the fact that the solutions end with "or false" here. It's a function of how the search algorithms work; the solver looked for more solutions, then failed. I'll admit, I don't totally understand why it only sometimes does this, but it's expected. " I think this is explained in The Power of Prolog[1] that the answers coming from Prolog are not printing text to a terminal, they are valid P…

This embodies why I don't like Prolog. Prolog's philosophy is that you should just write the predicates without thinking about how the engine works. But as soon as you do something actually complicated, you realize that the different optimization modes of the engine give different results, and shortly after that you'll find yourself in the "exhaustively try every possible combination until we get one that satisfies t…

What is your alternative suggestion?

Re: Prolog Basics Explained with Pokémon

#60
post #5

Earlier quoted context omitted.

All examples shown in the article can be ran with Datalog too (with stratified negation and arithmetic comparison), which has a clearer execution model and looks almost identical to Prolog. Prolog underneath is doing backtracking, while Datalog is finding a least fixed point of derived relations where iterating on data won't produce more relations, and is akind to SQL (but usually stronger because of recursion).

Ahem. "Prolog underneath" is doing SLD-Resolution implemented as a Depth-First Search with backtracking. Saying it's "doing backtracking" is really fudging quite a bit. Datalog, instead, is "underneath" implementing a TP-Operator, a procedure that finds the fix-point of a Datalog program which happens to be the same as its Least Herbrand Model, which is what SLD-Resolution also finds, except that SLD-Resolution allow…

By the same logic, would you say that the fact that there's only one mainstream Rust is testament to the simplicity of implementing a macro-heavy, borrow checked language without breaking safety, performance and expressiveness?
Post reply on HN