\

Prolog Basics Explained with Pokémon

253 points - last Friday at 11:02 AM

Source
  • deosjr

    today at 7:46 AM

    A while ago I was playing the romhack Run&Bun, which drastically ups the difficulty of Pokemon Emerald. Watching others play, most of the game was doing 'calcs': calculating how to approach a battle. This was done using a javascript frontend to a simulator, and a lot of excel sheets.

    So I forked the calculator and added a Prolog wrapper so I could find solutions to battles based on the team of Pokemon that I caught. https://github.com/deosjr/runbuncalc/blob/master/main.pl

    My runs died pretty early, but there are some notes here as I was implementing that are fun to read. I implemented each battle as a test case and let my solver find a solution, then amended the plan and commented on what actually happened. For example: https://github.com/deosjr/runbuncalc/blob/master/run10.pl#L2...

    • jodrellblank

      yesterday at 3:10 PM

      > "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 Prolog terms(/data/code). That's why the result uses the same `;` for OR as code does. Answer (x ; y ; false) is "query can be answered by x or y or no other answer found". (This would let you do meta-programming, reasoning about the results and rewriting the results in a LISPy data-as-code way, if you were more advanced than I am).

      Prolog systems do optimisations to jump to the correct answer without searching, if they can, (e.g. database style indexing on the facts and rules) and in those cases there is no code left to search after showing the first answer, no need to prompt the user "should I search for more answers in the remaining code?", and so no need for an output "false" to say "I finished searching and found no more solutions".

      [1] https://www.metalevel.at/prolog

        • Bratmon

          yesterday at 6:04 PM

          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 the predicates" mode, and your code will go from taking 1 second to run to taking 8 days.

          And because you don't control the engine (you're not supposed to think about it, after all), there's nothing you can do but rewrite the whole thing in a traditional programming language.

            • lll-o-lll

              yesterday at 9:10 PM

              > as soon as you do something actually complicated, you realize that the different optimization modes of the engine give different results

              The same is true of SQL query planners. You can perform basic queries without understanding how your SQL engine of choice works under the hood, but if you want performance, you must understand how your DB works. SQL is just the interface.

              This is different in kind from imperative programming languages (which are much closer in abstraction to the underlying machine architecture), but we rub along with SQL ok; why not Prolog?

                • Bratmon

                  today at 5:28 AM

                  Yeah, but the difference is that SQL provides a huge number of ways to solve the "My query got slow when it got complicated" problem. In Prolog, you have the cut operator, and when that stops working for your usecase, you're just SOL.

              • repelsteeltje

                yesterday at 7:20 PM

                I somewhat disagree that you shouldn't be aware of how the engine works. The mechanics are quite simple. Prolog's horn clauses are combined in depth first search manner trying to proof that the negated goal is false.

                However, most prolog books focus on rooting the declarative mindset because programmers are generally more familiar with imperative programming. But just as with SQL or lisp there are definitely good ways, bad ways and plain mistakes you can make when approaching a problem.

                • triska

                  yesterday at 6:17 PM

                  How is this different from other programming languages though?

                  One example I often think about is from Ken Silverman: "sub eax, 128" → "add eax, -128". So equivalent ways to write the same program may have different performance characteristics also depending on the tools that are applied. How many people could tell without trying which way to write this example is preferable?

                  The same phenomenon will be encountered in all kinds of languages, where engine and compiler improvements make existing code faster or slower.

                    • Bratmon

                      yesterday at 6:23 PM

                      In other languages, you can find the lines where the performance problems are and fix them without breaking the abstraction everywhere else.

                        • triska

                          yesterday at 6:36 PM

                          I think this is very well phrased, and I would argue the same holds for Prolog too.

                          In my opinion, a key difference between Prolog and other languages in that regard is one of degree, not kind: Compared to other languages, addressing performance problems in Prolog engines tends to have far greater effects on Prolog programs, because so much is implicit (i.e., left to the engine).

                          If the performance problem is not in the engine, but in the program itself, then we will face the same questions with Prolog as with other languages: How to formulate the program better, is there a better approach altogether?

                          For example, earlier today an interesting question regarding performance was posted in the Scryer discussions:

                          https://github.com/mthom/scryer-prolog/discussions/3341

                          The comparison in this case is between Gecode and Scryer on a seemingly simple but nontrivial combinatorial task. What is the problem here? Most likely the Scryer engine itself can be improved. And also very likely, there are better ways to model the task, and also better search strategies, and these tend to have far greater performance impact than the base language, and these questions remain also if we change the base language.

                          In my opinion, these questions regarding different kinds of formulations tend to be more frequently associated with Prolog than with other languages because Prolog is more frequently used for complex tasks where it is not a priori clear how to even approach the problem.

                  • AdieuToLogic

                    today at 2:19 AM

                    > Prolog's philosophy is that you should just write the predicates without thinking about how the engine works.

                    This is the definition of declarative programming[0].

                    0 - https://en.wikipedia.org/wiki/Declarative_programming

            • triska

              yesterday at 1:40 PM

              Very nice!

              In the Scryer Prolog discussions, Alex has shared a few ideas and considerations for possible improvements to the Prolog code, including the use of metaprogramming to automatically generate more general relations:

              https://github.com/mthom/scryer-prolog/discussions/3221

              I hope for an interesting followup article!

              Another very interesting Prolog program by Alex is factgraph.pl:

              https://github.com/alexpetros/factgraph.pl

              It's a Prolog implementation of the IRS Fact Graph, an application of Law as Code.

              • macintux

                yesterday at 12:03 PM

                It continues to be immensely surprising to me that Joe Armstrong was able to write the initial Erlang implementation in Prolog. I wish I’d asked him about getting a copy of the source code.

                  • Joker_vD

                    yesterday at 5:20 PM

                        What does this say about Forth? Not much except that it isn't for me.
                        Take Prolog. I know few things more insulting than having to code in
                        Prolog. Whereas Armstrong developed Erlang in Prolog and liked it much
                        better than reimplementing Erlang in C for speed. I can't imagine how
                        this could be, but this is how it was. People are different.
                    
                    from Yossi Kreinin's "My history with Forth & stack machines" [0]. Some people write APL and enjoy it. Some can't bear Lisp even after 10 years of working with it.

                    [0] https://yosefk.com/blog/my-history-with-forth-stack-machines...

                    • triska

                      yesterday at 5:52 PM

                      Regarding distributed systems, I find Torbjörn Lager's recent work on Web Prolog particularly interesting. He recently posted about it here:

                      https://github.com/mthom/scryer-prolog/discussions/3322

                      and also in the course of a discussion on various approaches to implement concurrency in Prolog:

                      https://github.com/mthom/scryer-prolog/discussions/3307

                  • lagrange77

                    yesterday at 1:20 PM

                    When i was in uni, the course teaching Prolog and Lisp was called "Artificial Intelligence for Engineers".

                      • sevenseacat

                        yesterday at 1:23 PM

                        Same!

                        Man, where was a post like this when I was struggling trying to learn Prolog, modelling something with knights and knaves...

                    • Modified3019

                      yesterday at 11:02 AM

                      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.

                        • tannhaeuser

                          yesterday at 2:47 PM

                          Prolog is actually a perfect fit for all kinds of adventure, role playing, strategy, and classic board/card games, with clauses representing game rules and facts representing the game state and universe in the most natural way.

                          Simple general-purpose opponents can be coded using just recursive backtracking search, while more advanced ones (supporting moves that need to destructively change state) can still be conveniently modelled by reifying facts and thereby enable backtracking over assert/retract-like Prolog DB modifications, as used in discrete combinatorial planners [1].

                          [1]: https://quantumprolog.sgml.net/container-planning-demo/part1...

                          • gobdovan

                            yesterday at 12:08 PM

                            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).

                              • ModernMech

                                yesterday at 12:53 PM

                                Importantly, Datalog is not Turing-complete though.

                                  • AlotOfReading

                                    yesterday at 2:39 PM

                                    You can get Turing completeness by wrapping your datalog query in a while loop, so that's not particularly restrictive.

                                      • Dylan16807

                                        today at 1:56 AM

                                        You can get Turing completeness by wrapping basically any math or logic system in a while loop, even arithmetic. So that doesn't tell us much about the restrictiveness of the overall system since I'd call "you can only use arithmetic" pretty damn restrictive.

                                        • ModernMech

                                          yesterday at 3:06 PM

                                          In the case of Datalog, it not being Turing-complete is usually seen as a feature rather than restrictive.

                                      • gobdovan

                                        yesterday at 1:31 PM

                                        Exactly :) It is terminating due to the LFP semantics I was pointing out, it's more akin to SQL than to Prolog. The article doesn't even show the usage of the Prolog cut (`!`).

                                        And yet Prolog can express all examples in the article. For these kinds of problems, giving up TC is mostly a feature. And if you need more expressiveness, there's a lot of practical Datalog-ish systems that can recover Turing completeness (Flix, Formulog, parts of Souffle), while still being saner than SWI Prolog and co. for this type of work, as you generally don't have to care about atom order or search order in the same way. They act so much more predictably.

                                • satvikpendem

                                  yesterday at 4:58 PM

                                  Nonplussed like initially surprised? It does not mean bored or nonchalant which many people seem to think, probably due to the non- prefix.

                            • cluckindan

                              today at 5:45 AM

                              ”I am about to describe the mechanics of a children’s video game in great detail”

                              Prolog must be an adult programming language :)

                              • Almondsetat

                                yesterday at 11:43 AM

                                Are there public tournaments of games like Pokemon where contestants have to compete with eachother using a specific class of algorithms (e.g., logic programming, neural nets, linear programming, etc.)?

                                • stellartux

                                  yesterday at 4:25 PM

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

                                    • alexpetros

                                      yesterday at 11:14 PM

                                      Thank you!

                                  • Joker_vD

                                    yesterday at 5:16 PM

                                    > Then query it like so:

                                        SELECT DISTINCT pokemon, special_attack
                                        FROM pokemon as p
                                        WHERE
                                          p.special_attack > 120
                                          AND EXISTS (
                                            SELECT 1
                                            FROM pokemon_moves as pm
                                            WHERE p.pokemon_name = pm.pokemon_name AND move = 'freezedry'
                                          )
                                          AND EXISTS (
                                            SELECT 1
                                            FROM pokemon_types as pt
                                            WHERE p.pokemon_name = pt.pokemon_name AND type = 'ice'
                                          );
                                    
                                    Hmm. I wonder if this

                                        SELECT DISTINCT pokemon, special_attack
                                        FROM pokemon as p
                                          NATURAL JOIN pokemon_moves as pm
                                          NATURAL JOIN pokemon_types as pt
                                        WHERE
                                          p.special_attack > 120 AND
                                          pm.move = 'freezedry' AND
                                          pt.type = 'ice'
                                        ;
                                    
                                    would work instead.

                                      • sgarland

                                        yesterday at 5:42 PM

                                        It would, but it forces the requirement of DISTINCT. With the original, if there were declared PKs (pokemon_name is fine for the main table, with a composite for others), the semi-join (EXISTS) would eliminate the need for DISTINCT entirely.

                                        I think. Doing this in my head, but you could verify it trivially with SQLite or any other RDBMS.

                                        • yesterday at 5:44 PM

                                      • fl1pper

                                        yesterday at 5:28 PM

                                        I'm not familiar with Pokemon universe :( Can somebody please explain Pokemon using Prolog?

                                          • zzo38computer

                                            today at 6:13 AM

                                            There is a lot more than only what they explain in that article (I don't know of any full implementation of Pokemon in Prolog, although there is an implementation in TypeScript).

                                            For example, you can also switch out (to activate a different pokemon) instead of attacking (switching has priority over attacking, so if you switch out then the opponent's attack will hit your newly active pokemon).

                                            There are also many calculations involved, and it can be helpful to know how most of them work.

                                            • wk_end

                                              today at 5:23 AM

                                              One of the neat things about Prolog is that queries can be run both forwards and backwards, so I believe this article should already have you covered ;)

                                          • Fokamul

                                            today at 7:49 AM

                                            Where "Getting C&D from Nintendo for a blog post" fits in?

                                            • admeliora01

                                              yesterday at 3:01 PM

                                              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.

                                                • deosjr

                                                  today at 7:49 AM

                                                  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 set of design space can fit, I think.

                                                  See https://github.com/deosjr/pmtg/blob/master/parse.pl

                                                  • alexpetros

                                                    yesterday at 11:12 PM

                                                    I'm not as familiar with Magic, but I've always been curious if that community has tooling at a comparable level of maturity to Pokemon Showdown.

                                                • zombot

                                                  yesterday at 12:38 PM

                                                  Are there pokémon with backtracking and unification traits? Those could do real Prolog!

                                                  • SilentM68

                                                    yesterday at 1:32 PM

                                                    That's very helpful & easy to follow.

                                                    Do you have an Odin tutorial that's as easy to digest?

                                                    Sol