Java-based Prolog Engine with GUI Editor
I used to buy and collect books about computer technologies since my childhood and in 1990 I bought a book titled as “Prolog programming for Artificial Intelligence” by Ivan Bratko but didn’t have enough time to read it for about 20 years. In 2009 I found some free time to read the book and was amazed by the power of the computer language. To learn it better I wrote small Java based Prolog embeddable engine and called it JProl.
In 2014 Yuki Katsura made iPad version.
Initially the engine was a mono-block with embedded GUI part, but later it reworked and split solid-module into two modules: the engine and the GUI editor.
The engine is published in the maven central and can be used in maven projects as a dependency. It supports JDK 11+ and also I keep it compatible with Android API.
<dependency>
<groupId>com.igormaznitsa</groupId>
<artifactId>jprol-core</artifactId>
<version>2.2.1</version>
</dependency>
It uses another project java-prolog-parser which initially was a part of the JPprol engine but then it was extracted into autonomous module because sometime it is useful to have only prolog parser for data files.
The engine communicates with Java methods marked by special annotationas and having special signature.
For instance, below I show how implemented the <</2
predicate of the core JProl library. As you can see, the method is just marked by @JProlPredicate
annotation. It is imp[ossible just mark any method but only methods with special signature and arguments JProlChoicePoint
and TermStruct
because they provided during a call.
@JProlPredicate(evaluable = true, signature = "<</2", args = {
"+evaluable,+evaluable"}, reference = "Bitwise left shift")
public static Term predicateSHIFTL2(final JProlChoicePoint goal, final TermStruct predicate) {
final NumericTerm left = calculatEvaluable(goal, predicate.getElement(0).findNonVarOrSame());
final NumericTerm right = calculatEvaluable(goal, predicate.getElement(1).findNonVarOrSame());
final long value = left.toNumber().longValue();
final long shift = right.toNumber().longValue();
return Terms.newLong(value << shift);
}
Sample below shows how it is easy to inject a prolog based Eight Queens puzzle resolver into Java and get all solutions.
JProlContext context = new JProlContext(
"test-context",
new JProlCoreLibrary()
);
context.consult(new StringReader(
"solution([]). solution([X/Y|Others]):-solution(Others),member(Y,[1,2,3,4,5,6,7,8]),notattack(X/Y,Others). notattack(_,[]). notattack(X/Y,[X1/Y1 | Others]):- Y=\\=Y1, Y1-Y=\\=X1-X, Y1-Y=\\=X-X1, notattack(X/Y,Others). member(Item,[Item|Rest]). member(Item,[First|Rest]):-member(Item,Rest). template([1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8])."));
JProlChoicePoint goal = new JProlChoicePoint(
"solution([1/Y1,2/Y2,3/Y3,4/Y4,5/Y5,6/Y6,7/Y7,8/Y8]),Res = [Y1,Y2,Y3,Y4,Y5,Y6,Y7,Y8].",
context);
Term result;
while((result = goal.prove()) != null) {
TermList solution = (TermList) goal.findVar("Res").get().getValue();
System.out.println(solution.toSrcString());
}
The bootstrap library of the engine has two predicates set_prolog_flag/2
and current_prolog_flag/2
. They can be used to tune work of the engine or get extra info. Not all of them still supported.
jprol
error
,fail
or warning
by default error
Prolog is a declarative language and it is well for multi-threading. I have added pair predicates to span new threads (fork/1
and async/1
), also the engine provides special predicate waitasync/0
to make inter-process synchronization to wait completion of all spawned threads. The waitasync/0
predicate can be used only from the main thread (the root goal). Also there is support for locks. You can create critical sections with lock/1
, unlock/1
and trylock/1
predicates. Take a look at the prolog program below, it draws 3000000 color dots through three threads.
threadRed(P) :- for(_, 0, P), rnd(500, X), rnd(400, Y), lock(gfx), pencolor(red), dot(X, Y), unlock(gfx), fail.
threadGreen(P) :- for(_, 0, P), rnd(500,X), rnd(400, Y), lock(gfx), pencolor(green), dot(X, Y), unlock(gfx), fail.
threadBlue(P) :- for(_, 0, P), rnd(500, X), rnd(400, Y), lock(gfx), pencolor(blue), dot(X,Y), unlock(gfx), fail.
?- P = 1000000, time((graphics(500,400), fork([threadRed(P), threadGreen(P), threadBlue(P)]))).
For demo I have written a small GUI application. It is a version of Conway’s Life game where all business rules for colony described in prolog part.
For my prolog learning purposes I maded small GUI script editor which allows to execute prolog scripts and even make some graphics. It is not very strong in debugging but can be used for start of small scripts and test programs.
Its pre-built version for misc OS can be downloaded from the release page. It contains as standcalone versions as versions with embedded JDK.
JProl GUI editor module provides predicates to work with graphics and save generated images. As an example, below you can find a program to draw a Koch snowflake fractal.
kochsnowflake(N,L) :- settitle('Koch snowflake'), brushcolor(white), graphics(300,300), pencolor(red), time((fractal(N,30,80,L,0,X1,Y1), fractal(N,X1,Y1,L,2.0943951024,X2,Y2), fractal(N,X2,Y2,L,4.1887902048,_,_))).
fractal(0,X,Y,L,A,XN,YN):- !, XN is X+L*cos(A), YN is Y+L*sin(A), XX is round(X),XXN is round(XN), YY is round(Y),YYN is round(YN), plot(XX,YY,XXN,YYN).
fractal(I,X,Y,L,A,XN,YN):- II is I-1, LL is L/3, A2 is A-1.0471975512, A3 is A+1.0471975512, fractal(II,X,Y,LL,A,X1,Y1), fractal(II,X1,Y1,LL,A2,X2,Y2), fractal(II,X2,Y2,LL,A3,X3,Y3), fractal(II,X3,Y3,LL,A,XN,YN).
?-kochsnowflake(5,300).