Prolog is the unique logic programming language. The identify comes from professionalgramming in logic. Extra particularly, the identify comes from professionalgrammation en logique as a result of the inventor of the language, Philippe Roussel, is French.
Prolog has its benefits and drawbacks. One of many benefits is that the language represents logical issues straight. One of many disadvantages is that the syntax may be quirky. But when an LLM is writing the code, or at the very least serving to to write down the code, the syntax doesn’t matter a lot.
I wished to see how properly Claude (Sonnet 4.6, medium effort) may resolve a chess puzzle by Martin Gardner that I wrote about a little bit over a 12 months in the past. I selected a comparatively obscure downside slightly than one thing just like the Eight Queens puzzle as a result of an LLM may merely quote certainly one of numerous articles on the puzzle.
The puzzle
As I acknowledged within the publish final 12 months, the duty is to position two rooks, two bishops, and two knights on a 4 by 4 chessboard in order that no piece assaults every other.
There are two fundamental options, twelve in case you depend rotations and reflections as completely different options.
Prolog outcomes
Claude wrote an SWI-Prolog program that I ran with
swipl -g "run, halt" chess_placement.pl
and it gave the next output.
Complete options (uncooked): 12 Options as much as D4 symmetry: 2 Options as much as D4 symmetry: 2 Answer 1: B . . N N . . B . R . . . . R . Answer 2: . B N . . . . R R . . . . N B .
Claude made a pleasant picture of the options.
Notice you could make eight variations of the primary answer by a mixture of rotations and flips. There are solely 4 variations of the second answer as a result of two rotations put you again the place you began.
Prolog supply
Right here’s the Prolog code that Claude generated.
% ============================================================
% Non-attacking placement of two Rooks, 2 Bishops, 2 Knights
% on a 4x4 chessboard
%
% Run with SWI-Prolog:
% swipl -g "run, halt" chess_placement.pl
%
% Or interactively:
% swipl chess_placement.pl
% ?- run.
% ?- count_solutions(N).
% ?- count_unique(N).
% ============================================================
:- use_module(library(lists)).
% ---- Board squares: (Row, Col) with Row, Col in 0..3 ----------------
sq.(R, C) :- member(R, [0,1,2,3]), member(C, [0,1,2,3]).
% ---- Assault predicates ----------------------------------------------
rook_attacks(R,_,R,_). % similar row
rook_attacks(_,C,_,C). % similar column
bishop_attacks(R1,C1,R2,C2) :-
DR is abs(R1-R2), DC is abs(C1-C2),
DR =:= DC, DR > 0.
knight_attacks(R1,C1,R2,C2) :-
DR is abs(R1-R2), DC is abs(C1-C2),
( DR =:= 1, DC =:= 2 ; DR =:= 2, DC =:= 1 ).
% assaults(+Type1, +R1, +C1, +Type2, +R2, +C2)
% True if both piece threatens the opposite's sq..
assaults(rook, R1,C1, _, R2,C2) :- rook_attacks(R1,C1,R2,C2).
assaults(_, R1,C1, rook, R2,C2) :- rook_attacks(R1,C1,R2,C2).
assaults(bishop, R1,C1, _, R2,C2) :- bishop_attacks(R1,C1,R2,C2).
assaults(_, R1,C1, bishop,R2,C2) :- bishop_attacks(R1,C1,R2,C2).
assaults(knight, R1,C1, _, R2,C2) :- knight_attacks(R1,C1,R2,C2).
assaults(_, R1,C1, knight,R2,C2) :- knight_attacks(R1,C1,R2,C2).
% ---- All pairs protected -------------------------------------------------
% Verify all unordered pairs in a listing.
no_attack_pair(_, []).
no_attack_pair(T1-(R1,C1), [T2-(R2,C2)|Rest]) :-
+ assaults(T1,R1,C1,T2,R2,C2),
no_attack_pair(T1-(R1,C1), Relaxation).
all_pairs_safe([]).
all_pairs_safe([P|Rest]) :-
no_attack_pair(P, Relaxation),
all_pairs_safe(Relaxation).
% ---- Generate a placement -------------------------------------------
%
% Placement = [rook-(R1,C1), rook-(R2,C2),
% bishop-(R3,C3), bishop-(R4,C4),
% knight-(R5,C5), knight-(R6,C6)]
%
% We implement an ordering inside every pair to keep away from counting
% same-type duplicates (rook1 vs rook2 swapped).
% Squares are represented canonically: (R,C) with R*4+C as index.
sq_index(R, C, I) :- I is R*4 + C.
placement(Placement) :-
% --- Rooks ---
sq.(R1r, C1r), sq.(R2r, C2r),
sq_index(R1r,C1r,I1r), sq_index(R2r,C2r,I2r),
I1r < I2r, % canonical order for the 2 rooks
% --- Bishops ---
sq.(R1b, C1b), sq.(R2b, C2b),
sq_index(R1b,C1b,I1b), sq_index(R2b,C2b,I2b),
I1b < I2b, % canonical order for the 2 bishops
% --- Knights ---
sq.(R1n, C1n), sq.(R2n, C2n),
sq_index(R1n,C1n,I1n), sq_index(R2n,C2n,I2n),
I1n < I2n, % canonical order for the 2 knights
% --- All six squares distinct ---
Squares = [(R1r,C1r),(R2r,C2r),(R1b,C1b),(R2b,C2b),(R1n,C1n),(R2n,C2n)],
all_distinct_squares(Squares),
% --- Construct placement record ---
Placement = [ rook-(R1r,C1r), rook-(R2r,C2r),
bishop-(R1b,C1b), bishop-(R2b,C2b),
knight-(R1n,C1n), knight-(R2n,C2n) ],
% --- No piece assaults every other ---
all_pairs_safe(Placement).
all_distinct_squares([]).
all_distinct_squares([S|Rest]) :-
+ member(S, Relaxation),
all_distinct_squares(Relaxation).
% ---- Rely all options --------------------------------------------
count_solutions(N) :-
findall(P, placement(P), Ps),
size(Ps, N),
format("Complete options (uncooked): ~w~n", [N]).
% ---- D4 symmetry transformations on a 4x4 board ---------------------
% Squares (R,C) in 0..3
rework(id, R, C, R, C).
rework(rot90, R, C, C, Nr) :- Nr is 3-R.
rework(rot180, R, C, Nr, Nc) :- Nr is 3-R, Nc is 3-C.
rework(rot270, R, C, Nc, R) :- Nc is 3-C.
rework(flipH, R, C, R, Nc) :- Nc is 3-C.
rework(flipV, R, C, Nr, C) :- Nr is 3-R.
rework(flipD1, R, C, C, R).
rework(flipD2, R, C, Nr, Nc) :- Nr is 3-C, Nc is 3-R.
apply_transform(_, [], []).
apply_transform(T, [Type-(R,C)|Rest], [Type-(NR,NC)|TRest]) :-
rework(T, R, C, NR, NC),
apply_transform(T, Relaxation, TRest).
% Canonical kind: type items inside same-type pairs, then type the
% complete record to get a novel consultant.
canonical_placement(Placement, Canonical) :-
findall(T, member(T,[identity,rot90,rot180,rot270,
flipH,flipV,flipD1,flipD2]), Ts),
maplist(transform_and_sort(Placement), Ts, AllForms),
msort(AllForms, Sorted),
Sorted = [Canonical|_].
transform_and_sort(Placement, T, Sorted) :-
apply_transform(T, Placement, TPl),
msort(TPl, Sorted).
% ---- Rely options as much as D4 symmetry ------------------------------
count_unique(N) :-
findall(P, placement(P), Ps),
maplist(canonical_placement, Ps, Canonicals),
list_to_set(Canonicals, Distinctive),
size(Distinctive, N),
format("Options as much as D4 symmetry: ~w~n", [N]).
% ---- Fairly-print a board -------------------------------------------
print_board(Placement) :-
forall(member(R, [0,1,2,3]),
print_row(R, Placement)),
nl.
print_row(R, Placement) :-
forall(member(C, [0,1,2,3]),
print_cell(R, C, Placement)),
nl.
print_cell(R, C, Placement) :-
( member(rook-(R,C), Placement) -> write('R ')
; member(bishop-(R,C), Placement) -> write('B ')
; member(knight-(R,C), Placement) -> write('N ')
; write('. ')
).
% ---- Print all distinctive options -------------------------------------
print_unique_solutions :-
findall(P, placement(P), Ps),
maplist(canonical_placement, Ps, Canonicals),
list_to_set(Canonicals, Distinctive),
size(Distinctive, N),
format("~nSolutions as much as D4 symmetry: ~w~n~n", [N]),
forall(nth1(I, Distinctive, Sol),
( format("Answer ~w:~n", [I]),
print_board(Sol) )).
% ---- High-level entry level ------------------------------------------
run :-
count_solutions(Uncooked),
count_unique(Sym),
format("~n"),
print_unique_solutions,
format("Abstract: ~w uncooked options, ~w as much as D4 symmetry.~n",
[Raw, Sym]).
