File Coverage

blib/lib/Chess/ChessKit/Board.pm
Criterion Covered Total %
statement 9 305 2.9
branch 0 106 0.0
condition 0 30 0.0
subroutine 3 23 13.0
pod n/a
total 12 464 2.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl -w
2             ####-----------------------------------
3             ### File : Board.pm
4             ### Author : Ch.Minc
5             ### Purpose : Package for ChessKit
6             ### Version : 1.2 2007/02/22
7             ### copyright GNU license
8             ####-----------------------------------
9              
10             package Board ;
11              
12             #package Chess::ChessKit::Board;
13              
14 1     1   1475 use warnings;
  1         2  
  1         30  
15 1     1   6 use strict;
  1         1  
  1         31  
16 1     1   5 use Chess::ChessKit::Move ;
  1         2  
  1         4543  
17              
18              
19             =head1 NAME
20              
21             ChessKit::Board
22              
23             =head1 VERSION
24              
25             Version 1.2
26              
27             =cut
28              
29             =head1 SYNOPSIS
30              
31             see ChessKit
32              
33             =head1 FUNCTIONS
34              
35             =head2 sub bestmove
36              
37             Get the Best move as TMN means
38             see ChessKit
39              
40             =head2 sub boardcopy
41              
42             see ChessKit
43              
44             =head2 sub can_castling
45              
46             see ChessKit
47              
48             =head2 sub cantake
49              
50             see ChessKit
51              
52             =head2 sub castling
53              
54             see ChessKit
55              
56             =head2 sub chessmovcnt
57              
58             see ChessKit
59              
60             =head2 sub chessmovcntint
61              
62             see ChessKit
63              
64             =head2 sub chessview
65              
66             see ChessKit
67              
68             =head2 sub deletepiece
69              
70             see ChessKit
71              
72             =head2 sub getpiece
73              
74             see ChessKit
75              
76             =head2 sub has_moved
77              
78             see ChessKit
79              
80             =head2 sub is_shaked
81              
82             see ChessKit
83              
84             =head2 sub new
85              
86             see ChessKit
87              
88             =head2 sub print
89              
90             see ChessKit
91              
92             =head2 sub put
93              
94             see ChessKit
95              
96             =head2 sub startgame
97              
98             see ChessKit
99              
100             =head2 sub valid
101              
102             see ChessKit
103              
104             =head2 sub vldmov
105              
106             see ChessKit
107              
108             =head2 sub genfen
109              
110             see ChessKit
111              
112             =head2 getfen
113              
114             see ChessKit
115              
116             =cut
117              
118             =head1 AUTHOR
119              
120             Charles Minc, C<< >>
121              
122             =head1 BUGS
123              
124             Please report any bugs or feature requests to
125             C, or through the web interface at
126             L.
127             I will be notified, and then you'll automatically be notified of progress on
128             your bug as I make changes.
129              
130             =head1 ACKNOWLEDGEMENTS
131              
132             =head1 COPYRIGHT & LICENSE
133              
134             Copyright 2005 Charles Minc, all rights reserved.
135              
136             This program is free software; you can redistribute it and/or modify it
137             under the same terms as Perl itself.
138              
139             =cut
140              
141             # NOTE: piece means any chess piece or pawn
142             # NOTE Blacks are designed by lower case letters and Whites by upper ones
143              
144             # Board means here the chessboard with the pieces on it.
145             # The object here hereafter is nothing more that the position
146             # of pieces on their sqares.
147              
148             # pieces are in upper case for Whites and lower case for Blacks
149             # as usually written in FEN
150              
151             our $VERSION = '1.1' ;
152              
153              
154              
155             sub new {
156 0     0     my ($class,@args)=@_ ;
157 0           my $self={} ;
158 0           return bless ($self,$class) ;
159             }
160              
161             sub startgame {
162             # initial starting position of the game
163             #usage $self->startgame(%position)
164              
165 0     0     my ($self,%arg)=@_ ;
166 0           %{$self}=() ;
  0            
167 0           my %poset=(
168             'K'=>["e1"],
169             'k'=>["e8"],
170             'Q'=>["d1"],
171             'q'=>["d8"],
172             'R'=>[qw(a1 h1)],
173             'r'=>[qw(a8 h8)],
174             'B'=>[qw(c1 f1)],
175             'b'=>[qw(c8 f8)],
176             'N'=>[qw(b1 g1)],
177             'n'=>[qw(b8 g8)],
178             'P'=>[qw(a2 b2 c2 d2 e2 f2 g2 h2)],
179             'p'=>[qw(a7 b7 c7 d7 e7 f7 g7 h7)]
180             ) ;
181 0 0         %poset=%arg ?%arg :%poset ;
182 0           foreach my $p (qw(K Q R N B P k q r n b p)) {
183 0           foreach (@{$poset{$p}}) {
  0            
184 0           $self->put($p,$_) ;
185             }
186             }
187              
188             }
189              
190             sub put {
191             # place a piece on the board
192 0     0     my($self,$piece,$location)=@_ ;
193 0           $$self{$location}=$piece ;
194             }
195              
196             sub print {
197             # print the position of the pieces
198 0     0     my($self)=@_ ;
199 0           foreach (keys %{$self}) {
  0            
200 0           print " loc= $_ piece=$$self{$_} \n" ;
201             }
202             }
203              
204             sub boardcopy {
205 0     0     my ($self,$bddest)=@_ ;
206 0           %{$bddest}=() ;
  0            
207 0           %{$bddest}=%{$self} ;
  0            
  0            
208             }
209             sub getpiece{
210 0     0     my($self,$loc)=@_ ;
211 0           return ($$self{$loc} ) ;
212             }
213              
214             sub deletepiece{
215 0     0     my($self,$loc)=@_ ;
216 0           delete $$self{$loc} ;
217             }
218              
219             sub chessview {
220 0     0     my $self=shift ;
221 0           print "\n" ;
222 0           my @alpha=qw(a b c d e f g h) ;
223 0           my @digit=qw(8 7 6 5 4 3 2 1) ;
224 0           foreach my $num (@digit) {
225 0           foreach my $let (@alpha) {
226 0           my $p=$$self{$let.$num} ;
227 0 0         print defined($p) ? $p : "." , " " ;
228             }
229 0           print "\n" ;
230             }
231 0           print "\n" ;
232             }
233              
234             sub cantake{
235              
236             #usage $self->cantake(who=>'Q',where=>'h8')
237              
238 0     0     my ($self,%arg)=@_ ;
239 0           my $piece=$arg{who} ;
240 0           my $where=$arg{where} ;
241              
242 0           my %color=(qw /B 0 K 0 N 0 P 0 Q 0 R 0 U 0 b 1 k 1 n 1 p 1 q 1 r 1 u 1/ ) ;
243             # U,u is used for pawn taking
244 0           my $wanted=$self->getpiece($where) ;
245 0 0 0       if (defined($wanted) && ($color{$piece} != $color{$wanted} )) {
246 0           return $wanted ;
247             } else {
248 0           return "no" ;
249             }
250             }
251              
252             sub valid{
253             # check valid moves
254 0     0     my ($self,%arg)=@_ ;
255 0           my $caval=$arg{mov} ;
256 0           my $set=$arg{valid} ;
257 0           my $piece=$arg{piece} ;
258              
259 0           my $i=0 ;
260 0           for ( my $m=0 ; $m<=7 ; $m++ ) {
261 0 0         if (defined( @{$caval->[$m]})) {
  0            
262 0           foreach (0..$#{$caval->[$m]}) {
  0            
263 0           my $c= $caval->[$m][$_];
264 0           my $p=$self->getpiece($c) ;
265             # pawn catching
266 0 0         if ($piece =~/U/i) {
267 0 0         $set->[$i++]=$c if ( $self->cantake(who=>$piece,where=>$c) ne "no" ) ;
268 0           last ;
269             }
270 0 0         if ( !defined($p) ) {
    0          
    0          
271 0           $set->[$i++]=$c ;
272             } elsif ($piece =~/P/i) {
273 0           last ;
274             } elsif ( $self->cantake(who=>$piece,where=>$c) eq "no") {
275 0           last ;
276             } else {
277 0           $set->[$i++]=$c ; last ;
  0            
278             }
279             }
280             }
281             }
282             }
283              
284              
285              
286             sub vldmov{
287              
288 0     0     my %default=(qw/recur yes/ ) ;
289 0           my ($self,%arg)=@_ ;
290 0           %arg=(%default,%arg) ;
291              
292 0           my $set=$arg{valid} ;
293 0           my $piece=$arg{piece} ;
294 0           my $num=$arg{row} ;
295 0           my $let=$arg{col} ;
296 0           my $control=$arg{recur} ;
297 0           my $caval=[[]] ;
298 0           $Move::gmv{$piece}(row=>$num-1, col =>ord($let)-ord('a'),mov=>$caval,color=>$piece) ;
299 0           $self->valid(valid=>$set,mov=>$caval,piece=>$piece) ;
300              
301             # Treat the Pawn move as a special piece called U when capture
302              
303 0 0         if ($piece =~ /P/i) {
304 0 0         my $upiece=($piece =~ /P/) ? 'U':'u' ;
305 0           $caval=[[]] ;
306 0           $Move::gmv{$upiece}(row=>$num-1, col =>ord($let)-ord('a'),mov=>$caval,color=>$upiece) ;
307 0           my $uset=[] ;
308 0           $self->valid(valid=>$uset,mov=>$caval,piece=>$upiece) ;
309 0           @{$set}=(@{$set},@{$uset}) ;
  0            
  0            
  0            
310             }
311 0 0         return if ($control eq 'no') ;
312              
313             # a valid move is without check
314             # if king is checked and no move without checks means mat
315             # if king is w/o and no move again " " pat but
316              
317              
318 0           my $uset=[] ;
319 0           my $chkbd=Board->new() ;
320 0 0         my $king= $piece =~ /[QKNBRPU]/ ? 'K':'k' ;
321 0           foreach my $to (0..$#{$set}) {
  0            
322 0           $self->boardcopy($chkbd) ;
323 0           $chkbd->deletepiece($let.$num) ;
324 0           $chkbd->put($piece,$set->[$to]) ;
325 0 0         if ($chkbd->is_shaked(king=>$king,out=>'n') ne 'yes') {
326 0           push(@{$uset},$set->[$to]) ;
  0            
327             }
328             }
329 0           @{$set}=(@{$uset}) ;
  0            
  0            
330             }
331              
332             sub chessmovcnt{
333             # scan the board and count the valid moves
334             # not removed for sake of backward compatibility
335             # replaced by chessmovcnt
336 0     0     my ($self,%arg)=@_ ;
337              
338 0           my %sum=('w'=>0,'b'=>0) ;
339              
340 0           foreach my $num (reverse 1..8) {
341 0           foreach my $let ('a'..'h') {
342 0           my $p=$$self{$let.$num} ;
343 0 0         if (defined($p)) {
344 0           my $set=[] ;
345 0           $self->vldmov(row=>$num, col=>$let,piece=>$p,valid=>$set) ;
346 0 0         $p =~ /[BKNPQR]/ ? ( $sum{w} +=@{$set}) :($sum{b} +=@{$set} ) ;
  0            
  0            
347             }
348             }
349             }
350 0           return ($sum{w},$sum{b}) ;
351             }
352              
353             sub chessmovcntint{
354             # scan the board and count the valid moves
355 0     0     my ($self,%arg)=@_ ;
356              
357 0           my %sum=('w'=>[],'b'=>[]) ;
358              
359 0           foreach my $num (reverse 1..8) {
360 0           foreach my $let ('a'..'h') {
361 0           my $p=$$self{$let.$num} ;
362 0 0         if (defined($p)) {
363 0           my $set=[] ;
364 0           $self->vldmov(row=>$num, col=>$let,piece=>$p,valid=>$set) ;
365 0 0         $p =~ /[BKNPQR]/ ? (push @{$sum{w}}, @{$set}) :(push @{$sum{b}},@{$set} ) ;
  0            
  0            
  0            
  0            
366             }
367             }
368             }
369 0           return %sum ;
370             }
371              
372             sub bestmove{
373 0     0     my ($self)=@_ ;
374             # parameters w or b
375 0           my %bm ;
376 0           foreach my $loc (sort keys %{$self} ) {
  0            
377 0           my $piece=$self->{$loc} ;
378 0           my ($let,$num)=split('',$loc) ;
379 0           my $set=[] ;
380 0           $self->vldmov(row=>$num, col=>$let,piece=>$piece,valid=>$set) ;
381 0           my $size=@{$set} ;
  0            
382 0           foreach my $try (@{$set}) {
  0            
383 0           my $capture=$self->{$try} ;
384 0           $self->deletepiece($loc) ;
385 0           $self->put($piece,$try) ;
386 0           my $bset=[] ;
387 0           my($sumw,$sumb)=$self->chessmovcnt ;
388 0           $bm{$piece}{$loc}{$try}{'w'}=$sumw;
389 0           $bm{$piece}{$loc}{$try}{'b'}=$sumb;
390 0 0         defined($capture) ? $self->put($capture,$try):$self->deletepiece($try) ;
391 0           $self->put($piece,$loc) ;
392             }
393             }
394 0           my %max ;
395             my %min ;
396             #return $bm ;
397 0           foreach my $color ('w','b') {
398 0           my $max=0 ;
399 0           my $min=1000 ;
400 0           foreach my $piece (keys %bm) {
401 0           foreach my $where (keys %{$bm{$piece}} ) {
  0            
402 0           foreach my $to (keys %{$bm{$piece}{$where}} ) {
  0            
403              
404 0           my $value= $bm{$piece}{$where}{$to}{$color} ;
405 0 0         $max=$max > $value ? $max : $value ;
406 0 0         $min=$min < $value ? $min : $value ;
407             }
408             }
409             }
410 0           $max{$color}=$max ;
411 0           $min{$color}=$min ;
412             }
413             #print the min & max
414 0           foreach my $color ('w','b') {
415 0           foreach my $piece (keys %bm) {
416 0           foreach my $where (keys %{$bm{$piece}} ) {
  0            
417 0           foreach my $to (keys %{$bm{$piece}{$where}} ) {
  0            
418              
419 0           my $value= $bm{$piece}{$where}{$to}{$color} ;
420 0 0 0       if ( ($value == $min{$color}) || ($value == $max{$color}) ) {
421 0           print " $piece from $where to $to give for $color :",$value,"\n" ;
422             }
423             }
424             }
425             }
426             }
427             }
428              
429             sub is_shaked {
430 0     0     my ($self,%arg)=@_ ;
431 0           my $king=$arg{king} ;
432 0           my $out=$arg{out} ;
433              
434 0           my $diag="" ;
435 0           my $locking ;
436              
437             # where is the king
438 0           foreach ( keys %{$self} ) {
  0            
439 0 0         if ($king eq $self->{$_}) {
440 0           $locking=$_ ; last ;
  0            
441             }
442             }
443 0 0         my $color=$king =~/K/ ? "qknbrpu" : "QKNBRPU" ;
444 0           foreach my $loc ( keys %{$self} ) {
  0            
445 0           my $piece=$self->{$loc} ;
446 0 0         if ($piece =~ /[$color]/) {
447 0           my ($let,$num)=split('',$loc) ;
448 0           my $set=[] ;
449 0           $self->vldmov(row=>$num, col=>$let,piece=>$piece,valid=>$set,recur=>'no') ;
450 0           my $size=@{$set} ;
  0            
451             # for debug
452             # foreach (0..$#{$set}){
453             # print " $king is shaked by $piece at $loc \n" if ($locking eq $set->[$_]) ;}
454             # conditional outputting by flag $out='y'
455 0           foreach (0..$#{$set}) {
  0            
456 0 0 0       if ( defined( $set->[$_]) && ($locking eq $set->[$_]) ) {
457 0           $diag='yes' ;
458 0 0         print " $king is shaked by $piece at $loc \n" if ($out eq 'y') ;
459             # note: "shake" is a joke written here in place of "check" after a while writing this, ouf!
460             }
461             }
462             }
463             }
464 0           return $diag ;
465             }
466              
467              
468             sub has_moved {
469              
470             # set the status of Rook and king to know if they are
471             # always at there original place
472             # must be called after every moves
473              
474 0     0     my ($self,%arg)=@_ ;
475 0           my $status=$arg{status} ;
476 0           my %default= (qw/ini no/) ;
477 0           %arg=(%default,%arg) ;
478              
479 0 0         %{$status}=( qw/KK no kk no KR no QR no kr no qr no/) if ($arg{ini} eq 'y') ;
  0            
480 0           my %hereis=qw(KK e1 kk e8 KR h1 QR a1 kr h8 qr a8) ;
481              
482 0           foreach my $piece (keys %hereis ) {
483 0           my $p=substr($piece,1,1) ;
484 0 0         my $q=defined($self->{$hereis{$piece }}) ? $self->{$hereis{$piece }}:"rien" ;
485 0 0 0       $status->{$piece}=
486             ( $p eq $q ) && ($status->{$piece} eq 'no') ? 'no' : 'yes' ;
487              
488             }
489              
490             }
491              
492             sub castling{
493 0     0     my ($self,%arg)=@_ ;
494 0           my $couleur=$arg{couleur} ;
495 0           my $side=$arg{side} ;
496 0           my $status=$arg{status} ;
497 0 0         my $out=$arg{out}?$arg{out}:'no' ;
498              
499              
500 0 0         my ($la,$Kk,$Rr,$babor)=$couleur eq 'White' ? (1,'K','R', $side) : (8,'k','r',lc($side) ) ;
501 0 0         my @rank = $side eq 'K' ? (qw/e h g f/): (qw/e a c d/) ;
502              
503 0 0         $self->can_castling(roq=>$babor . $Kk,status=>$status,out=>$out) ne 'no' or die " castling move is wrong" ;
504              
505 0           $self->deletepiece($rank[0].$la ) ;
506 0           $self->deletepiece($rank[1].$la ) ;
507 0           $self->put($Kk,$rank[2].$la) ;
508 0           $self->put($Rr,$rank[3].$la) ;
509              
510 0           return ;
511              
512             }
513              
514              
515             sub can_castling {
516              
517             # return $diag and if $diag = 'no' means: castling could not be done
518 0     0     my ($self,%arg)=@_ ;
519 0           my $roq=$arg{roq} ;
520 0           my $status=$arg{status} ;
521 0 0         my $out=$arg{out}?$arg{out}:'no' ;
522 0           my $diag="yes" ;
523              
524             # setting conditions
525             # Are the king and rook at their initial position (special) with no moves ($status)
526             # status (ep,wkr,wqr,,K,bkr,bqr,k)
527             # KK for white King(K) King side castling (K) and so on.
528              
529 0           my %cds=(
530             KK=>[qw(KK KR V1 V2)],
531             QK=>[qw(KK QR V3 V4)],
532             kk=>[qw(kk kr v1 v2)],
533             qk=>[qw(kk qr v1 v2)]) ;
534              
535 0           my %special=
536             qw(KK e1 V1 f1 V2 g1 V3 d1 V4 c1 kk e8 v1 f8 v2 g8 v3 d8 v4 c8 KR h1 QR a1 kr h8 qr a8) ;
537              
538 0   0       my $resu=( $status->{ $cds{$roq}->[0]} eq 'no') &&
539             ( $status->{ $cds{$roq}->[1]} eq 'no') &&
540             (!defined $self->{ $special{$cds{$roq}->[2]} } ) &&
541             (!defined $self->{ $special{$cds{$roq}->[3]} } ) ;
542              
543 0 0         return $diag='no' unless ($resu) ;
544              
545             # then is it in "checks" because of the virtual king move during castling ?
546              
547 0 0         my $color=$cds{$roq}->[0] =~/KK/ ? "qknbrpu" : "QKNBRPU" ;
548 0           my @locking=( $special{$cds{$roq}->[0]}, $special{$cds{$roq}->[1]}, $special{$cds{$roq}->[2]} ) ;
549              
550 0           foreach my $loc ( keys %{$self} ) {
  0            
551 0           my $piece=$self->{$loc} ;
552 0 0         if ($piece =~ /[$color]/) {
553 0           my ($let,$num)=split('',$loc) ;
554 0           my $set=[] ;
555 0           $self->vldmov(row=>$num, col=>$let,piece=>$piece,valid=>$set,recur=>'no') ;
556 0           my $size=@{$set} ;
  0            
557             # foreach (0..$#{$set}){
558             # print " $king is shaked by $piece at $loc \n" if ($locking eq $set->[$_]) ;}
559 0           foreach (0..$#{$set}) {
  0            
560 0           foreach my $l (0..2) {
561 0 0 0       if ( defined( $set->[$_]) && ($locking[$l] eq $set->[$_]) ) {
562 0           $diag='no' ;
563 0 0         print " roq is shaked by $piece at $loc \n" if ($out eq 'yes') ;
564             }
565             }
566             }
567             }
568             }
569 0           return $diag ;
570             }
571              
572             sub getfen {
573              
574             # translate from the fen string $fen
575             # to a hash reference $posini used
576             # as initialisation for the game
577              
578             # work with pointers $posini & $status
579              
580 0     0     my $self=shift ;
581 0           my $fen=shift ;
582 0           my $posini=shift ;
583 0           my $status=shift ;
584              
585 0           my @fields=split ' ', $fen ;
586             # reinitialisation of the chessboard means reset of :
587 0           %{$posini}=() ;
  0            
588             # extended empty squares with '-'
589 0           $fields[0] =~ s/(\d)/'-'x $1/ge ;
  0            
590              
591             # delete useless '/'
592 0           $fields[0] =~ s!/!!g ;
593 0           my @pospck=split '',$fields[0] ;
594              
595             # suppose le string bien forme
596 0 0         $#pospck == 63 or die ("**** fen mal formé ($#pospck) **** \n") ;
597              
598 0           COL:for (reverse 1..8){
599 0           ROW:for my $rank ('a'..'h'){
600 0           my $piece=shift @pospck ;
601 0 0         push( @{${$posini}{$piece}} ,$rank . $_) unless ($piece eq '-') ;
  0            
  0            
602             }
603             }
604             # except status others values are not in use .
605             # shift @fields ;
606             # my $colmove=shift @fields ;
607 0           my $fenstat=$fields[2] ;
608             # my $ep=shift @fields ;
609             # my $ply=shift @fields ;
610             # my $nmove=shift @fields ;
611              
612 0 0         ($fenstat =~ /K/) ?( $status->{KR}='no'): ($status->{KR}='yes') ;
613 0 0         ($fenstat =~ /Q/) ? ($status->{QR}='no') : ($status->{QR}='yes') ;
614 0 0         ($fenstat =~ /k/) ? ($status->{kr}='no'): ($status->{kr}='yes') ;
615 0 0         ($fenstat =~ /q/) ? ($status->{qr}='no'): ($status->{qr}='yes') ;
616              
617             #don't know which ones, of the king or the rook, has moved
618             # assume it's only the rook for sake of simplicity
619 0           $status->{KK}='no' ;
620 0           $status->{kk}='no' ;
621             }
622              
623             sub genfen {
624             # generate the fen from the current board
625             # parameters $objet,$move color('w',-,'b'),$roque status
626             # $ep en passant , $ply ply count, $move number
627 0     0     my $self=shift ;
628 0           my $colmove=shift ;
629 0           my $status=shift ;
630 0           my $ep=shift ;
631 0           my $ply=shift ;
632 0           my $nmove=shift ;
633            
634 0           my $fen ;
635 0           foreach my $num (reverse 1..8) {
636 0           foreach my $let ('a'..'h') {
637 0           my $p=$$self{$let.$num} ;
638 0 0         $fen .= defined($p) ? $p : "." ;
639             }
640 0 0         $fen .='/' unless $num ==1;
641             }
642 0           $fen=~ s/(\.+)/length($1)/eg ;
  0            
643              
644 0 0 0       my $fenstat .=($status->{KK} eq 'yes' || $status->{KR} eq 'yes') ? "" : "K" ;
645 0 0 0       $fenstat .=($status->{KK} eq 'yes' || $status->{QR} eq 'yes') ? "": "Q" ;
646 0 0 0       $fenstat .=($status->{kk} eq 'yes' || $status->{kr} eq 'yes') ? "": "k" ;
647 0 0 0       $fenstat .=($status->{kk} eq 'yes' || $status->{qr} eq 'yes') ? "" : "q" ;
648 0 0         $fenstat ="-" unless length($fenstat) ;
649 0           print " genfen $fenstat \n" ;
650 0           return ($fen . ' ' . $colmove . ' ' . $fenstat . ' '. $ep .' ' . $ply .' ' . $nmove );
651             }
652              
653             1;