Nice! For comparison, here is how one could approach this task in Prolog. First, let us introduce a few suitable
operators, so that we can naturally state the given facts:
:- op(700, xfy, and).
:- op(700, xfy, ==>).
:- op(750, xfy, or).
:- op(700, fx, not).
With these operators, the given knowledge could be represented as follows:
fact('I go to school' and 'I study' and 'I exercise').
fact('I exercise' ==> 'I\'m healthy').
fact('I study' or 'I\'m sick').
fact(not 'it rains today').
fact('I study' and ('I go to gym' or 'I drink a coffee')).
fact('it rains today' or ('I exercise' and 'I study')).
fact('I\'m healthy' ==> ('I\'m happy' or 'I\'m fit')).
Now, all it takes is an
interpreter for such domain-specific rules. We can easily implement one in Prolog. Here is a starting point:
true(C) :- fact(F), conjunction_conjunct(F, C).
true(B) :- fact(A ==> B), true(A).
true(A or _) :- true(A).
true(_ or B) :- true(B).
conjunction_conjunct(C, C).
conjunction_conjunct(A and B, D) :-
( conjunction_conjunct(A, D)
; conjunction_conjunct(B, D)
).
With this knowledge base and the given deduction rules, we can query for example:
?- true('I study' and 'I exercise').
true .
?- true('I study' or 'I\'m sick').
true .
?- true('I go to gym').
false.
?- true('I\'m healthy').
true .
?- true('it rains today' or 'I\'m healthy').
true .
We can also use this interpreter to ask the most general query, and it enumerates statements it can derive:
?- true(T).
T = ('I go to school'and'I study'and'I exercise') ;
T = 'I go to school' ;
T = ('I study'and'I exercise') ;
T = 'I study' ;
T = 'I exercise' ;
T = ('I exercise'==>'I\'m healthy') ;
T = ('I study'or'I\'m sick') ;
T = (not'it rains today') ;
T = ('I study'and('I go to gym'or'I drink a coffee')) .
Note that a more direct formulation is possible too with Prolog. However, an important advantage of the interpreter-based approach is that it lets us stay very close to the task description. Further, we can easily extend this interpreter with more features that can be implemented on top of Prolog.