File Coverage

blib/lib/Games/Chess/Referee.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             #
2             # Games::Chess::Referee
3             #
4             # A Perl Module for validating chess moves.
5             #
6             # Copyright (C) 1999-2006 Gregor N. Purdy. All rights reserved.
7             # This program is free software; you can redistribute it and/or
8             # modify it under the same terms as Perl iteself.
9             #
10              
11             package Games::Chess::Referee;
12              
13 1     1   615 use base 'Exporter';
  1         2  
  1         108  
14 1     1   4 use strict;
  1         2  
  1         33  
15 1     1   4 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  1         5  
  1         107  
16             $VERSION = '0.003';
17             @EXPORT = qw(&ply &move &new_game &show_board);
18             @EXPORT_OK = @EXPORT;
19              
20 1     1   1373 use Games::Chess qw(:constants :functions);
  0            
  0            
21             use Carp;
22              
23             my $board;
24              
25             my $occupy = '-';
26             my $capture = 'x';
27              
28              
29             #
30             # new_game()
31             #
32              
33             sub new_game ()
34             {
35             $board = Games::Chess::Position->new();
36              
37             # print STDERR "Game: ", $board->to_FEN(), "\n";
38             }
39              
40              
41             #
42             # show_board()
43             #
44              
45             sub show_board ()
46             { print $board->to_text(), "\n"; }
47              
48              
49             #
50             # ply()
51             #
52              
53             sub ply ($)
54             {
55             my ($ply) = @_;
56             my ($piece, $ff, $fr, $act, $tf, $tr, $note);
57             my $notation = undef;
58            
59             #
60             # Translate castling notations:
61             #
62              
63             if ($ply eq '0-0') {
64             if ($board->player_to_move() eq &WHITE) { $ply = 'E1G1'; }
65             else { $ply = 'E8G8'; }
66             }
67             elsif ($ply eq '0-0-0') {
68             if ($board->player_to_move() eq &WHITE) { $ply = 'E1C1'; }
69             else { $ply = 'E8C8'; }
70             }
71              
72             #
73             # Parse the ply notation:
74             #
75              
76             if ($ply =~ m/^([prnbqkPRNBQK]|)([a-hA-H])([1-8])(x|-|)([a-hA-H])([1-8])(.*)$/) {
77             ($piece, $ff, $fr, $act, $tf, $tr, $note) = ($ply =~ m/^([prnbqkPRNBQK]|)([a-hA-H])([1-8])(x|-|)([a-hA-H])([1-8])(.*)$/);
78             $piece = uc($piece);
79             $ff = uc($ff);
80             $tf = uc($tf);
81             }
82             else {
83             carp "Unsupported notation: `$ply'!";
84             return 0;
85             }
86              
87             my $from = lc("$ff$fr");
88             my $to = lc("$tf$tr");
89              
90             my @from = algebraic_to_xy($from);
91             my @to = algebraic_to_xy($to);
92              
93             my $from_piece = $board->at(@from);
94             my $to_piece = $board->at(@to);
95              
96             my $from_kind = uc($from_piece->code());
97             my $to_kind = uc($to_piece->code());
98              
99             #
100             # Check for attempts to castle:
101             #
102             # 1. Ensure castling is permitted (neither King nor Rook has moved prior).
103             # 2. Ensure the way is clear between the King and Rook.
104             # 3. Move the Rook to its final location (the King's move will be
105             # effected by the later code.
106             #
107             # TODO: Ensure that the King is not in check, and that none of the
108             # relevant squares are under attack.
109             #
110              
111             my $castling;
112              
113             if ($ff eq 'E' and $fr == 1 and $tf eq 'G' and $tr == 1) {
114             # print STDERR "ATTEMPT BY WHITE TO CASTLE SHORT...\n";
115              
116             if (!$board->can_castle(&WHITE, &KING)) {
117             carp "Castling short by white not permitted!";
118             # print STDERR "Game = ", $board->to_FEN(), "\n";
119             return 0;
120             } elsif (!$board->at(5, 0)->code() eq ' ') {
121             carp "Way not clear (space `f1') for castling short!";
122             return 0;
123             } elsif (!$board->at(6, 0)->code() eq ' ') {
124             carp "Way not clear (space `g1') for castling short!";
125             return 0;
126             } else {
127             $board->at(5, 0, $board->at(7, 0));
128             $board->at(7, 0, Games::Chess::Piece->new);
129              
130             $notation = '0-0';
131             $castling = 'SHORT';
132             }
133             }
134             elsif ($ff eq 'E' and $fr == 8 and $tf eq 'G' and $tr == 8) {
135             # print STDERR "ATTEMPT BY BLACK TO CASTLE SHORT...\n";
136              
137             if (!$board->can_castle(&BLACK, &KING)) {
138             carp "Castling short by black not permitted!";
139             return 0;
140             } elsif (!$board->at(5, 7)->code() eq ' ') {
141             carp "Way not clear (space `f8') for castling short!";
142             return 0;
143             } elsif (!$board->at(6, 7)->code() eq ' ') {
144             carp "Way not clear (space `g8') for castling short!";
145             return 0;
146             } else {
147             $board->at(5, 7, $board->at(7, 7));
148             $board->at(7, 7, Games::Chess::Piece->new);
149              
150             $notation = '0-0';
151             $castling = 'SHORT';
152             }
153             }
154             elsif ($ff eq 'E' and $fr == 1 and $tf eq 'C' and $tr == 1) {
155             # print STDERR "ATTEMPT BY WHITE TO CASTLE LONG...\n";
156              
157             if (!$board->can_castle(&WHITE, &QUEEN)) {
158             carp "Castling long by white not permitted!";
159             return 0;
160             } elsif (!$board->at(1, 0)->code() eq ' ') {
161             carp "Way not clear (space `b1') for castling long!";
162             return 0;
163             } elsif (!$board->at(2, 0)->code() eq ' ') {
164             carp "Way not clear (space `c1') for castling long!";
165             return 0;
166             } elsif (!$board->at(3, 0)->code() eq ' ') {
167             carp "Way not clear (space `d1') for castling long!";
168             return 0;
169             } else {
170             $board->at(3, 0, $board->at(0, 0));
171             $board->at(0, 0, Games::Chess::Piece->new);
172              
173             $notation = '0-0-0';
174             $castling = 'LONG';
175             }
176             }
177             elsif ($ff eq 'E' and $fr == 8 and $tf eq 'C' and $tr == 8) {
178             # print STDERR "ATTEMPT BY BLACK TO CASTLE LONG...\n";
179              
180             if (!$board->can_castle(&BLACK, &QUEEN)) {
181             carp "Castling long by black not permitted!";
182             return 0;
183             } elsif (!$board->at(1, 0)->code() eq ' ') {
184             carp "Way not clear (space `b8') for castling long!";
185             return 0;
186             } elsif (!$board->at(2, 0)->code() eq ' ') {
187             carp "Way not clear (space `c8') for castling long!";
188             return 0;
189             } elsif (!$board->at(3, 0)->code() eq ' ') {
190             carp "Way not clear (space `d8') for castling long!";
191             return 0;
192             } else {
193             $board->at(3, 7, $board->at(0, 7));
194             $board->at(0, 7, Games::Chess::Piece->new);
195              
196             $notation = '0-0-0';
197             $castling = 'LONG';
198             }
199             }
200             else {
201             # Not castling.
202             }
203              
204             #
205             # Record new castling permissions:
206             #
207             # TODO: Write tests that exercise this code! The warnings weren't printing
208             # when they should.
209             #
210              
211             if ($from eq 'A1') {
212             $board->can_castle(&WHITE, &QUEEN, 0);
213             # print STDERR "Warning: Castling long by white no longer permitted.\n";
214             } elsif ($from eq 'A8') {
215             $board->can_castle(&BLACK, &QUEEN, 0);
216             # print STDERR "Warning: Castling long by black no longer permitted.\n";
217             } elsif ($from eq 'H1') {
218             $board->can_castle(&WHITE, &KING, 0);
219             # print STDERR "Warning: Castling short by white no longer permitted.\n";
220             } elsif ($from eq 'H8') {
221             $board->can_castle(&BLACK, &KING, 0);
222             # print STDERR "Warning: Castling short by black no longer permitted.\n";
223             } elsif ($from eq 'E1') {
224             $board->can_castle(&WHITE, &QUEEN, 0);
225             $board->can_castle(&WHITE, &KING, 0);
226             # print STDERR "Warning: Castling short by white no longer permitted.\n";
227             # print STDERR "Warning: Castling long by white no longer permitted.\n";
228             } elsif ($from eq 'E8') {
229             $board->can_castle(&BLACK, &QUEEN, 0);
230             $board->can_castle(&BLACK, &KING, 0);
231             # print STDERR "Warning: Castling short by black no longer permitted.\n";
232             # print STDERR "Warning: Castling long by black no longer permitted.\n";
233             } else {
234             # No change to castling status.
235             }
236              
237             #
238             # Detect the piece:
239             #
240              
241             if (!$piece) { $piece = $from_kind; };
242              
243             if ($piece ne $from_kind) {
244             # print STDERR "\n";
245             # print STDERR "Piece: $piece\n";
246             # print STDERR "Ply: $ply\n";
247             # print STDERR "From Space: $from\n";
248             # print STDERR "From Kind: $from_kind\n";
249             carp "Piece (`$piece') from ply (`$ply') does not match board piece (`$from_kind') at space `$from'!";
250             return 0;
251             }
252              
253             #
254             # Detect the action:
255             #
256             # TODO: Make sure we only permit capture of other color's pieces.
257             #
258              
259             my $board_act;
260              
261             if ($to_kind eq ' ') { $board_act = $occupy; }
262             else { $board_act = $capture; }
263              
264             if (!$act) { $act = $board_act; }
265              
266             if ($act ne $board_act) {
267             carp "Action (`$act') from ply (`$ply') does not match board (space `$to' contains `$to_kind')!";
268             return 0;
269             }
270              
271             #
272             # Effect the move:
273             #
274             # TODO: Deal with en passant target.
275             # TODO: Detect check and checkmate for notes (and validate against those
276             # passed in, if any).
277             # TODO: Detect en passant capture for notes.
278             # TODO: Detect en passant capture for notes.
279             # TODO: Detect illegal moves based on move pattern of piece, or intervening
280             # pieces, etc.
281             # TODO: Detect forced for notes.
282             #
283              
284             $board->at(@to, $from_piece);
285             $board->at(@from, Games::Chess::Piece->new);
286              
287             #
288             # Print the move:
289             #
290              
291             if (!defined $notation) {
292             if ($from_kind eq 'P') { $piece = ' '; }
293             $notation = $piece . lc($from) . $act . lc($to) . $note;
294             $notation = $notation . (' ' x (8 - length($notation)));
295             }
296              
297             if ($board->player_to_move() == &WHITE) {
298             print $board->move_number(), ". $notation ";
299              
300             $board->player_to_move(&BLACK);
301             $board->halfmove_clock($board->halfmove_clock() + 1);
302             }
303             else {
304             print "$notation\n";
305              
306             $board->player_to_move(&WHITE);
307             $board->halfmove_clock($board->halfmove_clock() + 1);
308             $board->move_number($board->move_number() + 1);
309             }
310              
311             return 1;
312             }
313              
314              
315             #
316             # move()
317             #
318              
319             sub move ($$)
320             {
321             if (!&ply($_[0])) {
322             carp "First ply (`$_[0]') of move failed.";
323             return 0;
324             }
325              
326             if (!&ply($_[1])) {
327             carp "Second ply (`$_[1]') of move failed.";
328             return 0;
329             }
330              
331             return 1;
332             }
333              
334              
335             #
336             # Return success:
337             #
338              
339             1;
340              
341             #
342             # End of file.
343             #