File Coverage

blib/lib/Games/SGF/Go/Rotator.pm
Criterion Covered Total %
statement 40 40 100.0
branch n/a
condition n/a
subroutine 8 8 100.0
pod 2 2 100.0
total 50 50 100.0


line stmt bran cond sub pod time code
1             package Games::SGF::Go::Rotator;
2              
3 3     3   24760 use strict;
  3         6  
  3         100  
4 3     3   17 use warnings;
  3         5  
  3         97  
5              
6 3     3   14 use base 'Games::SGF::Go';
  3         9  
  3         2789  
7 3     3   1475230 use vars qw($VERSION);
  3         9  
  3         1682  
8             $VERSION = '1.21';
9              
10             =head1 NAME
11              
12             Games::SGF::Go::Rotator - subclass of Games::SGF::Go that can rotate the board
13              
14             =head1 DESCRIPTION
15              
16             If you have an SGF file of a game that your opponent recorded, it will be
17             the wrong way up from your perspective, and so harder for you to remember
18             what's going on when you analyse the game later. This subclass of
19             Games::SGF::Go provides extra methods for rotating it.
20              
21             =head1 SYNOPSIS
22              
23             my $sgf = Games::SGF::Go::Rotator->new();
24             $sgf->readFile('mygame.sgf');
25             $sgf->rotate(); # rotate by 180 degrees, what you'd normally want
26             $sgf->rotate90(); # rotate 90 degrees clockwise.
27              
28             =head1 METHODS
29              
30             In addition to the methods documented below, all of Games::SGF::Go's
31             methods are, of course, also available. Neither of the new methods
32             take any arguments, and they return the rotated SGF as well as altering
33             it in-place, for convenience when chaining methods.
34              
35             =head2 rotate
36              
37             Rotate the SGF through 180 degrees.
38              
39             =cut
40              
41 2     2 1 2500992 sub rotate { shift()->rotate90()->rotate90(); }
42              
43             =head2 rotate90
44              
45             Rotate the SGF through 90 degrees clockwise.
46              
47             =cut
48              
49             sub rotate90 {
50 6     6 1 5824968 my $self = shift;
51 6         51 my $text = $self->writeText();
52              
53 6         82623 (my $size = $text) =~ s/.*SZ\[(\d+)\].*/$1/gs;
54              
55             # first rotate any [AA] apart from C[AA]
56 6         110 $text =~ s/([^C])\[([a-z]{2})]/"${1}["._rotate90(_splitcoord($2), $size).']'/eg;
  660         11966  
57             # now rotate any A{B,E,W}[AA:AA]
58             # note this is the only place where [AA:AA] is legal in Go
59 6         263 $text =~ s/A([BEW])\[([a-z]{2}):([a-z]{2})\]/
60 6         13 my @first = _splitcoord(_rotate90(_splitcoord($2), $size));
61 6         19 my @second = _splitcoord(_rotate90(_splitcoord($3), $size));
62 6         28 my @northings = sort { $a cmp $b } ($first[0], $second[0]);
  6         22  
63 6         14 my @eastings = sort { $a cmp $b } ($first[1], $second[1]);
  6         12  
64 6         41 "A${1}[$northings[0]$eastings[0]:$northings[1]$eastings[1]]"
65             /eg;
66              
67 6         42 $self->readText($text);
68              
69             # ick, diving into the guts - readText adds a new game, this
70             # deletes the first game
71 6         1681205 shift @{$self->{collection}};
  6         26  
72              
73 6         38 return $self;
74             }
75              
76             # algorithm:
77             # consider a square NxN board ...
78             # abcde Each 90 degree rotation moves W to X to Y to Z.
79             # a .W... So (x1, y1) => (N-y1, x1)
80             # b ....X
81             # c .....
82             # d Z....
83             # e ...Y.
84              
85 684     684   2410 sub _splitcoord { return split(//, shift()) }
86              
87             sub _rotate90 {
88 672     672   1239 my($x, $y, $size) = @_;
89 672         4099 my @letters = (qw(a b c d e f g h i j k l m n o p q r s))[0 .. $size - 1];
90 672         2619 my %letter_to_number = map { $letters[$_] => $_ } (0 .. $size - 1);
  12768         29871  
91 672         3564 ($x, $y) = map { $letter_to_number{$_} } ($x, $y);
  1344         2710  
92              
93 672         1831 ($x, $y) = (($size - 1) - $y, $x);
94              
95 672         931 return join('', map { $letters[$_] } ($x, $y));
  1344         10337  
96             }
97              
98             =head1 BUGS and FEEDBACK
99              
100             I welcome feedback about my code, including constructive criticism.
101             Bug reports should be made using L or by email,
102             and should include the smallest possible chunk of code, along with
103             any necessary data, which demonstrates the bug. Ideally, this
104             will be in the form of a file which I can drop in to the module's
105             test suite.
106              
107             Rotating a game will probably reset the pointer used when navigating
108             around the file. This doesn't matter to me. If it matters to you,
109             then please submit a patch with tests.
110              
111             If you have multiple games in a single file, it will probably screw
112             up. Again, I don't care. If you care, then please submit a patch
113             with tests.
114              
115             =head1 SEE ALSO
116              
117             L
118              
119             =head1 THANKS TO ...
120              
121             Daniel Gilder for pointing out the bug where stuff like AE[aa:ee]
122             wasn't being rotated, and providing a fix.
123              
124             =head1 AUTHOR, COPYRIGHT and LICENCE
125              
126             David Cantrell EFE
127              
128             Copyright 2010 David Cantrell Edavid@cantrell.org.ukE
129              
130             This software is free-as-in-speech software, and may be used,
131             distributed, and modified under the terms of either the GNU
132             General Public Licence version 2 or the Artistic Licence. It's
133             up to you which one you use. The full text of the licences can
134             be found in the files GPL2.txt and ARTISTIC.txt, respectively.
135              
136             =head1 CONSPIRACY
137              
138             This module is also free-as-in-mason software.
139              
140             =cut
141              
142             1;