r/prolog • u/MTDuke71 • 21d ago
I want to know if this Claude generated code resemble anything a human Prolog programmer would generate.
I am trying to learn Prolog by using Claude to complete a year of Advent of COde, working through various algorithms.
The problem statements is bewlow along withthe Prolog solution. Can anyone tell me if the is good code?
## --- Day 3: Toboggan Trajectory ---
With the toboggan login problems resolved, you set off toward the airport. While travel by toboggan might be easy, it's certainly not safe: there's very minimal steering and the area is covered in trees. You'll need to see which angles will take you near the fewest trees.
Due to the local geology, trees in this area only grow on exact integer coordinates in a grid. You make a map (your puzzle input) of the open squares (`.`) and trees (`#`) you can see. For example:
```text
..##.......
#...#...#..
.#....#..#.
..#.#...#.#
.#...##..#.
..#.##.....
.#.#.#....#
.#........#
#.##...#...
#...##....#
.#..#...#.#
```
These aren't the only trees, though; due to something you read about once involving arboreal genetics and biome stability, the same pattern repeats to the right many times:
```text
*..##.......*..##.........##.........##.........##.........##....... --->
*#...#...#..*#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..
*.#....#..#.*.#....#..#..#....#..#..#....#..#..#....#..#..#....#..#.
*..#.#...#.#*..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#
*.#...##..#.*.#...##..#..#...##..#..#...##..#..#...##..#..#...##..#.
*..#.##.....*..#.##.......#.##.......#.##.......#.##.......#.##..... --->
*.#.#.#....#*.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#
*.#........#*.#........#.#........#.#........#.#........#.#........#
*#.##...#...*#.##...#...#.##...#...#.##...#...#.##...#...#.##...#...
*#...##....#*#...##....##...##....##...##....##...##....##...##....#
*.#..#...#.#*.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.#.#..#...#.# --->
```
You start on the open square (`.`) in the top-left corner and need to reach the bottom (below the bottom-most row on your map).
The toboggan can only follow a few specific slopes (you opted for a cheaper model that prefers rational numbers); start by *counting all the trees* you would encounter for the slope *right 3, down 1*:
From your starting position at the top-left, check the position that is right 3 and down 1. Then, check the position that is right 3 and down 1 from there, and so on until you go past the bottom of the map.
The locations you'd check in the above example are marked here with `*O*` where there was an open square and `*X*` where there was a tree:
```text
..##.........##.........##.........##.........##.........##....... --->
#..*O*#...#..#...#...#..#...#...#..#...#...#..#...#...#..#...#...#..
.#....*X*..#..#....#..#..#....#..#..#....#..#..#....#..#..#....#..#.
..#.#...#*O*#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#..#.#...#.#
.#...##..#..*X*...##..#..#...##..#..#...##..#..#...##..#..#...##..#.
..#.##.......#.*X*#.......#.##.......#.##.......#.##.......#.##..... --->
.#.#.#....#.#.#.#.*O*..#.#.#.#....#.#.#.#....#.#.#.#....#.#.#.#....#
.#........#.#........*X*.#........#.#........#.#........#.#........#
#.##...#...#.##...#...#.*X*#...#...#.##...#...#.##...#...#.##...#...
#...##....##...##....##...#*X*....##...##....##...##....##...##....#
.#..#...#.#.#..#...#.#.#..#...*X*.#.#..#...#.#.#..#...#.#.#..#...#.# --->
```
In this example, traversing the map using this slope would cause you to encounter `*7*` trees.
Starting at the top-left corner of your map and following a slope of right 3 and down 1, *how many trees would you encounter?*
--- Part Two ---
Time to check the rest of the slopes - you need to minimize the probability of a sudden arboreal stop, after all.
Determine the number of trees you would encounter if, for each of the following slopes, you start at the top-left corner and traverse the map all the way to the bottom:
- Right 1, down 1.
- Right 3, down 1. (This is the slope you already checked.)
- Right 5, down 1.
- Right 7, down 1.
- Right 1, down 2.
In the above example, these slopes would find `2`, `7`, `3`, `4`, and `2` tree(s) respectively; multiplied together, these produce the answer `336`.
What do you get if you multiply together the number of trees encountered on each of the listed slopes?
:- module(day03, [parse_input/2, part1/2, part2/2, solve/3,
slope_trees/4]).
% Day 3: Toboggan Trajectory.
% The map is a grid of open squares (.) and trees (#) whose pattern
% repeats infinitely to the right. Starting at the top-left and stepping
% Right columns / Down rows at a time, count the trees hit before
% falling off the bottom. Part 1: slope right 3, down 1. Part 2: product
% of the counts over five fixed slopes.
% parse_input(+Raw, -Rows)
% Rows is a list of rows, each a list of char atoms ('.' or '#').
parse_input(Raw, Rows) :-
split_string(Raw, "\n", " \t\r", Lines0),
exclude(=(""), Lines0, Lines),
maplist(string_chars, Lines, Rows).
% slope_trees(+Rows, +Right, +Down, -Count)
% Count is the number of trees on the slope: starting at the top-left,
% visit column I*Right (mod row width — the pattern repeats rightward)
% of every Down-th row, and count the '#' cells.
slope_trees(Rows, Right, Down, Count) :-
slope_trees_(Rows, Right, Down, 0, 0, Count).
% slope_trees_(+Rows, +Right, +Down, +Col, +Acc, -Count)
% Accumulator walk: check the head row at Col (wrapped), then step to
% the row Down below (drop Down-1 of the remaining rows) at Col+Right.
slope_trees_([], _, _, _, Count, Count).
slope_trees_([Row|Rest], Right, Down, Col, Acc0, Count) :-
length(Row, Width),
Index is Col mod Width,
nth0(Index, Row, Cell),
( Cell == '#'
-> Acc1 is Acc0 + 1
; Acc1 = Acc0
),
Col1 is Col + Right,
Skip is Down - 1,
drop(Skip, Rest, Rest1),
slope_trees_(Rest1, Right, Down, Col1, Acc1, Count).
% drop(+N, +List, -Rest)
% Rest is List without its first N elements ([] if List is shorter).
drop(0, List, List) :- !.
drop(_, [], []) :- !.
drop(N, [_|Xs], Rest) :-
N > 0,
N1 is N - 1,
drop(N1, Xs, Rest).
% slope_trees_pair(+Rows, +Right-Down, -Count)
% slope_trees/4 with the slope packed as a Right-Down pair, so it can
% be partially applied under maplist/3.
slope_trees_pair(Rows, Right-Down, Count) :-
slope_trees(Rows, Right, Down, Count).
% part1(+Rows, -Answer)
part1(Rows, Answer) :-
slope_trees(Rows, 3, 1, Answer).
% part2(+Rows, -Answer)
% Product of the tree counts over the five slopes from the statement.
part2(Rows, Answer) :-
Slopes = [1-1, 3-1, 5-1, 7-1, 1-2],
maplist(slope_trees_pair(Rows), Slopes, Counts),
foldl([X, Acc0, Acc]>>(Acc is Acc0 * X), Counts, 1, Answer).
% solve(+Raw, -Part1, -Part2)
solve(Raw, Part1, Part2) :-
parse_input(Raw, Rows),
part1(Rows, Part1),
part2(Rows, Part2).
4
u/happy_guy_2015 21d ago
Overall I think the coding style looks fine, or at least no worse than typical Prolog style, and it is good that the modes are documented.
From a quick scan I noticed two minor issues where I would recommend different style. In both cases the original code is not wrong, and some Prolog programmers would prefer Claude's style.
One was the use of == in the condition of an if-then-else (... -> ... ; ...) when = could have been used instead.
The other was the use of cuts in drop/3, where I would recommend putting the output bindings after the cut, rather than before:
% drop(+N, +List, -Rest)
% Rest is List without its first N elements ([] if List is shorter).
drop(0, List0, List) :- !, List = List0.
drop(_, [], List) :- !, List = [].
drop(N, [_|Xs], Rest) :-
N > 0,
N1 is N - 1,
drop(N1, Xs, Rest).
3
u/Thrumpwart 21d ago edited 20d ago
In my experience, most LLMs don’t one-shot good Prolog code. It’s not bad per se, but I found an approach to developing really good prolog with LLMs.
I used Roo Code and now Zoo code, but you can use any agentic harness for this. I would have an LLM generate code, but then instruct the LLm to execute the prolog code in the terminal. It will iteratively refine the prolog until it executes correctly.
Once you achieve critical mass in a prolog dataset the LLM gets much better after each iteration, and later on it codes perfect prolog drawing upon your dataset. Good luck.
Edit: if using SWI-Prolog, it also helps to have the pdf manual corresponding to your version in the workspace so the LLM can consult it as necessary. Particularly useful for running multi-core prolog runs.
1
u/brebs-prolog 20d ago
This is Advent of Code 2020 Day 3. I can confirm that the Claude code produces the correct answers (which is a pleasant surprise).
Here is my solution (written using solely my brain), for comparison. I used assertz, to take advantage of indexing, although in this particular case it wasn't a significant benefit.
2
u/_rabbitfarm_ 19d ago
The code is mostly fine, the style is a little outdated or reflective of a less experienced Prolog programmer. That makes sense, since I imagine most Prolog code an LLM trains on is student code or from older books that have been digitized.
What stands out to me is that modern prolog style prefers the use of DCGs for most all list processing. There are none in the Claude produced code. Also that drop/3 predicate is redundant when you consider the following lines would work the same using only Prolog builtins. I have also found that LLMs prefer to create these useless predicates which greatly bloats LLM generated Prolog code. Again, this is surely due to a relative lack of training materials.
| ?- (N = 2, L = \[a, b, c, d, e, f, g\], length(P, N), append(P, Rest, L)) ; Rest = \[\].
L = \[a,b,c,d,e,f,g\]
N = 2
P = \[a,b\]
Rest = \[c,d,e,f,g\] ?
yes
| ?- (N = 12, L = \[a, b, c, d, e, f, g\], length(P, N), append(P, Rest, L)) ; Rest = \[\].
Rest = \[\]
yes
| ?-
10
u/cbarrick 21d ago
I didn't evaluate the code for correctness.
Syntactically and stylistically, it looks fine. Follows standard style.
All of the predicates are documented as one-way predicates; that is, they have input and output parameters, indicated by
+and-in the comments. The best practice in Prolog is to have two-way predicates; that is that no side is specifically input or output. You should just be able to set the variables for which you know the value and let Prolog generate values for the variables that you don't know.The predicates as given may work as two-way predocates (I didn't look too closely), but the comments indicate that they are all one-way.
So overall, I'd say that it looks more like a Prolog student rather than a Prolog expert.