@p typesetter = tex @p maximum_input_line_length = 100 @p maximum_output_line_length = 120 \documentclass[11pt]{artikel3} %\input{phen.tex} \newcommand{\num}[1]{\oldstylenums{#1}} \newcommand{\kdag}[3]{\num{#1} \ifcase#2\or januari\or februari\or maart\or april\or mei\or juni\or juli\or augustus\or september\or oktober\or november\or december\fi~\num{#3}} %\def\vandaag{\num{\number\day}~\ifcase\month\or % januari\or februari\or maart\or april\or mei\or juni\or juli\or % augustus\or september\or oktober\or november\or december\fi % \space \num{\number\year}} \renewcommand{\today}{\ifcase\month\or January\or February\or March\or April\or May\or June\or July\or August\or September\or October\or November\or December\fi \space\num{\number\day}, \num{\number\year} } \renewcommand{\kdag}[3]{\ifcase#2\or January\or February\or March\or April\or May\or June\or July\or August\or September\or October\or November\or December\fi \space\num{#1}, \num{#3} } \usepackage{index} \usepackage{a4wide} \makeindex \newindex{term}{tdx}{tnd}{Identifier index} \newindex{scrap}{sdx}{snd}{Scrap index} \newcommand{\iin}[1]{\index[term]{#1@@\fwlit{#1}}} \newcommand{\scin}[1]{\index[scrap]{#1}} % \title{Dialaw: the program} \author{Arno Lodder and Paul Huygen} \begin{document} \maketitle \tableofcontents % \section{Introduction} This document contains the program code of a program called \emph{Dialaw}. This program models judicial dialogs. A description of the program can be found in~\cite{lodd98a}. The program code in this document deviates from the original listing in~\cite{lodd98a}, because it has been adapted to another software platform. Dialaw has been written in the Prolog programming language. In order to facilitate a comfortable user interface, a Prolog dialect that provides tools for such an interface has been used. However, care has been taken to separate the platform-dependent code as good as possible from the code that uses the standard language, in order to facilitate porting to other platforms. The code and the explanations are best understood if one is acquainted with Prolog or a similar declarative language. In the text the number of objects are often replaced with a number indicating its arity\index{arity}. For instance, the structure @{date(Year, Month, Day)@} would become @{date/3@}. The original Dialaw, as listed in~\cite{lodd98a}, was written for a Prolog interpreter called \emph{Prolog~\num{2} for Windows}. However, this Prolog dialect is no longer supported by its manufacturer. Therefore, in this version another software platform was used, i.e.\ \textsc{swi}-Prolog, extended with \textsc{xpce}. Both products have been developed by the department of Social Sciences Informatics (\textsc{swi}) of the University of Amsterdam (Roetersstraat~\num{15} \num{1018}\textsc{wb} Amsterdam, the Netherlands). They can be downloaded from their Internet site~\texttt{www.swi.psy.uva.nl}. \textsc{swi}-Prolog is a free product. For \textsc{xpce} a licence must be bought. However, without licence \textsc{xpce} can be used to its full capabilities, but only for a limited time, after whch it has to be restarted. \subsection{Literate programming} For this document the \emph{Literate Programming} technique has been applied. Literate programming is a methodology that combines a programming language with a documentation language, thereby making programs more robust, more portable and more easily maintained. The main idea is to treat a program as a piece of literature, addressed to human beings rather than to a computer. The Literate Programming concept was developed by D.~Knuth~\cite{knut92a}. Several literate programming systems exist, with different features, advantages and disadvantages. Generally they work as follows: An electronic document contains an alternation of chunks of progam code and text for the human reader. Generally there is a macro facility: a chunk of code can be replaced by a reference to a macro that is defined in some other place of the electronic document. This mechanism encourages a top down approach of programming and documenting. The literate programming system generally consists of two programs. One program strips the texts and substitutes the macro contents for the macro bodies (recursively), creating a source file that is readable for the compiler. The other program turns the electronic document into a document for a wordprocessor or typesetter. This progam/document has been written using the literate programming tool Funnelweb. The program and documentation are obtainable via Internet, e.g.\ from the following location:\par \verb!http://www.cs.ruu.nl/mirror/tex-archive/web/funnelweb/! \section{The main structures of the program} \subsection{The data-types \texttt{move}, \texttt{dialog} and \texttt{commitment store}} \label{sec:move} The move, dialog, and commitment store are based on the definitions in chapter~\num{3} of~\cite{lodd98a}. A move\index{move} is stored in a variable @{DialogMove@}\iin{Dialogmove} that refers to a structure with three elements, e.g.\ @{(Player, (Act, Sentence), (Level, MoveAbout))@}, where \begin{description} \item[@{Player@}\iin{Player}: ] Reference to the player whose turn to move it is. The players are called either @{bert@}\iin{bert} (@{player1@})\index{player1@@\fwlit{player1}} or @{ernie@}\iin{ernie} (@{player2@}\index{player2@@\fwlit{player2}}); \item[(@{Act, Sentence@}): ], the variable @{Act@}\iin{Act} refers to the illocutionary act performed by the player of the dialog move, the variable @{Sentence@}\iin{Sentence} refers to the propositional content of the illocutionary act; @{Act@} may have the following values: \texttt{claim}, \texttt{question}, \texttt{accept}, \texttt{withdraw} or @{quit@}. \item[(@{Level@}, @{MoveAbout@}):] @{Level@}\iin{Level} refers to the level the dialog move was made on an @{MoveAbout@}\iin{MoveAbout} refers to the sentence the move is an argument for or reaction to. \end{description} The entire dialog is stored in a variable @{DialogMoves@}\iin{DialogMoves} that refers to a Prolog-list with one or more elements of the type @{DialogMove@}\iin{DialogMove}. The number of elements in @{Dialogmoves@} is identical to the number of valid moves of the game. For instance, after three valid moves @{DialogMoves@} is a list with three elements. The variable @{CommitmentStore@}\iin{CommitmentStore}\index{commitment store} refers to the Prolog list that contains the commitments of each player at a particular stage of the dialog. Suppose only the first player is committed to @{murderer(oj)@}, and both are committed to @{shot_wife(oj)@}. The commitment store would then be the following list: \begin{verbatim} [(bert, murderer(oj)), (bert, shot_wife(oj)), (ernie, shot_wife(oj))]. \end{verbatim} \section{The main program loop} \label{sec:mainclause} Typing @{dialaw@} starts the program. It calls the following Prolog-rule, which is the main clause of the program. @$@==@{@- dialaw :- clean, create_windows, first_move(CS, FirstSentence, DialogMoves), dialog(CS, FirstSentence, DialogMoves), @.@}\scin{the main clause}\iin{clean}\iin{first\_move}% \iin{dialog}\iin{FirstSentence}\iin{DialogMoves}\iin{CS} Apart from some administrative chores, this rule defines under what circumstances @{dialaw/0@} is true, or, in other words, under what circumstances the program ends. This is the case if both @{first_move/3@} and @{dialog/3@} are true. \subsection{@{first_move/3@}} The first move is always a claim of one of the players (call this player \emph{player 1} or simply \emph{Bert}). Therefore the illocutionary act of the first move is fixed as claim, and the player is allowed to enter the propositional content of the claim. The following clause represents the first move: \begin{verbatim} first_move(CommitmentStore, FirstSentence, DialogMoves) \end{verbatim}\index{first_move@@\fwlit{first\_move}} When the player has entered the claim, @{first_move/3@} becomes true. After the first move @{CommitmentStore@} has a single element, i.e.\ the list containing the term \texttt{bert} and the text of the first claim. The list @{DialogMoves@} also contains only one element, recording that \texttt{Bert} has claimed a particular sentence on the zero level. Because his move is not a reaction to another move the variable @{MoveAbout@} is initiated as @{dialaw@} (see definition 5). Assume as an example that Bert claims in the first move the sentence @{murderer(oj)@}. In that case @{first_move/3@} becomes true with the following content: \begin{verbatim} first_move( [(bert, murderer(oj))] , murderer(oj) , [(bert, (claim, murderer(oj)), (0, dialaw))] ) \end{verbatim} Here comes the actual code of @{first_move@}. @$@==@{@- first_move([(bert, Sentence)], Sentence, [(bert, (claim, Sentence), (0, dialaw))]) :- @ @ @, @. @}\scin{the first move}\iin{bert}\iin{Sentence}\iin{claim}\iin{dialaw} \subsection{@{dialog/3@}} \label{sec:dialog} The clause @{dialog/3@} has the same elements as that of the first move, namely: @{dialog(CommitmentStore, FirstSentence, DialogMoves)@}. After the first move, the program continues until @{dialog/3@} is true. There are basically two situations in which @{dialog/3@} becomes true, and hence the dialog ends. The first situation occurs, if after a move the set of open sentences has become empty. This is the case when the first sentence has been accepted (by player~\num{2}), withdrawn (by player~\num{1}), or when the denial of the sentence has been accpted (by player~\num{1} or withdrawn (by player~\num{2}). Each of those conditions make one of the following clauses true, thereby ending the game: @$@+=@{@- dialog(_, S, [(_, (accept, S), _)|_]). dialog(_, S, [(_, (withdraw, S), _)|_]). dialog(_, S, [(_, (accept, not(S)), _)|_]). dialog(CS, S, [(_, (withdraw, not(S)), _)|_]):- not_inCS((_, S), CS). @}\scin{the conditions under which \fwlit{dialog/3} ends}% \iin{dialog}\iin{accept}\iin{withdraw}\iin{not\_inCS} The second situation that causes the game to come to an end, occurs if a player has entered \texttt{quit} as an illocutionary act. The quit-option is built in because otherwise the program could not be stopped in the case that the players do not want to continue an unfinished dialog. @$@+=@{@- dialog(_, _, [(_, (quit, _), _)|_]). @}\scin{end if player has entered "quit"}\iin{dialog}\iin{quit} In addition to the five clauses that define the circumstances under which @{dialog/3@} becomes true, there is a sixth @{dialog/3@} clause. This recursive clause runs in fact the whole of the dialog. @$@==@{@- dialog(CS, FS, DM) :- move_input(CS, DM, NewPlayer, NewLevels, Move), trace, valid_move(NewLevels, NewPlayer, CS, DM, ValidMove, Move), notrace, @, update_CS(DM, CS, NewCS, ValidMove, NewPlayer), dialog(NewCS, FS, [(NewPlayer, ValidMove, NewLevels)|DM]). @}\scin{the main dialog clause}\iin{dialog}% \iin{move\_input}\iin{CS}\iin{FS}\iin{DM}% \iin{NewPlayer}\iin{ValidMove}\iin{Move}% \iin{update\_CS}\iin{NewCS}\iin{NewLevels} In @{move_input/5@} the next move is done. This clause returns the performed move and the current levels of the dialog. Clause (@{valid_move/6@}) checks the validity of the latest move. Note that the validity of a forced move is checked although it has to be always valid. In case the move turns out to be invalid, the player is asked to enter another move instead of the latest one. When a valid move has been obtained, clause @{update_CS/5@} updates the commitment store. Finally @{dialog/3@} is started again for the rest of the dialog. \section{The moves} \label{sec:themoves} Perform the next move. First check whether a forced move has to be done, and perform this move automatically. If that is not the case, check whether a special rule applies. If no special rule applies, perform the case as a default. @$@==@{@- @ @ @} \subsection{The forced moves} \paragraph{Rule~\num{16}b.} If a player accepts the sentence @{il_claim(S)@}, then he is forced to withdraw @{S@}. @$@+=@{@- move_input( CS , [(P, (accept, il_claim(S)), (L, _))|_] , P , (L, il_claim(S)) , (withdraw, S) ):- otherplayer(P, Op), only_inCS_other(CS, Op, S), teller, write_status([(P, (accept, S), (L, _))|_], P, (L, S)). @}\scin{the forced moves}\iin{move\_input}\iin{CS}\iin{P}\iin{accept}\iin{il\_claim}\iin{S}\iin{L}% \iin{withdraw}\iin{otherplayer}\iin{Op}\iin{Only\_inCS\_other}\iin{teller}\iin{write\_status} \paragraph{Rule~\num{7}a.} A player is forced to accept that a rule is applicable, if he is committed to the statement that this rule is valid, and he is also committed to the statement that the precondition of this rule is satisfied. First, deal with the situation that @{P@} accepted the precondition of the rule in the last move, whereas he accepted the rule itself previously. @$@+=@{@- move_input( CS , [(P, (accept, A), (L, _))|_] , P , (L, A) , ( accept, reason( applicable(rule(A, B)) , applies(rule(A, B)) ) ) ) :- inCS((P, valid(rule(A, B))), CS), only_inCS_other( CS, P , reason( applicable(rule(A, B)) , applies(rule(A, B))) ), teller, write_status([(P, (accept, A), (L, _))|_], P, (L, A)). @}\scin{the forced moves} Next, deal with the situation that the validity of the rule was accepted in the latest move, and the precontition was recgnized earlier. @$@+=@{@- move_input( CS , [(P, (accept, valid(rule(A, B))), (L, _))|_] , P , (L, valid(rule(A, B))) , (accept, reason(applicable(rule(A, B)), applies(rule(A, B)))) ):- inCS((P, A), CS), only_inCS_other(CS, P, reason(applicable(rule(A, B)), applies(rule(A, B)))), teller, write_status( [(P, (accept, valid(rule(A, B))), (L, _))|_] , P, (L, valid(rule(A, B))) ). @}\scin{the forced moves} Finally, if a player claims the reaon that a rule is applicable (?) and his opponent is commited to the the preposition of the rule as well as to the validity to the rule, the opponent is forced to accept the reaon that the rule is applicable. @$@+=@{@- move_input( CS , [( P , (claim, reason(applicable(rule(A, B)), applies(rule(A,B)))) , (L, _) )|_ ] , Op , ( L , reason(applicable(rule(A, B)), applies(rule(A, B))) ) , (accept, reason(applicable(rule(A, B)), applies(rule(A, B)))) ):- otherplayer(P, Op), inCS((Op, A), CS), inCS((Op, valid(rule(A, B))), CS), teller, write_status( [(P, (claim, reason(applicable(rule(A, B)) , applies(rule(A, B)))), (L, _))|_ ] , Op , (L, reason( applicable(rule(A, B)) , applies(rule(A, B)) ) ) ). @}\scin{the forced moves} \paragraph{Rule~\num{8}~a} Suppose a player is committed to the statement that a rule applies, but not committed on the reason based on that rule. If his opponent has claimed the reason based on that rule, the player is forced to accept it. @$@+=@{@- move_input( CS , [(P, (accept, applies(rule(A, B))), (L, _))|_] , P , (L, applies(rule(A, B))) , (accept, reason(A, B)) ):- only_inCS_other(CS, P, reason(A, B)), teller, write_status([(P, (accept, applies(rule(A, B))), (L, _))|_], P, (L, applies(rule(A, B)))). @}\scin{the forced moves} \paragraph{Rule~\num{8}~b} Suppose a player is committed to the statement that a rule is excluded. If he previoulsy claimed this still open sentence, then he is forced to withdraw that claim. Furthermore, he is not allowed to claim that that the rule applies. @$@+=@{@- move_input( CS , [(P, (accept, excluded(rule(A, B))), (L, _))|_] , P , (L, excluded(rule(A, B))) , (withdraw, applies(rule(A, B))) ):- otherplayer(P, Op), only_inCS_other(CS, Op, applies(rule(A, B))), teller, write_status([(P, (accept, applies(rule(A, B))), (L, _))|_], P, (L, applies(rule(A, B)))). @}\scin{the forced moves} \paragraph{Rule~\num{12}~a} If a player accepts that aguments pro a statement $S$ outweigh arguments contra $S$, but he is committed to $\neg{}S$, then he is forced to withdraw $\neg{}S$. @$@+=@{@- move_input( CS , [(P, (accept, outweighs(Pro, Con, S)), (L, _))|_] , P , (L, outweighs(Pro, Con, S)) , (withdraw, not(S)) ):- inCS((P, not(S)), CS), teller, write_status( [(P, (accept, outweighs(Pro, Con, S)), (L,_))|_] , P , (L, outweighs(Pro, Con, S)) ) . @}\scin{the forced moves}\iin{outweighs} If a player has accepted that the pro arguments outweigh the contra arguments with respect to statement $S$, and, as a consequence, has withdrawn $\neg{}S$, he is ready to accept $S$. @$@+=@{@- move_input( CS , [(P, (withdraw, not(S)), (L, _))|_] , P , (L, not(S)) , (accept, S) ):- inCS((P, outweighs(Pro, Con, S)), CS), teller, write_status( [(P, (withdraw, not(S)), (L, _))|_] , P , (L, outweighs(Pro, Con, S)) ) . @}\scin{the forced moves}\iin{outweighs} If a player accepts that the arguments pro statement $S$ outweigh the statements contra $S$, then he is forced to accept the statement $S$ itself. @$@+=@{@- move_input( _ , [(P, (accept, outweighs(Pro, Con, S)), (L, _))|_] , P , (L, outweighs(Pro, Con, S)) , (accept, S) ):- teller, write_status( [(P, (accept, outweighs(Pro, Con, S)), (L,_))|_] , P , (L, outweighs(Pro, Con, S)) ) . @}\scin{the forced moves}\iin{outweighs} \paragraph{Rule~\num{14}~c\num{1}}\par If a player accepts that a statement $S$ is not valid because of a reason $R$, but he was committed to the statement that arguments pro $S$ outweigh the arguments contra $S$, then he is forced to withdraw the latter commitment. @$@+=@{@- move_input(CS , [(P, (accept, reason(R, not(S))), (L, _))|_] , P , (L, reason(R, not(S))) , (withdraw, outweighs(Pro, Con, S)) ):- otherplayer(P, Op), only_inCS_other(CS, Op, outweighs(Pro, Con, S)), teller, write_status([(P, (accept, reason(R, not(S))), (L, _))|_], P, (L, reason(R, not(S)))). @}\scin{the forced moves}\iin{outweighs} \subsection{General move input (voluntary, non-forced moves)} Determine what the level of the next move is, and which player's turn it is (function @{next_move@}\iin{next\_move}). Write the status and let the player perform the move. @$@+=@{@- move_input(CS, DM, NewPlayer, NewLevels, Move):- teller, next_move(DM, CS, NewPlayer, NewLevels), write_status(DM, NewPlayer, NewLevels), read_move(NewPlayer, Move). @}\scin{general move input}\iin{read\_move}\iin{next\_move}\iin{move\_input} The new move, and who the new player is, depends on the previous move and previous player. The @{next_move@} statement analyses @{DialogMoves@} and obtains values for @{NewPlayer@} and @{Newlevels@} (in other words, @{DialogMoves@} and @{commitmentStore@} are input variables, and @{NewPlayer@} and @{Newlevels@} are output variables. \begin{verbatim} next_move(DialogMoves, commitmentStore, NewPlayer, Newlevels) \end{verbatim} \subsubsection{Response to a claim} In case a player claims a statement, let his opponent respond. @$@+=@{@- next_move( [(P, (claim, B), (L, _))|_] , _ , NewPlayer , (L, B) ) :- otherplayer(P, NewPlayer). @}\scin{general move input}\iin{next\_move}\iin{claim} \subsubsection{Response to a question} If player questions a statement, increase the level, and let his opponent respond to the question. @$@+=@{@- next_move( [(P, (question, _), (L, B))|_] , _ , NewPlayer , (LL, B) ) :- otherplayer(P, NewPlayer), LL is L +1. @}\scin{general move input}\iin{next\_move}\iin{question} \subsubsection{Response to accept and withdrawal} Several rules have to be checked in case a player acepted or withdrew a statement. Only if none of these rules are applicable, handle the case as a general case. @$@+=@{@- @ @ @} \paragraph{general accept and withdrawal}\par Generally, if a player $P$ accepts a statement $S$, the level of the discussion returns to the level where $S$ has been claimed, and the other player is allowed to perform a move. This behaviour is prescribed by rule~\num{5}c\num{2}. @$@+=@{@- next_move( [(P, (accept, S), _)|Rest] , _ , NewPlayer , (L, B) ) :- find_move(Rest, S, (L, B)), otherplayer(P, NewPlayer). @}\scin{general move input}\iin{next\_move} Generally, if a player withdraws a statement $B$, the level returns to the lefel where $B$ was claimed, and the player is allowed to perform another move. This behaviour is prescribed by rule~\num{5}b\num{2}. @$@+=@{@- next_move( [(P, (withdraw, B), _)|Rest] , _ , P , NewLevels ) :- find_move(Rest, B, NewLevels). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{5}~a} If player $P$ withdraws a statement $B$, and his opponent is committed to $\neg{}B$, $P$ is on turn, and the level goes to the value where $B$ (actually $\neg{}B$) has been claimed. @$@+=@{@- next_move( [(P, (withdraw, B), _)|Rest] , CS , P , (L, not(B)) ) :- only_inCS_other(CS, P, not(B)), find_move(Rest, not(B), (L, _)). @}\scin{general move input}\iin{next\_move}\iin{withdraw}\iin{find\_move} \paragraph{Rule~\num{5}~c1}\par If a player $P$ accepts the negation of a statement $S$, then the level of the discussion must return to the level where $S$ has been claimed, and $P$ must perform another move. @$@+=@{@- `next_move( [(P, (accept, not(S)), _)|Rest] , _ , P , (L, B) ) :- find_move(Rest, S, (L, B)). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{5}~b1}\par If a player withdraws the negation of a statement $S$, then the other player, who is supposed to having claimed $S$, is allowed to continue on the level where $S$ had been claimed. @$@+=@{@- next_move( [(P, (withdraw, not(S)), _)|Rest] , _ , NewPlayer , (L, B) ) :- find_move(Rest, S, (L, B)), otherplayer(P, NewPlayer). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{16}~c}\par This rule handles acceptance or withdrawal of @{il_claim(S)@}. The player who claimed @{il_claim(S)@} is allowed toperform the next move. @$@+=@{@- next_move( [(P, (withdraw, il_claim(S)), _)|Rest] , CS , P , (L, S) ) :- not_inCS((_, not(il_claim(S))), CS), find_move(Rest, S, (L, _)). next_move( [(P, (withdraw, not(il_claim(S))), _)|Rest] , CS , Op , (L, S) ) :- not_inCS((_, il_claim(S)), CS), otherplayer(P, Op), find_move(Rest, S, (L, _)). next_move( [(P, (accept, not(il_claim(S))), _)|Rest] , _ , P , (L, S) ) :- find_move(Rest, S, (L, _)). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{14}~a}\par If a player withdraws a statement that a pro argument outweighs a con arguments with respect to a statem,ent $S$, the level goes back to the level where the con argument was claimed. @$@+=@{@- next_move( [(P, (withdraw, outweighs(_, _, S)), _)|Rest] , CS , P , (L, reason(R, not(S))) ) :- only_inCS_other(P, reason(R, not(S)), CS), find_move(Rest, reason(R, not(S)), (L, _)). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{14}~b}\par If a player accepts that a statement $R$ is an argument pro $\neg{}S$, then find out whether there was a outweigh claim with respect to $S$. If so, find the level where the outweigh has been claimed, and check wheher the outweigh is still in the commitment store. Finally, find the statement that the outweigh move was about ($B$). The latter procedure seems redundant. The outweigh statement should be claimed in a move about $S$ itself. The player who performed the accept move has to perform the next move. @$@+=@{@- next_move( [(P, (accept, reason(R, not(S))), _)|Rest] , CS , P , (L, B) ) :- find_move(Rest, reason(R, not(S)), (L, outweighs(_, _, S))), inCS((_, outweighs(_, _, S)), CS), find_move(Rest, outweighs(_, _, S), (_, B)). next_move( [(P, (withdraw, reason(R, not(S))), _)|Rest] , CS , NewPlayer , (L, B) ) :- find_move(Rest, reason(R, not(S)), (L, outweighs(_, _, S))), inCS((_, outweighs(_, _, S)), CS), find_move(Rest, outweighs(_, _, S), (_, B)), otherplayer(P, NewPlayer). @}\scin{general move input}\iin{next\_move} \paragraph{Rule~\num{14}~b}\par @$@+=@{@- next_move([(P, (withdraw, reason(_, not(S))), _)|Rest], CS, P, (L, B)) :- inCS((_, outweighs(_, _, S)), CS), find_move(Rest, outweighs(_, _, S), (L, B)). @}\scin{general move input}\iin{next\_move} \subsubsection{Find the level of a claim} Check at which level a statement has been claimed. The function @{find_move@} searches a list of dialog moves to find a move in which a statement $S$ has been claimed. If it has been found, the term $L$ contains on return the dialog level in which the claim took place. @$@+=@{@- find_move([( _, (claim, S), (L, B))|_], S, (L, B)). find_move([_|Rest], S, NL) :- find_move(Rest, S, NL). @}\scin{find the level of a claim}\iin{find\_move} \subsection{Check the validity of moves} \label{ssec:validitycheck} The general form of claus that cecks the validity of a move is: \begin{verbatim} valid_move(NewLevels, NewPlayer, CommitmentStore, DialogMoves,ValidMoves, ValidMove, Move) \end{verbatim} It is always valid to quit. @$@+=@{@- valid_move(_, _, _, _, (quit, _), (quit, _)). @}\scin{check validity of moves} Valid moves in case of accept: @$@+=@{@- valid_move(_, NP, CS, _, (accept, S), (accept, S)) :- only_inCS_other(CS, NP, S), not_inCS((_, not(S)), CS). /* valid moves in case of withdraw*/ valid_move(_, NP, CS, _, (withdraw, S), (withdraw, S)) :- otherplayer(NP, P), only_inCS_other(CS, P, S). @}\scin{check validity of moves} Valid moves in case of question: @$@+=@{@- valid_move(_, P, CS, [(P, (withdraw, S), _)|_], (question, not(S)), (question, not(S))) :- otherplayer(P, Op), inCS((Op, not(S)), CS). @}\scin{check validity of moves} @$@+=@{@- valid_move(NL, NP, CS, DM, ValidMove, (question, outweighs([_], [], _))):- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(NL, NP, CS, [(P, (question, S), L)|Rest], ValidMove, (question, S)):- read_again(NP, Move), valid_move(NL, NP, CS, [(P, (question, S), L)|Rest], ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(NL, NP, CS, _, ValidMove, (question, reason(applicable(rule(A, B)), applies(rule(A, B))))):- inCS((NP, A), CS), inCS((NP, valid(rule(A, B))), CS), read_again(NP, Move), valid_move(NL, NP, CS, [(_, (question, _), _)|_], ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(_, _, _, [(_, (claim, S), _)|_], (question, S), (question, S)). valid_move(_, _, CS, [(_, (withdraw, il_claim(S)), _)|_], (question, S), (question, S)):- not_inCS((_, not(il_claim(S))), CS). valid_move(_, _, CS, [(_, (withdraw, not(il_claim(S))), _)|_], (question, S), (question, S)):- not_inCS((_, il_claim(S)), CS). valid_move(_, _, _, [(_, (accept, not(il_claim(S))), _)|_], (question, S), (question, S)). valid_move(_, NP, CS, [(_, (withdraw, reason(_, not(S))), _)|_], (question, outweighs(_, _, S)), (question, outweighs(_, _, S))):- otherplayer(NP, P), only_inCS_other(CS, P, outweighs(_, _, S)). @}\scin{check validity of moves} Valid moves in case of claim: @$@+=@{@- valid_move(NL, NP, CS, DM, ValidMove, (claim, S)) :- previous_withdraw(NP, S, DM), read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(NL, NP, CS, [(P, (withdraw, S), Levels)|Rest], ValidMove, (claim, _)) :- otherplayer(P, Op), inCS((Op, not(S)), CS), read_again(NP, Move), valid_move(NL, NP, CS, [(P, (withdraw, S), Levels)|Rest], ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(NL, NP, CS, [(P, (withdraw, il_claim(S)), Levels)|Rest], ValidMove, (claim, _)) :- read_again(NP, Move), valid_move(NL, NP, CS, [(P, (withdraw, il_claim(S)), Levels)|Rest], ValidMove, Move). valid_move(NL, NP, CS, [(P, (withdraw, not(il_claim(S))), Levels)|Rest], ValidMove, (claim, _)) :- read_again(NP, Move), valid_move(NL, NP, CS, [(P, (withdraw, il_claim(S)), Levels)|Rest], ValidMove, Move). valid_move(NL, NP, CS, [(P, (accept, not(il_claim(S))), Levels)|Rest], ValidMove, (claim, _)) :- read_again(NP, Move), valid_move(NL, NP, CS, [(P, (withdraw, il_claim(S)), Levels)|Rest], ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(_, NP, CS, _, (claim, applies(rule(A, B))), (claim, applies(rule(A, B)))) :- not_inCS((_, applies(rule(A, B))), CS), not_inCS((NP, excluded(rule(A, B))), CS). valid_move(NL, NP, CS, DM, ValidMove, (claim, applies(rule(_, _)))) :- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move((_, applies(rule(A, B))), NP, CS, _, (claim, reason(applicable(rule(A, B)), applies(rule(A, B)))), (claim, reason(applicable(rule(A, B)), applies(rule(A, B))))) :- not_inCS((_, reason(applicable(rule(A, B)), applies(rule(A, B)))), CS), not_inCS((NP, not(A)), CS), not_inCS((NP, not(valid(rule(A, B)))), CS). valid_move(NL, NP, CS, DM, ValidMove, (claim, reason(applicable(rule(A, B)), applies(rule(A, B))))) :- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(_, _, _, [(_, (claim, B), _)|_], (claim, il_claim(B)), (claim, il_claim(B))) :- B \= il_claim(_). valid_move(NL, NP, CS, DM, ValidMove, (claim, il_claim(_))) :- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move((_, B), _, CS, _, (claim, reason(A, B)), (claim, reason(A, B))):- not_inCS((_, reason(A, B)), CS). valid_move((_, B), _, CS, _, (claim, reason(A, not(B))), (claim, reason(A, not(B)))) :- not_inCS((_, reason(A, not(B))), CS). valid_move(_, _, CS, [(_, (claim, outweighs(_, _, B)), _)|_], (claim, reason(A, not(B))), (claim, reason(A, not(B)))) :- not_inCS((_, reason(A, not(B))), CS). valid_move(NL, NP, CS, DM, ValidMove, (claim, reason(_, _))) :- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). valid_move((_, B), _, CS, _, (claim, outweighs(Proset, Conset, B)), (claim, outweighs(Proset, Conset, B))) :- not_inCS((_, outweighs(Proset, Conset, B)), CS), check_reasonsets(CS, Proset, Conset, B). @}\scin{check validity of moves}\iin{read\_move} @$@+=@{@- valid_move(_, _, _, [(_, (claim, S), _)|_], (claim, not(S)), (claim, not(S))):- S \= not(_). valid_move(_, _, CS, [(_, (question, _), _)|_], (claim, S), (claim, S)):- not_inCS((_, S), CS), not_inCS((_, not(S)), CS). valid_move(_, _, CS, [(_, (withdraw, _), _)|_], (claim, S), (claim, S)):- not_inCS((_, S), CS), not_inCS((_, not(S)), CS). valid_move(_, _, CS, [(_, (accept, _), _)|_], (claim, S), (claim, S)):- not_inCS((_, S), CS), not_inCS((_, not(S)), CS). valid_move(NL, NP, CS, DM, ValidMove, _) :- read_again(NP, Move), valid_move(NL, NP, CS, DM, ValidMove, Move). @}\scin{check validity of moves}\iin{read\_move} A non-empty proset is not allowed. Check it. @$@+=@{@- check_reasonsets(_, [], _, _):- fail. check_reasonsets(CS, Proset, Conset, B) :- peel(CS, Proset, Conset, B, _, _). @}\scin{check validity of moves} @$@+=@{@- peel([(bert, reason(R, B))|Rest], Proset, Conset, B, Checkpro, Checkcon) :- peel(Rest, Proset, Conset, B, [R|Checkpro], Checkcon). peel([(bert, reason(R, not(B)))|Rest], Proset, Conset, B, Checkpro, Checkcon) :- peel(Rest, Proset, Conset, B, Checkpro, [R|Checkcon]). peel([_|Rest], Proset, Conset, B, Checkpro, Checkcon) :- peel(Rest, Proset, Conset, B, Checkpro, Checkcon). peel([], Proset, Conset, _, Proset, Conset). @}\scin{check validity of moves} \section{The commitment store} \subsection{Update the commitment store} There are two update clauses: \begin{verbatim} update_CS(DialogMoves, CS, NewCS, Move, NewPlayer) bigupdate_CS(DialogMoves, CommitmentStore, NewCS, ValidMove, NewPlayer) \end{verbatim} @$@+=@{@- update_CS(_, CS, [(NP, S)|CS], (claim, S), NP):- write_CS([(NP, S)|CS]). @}\scin{update the commitment store} Delete elements: @$@+=@{@- update_CS(DM, CS, NewCS, (accept, S), NP) :- bigupdate_CS(DM, CS, NewCS, (accept, S), NP), write_CS(NewCS). update_CS(DM, CS, NewCS, (withdraw, S), NP) :- bigupdate_CS(DM, CS, NewCS, (withdraw, S), NP), write_CS(NewCS). update_CS(_, CS, CS, _, _):- write_CS(CS). @}\scin{update the commitment store} @$@+=@{@- bigupdate_CS([(_, (claim, S), _)|_], CS, [(NP, S)|CS], (accept, S), NP). bigupdate_CS([X|Rest], CS, NewCS, (accept, S), NP) :- bigupdate_CS(Rest, CS, NextCS, (accept, S), NP), realupdate_CS([X|Rest], NextCS , NewCS , (accept, S), NP). bigupdate_CS([(_, (claim, S), _)|_], CS, NewCS, (withdraw, S), NP) :- del((NP, S), CS, NewCS). bigupdate_CS([X|Rest], CS, NewCS, (withdraw, S), NP) :- bigupdate_CS(Rest, CS, NextCS, (withdraw, S), NP), realupdate_CS([X|Rest], NextCS, NewCS, (withdraw, S), NP). @}\scin{update the commitment store} The actual big update takes place in a @{realupdate_CS@}: \begin{verbatim} realupdate_CS(DialogMoves, CommitmentStore, NewCS, ValidMove, NewPlayer) \end{verbatim} @$@+=@{@- realupdate_CS([(P, (claim, not(S)), _)|_], CS, CS, (withdraw, S), _) :- inCS((P, not(S)), CS). realupdate_CS([(P, (claim, S), _)|_], CS, NewCS, _, _) :- inCS((P, S), CS), otherplayer(P, Op), not_inCS((Op, S), CS), del((_, S), CS, NewCS). realupdate_CS(_, CS, CS, _, _). @}\scin{update the commitment store} \subsubsection{Consult the commitment store} @$@+=@{@- only_inCS_other(CS, P, S) :- otherplayer(P, Op), inCS((Op, S), CS), not_inCS((P, S), CS). @}\scin{consult the commitment store} @$@+=@{@- inCS(_, []):-fail. inCS(Element, [Element|_]). inCS(Element, [_|Rest]) :- inCS(Element, Rest). @}\scin{consult the commitment store} @$@+=@{@- not_inCS(_, []). not_inCS(Element, [OtherElement|Rest]) :- Element \= OtherElement, not_inCS(Element, Rest). @}\scin{consult the commitment store} \section{Other elements of Dialaw} \subsection{Change the players} @$@==@{@- otherplayer(bert, ernie). otherplayer(ernie, bert). @}\scin{change the players} \subsection{previous withdrawal} @$@==@{@- /*previous_withdraw(Player, S, DM)*/ previous_withdraw(_, _, []):-fail. previous_withdraw(P, S, [(P, (withdraw, S), _)|_]). previous_withdraw(P, S, [_|Rest]):- previous_withdraw(P, S, Rest). @}\scin{previous withdrawal} \subsection{The move counter} @$@==@{retract(teller(_))@}\scin{initialize the move counter} @$@@{@- assert(teller(1)), teller(U),@}\scin{set the move counter to unity} @$@==@{@- teller :- retract(teller(Umin1)), U is Umin1 + 1, assert(teller(U)).@}\scin{update the move counter} \subsection{Some code that I don't understand} @$@==@{@- del(X, [X|Tail], Tail). del(X, [Y|Tail], [Y|Tail1]) :- del(X, Tail, Tail1). @}\scin{delX} \section{Input and output} The users have to pass moves to the program and to read information about the status of the dialog. Standard Prolog has little facilities to communicate with the user(s). Therefore we will use a Prolog extension for graphical user interfaces. As a consequence, much of the code in this chapter is not standard Prolog. \subsection{Low level input from/output to the terminal} Print a character string. The following code has been copied from~\cite{clock87a} @$@+=@{@- printreeks([]). printreeks([K|S]) :- put(K), printreeks(S). @}\scin{low-level read/write} \subsection{Windows management and user interface} \label{sec:winmngmnt} The status of the dialog is showed in a panel with four windows. At the bottom, two windows show the commitments of the two players, Bert at the left side, Ernie at the right side. On top of these two windows there are three windows on top of each other. The lowest of the three lists the acts of the two players (\emph{dialog} window), the middle window shows the current move and the top window shows the program status. @$@+=@{@- create_windows :- new(@@mainf, frame('Dialaw Window')), send(@@mainf, append, new(@@csb, view('Berts commitments', size(25,10)))), send(new(@@cse, view('Ernies commitments', size(25,10))), right, @@csb), send(new(@@dv, view('Dialog', size(@@default,10))), above, @@csb), send(new(@@sv, view('status', size(@@default,10))), above, @@dv), send(@@mainf, open). @}\scin{create/delete windows} Clear windows. @$@==@{@- send(@@cse,clear), send(@@csb,clear) @} When the dialog has been finished, delete the window panel. @$@+=@{@- delete_windows :- send(@@mainf, destroy). @}\scin{create/delete windows} Write a string to one of the windows in the panel with the following macro The macro has two arguments: the name of the window and the string to be written. @$@@(@2@)@M==@{@- send(@1, print, @2) @}\scin{write in screen-partition} Write a string to a window, and append the string with a newline character: @$@@(@2@)@M==@{@- send(@1, print, @2), send(@1, newline)@- @}\scin{write line in screen-partition} Write the first status, before asking for the first statement. @$@==@{@- @@(@@sv@,U@), @@(@@sv@, @"'. On level 0, move 1 of the dialog game'@"@),@- @}\scin{write the first status} Update the windows after the first move: @$@==@{@- @@(@@dv@,@"'1. bert: '@"@), @@(@@csb@,U@), @@(@@csb@,@"'. '@"@), @@(@@csb@,Sentence@), @@(@@cse@,U@), @@(@@cse@,@"'. '@"@), @@(@@cse@,[]@)@- @}\scin{update the screen after the initial move} Write the new status on the screen after other moves: \begin{verbatim} write_status(DialogMoves, NewPlayer, NewLevels) \end{verbatim} Update the information on the windows. @$@==@{@- write_status([(_, (A, S), _)|_], P, (L, N)) :- teller(U), send(@@sv, print,U), send(@@sv, print, '. The level is: '), send(@@sv, print, L), send(@@sv, print, '; the move is about '), send(@@sv, print, N), send(@@sv, newline), send(@@dv, clear), send(@@dv, print, A), send(@@dv, print, ', '), send(@@dv, print, S), send(@@dv, newline), send(@@dv, print, U), send(@@dv, print, '. '), send(@@dv, print, P), write_levelmark(L). @}\scin{write the status} Write the level mark in the dialog window. @$@@{@- write_levelmark(0) :-!. write_levelmark(L) :- send(@@dv, print, '>'), LL is L-1, write_levelmark(LL). @}\scin{write the level mark} The user input occurs via pop-up windows. The first move is always a claim of Bert, so, we only have to read the contents of the claim. This code is stolen from the \textsc{xpce} manual~\cite{wiel97a} (pp.\ 32--33). @$@==@{@- new(D, dialog('Berts statement:')), send(D, width, 500), send(D, append, new(TI, text_item(statement, ''))), send(TI, geometry,@@default,@@default,450,@@default ), send(D, append, button(ok, message(D, return, TI?selection))), send(D, append, button(cancel, message(D, return, @@nil))), send(D, default_button, ok), % Ok: default button get(D, confirm, Answer), % This blocks! send(D, destroy), Answer \== @@nil, % canceled Sentence = Answer @} Read the next move of the player(@{read_move(NewPlayer, Move)@}). @$@==@{@- read_move(P, (A, S)) :- new(@@act, frame(P)), send(@@act, append, new(D, dialog(P))), send(D, width, 500), @! send(D, append, new(AC, text_item(act, ''))), send(D, append, new(TI, text_item(sentence, ''))), send(TI, geometry,@@default,@@default,450,@@default ), send(D, append, button(accept, message(D, return, accept))), send(D, append, button(withdraw, message(D, return, withdraw))), send(D, append, button(question, message(D, return, question))), send(D, append, button(claim, message(D, return, claim))), @! send(D, append, button(ok, message(D, return, TI?selection))), @! send(D, default_button, ok), % Ok: default button get(D, confirm, A), % This blocks! set_act_type(A), get(TI, selection, S), act_type(A), send(@@act, destroy). set_act_type(Value) :- retractall(act_type(_)), assert(act_type(Value)). write_dialog([]). write_dialog([K|T]):- send(@@dv, print, K), write_dialog(T). @}\scin{read the next move}\iin{read\_move} If a move was wrong, try again. Unfortunately, on entry the variables A and S are not filled it. Therefore, this clause cannot inform the user what the worng move was. @$@==@{@- read_again(P, (A, S)) :- new(@@wrong, dialog('Wrong move')), send(@@wrong, append, new(@@wm, view)), @! send(@@wm, print, A), send(@@wm, newline), @! send(WV, print, S), send(@@wrong, append, button(ok, message(@@wrong, destroy))), send(@@wrong, open), read_move(P, (A, S)), send(@@wm, destroy), send(@@wrong, destroy). @}\scin(re-read a move)\iin(read\_again) Write the Commitment store to the screen: This part deviates from Lodder's original program~\cite{lodd98a}. Lodder used, apart from the \verb!write_CS/1! clause, also \verb!write_CS/4! clauses, in which he disassembled the commitment store into statements to which Bert is committed and statements to which Ernie is committed. Unfortunately they did not work for me, although I don't know why. @$@+=@{@- write_CS([(bert, S)|Rest]) :- teller(U), send(@@csb, newline), send(@@csb, print, U), send(@@csb, print, '. '), send(@@csb, print, S), write_CS(Rest). write_CS([(ernie, S)|Rest]) :- teller(U), send(@@cse, newline), send(@@cse, print, U), send(@@cse, print, '. '), send(@@cse, print, S), write_CS(Rest). write_CS([]). @}\scin{update the commitment store} @$@==@{@- new(@@stop, dialog('Stop')), send(@@stop, append, button(stop, message(@@stop, return, doit))), send(@@stop, default_button, stop), send(@@stop, open), get(@@stop, confirm, _), send(@@mainf, destroy), send(@@stop, destroy)@- @}\scin{stop the program} \section{Put everything in a Prolog file} \label{sec:thecode} @O@==@{@- /*DiaLaw 2.0, 1998, Prolog 2 for Windows*/ clean :- @, fail. clean. @ @ @ @ @ @ @!@ @ @ @ @ @ @ @ @ @ @ @ @ @}\scin{Dialaw.pl} \bibliographystyle{plain} \bibliography{ai,software} \clearpage \section{Indexes} \printindex[scrap] \printindex[term] \printindex \end{document}