File: chess.pl

package info (click to toggle)
swi-prolog 9.0.4%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 82,408 kB
  • sloc: ansic: 387,503; perl: 359,326; cpp: 6,613; lisp: 6,247; java: 5,540; sh: 3,147; javascript: 2,668; python: 1,900; ruby: 1,594; yacc: 845; makefile: 428; xml: 317; sed: 12; sql: 6
file content (796 lines) | stat: -rw-r--r-- 34,102 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
/*  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}
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */