File Coverage

blib/lib/Chess/960.pm
Criterion Covered Total %
statement 31 39 79.4
branch 1 2 50.0
condition 4 12 33.3
subroutine 4 6 66.6
pod 3 3 100.0
total 43 62 69.3


line stmt bran cond sub pod time code
1 1     1   28191 use 5.12.0;
  1         3  
  1         49  
2 1     1   6 use warnings;
  1         2  
  1         55  
3             package Chess::960;
4             # ABSTRACT: a Chess960 starting position generator
5             $Chess::960::VERSION = '0.002';
6 1     1   5 use Carp ();
  1         2  
  1         545  
7              
8             # =head1 OVERVIEW
9             #
10             # L is a chess variant invented
11             # by Bobby Fischer, designed to somewhat reduce the value of memorization to
12             # play, while retaining key properties of the game such as castling and one
13             # bishop per color.
14             #
15             # Chess::960 generates random starting positions for a Chess960 game.
16             #
17             # use Chess::960;
18             #
19             # my $fen = Chess::960->new->fen; # Forsyth-Edwards notation of position
20             #
21             # my $pos = Chess::960->new->generate_position; # simple data structure
22             #
23             # my $pos = Chess::960->new->generate_position(123); # get position by number
24             #
25             # =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             # =method new
44             #
45             # The constructor for Chess::960 does not, at present, take any argument. In the
46             # future, it may take arguments to pick different mappings between positions
47             # and numbers.
48             #
49             # =cut
50              
51             sub new {
52 0     0 1 0 my ($class) = @_;
53 0         0 bless {} => $class;
54             }
55              
56             # =method generate_position
57             #
58             # my $pos = $c960->generate_position($num);
59             #
60             # This returns a starting description, described by a hash. If C<$num> is not
61             # provided, a random position will be returned. If a value for C<$num> that
62             # isn't an integer between 0 and 959 is provided, an exception will be raised.
63             #
64             # Position 518 in the default mapping is the traditional chess starting position.
65             #
66             # The returned hashref has two entries:
67             #
68             # number - the number of the generated position
69             # rank - an eight-element arrayref giving the pieces' positions
70             # elements are characters in [BQNRK]
71             #
72             # =cut
73              
74             sub generate_position {
75 960     960 1 407027 my ($self, $num) = @_;
76 960   33     2282 $num //= int rand 960;
77              
78 960 50 33     10447 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         1409 my $b1 = $num % 4;
82 960         1861 my $b2 = int( $num / 4 ) % 4;
83              
84 960         1130 my $k = int( $num / 96 );
85 960         1247 my $q = ($num / 16) % 6;
86              
87 960         2614 my @rank = (undef) x 8;
88 960         1538 $rank[ $BRIGHT[ $b1 ] ] = 'B';
89 960         1218 $rank[ $DARK[ $b2 ] ] = 'B';
90              
91 960         1004 my @empty;
92              
93 960         2714 @empty = grep { ! $rank[$_] } keys @rank;
  7680         13956  
94 960         1615 $rank[ $empty[ $q ] ] = 'Q';
95              
96 960         2164 @empty = grep { ! $rank[$_] } keys @rank;
  7680         12326  
97 960         1713 @rank[ @empty[ @{ $KNIGHTS[$k] } ] ] = qw(N N);
  960         2340  
98              
99 960         1690 @empty = grep { ! $rank[$_] } keys @rank;
  7680         11459  
100 960         2187 @rank[ @empty ] = qw(R K R);
101              
102 960         4746 return { number => $num, rank => \@rank };
103             }
104              
105             # =method fen
106             #
107             # This method returns a
108             # L-format
109             # string describing the complete starting position of the board. For example:
110             #
111             # rnbbqkrn/pppppppp/8/8/8/8/PPPPPPPP/RNBBQKRN w KQkq - 0 1
112             #
113             # =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__