File Coverage

blib/lib/AI/Evolve/Befunge/Physics/ttt.pm
Criterion Covered Total %
statement 71 71 100.0
branch 36 36 100.0
condition 9 9 100.0
subroutine 14 14 100.0
pod 7 7 100.0
total 137 137 100.0


line stmt bran cond sub pod time code
1             package AI::Evolve::Befunge::Physics::ttt;
2 1     1   6 use strict;
  1         2  
  1         47  
3 1     1   6 use warnings;
  1         2  
  1         40  
4 1     1   7 use Carp;
  1         4  
  1         88  
5 1     1   7 use Language::Befunge::Vector;
  1         3  
  1         15  
6              
7 1     1   34 use AI::Evolve::Befunge::Util;
  1         2  
  1         10  
8 1     1   225 use AI::Evolve::Befunge::Physics qw(register_physics);
  1         3  
  1         8  
9 1     1   109 use base 'AI::Evolve::Befunge::Physics';
  1         1  
  1         1224  
10              
11              
12             =head1 NAME
13             AI::Evolve::Befunge::Physics::ttt - a tic tac toe game
14              
15              
16             =head1 SYNOPSIS
17              
18             my $ttt = AI::Evolve::Befunge::Physics->new('ttt');
19              
20              
21             =head1 DESCRIPTION
22              
23             This is an implementation of the "ttt" game ruleset. It is
24             implemented as a plugin for the AI::Evolve::Befunge Physics system;
25             essentially an AI creature exists in a "tic tac toe" universe,
26             and plays by its rules.
27              
28              
29             =head1 CONSTRUCTOR
30              
31             Use AI::Evolve::Befunge::Physics->new() to get a ttt object;
32             there is no constructor in this module for you to call directly.
33              
34              
35             =head1 METHODS
36              
37             =head2 setup_board
38              
39             $ttt->setup_board($board);
40              
41             Initialize the board to its default state. For tic tac toe, this
42             looks like:
43              
44             ...
45             ...
46             ...
47              
48             =cut
49              
50             sub setup_board {
51 1     1 1 4 my ($self, $board) = @_;
52 1         30 $board->clear();
53             }
54              
55              
56             =head2 valid_move
57              
58             my $valid = $ttt->valid_move($board, $player, $pos);
59              
60             Returns 1 if the move is valid, 0 otherwise. In tic tac toe, all
61             places on the board are valid unless the spot is already taken with
62             an existing piece.
63              
64             =cut
65              
66             sub valid_move {
67 27     27 1 147 my ($self, $board, $player, $v) = @_;
68 27 100       105 confess "board is not a ref!" unless ref $board;
69 26 100 100     156 confess "Usage: valid_move(self,board,player,vector)" unless defined($player) && defined($v);
70 24 100       69 confess("need a vector argument") unless ref($v) eq 'Language::Befunge::Vector';
71 23         91 my ($x, $y) = ($v->get_component(0), $v->get_component(1));
72 23 100 100     108 return 0 if $x < 0 || $y < 0;
73 21 100 100     120 return 0 if $x > 2 || $y > 2;
74 19         65 for my $dim (2..$v->get_dims()-1) {
75 2 100       13 return 0 if $v->get_component($dim);
76             }
77 18 100       56 return 0 if $board->fetch_value($v);
78 2         9 return 1;
79             }
80              
81              
82             =head2 won
83              
84             my $winner = $ttt->won($board);
85              
86             If the game has been won, returns the player who won. Returns 0
87             otherwise.
88              
89             =cut
90              
91             my @possible_wins = (
92             # row wins
93             [v(0,0), v(0,1), v(0,2)],
94             [v(1,0), v(1,1), v(1,2)],
95             [v(2,0), v(2,1), v(2,2)],
96             # col wins
97             [v(0,0), v(1,0), v(2,0)],
98             [v(0,1), v(1,1), v(2,1)],
99             [v(0,2), v(1,2), v(2,2)],
100             # diagonal wins
101             [v(0,0), v(1,1), v(2,2)],
102             [v(2,0), v(1,1), v(0,2)],
103             );
104              
105             sub won {
106 23     23 1 39 my $self = shift;
107 23         29 my $board = shift;
108 23         46 foreach my $player (1..2) {
109 40         41 my $score;
110 40         55 foreach my $row (@possible_wins) {
111 314         451 $score = 0;
112 314         525 foreach my $i (0..2) {
113 942         1336 my $v = $$row[$i];
114 942 100       2475 $score++ if $board->fetch_value($v) == $player;
115             }
116 314 100       847 return $player if $score == 3;
117             }
118             }
119 17         56 return 0;
120             }
121              
122              
123             =head2 over
124              
125             my $over = $ttt->over($board);
126              
127             Returns 1 if no more moves are valid from either player, and returns
128             0 otherwise.
129              
130             =cut
131              
132             sub over {
133 8     8 1 12 my $self = shift;
134 8         12 my $board = shift;
135 8 100       19 return 1 if $self->won($board);
136 7         17 foreach my $y (0..2) {
137 18         32 foreach my $x (0..2) {
138 51 100       152 return 0 unless $board->fetch_value(v($x, $y));
139             }
140             }
141 4         35 return 1;
142             }
143              
144              
145             =head2 score
146              
147             my $score = $ttt->score($board, $player, $number_of_moves);
148              
149             Return a relative score of how the player performed in a game.
150             Higher numbers are better.
151              
152             =cut
153              
154             sub score {
155 5     5 1 14 my ($self, $board, $player, $moves) = @_;
156 5 100       15 if($self->won($board) == $player) {
157             # won! the quicker, the better.
158 1         8 return 20 - $moves;
159             }
160 4 100       13 if($self->won($board)) {
161             # lost; prolonging defeat scores better
162 1         8 return $moves;
163             }
164             # draw
165 3 100       12 return 10 if $self->over($board);
166             # game isn't over yet
167 1         4 my $mine = 0;
168 1         4 foreach my $y (0..2) {
169 3         5 foreach my $x (0..2) {
170 9 100       28 if($board->fetch_value(v($x, $y)) == $player) {
171 4         15 $mine++;
172             }
173             }
174             }
175 1         8 return $mine;
176             }
177              
178              
179             =head2 can_pass
180              
181             my $can_pass = $ttt->can_pass($board, $player);
182              
183             Always returns 0; tic tac toe rules do not allow passes under any
184             circumstances.
185              
186             =cut
187              
188             sub can_pass {
189 1     1 1 6 return 0;
190             }
191              
192              
193             =head2 make_move
194              
195             $next_player = $ttt->make_move($board, $player, $pos)
196             if $ttt->valid_move($board, $player, $pos);
197              
198             Makes the given move, updates the board with the newly placed piece.
199              
200             =cut
201              
202             sub make_move {
203 5     5 1 49 my ($self, $board, $player, $v) = @_;
204 5 100       39 confess("need a vector argument") unless ref($v) eq 'Language::Befunge::Vector';
205 4         20 $board->set_value($v, $player);
206 4 100       12 return 0 if $self->won($board);
207 3 100       14 return 0 if $self->over($board);
208 1         9 return 3 - $player; # 2 => 1, 1 => 2
209             }
210              
211             register_physics(
212             name => "ttt",
213             token => ord('T'),
214             decorate => 0,
215             board_size => v(3, 3),
216             commands => {
217             M => \&AI::Evolve::Befunge::Physics::op_make_board_move,
218             T => \&AI::Evolve::Befunge::Physics::op_query_tokens
219             },
220             );
221              
222             1;