Xiaohan Fu, TS1
This prolog script is a sympathetic doctor, conversing with a patient who can answer only yes or no. The doctor is able to diagnose the patient’s condition while asking question sensitively depending upon patient’s pain level, mood level and symptoms. Additonally, as a sympathetic doctor, he's able to response accordingly to patient's condition with different gestures.
swlpl talkingbox.pl
in command line orconsult('talkingbox.pl').
in SWIPL interface.- type
start.
- Answer
y.
/n.
/q.
and type return
Note: you are expected to select exactly one pain level. If you choose no pain level, the program will quit with a corrresponding prompt.
Note: if no recorded disease can be found, a corresponding prompt will show saying that I'm not able to diagnose.
Here are two working samples.
A video for this sample is also available under the same directory.
?- start.
Doctor responses with gesture: ( joke )
Do you feel unbearable_pain? y/n/q: n.
Doctor responses with gesture: ( beaming_voice )
Do you feel lot_of_pain? y/n/q: |: y.
Doctor responses with gesture: ( look_concerned )
Do you feel calm? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel angry? y/n/q: |: y.
Doctor responses with gesture: ( faint_smile )
Do you feel weepy? y/n/q: |: y.
Doctor responses with gesture: ( faint_smile )
Do you feel stressed? y/n/q: |: n.
Doctor responses with gesture: ( look_concerned )
Do you feel sad? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel temperature? y/n/q: |: n.
Doctor responses with gesture: ( light_touch )
Do you feel sweat? y/n/q: |: y.
Doctor responses with gesture: ( mellow_voice )
Do you feel ache? y/n/q: |: n.
Doctor responses with gesture: ( light_touch )
Do you feel sneeze? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel cough? y/n/q: |: n.
Doctor responses with gesture: ( light_touch )
Do you feel blood? y/n/q: |: y.
Doctor responses with gesture: ( look_concerned )
Do you feel rash? y/n/q: |: y.
Doctor responses with gesture: ( mellow_voice )
Do you feel coryza? y/n/q: |: n.
You may have [injury].
true .
?- start.
Doctor responses with gesture: ( beaming_voice )
Do you feel unbearable_pain? y/n/q: n.
Doctor responses with gesture: ( joke )
Do you feel lot_of_pain? y/n/q: |: y.
Doctor responses with gesture: ( light_touch )
Do you feel calm? y/n/q: |: n.
Doctor responses with gesture: ( light_touch )
Do you feel angry? y/n/q: |: y.
Doctor responses with gesture: ( light_touch )
Do you feel weepy? y/n/q: |: y.
Doctor responses with gesture: ( look_concerned )
Do you feel stressed? y/n/q: |: n.
Doctor responses with gesture: ( mellow_voice )
Do you feel sad? y/n/q: |: n.
Doctor responses with gesture: ( look_concerned )
Do you feel temperature? y/n/q: |: n.
Doctor responses with gesture: ( light_touch )
Do you feel sweat? y/n/q: |: n.
Doctor responses with gesture: ( look_concerned )
Do you feel ache? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel sneeze? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel cough? y/n/q: |: n.
Doctor responses with gesture: ( faint_smile )
Do you feel blood? y/n/q: |: y.
Doctor responses with gesture: ( mellow_voice )
Do you feel rash? y/n/q: |: n.
Doctor responses with gesture: ( look_concerned )
Do you feel coryza? y/n/q: |: n.
Sorry I can't diagnose your disease, probably you should seek better doctors' help.
true .
Pain, mood and symptom: (basically use the ones given in hint.pdf)
pain_library([unbearable_pain, lot_of_pain, manageable_pain, mild_pain, no_pain]).
mood_library([calm, angry, weepy, stressed, sad]).
symptom_library([temperature, sweat, ache, sneeze, cough, blood, rash, coryza]).
Disease and their symptoms:
diseasemap(fever,[temperature, sweat, ache, weepy, manageable_pain]).
diseasemap(cold,[sneeze, cough, temperature, coryza, no_pain]).
diseasemap(injury,[blood, lot_of_pain, weepy, angry, sweat]).
diseasemap(anxiety_disorder,[no_pain, stressed, sad, weepy, angry]).
diseasemap(allergy,[rash, ache, angry, stressed, mild_pain]).
The doctor will always ask questions about pain level at the very beginning. It deserves to be noticed that the user is expected to say yes
to exactly one pain level. To accomplish this, our logic will have a condition judgement on the existence of a selected pain level before asking moods and symptoms. The correponding clauses asking pain levels will be skipped because the clauses for moods and symptoms are placed ahead.
% get questions for symptoms
get_question(Q):-
...
% get questions for mood
get_question(Q):-
% check the existence of painlevel (only ask mood after a pain level has been specified)
painlevel(_),
mood_library(ML),
remain_q(ML,ReL,_),
%print(Bool),
member(Q, ReL), !.
% get questions for pain level
get_question(Q):-
pain_library(PL),
remain_q(PL,ReL,Bool),
(Bool -> writeln('[Error]: You are expected to select exactly one pain level!!!');true),
member(Q, ReL), !.
The existence of a selected pain level is checked with the following fasion. We declare painlevel/
to be dynamic and use assert
to add the selected painlevel into it. Then we check the existence of painlevel
by using painlevel(_)
which will return true
if there's something inside otherwise false
. The reason we don't use current_predicate/1
is dynamic property will keep a predicate in memory and the return value of current_predicate/1
would always be true
.
:- dynamic painlevel/1.
...
% get questions for pain level
get_question(Q):-
pain_library(PL),
...
% maintain the according data structure if a yes is answered
answeryes(Q):-
pain_library(PL),
mood_library(ML),
symptom_library(SL),
(
member(Q, PL), assert(painlevel(Q)), assert(generalsymptoms(Q));
member(Q, ML), assert(moodlist(Q)), assert(generalsymptoms(Q));
member(Q, SL), assert(generalsymptoms(Q))
).
Similarly, our logic will check whether the doctor has completely asked all moods available as well as the existence of selected pain level before asking symptoms.
% get questions for symptoms
get_question(Q):-
% check whether the mood list has all been asked
% check the existence of painlevel
% only ask symptoms after all moods are covered and one pain level has been specified
mood_library(ML),
remain_q(ML,_,MoodFinished),
MoodFinished, painlevel(_),
symptom_library(SL),
remain_q(SL, ReL, _),
%print(Bool),
member(Q,ReL), !.
In the above code, remain_q
is used to check whether the doctor has completely asked all moods available. It is based on the embedded predicate subtract
.
% get remaining unasked questions from OriginList into RemainS
% isFinished is a bool showing the status of this set
remain_q(OriginList, RemainS, IsFinished):-
findall(X, answered(X), Answered),
list_to_set(Answered, AnsweredS),
list_to_set(OriginList, OriginSet),
subtract(OriginSet, AnsweredS, RemainS),
isempty(RemainS, IsFinished).
A cut is added after the base call to avoid backtracing to lower level askings like pain askings.
ask_question:-
get_question(Q),!, ...
Complete code:
:- dynamic answered/1.
:- dynamic generalsymptoms/1.
:- dynamic painlevel/1.
:- dynamic moodlist/1.
:- dynamic ready_to_diagnose/1.
% main, if ready to diagnose, show diagnose result, otherwise ask questions
start:-
ready_to_diagnose(_), show_diagnos;
show_gesture, ask_question, start.
show_diagnos:-
diagnos(Res),
% check whether the diagnose is empty
isempty(Res, Isempty),
(
Isempty -> write('Sorry I can\'t diagnose your disease, probably you should seek better doctors\' help.');
write("You may have "),
write(Res),
write(".")
), !.
diagnos(Res):-
% get the symptoms list of the patient
findall(S,generalsymptoms(S), GSL),
list_to_set(GSL,GSS),
findall(Disease, checkdisease(Disease,GSS), Res).
% check whether a certain desease matches the patient's symptoms
checkdisease(D, GSS):-
diseasemap(D,L),
list_to_set(L,S),
subset(S,GSS).
show_gesture:-
get_gesture(G),
write('Doctor responses with gesture: ( '),
write(G),
writeln(' )').
get_gesture(G):-
% response according to the patient's symptoms
(
(painlevel(unbearable_pain);painlevel(lot_of_pain);generalsymptoms(blood);moodlist(sad);moodlist(weepy)) -> gesture_library(polite_gesture, GL);
(painlevel(mild_pain);painlevel(manageable_pain);moodlist(angry);moodlist(stressed)) -> gesture_library(calming_gesture, GL);
gesture_library(normal_gesture, GL)
),
random_member(G, GL).
ask_question:-
get_question(Q),!,
write('Do you feel '), write(Q),
write('? y/n/q: '),
read(Userinput),
(
Userinput==q -> abort;
Userinput==y -> answeryes(Q);
Userinput==n -> true
),
assert(answered(Q)),
% check if all symptoms have been verified, if so, go diagnose
symptom_library(SL),
remain_q(SL,_,SymptomFinished),
(SymptomFinished -> assert(ready_to_diagnose(true));true).
% get questions for symptoms
get_question(Q):-
% check whether the mood list has all been asked
% check the existence of painlevel
% only ask symptoms after all moods are covered and one pain level has been specified
mood_library(ML),
remain_q(ML,_,MoodFinished),
MoodFinished, painlevel(_),
symptom_library(SL),
remain_q(SL, ReL, _),
%print(Bool),
member(Q,ReL), !.
% get questions for mood
get_question(Q):-
% check the existence of painlevel (only ask mood after a pain level has been specified)
painlevel(_),
mood_library(ML),
remain_q(ML,ReL,_),
%print(Bool),
member(Q, ReL), !.
% get questions for pain level
get_question(Q):-
pain_library(PL),
remain_q(PL,ReL,Bool),
(Bool -> writeln('[Error]: You are expected to select exactly one pain level!!!');true),
member(Q, ReL), !.
% maintain the according data structure if a yes is answered
answeryes(Q):-
pain_library(PL),
mood_library(ML),
symptom_library(SL),
(
member(Q, PL), assert(painlevel(Q)), assert(generalsymptoms(Q));
member(Q, ML), assert(moodlist(Q)), assert(generalsymptoms(Q));
member(Q, SL), assert(generalsymptoms(Q))
).
% get remaining unasked questions from OriginList into RemainS
% isFinished is a bool showing the status of this set
remain_q(OriginList, RemainS, IsFinished):-
findall(X, answered(X), Answered),
list_to_set(Answered, AnsweredS),
list_to_set(OriginList, OriginSet),
subtract(OriginSet, AnsweredS, RemainS),
isempty(RemainS, IsFinished).
isempty([], true).
isempty([_|_], false).
% The following are knowledge base
pain_library([unbearable_pain, lot_of_pain, manageable_pain, mild_pain, no_pain]).
mood_library([calm, angry, weepy, stressed, sad]).
symptom_library([temperature, sweat, ache, sneeze, cough, blood, rash, coryza]).
diseasemap(fever,[temperature, sweat, ache, weepy, manageable_pain]).
diseasemap(cold,[sneeze, cough, temperature, coryza, no_pain]).
diseasemap(injury,[blood, lot_of_pain, weepy, angry, sweat]).
diseasemap(anxiety_disorder,[no_pain, stressed, sad, weepy, angry]).
diseasemap(allergy,[rash, ache, angry, stressed, mild_pain]).
gesture_library(polite_gesture, [look_concerned, mellow_voice,light_touch, faint_smile]).
gesture_library(calming_gesture, [greet, look_composed, look_attentive]).
gesture_library(normal_gesture, [broad_smile, joke, beaming_voice]).
answered(nothing).
generalsymptoms(nothing).
%painlevel.
%paindecided.
%moodlist.