Skip to content

Instantly share code, notes, and snippets.

@Reapor-Yurnero
Last active April 15, 2019 11:05
Show Gist options
  • Save Reapor-Yurnero/ed15c9187e714c52fccae72220b826c9 to your computer and use it in GitHub Desktop.
Save Reapor-Yurnero/ed15c9187e714c52fccae72220b826c9 to your computer and use it in GitHub Desktop.
This prolog script is a sympathetic doctor

Talking Box (Sympathetic Doctor)

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.

Get Started:

  • swlpl talkingbox.pl in command line or consult('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.

Working Sample:

Here are two working samples.

Injury

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 .

I can't diagonose!

?- 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 .

Design

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]).

Core Logic

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),!, ...

Appendix

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment