/* Copyright (C) Wolfgang Menzel, Universität Hamburg, 2003-06-27 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. */ % a simple database-driven tutoring system using instance-based % knowledge and anticipation techniques :-module(instance_diag,[do_exercise/1,get_prompt/2,next_item/4,check_utt/6, get_sols/3]). %%% reading an utterance from the terminal / from an atom % read_utt(-Utterance) % read an utterance from the input, terminated by a return % all characters after the first full stop are ignored read_utt(Utt) :- read_line_to_codes(user,Line), % Read an input line get_u(Line,[],U), % Transform the input into a list of words reverse(U,Utt). % get_utt(+Text,-Utterance) % extract an utterance from the input string as delivered by the browser get_utt(Text,List) :- atom_codes(Text,Codes), % convert the input to a list of ascii values get_u(Codes,[],ListR), % extract the words reverse(ListR,List). % get_u(+InputLine,+AlreadyCollectedWords,-ListOfWords) % Transform an input line into a list of words (in reverse order % to be called with an empty list on second argument get_u([],Acc,Acc) :- !. % the input line ist exhausted get_u(Line,Acc,Utt) :- get_w(Line,[],Word,RestLine), % get the first word (Word = '' -> % if it is empty get_u(RestLine,Acc,Utt) ; % ignore it get_u(RestLine,[Word|Acc],Utt)). % otherwise keep it in the accumulator % get_w(+InputLine,+AlreadyCollectedCharacters,-CompleteWord,-RemainingInput) % collect a word from the beginning of the input line % and return it together with the remainder of the input line get_w([],Acc,Word,[]) :- % the input line is exhausted reverse(Acc,Chars), % return the last word atom_chars(Word,Chars). get_w([C|RestLine],Acc,Word,RestLine) :- del(C),!, % the first character is a word delimiter reverse(Acc,Chars), % return the current word atom_chars(Word,Chars). get_w([C|_],Acc,Word,[]) :- end(C),!, % the first character is a fullstop reverse(Acc,Chars), % return the last word atom_chars(Word,Chars). get_w([C|RestLine],Acc,Word,RLine) :- ( (C>=65,C=<90) -> C1 is C+32 ; C1=C), % upper case to lower case get_w(RestLine,[C1|Acc],Word,RLine). % collect the characters of a word %%% exercise control (main loop) for interactive use % do_exercise (+NameOfExercise) % cycles through all the items of an exercise do_exercise(Name) :- exercise(Name,Prompt), % find the prompt format('~w~n',[Prompt]), % and write it on the terminal item(Name,IId,Task,Solutions), % find an item tab(3),write_ln(Task), % and write its task read_utt(Utt), % read the utterance from the student feedback(Name,IId,Utt,Solutions), nl, fail. % iterate over all items do_exercise(_) :- write_ln('End of exercise. Bye.'). feedback(_,_,Utt,Solutions) :- member([Utt],Solutions), !, % in the list of goods without explanation? write_ln('well done'). % if so acknowledge feedback(Name,IId,Utt,Solutions) :- member([Utt,Expl],Solutions), !, % in the list of nogoods with explanation? (atom(Expl) -> show_expl(Name,[Expl],IId) ; show_expl(Name,Expl,IId)). feedback(Name,IId,_,_) :- show_expl(Name,sorry,IId). % otherwise reject as false show_expl(Name,sorry,IId) :- Message='Sorry, that was not what I expected', format('~w~n',[Message]), % write explanation read_utt(Utt), (Utt=[] -> fail ; (!, item(Name,IId,_,Solutions), feedback(Name,IId,Utt,Solutions))). show_expl(Name,Expl,IId) :- % IId\=end, %%%% why ??? member(E,Expl), message(Name,E,Message), % get explanation from DB format('~w~n',[Message]), % write explanation read_utt(Utt), (Utt=[] -> fail ; (!, item(Name,IId,_,Solutions), feedback(Name,IId,Utt,Solutions))). show_expl(Name,_,IId) :- get_sols(Name,IId,Sol), !, format('My proposal: ~w',[Sol]), nl. del(32). % space is a word delimiter end(46). % fulstop is a sentence delimiter %%% primitives for web-access % get_prompt(+Exercise,-Prompt) % get the prompt from the database get_prompt(Exercise,Prompt) :- exercise(Exercise,Prompt). % next_item(+Exercise,+ItemId,-Text,-NewItemId) % supply the next exercise item and return the ItemId increased by one next_item(Exercise,ItemId,Text,NewItemId) :- atom_to_number(ItemId,IdNumber), % convert the item number IdNumber1 is IdNumber+1, % increment it item(Exercise,IdNumber1,Text,_), % fetch the text number_to_atom(IdNumber1,NewItemId). % convert the increased number % check_utt(+Exercise,+ItemId,ExLevel,+Text,-Reply,-NewExLevel) % generate a feedback (Reply) to the student input (Text) % and increase the explanation level by one % ExLevel = 11111: last explanation available (no button "more information" % ExLevel = 22222: no more explanations available (make a correction proposal) % ExLevel = 33333: well done (no more information, no correction) check_utt(Exercise,ItemId,ExLevel,Text,Reply,NewExLevel) :- atom_to_number(ItemId,IdNumber), % convert the item number item(Exercise,IdNumber,_,Solutions), % fetch the solutions get_utt(Text,List), % get the student input as list atom_number(ExLevel,ExLevelNumber), (member([List,Expls],Solutions) -> % in the list of goods with explanation? (get_expl(Expls,ExLevelNumber,Message,NewExLevel), (Message=end__end -> Reply='No more explanations available' ; message(Exercise,Message,Reply))) % get explanation from DB ; (member([List],Solutions) -> % in the list of goods without explanation? (Reply='Well done!', % if so acknowledge NewExLevel='33333') ; (NewExLevel='22222', Reply='Sorry, that was not what I expected.'))). % else reject as false get_expl(Explanations,ExLevel,Expl,NewExLevel) :- (atom(Explanations) -> Expls=[Explanations] ; Expls=Explanations), length(Expls,NoExpl), ExLevel1 is ExLevel+1, (ExLevel>NoExpl -> (Expl=end__end, NewExLevel='22222') ; (nth1(ExLevel1,Expls,Expl), (NoExpl=:=ExLevel1 -> NewExLevel='11111' ; number_to_atom(ExLevel1,NewExLevel)))). get_sols(Exercise,ItemId,Sols) :- atom_to_number(ItemId,IdNumber), % convert the item number item(Exercise,IdNumber,_,Solutions), % fetch the solutions member([SolList],Solutions), % get one without explanation retranslate(SolList,Sols). % perhaps a more sophisticated solution is required % atom_to_number(+Atom,?Number) atom_to_number(Atom,Number) :- atom_chars(Atom,AtomList), number_chars(Number,AtomList). % number_to_atom(+Number,?Atom) number_to_atom(Number,Atom) :- number_chars(Number,AtomList), atom_chars(Atom,AtomList). retranslate([H|List],Result) :- capitalize(H,HC), insert_blanks(List,ListB), concat_atom(ListB,RR), concat_atom([HC,RR,'.'],Result). capitalize(Word,WordC) :- atom_codes(Word,[H|WList]), HC is H-32, atom_codes(WordC,[HC|WList]). insert_blanks([],[]). insert_blanks([H|R],[' ',H|RB]) :- insert_blanks(R,RB). %%% To DO %%%%% % several (three ?) attempts to produce a good solution % several explanations % alternative explanations % different explanation levels (including corrections) % all this will require a different feedback representation in the DB % pictures in the item-text