File Coverage

blib/lib/AI/Evolve/Befunge/Board.pm
Criterion Covered Total %
statement 87 87 100.0
branch 32 32 100.0
condition 15 15 100.0
subroutine 14 14 100.0
pod 8 8 100.0
total 156 156 100.0


line stmt bran cond sub pod time code
1             package AI::Evolve::Befunge::Board;
2 5     5   36809 use strict;
  5         10  
  5         227  
3 5     5   28 use warnings;
  5         10  
  5         178  
4 5     5   27 use Carp;
  5         11  
  5         740  
5              
6 5     5   30 use AI::Evolve::Befunge::Util qw(code_print);
  5         7  
  5         35  
7 5     5   3760 use AI::Evolve::Befunge::Critter;
  5         14  
  5         46  
8              
9 5     5   437 use base 'Class::Accessor::Fast';
  5         9  
  5         6030  
10             __PACKAGE__->mk_accessors( qw{ size dimensions } );
11              
12             =head1 NAME
13              
14             AI::Evolve::Befunge::Board - board game object
15              
16              
17             =head1 SYNOPSIS
18              
19             my $board = AI::Evolve::Befunge::Board->new(Size => $vector);
20             $board->set_value($vector, $value);
21             $board->clear();
22              
23              
24             =head1 DESCRIPTION
25              
26             This module tracks board-game state for AI::Evolve::Befunge. It is only used
27             for board-game-style physics, like tic tac toe, othello, go, chess, etc.
28             Non-boardgame applications do not use a Board object.
29              
30              
31             =head1 CONSTRUCTOR
32              
33             =head2 new
34              
35             AI::Evolve::Befunge::Board->new(Size => $vector);
36             AI::Evolve::Befunge::Board->new(Size => $number, Dimensions => $number);
37              
38             Creates a new Board object. You need to specify the board-size somehow, either
39             by providing a Language::Befunge::Vector object, or by specifying the size of
40             the side of a hypercube and the number of dimensions it exists in (2 is the
41             most likely number of dimensions). If the Size argument is numeric, the
42             Dimensions argument is required, and a size vector will be generated
43             internally.
44              
45             =cut
46              
47             # FIXME: fully vectorize this, and make this module dimensionality-independent
48             # (maybe just use another laheyspace for the storage object)
49              
50             sub new {
51 14     14 1 10745 my $self = bless({}, shift);
52 14         50 my %args = @_;
53 14         32 my $usage = "\nUsage: ...Board->new(Dimensions => 4, Size => 8) or ...Board->new(Size => \$vector)";
54 14 100       74 croak($usage) unless exists $args{Size};
55 13 100       49 if(ref($args{Size})) {
56 8 100       22 if(exists($args{Dimensions})) {
57 2 100       22 croak "Dimensions argument doesn't match the number of dimensions in the vector"
58             unless $args{Size}->get_dims() == $args{Dimensions};
59             } else {
60 6         29 $args{Dimensions} = $args{Size}->get_dims();
61             }
62             } else {
63 5 100       22 if(exists($args{Dimensions})) {
64 8         47 $args{Size} = Language::Befunge::Vector->new(
65 4         15 map { $args{Size} } (1..$args{Dimensions}));
66             } else {
67 1         12 croak "No Dimensions argument given, and Size isn't a vector";
68             }
69             }
70              
71 11         92 $$self{size} = $args{Size};
72 11         29 $$self{dimensions} = $args{Dimensions};
73              
74 11         236 foreach my $dim (0..$$self{size}->get_dims()-1) {
75 23 100       113 croak("Size[$dim] must be at least 1!")
76             unless $$self{size}->get_component($dim) >= 1;
77 22 100       72 if($dim >= 2) {
78 2 100       21 croak("This module isn't smart enough to handle more than 2 dimensions yet")
79             unless $$self{size}->get_component($dim) == 1;
80             }
81             }
82 9         42 $$self{sizex} = $$self{size}->get_component(0);
83 9         35 $$self{sizey} = $$self{size}->get_component(1);
84              
85 9         37 $$self{b} = [];
86 9         46 for(0..$$self{sizey}-1) {
87 38         48 push(@{$$self{b}}, [ map { 0 } (1..$$self{sizex})]);
  38         104  
  190         370  
88             }
89 9         59 return $self;
90             }
91              
92              
93             =head1 METHODS
94              
95             =head2 clear
96              
97             $board->clear();
98              
99             Clear the board - set all spaces to 0.
100              
101             =cut
102              
103             sub clear {
104 8     8 1 33 my $self = shift;
105 8         22 $$self{b} = [];
106 8         44 for(0..$$self{sizey}-1) {
107 26         32 push(@{$$self{b}}, [ map { 0 } (0..$$self{sizex}-1)]);
  26         76  
  118         234  
108             }
109             }
110              
111              
112             =head2 as_string
113              
114             my $string = $board->as_string();
115              
116             Returns an ascii-art display of the current board state. The return value
117             looks like this (without indentation):
118              
119             .ox
120             .x.
121             oxo
122              
123             =cut
124              
125             sub as_string {
126 22     22 1 50 my $self = shift;
127 22         65 my @char = ('.', 'x', 'o');
128 22         77 my $code = join("\n", map { join('', map { $char[$_] } (@{$$self{b}[$_]}))} (0..$$self{sizey}-1));
  76         100  
  380         908  
  76         175  
129 22         243 return "$code\n";
130             }
131              
132              
133             =head2 as_binary_string
134              
135             my $binary = $board->as_binary_string();
136              
137             Returns an ascii-art display of the current board state. It looks the same as
138             ->as_string(), above, except that the values it uses are binary values 0, 1,
139             and 2, rather than plaintext descriptive tokens. This is suitable for passing
140             to Language::Befunge::LaheySpace::Generic's ->store() method.
141              
142             =cut
143              
144             sub as_binary_string {
145 1     1 1 3 my $self = shift;
146 25         57 my $code = join("\n",
147 1         5 map { join('', map { chr($_) } (@{$$self{b}[$_]}))} (0..$$self{sizey}-1));
  5         6  
  5         9  
148 1         10 return "$code\n";
149             }
150              
151              
152             =head2 output
153              
154             $board->output();
155              
156             Prints the return value of the ->as_string() method to the console, decorated
157             with row and column indexes. The output looks like this (without indentation):
158              
159             012
160             0 .ox
161             1 .x.
162             2 oxo
163              
164             =cut
165              
166             sub output {
167 2     2 1 341635 my $self = shift;
168 2         10 code_print($self->as_string(),$$self{sizex},$$self{sizey});
169             }
170              
171              
172             =head2 fetch_value
173              
174             $board->fetch_value($vector);
175              
176             Returns the value of the board space specified by the vector argument. This
177             is typically a numeric value; 0 means the space is unoccupied, otherwise the
178             value is typically the player number who owns the space, or the piece-type (for
179             games which have multiple types of pieces), or whatever.
180              
181             =cut
182              
183             sub fetch_value {
184 25869     25869 1 47872 my ($self, $v) = @_;
185 25869 100       76401 croak("need a vector argument") unless ref($v) eq 'Language::Befunge::Vector';
186 25868         77985 my ($x, $y, @overflow) = $v->get_all_components();
187 25868 100 100     152773 croak "fetch_value: x value '$x' out of range!" if $x < 0 or $x >= $$self{sizex};
188 25866 100 100     128022 croak "fetch_value: y value '$y' out of range!" if $y < 0 or $y >= $$self{sizey};
189 25864         129417 return $$self{b}[$y][$x];
190             }
191              
192              
193             =head2 set_value
194              
195             $board->fetch_value($vector, $value);
196              
197             Set the value of the board space specified by the vector argument.
198              
199             =cut
200              
201             sub set_value {
202 62     62 1 211 my ($self, $v, $val) = @_;
203 62 100       178 croak("need a vector argument") unless ref($v) eq 'Language::Befunge::Vector';
204 61         196 my ($x, $y, @overflow) = $v->get_all_components();
205 61 100 100     365 croak "set_value: x value '$x' out of range!" if $x < 0 or $x >= $$self{sizex};
206 59 100 100     317 croak "set_value: y value '$y' out of range!" if $y < 0 or $y >= $$self{sizey};
207 57 100       137 croak "undef value!" unless defined $val;
208 56 100 100     280 croak "data '$val' out of range!" unless $val >= 0 && $val < 3;
209 54         312 $$self{b}[$y][$x] = $val;
210             }
211              
212              
213             =head2 copy
214              
215             my $new_board = $board->copy();
216              
217             Create a new copy of the board.
218              
219             =cut
220              
221             sub copy {
222 1     1 1 3 my ($self) = @_;
223 1         7 my $new = ref($self)->new(Size => $$self{size});
224 1         8 my $min = Language::Befunge::Vector->new_zeroes($$self{dimensions});
225 1         6 my $max = Language::Befunge::Vector->new(map { $_ - 1 } ($$self{size}->get_all_components));
  2         9  
226 1         10 for(my $this = $min->copy; defined $this; $this = $this->rasterize($min,$max)) {
227 25         55 $new->set_value($this,$self->fetch_value($this));
228             }
229 1         4 return $new;
230             }
231              
232             1;