/* Part of XPCE --- The SWI-Prolog GUI toolkit Author: Jan Wielemaker and Anjo Anjewierden E-mail: jan@swi.psy.uva.nl WWW: http://www.swi.psy.uva.nl/projects/xpce/ Copyright (c) 1995-2011, University of Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \chapter{Example: A Chess Interface} \label{sec:chess} \index{chess front-end} In chapters~\ref{sec:starting},~\ref{sec:graphics} and \ref{sec:pceprolog} we have explained the fundamentals of programming PCE/Prolog. In this section we will combine much of this material in a realistic example. The domain is a graphics front-end for the terminal oriented program gnuchessc. The interface presented in this chapter is rather primitive: no administration of taken pieces, no time management, no indication who is to move next, etc. The (annotated) sources of this example may be found in the library file demo/chess.pl. The chesstool may be started from the manual tools or from the Prolog top level by typing: 1 ?- [demo(chess)]. 2 ?- chess. Which will (after some moves) produce the window shown in figure~\ref{fig:chess}. \postscriptfig{chess}{Graphical front-end for chess} \section{Functional Decomposition} A chess-board consists of $8\times8$ squares, each of which has a black- or white background and optionally a piece on it. The basic following operations need to be implemented on the board: - Create board. - Put a black- or white piece at some square. - Remove a piece from a square. - Generate the initial chess position. - Detect a gesture with the mouse to move a piece and generate the moved piece and coordinates from the gesture. \section{Design} The most obvious realisation is to use a compound graphical object as a starting point to represent the board and primitive graphical objects to represent each of the 64 squares. The pieces themselves are represented by bitmap objects. A bitmap is a graphical object that displays an {\em image} object: a two dimensional array of (possibly coloured) pixels. One possible implementation would be to create the board as a whole using black (grey) and white bitmaps for each of the squares and display the pieces on top of these squares. In the current implementation of PCE images are not transparent and are always rectangular, which makes it impossible to display a piece on a black square. For this reason we use 4 images for each piece: a white piece on a white square, a white piece on a black square, etc. The PCE bitmap library defines bitmaps for the various chess-pieces in both colours and with both a black- and a white background. The image `bishop.bm' is a $64\times64$ image. See figure~\ref{fig:bishop}. \postscriptfig{bishop}{Library bitmap for a bishop} \section{Implementation} \subsection{Declarations: Using Prolog Modules} \index{prolog,modules}\index{module (prolog)} The chess interface contains a large number of local predicates. To avoid the necessity to use long names for these local predicates it is implemented as a Prolog module. See \cite{SWI-Prolog:manual} and/or \cite{SICStus:manual} for details on Prolog modules. The module `library(pce)' defines the basic interface predicates and is typically imported into any module using PCE. Additional library predicates are best declared using the directive require/1, see \cite{SICStus:manual}. Note that SWI-Prolog does not need either of these declarations. It will automatically import the basic PCE predicates from the module `user' when they are encountered. Library predicates are normally loaded by SWI-Prolog's autoloader. PCE/SWI-Prolog processes the use_module/1 declaration similar to SICStus Prolog and ignores the require/1 directive. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- module(pcechess, [ chess/0, chess/1 % +Program ]). :- use_module(library(pce)). :- require([ between/3 , call/2 , atomic_list_concat/2 , term_variables/2 ]). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \subsection{Squares and Pieces} \label{sec:squareimage} As described above bitmaps are used to represent squares with optional pieces on them. The pixels of a bitmap are represented by an image object. An image object may be used in many bitmaps simultaneously. The predicate square_image/4 maps a piece, its colour and the colour of the square into an image object for this combination. Because the image is reusable and many copies are needed it maintains a table of already computed images using the dynamic predicate computed_image/4. The computed images are $32 \times 32$ sub-images of the image loaded from file and is created using the method `image <-clip: area'. The resulting image is stored in the Prolog database. The method `object ->lock_object' informs PCE that Prolog has a permanent reference to this object and protects it from PCE garbage collection (see appendix~\ref{sec:memory}). \index{attribute,to object} Next, it is useful to know {\em what} is represented on the image object. This is achieved by associating two {\em attribute} objects to the image: `piece' and `colour'. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- dynamic computed_image/4. % square_image(+PieceName, +PieceColour, +SquareColour, -Image) square_image(Piece, PieceColour, SquareColour, Image) :- computed_image(Piece, PieceColour, SquareColour, Image), !. square_image(Piece, PieceColour, SquareColour, Image) :- image_name(Piece, _, ImageName), new(TotalImage, image(ImageName)), sub_area(PieceColour, SquareColour, Area), !, get(TotalImage, clip, Area, Image), send(Image, lock_object, @on), send(Image, attribute, attribute(piece, Piece)), send(Image, attribute, attribute(colour, PieceColour)), asserta(computed_image(Piece, PieceColour, SquareColour, Image)). % image_name(?PieceName, ?ChessProgram Id, ?ImageName) image_name(empty, _, 'chesssquare.bm'). image_name(pawn, p, 'pawn.bm'). image_name(rook, r, 'rook.bm'). image_name(knight, n, 'knight.bm'). image_name(bishop, b, 'bishop.bm'). image_name(king, k, 'king.bm'). image_name(queen, q, 'queen.bm'). % sub_area(+PieceColour, +SquareColour, -AreaTerm) sub_area(white, white, area(32, 0, 32, 32)). sub_area(white, black, area(0, 0, 32, 32)). sub_area(black, white, area(32, 32, 32, 32)). sub_area(black, black, area(0, 32, 32, 32)). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Below is a small test to demonstrate the usage of this part of the program. 1 ?- test_square(rook, black, white, Image), get(Image, colour, Colour), get(Image, piece, Piece). Colour = black, Piece = rook, Image = @773336/image test_square(Piece, Colour, SquareColour, Image) :- (object(@p) -> true ; send(new(@p, picture), open)), square_image(Piece, Colour, SquareColour, Image), send(@p, display, bitmap(Image)). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /******************************** * THE BOARD * ********************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - In this section we will create the chess-board. The chess-board is an instance of class \class{device}, which represents a collection of graphical objects. Each of the objects is a bitmap of $32\times32$ pixels. Each of the bitmap objects will be extended with various attributes: # square_colour Represents the colour of the square. This attribute is attached to the bitmap rather than to the image because it will not change during the life-time of the board. # Get methods for <-piece and <-colour These methods allow us to ask the piece and colour currently represented by this bitmap. The information itself is stored on the <-image of the bitmap (see above). A \class{get_method} object consists of a selector, the type of the return value, a vector describing the types of the requested arguments and the implementation. The latter must be an instance of class \class{function}. In section~\ref{sec:starting} we introduced the \class{message} object as a template for a send-operation. The get-operation has a similar object: an \idx{obtainer}. The class name for an obtainer is `?'\index{? (obtainer)}. The PCE/Prolog interface defines `?' as an infix operator, which allows us to write \line{Receiver?Selector} for obtainers that do not require arguments. When the get_method is invoked, it will process the arguments and evaluate the obtainer object, returning the result of the get-operation. \index{method,arguments to} The implementation of a method may refer to the object it is attached to using the function @receiver. The arguments to the method may be referred to as @arg1, @arg2, ... As the resulting method object has no direct references to the object it is attached to it may be associated with multiple objects. # Name = location in algebraic chess notation Graphical objects have a name. This name may be used to refer to them (see section~\ref{sec:devicemember}). The default name of a graphical is its class name. In our case it is more natural to give each bitmap the name of its location in the chess notation: a1, a2, ... a8, b1, ... b8, ... h8. The directive pce_global/2 declares a globally available object. See section~\ref{sec:global}. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- pce_global(@square_colour, new(get_method(colour, name, new(vector), @receiver?image?colour))). :- pce_global(@square_piece, new(get_method(piece, name, new(vector), @receiver?image?piece))). make_chess_board(Board) :- new(Board, device), ( between(0, 7, X), between(0, 7, Y), GX is X * 32, GY is (7-Y) * 32, square_colour(X, Y, Colour), square_image(empty, _, Colour, Image), send(Board, display, new(Bitmap, bitmap(Image)), point(GX, GY)), send(Bitmap, attribute, attribute(square_colour, Colour)), send(Bitmap, get_method, @square_piece), send(Bitmap, get_method, @square_colour), xy_where(X, Y, Where), send(Bitmap, name, Where), fail ; true ). % square_colour(+X, +Y, -Colour) square_colour(X, Y, Colour) :- (X+Y) mod 2 =:= 0, !, Colour = black. square_colour(_, _, white). % xy_where(?X, ?Y, ?Where). xy_where(X, Y, Where) :- var(Where), !, CX is X + 0'a, CY is Y + 0'1, name(Where, [CX, CY]). xy_where(X, Y, Where) :- name(Where, [CX, CY]), X is CX - 0'a, Y is CY - 0'1. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \index{function objects}\index{garbage collection} The creation of the empty board may now be tested using the query below. Note that class \class{*} denotes an (integer) multiplication. It is a subclass of the PCE class \class{function}. The term `size(8*32,~8*32))' is evaluated by send/[2-12]. First both `8*32' are translated into two instances of class \class{*}. Next a size instance is created. The initialisation arguments of a size do not accept functions. Therefore both functions are evaluated and the size object is initialised from the result. Finally the incremental garbage collector of PCE destroys both * objects. See appendix~\ref{sec:memory}. 1 ?- make_chess_board(@cb), new(W, window('Chess Board', size(8*32, 8*32))), send(W, display, @cb), send(W, open). We may now issue various requests to the board: 2 ?- get(@cb, member, e2, Bitmap), get(Bitmap, square_colour, SquareColour), get(Bitmap, piece, Piece). SquareColour = black, Bitmap = @802111/bitmap, Piece = empty The predicates put_piece/4 and move/3 below complete the primitive layer. put_piece/4 allows us to put pieces of any colour at any square. First it exploits the <-member method of class \class{device} to find the bitmap for the specified location. Then it calls square_image/4 to compute the (new) image and associates the image with the bitmap. Try: 3 ?- put_piece(@cb, bishop, white, b1). The predicate move/3 moves a piece. First it determines the piece and colour of the `from' location. Then it puts this piece on the `to' location and clears the `from' location to empty. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ % put_piece(+Board, +Piece, +Colour, +Where) put_piece(Board, Piece, Colour, Where) :- get(Board, member, Where, Bitmap), get(Bitmap, square_colour, SquareColour), square_image(Piece, Colour, SquareColour, Image), send(Bitmap, image, Image). % move(+Board, +From, +To). move(Board, From, To) :- get(Board, member, From, FromBitmap), get(FromBitmap, piece, Piece), get(FromBitmap, colour, Colour), put_piece(Board, Piece, Colour, To), put_piece(Board, empty, _, From). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The predicate below resets the board to the initial position. Test it using: 3 ?- initial_position(@cb). - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ initial_position(Board) :- between(0, 7, X), between(0, 7, Y), initial_piece(X, Y, Piece, Colour), xy_where(X, Y, Where), put_piece(Board, Piece, Colour, Where), fail. initial_position(_). initial_piece(0, 0, rook, white) :- !. initial_piece(1, 0, knight, white) :- !. initial_piece(2, 0, bishop, white) :- !. initial_piece(3, 0, queen, white) :- !. initial_piece(4, 0, king, white) :- !. initial_piece(5, 0, bishop, white) :- !. initial_piece(6, 0, knight, white) :- !. initial_piece(7, 0, rook, white) :- !. initial_piece(_, 1, pawn, white) :- !. initial_piece(X, Y, Piece, black) :- WY is 7 - Y, initial_piece(X, WY, Piece, white), !. initial_piece(_, _, empty, black). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This definition completes the basic board routines. We can paint the initial position and move pieces around by calling Prolog predicates. In the next section we will discuss how we can detect and interpret an attempt to move a piece by the user. \subsection{Moving Pieces with the mouse} \label{sec:movepiece} \index{event,processing}\index{moving graphicals}\index{graphical,moving} In this section we will make it possible to move a piece. Graphical objects are made sensitive to mouse operations by adding a {\em recogniser} object to them. PCE predefines various recogniser objects handling both simple events such as a depressed key and complex operations such as connecting two graphical objects. In our case the standard move-gestures won't work as they move arbitrary graphicals to arbitrary positions. We want to move pieces rather than squares. Also, we only want to actually move the piece if it is a valid move. Below, we will define a gesture object that checks whether a square with a piece is moved and calls the predicate user_move(Board,~Piece,~From,~To) instead of moving the piece itself. The starting point will be the predefined \class{move_outline_gesture}. Class \class{gesture} traps user-events (mouse-button actions, mouse movement, modifier keys and normal keys). It is designed to detect and handle the most typical mouse gesture: button-down, optional move events followed by a button-up. A gesture sends various messages to itself, allowing refined versions of it to modify the start, dragging and termination of the gesture. In this particular case we will redefine ->verify and ->terminate. There are two ways to redefine these methods. The first is to create a subclass and define a method on this subclass. This approach is taken in PceDraw \cite{PCE:draw}. The second is to attach an object-level method object. Attaching get_method objects is described at the start of this chapter. Attaching send_methods is similar (except a send_method has no return-type argument). We will make these methods call a Prolog predicate. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ :- pce_global(@move_piece_gesture, make_move_piece_gesture). make_move_piece_gesture(G) :- new(G, move_outline_gesture(left)), send(G, send_method, send_method(verify, vector(event), message(@prolog, verify, @receiver, @arg1))), send(G, send_method, send_method(terminate, vector(event), message(@prolog, terminate, @receiver, @arg1))). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The method ->verify is invoked to verify all conditions to start the gesture are met. If it fails, the gesture will refuse the event. The move_outline_gesture moves any graphical object. In our case only bitmaps with pieces may be moved. We will first test whether the receiver of the event (the bitmap) is not an empty square. If this test is passed we will invoke the original message defined at the class-level to complete the test. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ verify(Gesture, Event) :- get(Event, receiver, SquareBitmap), \+ get(SquareBitmap, piece, empty), send(Gesture, send_class, verify, Event). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The ->terminate method is invoked on the button-up. The normal behaviour is to move the object to the current location of the outline. Our modified definition will determine the location of the outline in the chess coordinate system. First it determines the original bitmap, the board, the outline and the current area object associated with the outline. It removes the outline from the board. Next it will find all bitmaps on the board overlapping with the outlines' area. In the next step it will sort the overlapping bitmaps according to the size of the common sub area with the outline. The method `chain ->sort' accepts a function as argument. This function will be called to compare pairs of elements of the chain. In this case the function is <-compare on the <-measure% \footnote{The method `area <-measure' returns $abs(w) \times abs(h)$ of the area object. Actually class `area' should have been named `region' or `rectangle' and <-measure should have been named `area'.} of both <-intersection areas. Sorting is done `smallest first' and the <-tail of the chain thus holds the bitmap with the largest intersection. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ terminate(Gesture, Event) :- get(Event, receiver, FromBitmap), get(FromBitmap, device, Board), % the board get(Gesture, outline, Box), get(Box, area, Area), % current area send(Box, device, @nil), % Undisplay the outline get(Board?graphicals, find_all, message(@arg1?area, overlap, Area), OverlappingSquares), send(OverlappingSquares, sort, ?((?(@arg1?area, intersection, Area)) ? measure, compare, (?(@arg2?area, intersection, Area)) ? measure)), get(OverlappingSquares, tail, Best), get(Best, name, ToLocation), get(FromBitmap, name, FromLocation), get(FromBitmap, piece, Piece), user_move(Board, Piece, FromLocation, ToLocation). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - With this definition the generic piece-move-gesture has been completed. If the user moves a piece the predicate user_move/4 is called. \index{for_all} The predicate attach_recognisers/1 attaches a recogniser to each of the squares on the board. The method `chain ->for_all' takes a code object as argument and invokes this code object on all members of the chain. Most of PCE's classes that manage a set of objects provide the methods ->for_all, ->for_some, <-find and <-find_all. Without these methods the individual members had to be transferred to the host language and the iteration had to take place there. Using ->for_all, etc.\ is much easier to read and write and much faster. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ attach_recognisers(Board) :- send(Board?graphicals, for_all, message(@arg1, recogniser, @move_piece_gesture)). /******************************** * CHESS PROCESS INTERFACE * ********************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - In this section we will connect our interface to the Unix program gnuchessc. To interface with an external Unix process, PCE defines the class \class{process}. A process object allows us to start and control a \idx{Unix process}. The method `process ->format' sends (formatted) text to the process' standard input. Output from the process is cut into records (default a line). For each complete record the `process <->input_message' attribute of the process in invoked. The predicate attach_chess_program/1 creates a process for gnuchessc and attaches it as an attribute to the chess-board. The message `process ->open' starts the Unix process. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ attach_chess_program(Board, Program) :- send(Board, attribute, attribute(process, new(P, process(Program)))), send(P, use_tty, @off), % use pipes to communicate send(P, input_message, message(@prolog, chess_utterance, Board, @arg1)), send(P, open). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The predicate user_move/4 is called from the move-gesture attached to the squares. It translates the notation used in the interface to the notation expected by the chess-program and invokes `process ->format'. This call sends the data to the process and returns without waiting for a response. The chess program will echo the move if it was legal and `Illegal Move' otherwise. As the process may have died or could not be started in the first place, pce_catch_error/2 is used to send the message. If the error {\tt not_open} is raised by `process ->format', the call will fail silently. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ user_move(Board, Piece, FromLocation, ToLocation) :- chess_move_name(Piece, FromLocation, ToLocation, Move), format('Sending ~w~n', Move), get(Board, process, ChessProcess), ( pce_catch_error(not_open, send(ChessProcess, format, '%s\n', Move)) -> true ; send(Board, report, error, 'No chess process') ). chess_move_name(_Piece, F, T, Move) :- chess_coordinate(F, CF), chess_coordinate(T, CT), atomic_list_concat([CF, CT], Move). chess_coordinate(Where, C) :- xy_where(X, Y, Where), CY is Y + 1, chess_x(X, CX), atom_concat(CX, CY, C). chess_x(0, a). chess_x(1, b). chess_x(2, c). chess_x(3, d). chess_x(4, e). chess_x(5, f). chess_x(6, g). chess_x(7, h). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The predicate chess_utterance/2 is called from the <-input_message of the process object. The process object will invoke <-input_message each time it has received an input record, which is by default a line. The output of the chess process may be parsed in several manners. In this case we decided to use regular expressions for this purpose. The predicate utterance/2 defines regular expressions and goals to be called if the expression matches on the input line. The arguments of the goal are filled from the register values (indicated with \verb$\($...\verb$\)$). The predicate make_regex/2 is similar to square_image/4 in maintaining a database of reusable objects. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ chess_utterance(Board, Utterance) :- send(@pce, format, 'Got %s\n', Utterance), utterance(Pattern, Goal), make_regex(Pattern, RegEx), send(RegEx, search, Utterance), !, term_variables(Goal, List), bind_args(List, 1, RegEx, Utterance), call(Goal, Board). :- dynamic regex/2. make_regex(Pattern, RegEx) :- regex(Pattern, RegEx), !. make_regex(Pattern, RegEx) :- new(RegEx, regex(Pattern)), send(RegEx, lock_object, @on), assert(regex(Pattern, RegEx)). bind_args([], _, _, _). bind_args([H|T], N, R, U) :- get(R, register_value, U, N, Str), get(Str, value, H), NN is N + 1, bind_args(T, NN, R, U). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \index{regex}\index{regular expressions}\index{GNU-Emacs} The chess program utterances. PCE uses GNU-Emacs regular expressions. The main difference to standard Unix (egrep) regular expressions is \verb$\w$, which represent any word-constituent character. The last clause catches anything unrecognised. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ utterance('[0-9]+\\. \\(\\w\\)\\([1-8]\\)\\(\\w\\)\\([1-8]\\)', player_move(_FX, _FY, _TX, _TY)). utterance('[0-9]+\\. \\(\\w\\)\\([1-8]\\)x\\(\\w\\)\\([1-8]\\)', player_move(_FX, _FY, _TX, _TY)). utterance('[0-9]+\\. \\(o-o\\(-o\\)?\\)', player_oo(_OO)). utterance('[0-9]+\\. \\.\\.\\. \\(\\w+\\)\\([1-8]\\)x\\w/\\(\\w+\\)\\([1-8]\\)', opponent_move(_FX, _FY, _TX, _TY)). utterance('[0-9]+\\. \\.\\.\\. \\(o-o\\(-o\\)?\\)', opponent_oo(_OO)). utterance('[0-9]+\\. \\.\\.\\. \\(\\w\\)\\([1-8]\\)\\(\\w\\)\\([1-8]\\)', opponent_move(_FX, _FY, _TX, _TY)). utterance('Chess', banner). utterance('\\(.*\\)', warn(_Message)). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Below are the various predicates called by chess_utterance/2 through the utterance/2 matcher and argument binder. They basically translate chess' coordinate system into ours and call move/3 to make the moves on the board. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ warn(Utterance, _Board) :- send(@display, inform, Utterance). player_move(CFX, CFY, CTX, CTY, Board) :- chess_x(FX, CFX), chess_x(TX, CTX), get(CFY - 1, value, FY), get(CTY - 1, value, TY), xy_where(FX, FY, F), xy_where(TX, TY, T), move(Board, F, T). player_oo('o-o', Board) :- move(Board, e1, g1), move(Board, h1, f1). player_oo('o-o-o', Board) :- move(Board, e1, c1), move(Board, a1, d1). opponent_move(CFX, CFY, CTX, CTY, Board) :- player_move(CFX, CFY, CTX, CTY, Board). opponent_oo('o-o', Board) :- move(Board, e8, g8), move(Board, h8, f8). opponent_oo('o-o-o', Board) :- move(Board, e8, c8), move(Board, a8, d8). banner(_). /******************************** * TOPLEVEL * ********************************/ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Below is the toplevel that combines all the declarations above to create the final chess-tool. Most graphical applications use an instance of \class{picture} for the drawing window. The difference between a window and a picture is that the latter has scrollbars. We do not want to scroll the chess-board. \index{window,destruction} The message <->done_message is called on a request from the X-window manager to delete the window (normally from the menu associated with the title-bar of the window). The default message destroys the window. In our case we also have to kill the chess program. The message to kill the process is placed in an if with epty then- and else-branch to ensure success of the message. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ chess :- chess(gnuchessc). % chesstool compatible GNU-chess chess(Program) :- new(Window, window('ChessBoard 0.1', size(8*32, 8*32))), make_chess_board(Board), send(Window, display, Board), attach_recognisers(Board), initial_position(Board), send(Window, open), attach_chess_program(Board, Program), send(Window, done_message, and(if(message(Board?process, kill)), message(Window, destroy))). /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \section{Summary} This chapter contains an annotated version of a complete and realistic PCE/Prolog program. In introduces various aspects of programming PCE/Prolog: \begin{itemize} \tick{Using Prolog Modules} PCE is transparent to the Prolog module system. When using SICStus Prolog modules we need to import the module `library(pce)' and declare the PCE library predicates (see appendix~\ref{sec:interface}) using the require/1 directive. \tick{Compound Graphicals} The PCE class \class{device} is a graphical that can display other graphicals. Graphical devices have their own coordinate system. \tick{Bitmaps and Images} A bitmap allows an image (two-dimensional array of pixels) to be used as a graphical object. \tick{Object-level attributes} Information about an object may be stored with the object itself using object level attributes (see `object ->attribute'). See also chapter~\ref{sec:pceprolog}. \tick{Object-level methods} Methods may be attached both to objects and classes. The implementation of a method is a PCE code object (function for get_methods). The var object @receiver evaluates to the receiver of a message. @arg1 ... refer to the arguments by position. \tick{Functions: obtainers (`?'), var objects and arithmetic} Functions are objects that may be evaluated. `?' (obtainer) evaluates by executing a get-operation. Functions are evaluated if they appear in a code object and the code object is executed or if they are passed to an argument whose type-specification does not accept a function (except for code objects only very few methods accept functions without evaluating them). \tick{Declaring reusable objects using :- pce_global/2} Global objects that are reused are normally declared using pce_global/2. See section~\ref{sec:global} for details. \tick{Recognisers: handling mouse events} Graphical objects are made sensitive to the mouse and keyboard using {\em recogniser} objects. PCE defines both primitive recognisers and recognisers to handle event-sequences\index{event,sequences}. A \class{gesture} handles the sequence mouse-button-down, dragging, mouse-button-up. Various standard gestures are defined. New gestures are created using subclasses or by attaching object-level methods to an instance of one of the standard classes. \tick{Interface to Unix Processes} Class \class{process} offers an interface to Unix processes. The managed process runs asynchronously. Data may be send to the process' standard input. Output data from the process is received interleaved with events. It is collected in records and handled by the `processes <->input_message'. \end{itemize} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */