File Coverage

blib/lib/Chess/960.pm
Criterion Covered Total %
statement 30 38 78.9
branch 1 2 50.0
condition 4 12 33.3
subroutine 4 6 66.6
pod 3 3 100.0
total 42 61 68.8


line stmt bran cond sub pod time code
1 1     1   72211 use 5.12.0;
  1         16  
2 1     1   6 use warnings;
  1         2  
  1         41  
3             package Chess::960 0.003;
4             # ABSTRACT: a Chess960 starting position generator
5              
6 1     1   5 use Carp ();
  1         2  
  1         477  
7              
8             #pod =head1 OVERVIEW
9             #pod
10             #pod L is a chess variant invented
11             #pod by Bobby Fischer, designed to somewhat reduce the value of memorization to
12             #pod play, while retaining key properties of the game such as castling and one
13             #pod bishop per color.
14             #pod
15             #pod Chess::960 generates random starting positions for a Chess960 game.
16             #pod
17             #pod use Chess::960;
18             #pod
19             #pod my $fen = Chess::960->new->fen; # Forsyth-Edwards notation of position
20             #pod
21             #pod my $pos = Chess::960->new->generate_position; # simple data structure
22             #pod
23             #pod my $pos = Chess::960->new->generate_position(123); # get position by number
24             #pod
25             #pod =cut
26              
27             my @BRIGHT = qw(1 3 5 7);
28             my @DARK = qw(0 2 4 6);
29              
30             my @KNIGHTS = (
31             [ 0, 1 ],
32             [ 0, 2 ],
33             [ 0, 3 ],
34             [ 0, 4 ],
35             [ 1, 2 ],
36             [ 1, 3 ],
37             [ 1, 4 ],
38             [ 2, 3 ],
39             [ 2, 4 ],
40             [ 3, 4 ],
41             );
42              
43             #pod =method new
44             #pod
45             #pod The constructor for Chess::960 does not, at present, take any argument. In the
46             #pod future, it may take arguments to pick different mappings between positions
47             #pod and numbers.
48             #pod
49             #pod =cut
50              
51             sub new {
52 0     0 1 0 my ($class) = @_;
53 0         0 bless {} => $class;
54             }
55              
56             #pod =method generate_position
57             #pod
58             #pod my $pos = $c960->generate_position($num);
59             #pod
60             #pod This returns a starting description, described by a hash. If C<$num> is not
61             #pod provided, a random position will be returned. If a value for C<$num> that
62             #pod isn't an integer between 0 and 959 is provided, an exception will be raised.
63             #pod
64             #pod Position 518 in the default mapping is the traditional chess starting position.
65             #pod
66             #pod The returned hashref has two entries:
67             #pod
68             #pod number - the number of the generated position
69             #pod rank - an eight-element arrayref giving the pieces' positions
70             #pod elements are characters in [BQNRK]
71             #pod
72             #pod =cut
73              
74             sub generate_position {
75 960     960 1 273136 my ($self, $num) = @_;
76 960   33     2239 $num //= int rand 960;
77              
78 960 50 33     7996 Carp::confess("starting position number must be between 0 and 959")
      33        
      33        
79             unless defined $num && $num =~ /\A[0-9]{1,3}\z/ && $num >= 0 && $num <= 959;
80              
81 960         1712 my $b1 = $num % 4;
82 960         2125 my $b2 = int( $num / 4 ) % 4;
83              
84 960         1421 my $k = int( $num / 96 );
85 960         1340 my $q = ($num / 16) % 6;
86              
87 960         2398 my @rank = (undef) x 8;
88 960         1738 $rank[ $BRIGHT[ $b1 ] ] = 'B';
89 960         1334 $rank[ $DARK[ $b2 ] ] = 'B';
90              
91 960         1070 my @empty;
92              
93 960         2658 @empty = grep { ! $rank[$_] } keys @rank;
  7680         13473  
94 960         1735 $rank[ $empty[ $q ] ] = 'Q';
95              
96 960         1838 @empty = grep { ! $rank[$_] } keys @rank;
  7680         11839  
97 960         1571 @rank[ @empty[ @{ $KNIGHTS[$k] } ] ] = qw(N N);
  960         1894  
98              
99 960         1691 @empty = grep { ! $rank[$_] } keys @rank;
  7680         11906  
100 960         1864 @rank[ @empty ] = qw(R K R);
101              
102 960         3333 return { number => $num, rank => \@rank };
103             }
104              
105             #pod =method fen
106             #pod
107             #pod This method returns a
108             #pod L-format
109             #pod string describing the complete starting position of the board. For example:
110             #pod
111             #pod rnbbqkrn/pppppppp/8/8/8/8/PPPPPPPP/RNBBQKRN w KQkq - 0 1
112             #pod
113             #pod =cut
114              
115             sub fen {
116 0     0 1   my ($self, $num) = @_;
117              
118 0           my $pos = $self->generate_position($num);
119 0           my $rank = join q{}, @{ $pos->{rank} };
  0            
120 0           my $fen = sprintf "%s/%s/8/8/8/8/%s/%s w KQkq - 0 1",
121             lc $rank,
122             'p' x 8,
123             'P' x 8,
124             $rank;
125              
126 0           return $fen;
127             }
128              
129             1;
130              
131             __END__