Arxcis / adventofcode2020

Community-project solving https://adventofcode.com/ - problems, with Github CI, Docker and support for many languages.
6 stars 8 forks source link

Support prolog #74

Open Stektpotet opened 3 years ago

Stektpotet commented 3 years ago

Prolog could be an interesting language to add to the stack of supported languages. During the day07 puzzle I again remembered the hell it was to was to write, but also the power of it once you finally managed to wrap your head around how the facts, predicates and queries interact with each other.

I started looking into it over on my own fork I still haven't managed to get it up and running due to not having touched apt-get in ages and ages, but I don't think I'm far of with it.


Likely one of the biggest difficulties of using prolog is going to be processing the various input for the puzzles, but from the little research I did before adding this issue, it's possible to read stdin and write to stdout fairly easily through the builtin "commands"

Stektpotet commented 3 years ago

In terms of input processing, I also think that writing multi stage (multi program) solutions shouldn't be a problem (other than for the stats in the readme) where one could technically have an e.g. python program process the input, constructing a prolog knowledge base (i.e. the facts), and then have the logic be processed by prolog

Arxcis commented 3 years ago

If you are positive, I am positive :+1: I would accept a PR, as long as it provides minimum 1 solution of 1 of the days. Yes, we have 3 languages with 0 solutions today, but I want that number to decrease rather than increase :wink:

Arxcis commented 3 years ago

This looks like the way to do it with apt -> https://wwu-pi.github.io/tutorials/lectures/lsp/010_install_swi_prolog.html

I can help with updating the Dockerfile and docker-image, when the time comes.

Here is what I am envisioning:

  1. Add a new RUN-directive right before the last RUN-directive in the Dockerfile, adding the ppa:swi-prolog/stable
  2. Add apt install swi-prolog to the list of apt-installs in the last RUN-directive

# 7. Tell apt to install swi-prolog from PPA
RUN add-apt-repository ppa:swi-prolog/stable

# 8. Install all other compilers, from apt-get
RUN apt-get update && apt-get install -yqq --no-install-recommends\
    default-jdk golang nodejs php-cli python3 ruby rustc swi-prolog\
  # Cleanup what we don't need
  && rm -rf /var/lib/apt/lists/* \
  && apt-get autoremove -yqq git wget
Arxcis commented 3 years ago

UPDATE I just checked with apt search and it seems like we don't need the extra PPA-step:

$ apt search swi-prolog
Sorting... Done
Full Text Search... Done
elpa-ediprolog/groovy,groovy 2.1-1 all
  Emacs Does Interactive Prolog

swi-prolog/groovy 8.2.1+dfsg-2ubuntu1 amd64
  ISO/Edinburgh-style Prolog interpreter
Stektpotet commented 3 years ago

So I technically have a solution for day07 in prolog, but the input parsing is practically impossible (not really, but really really really really really really really overly complicated to do in prolog, especcially if you want to create a knowledge base out of the input).

As a result, the easiest way of solving the problem would be to build the knowledge base by generating a file from another script (i.e. reading and parsing the input, and writing a prolog file using another programming language, e.g. python).

Here's the general solution solving the example problems stated for day07

% Knowledge base 
bags(red, 1, white).    % red bags (holds) 1 white
bags(red, 2, yellow).   
bags(orange, 3, white).
bags(orange, 4, yellow).
bags(white, 1, gold).
bags(yellow, 2, gold).
bags(yellow, 9, blue).
bags(gold, 1, olive).
bags(gold, 2, plum).
bags(olive, 3, blue).
bags(olive, 4, black).
bags(plum, 5, blue).
bags(plum, 6, black).

% Recursive relation of occurances
% N occurances of X is in E 
% alternatively: E holds N of X
in(X, E, N):- 
    bags(X,N,E).
in(X, E, N):- 
    bags(X, W, Y), 
    in(Y, E, K),
    N is W * K.

% X is in E
in(X,E):- 
    bags(X,_,E).
in(X,E):- 
    bags(X,_,Y), 
    in(Y,E).

% Sum elements of a list
sum([], 0).
sum([H|T], Sum) :-
   sum(T, Rest),
   Sum is H + Rest.

% Find the X bag atoms that can hold element E
find_holding(E, X):- 
    setof(A, in(A, E), X).

% Find the number N of bags that can hold element E
find_num_holding(E, N):- 
    setof(A, in(A, E), X),
    length(X, N).

% Find the X bag atoms inside of element E
find_in(E, X):- 
    setof(A, in(E, A), X).

% Find the number N of bags inside of element E
find_num_in(E, N):- 
    findall(A, in(E, _, A), X),
    sum(X, N).
?-  find_num(gold, X0),
    find_num_in(gold, X1),
    write_ln(X0),
    write_ln(X1)
Stektpotet commented 3 years ago

The most problematic part of doing it in such a way, admittedly would be resolving the stats for solves per language in the README?

I guess it could show up as a separate category:

language: mixed

Arxcis commented 3 years ago

The most problematic part of doing it in such a way, admittedly would be resolving the stats for solves per language in the README?

I guess it could show up as a separate category:

language: mixed

I don't see any problem. What happens in ./languages/prolog.sh stays in ./languages/prolog.sh. What I mean is that this could be a prolog-specific build-step. Many programs require external tools to build, which are written in a completely different language. If this is what it takes to run a prolog program, then so be it.

We will have to update the README.md to mention the prolog-edge-case, and that you should not expect cat input.txt | solutions/my.pl | diff - output.txt to work.

Instead you should run:

./languages/prolog.sh days/day-03/input.txt days/day03/output.txt days/day03/my.pl

And after #72 has been merged, this is changed to:

./lang/prolog.sh days/day-03/solutions/my.pl "days/day-03/io/my.input days/day-03/io/my.output"

# ....or use the short-hand :rocket: 
./lang/prolog.sh "days/day-03/**/*.pl" "days/day-03/io/*"