Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

Math::MagicSquare::Generator

by Juerd (Abbot)
on Jan 27, 2002 at 17:52 UTC ( #141932=sourcecode: print w/ replies, xml ) Need Help??

Category: Fun stuff
Author/Contact Info Juerd
Description: This module creates magic squares. A magic square is a square in which all numbers are differet and the sums of all rows, all columns and the two diagonals are equal.

(To be uploaded to CPAN soon)
package Math::MagicSquare::Generator;
use strict;
use Carp;
use vars qw($VERSION);

$VERSION = '0.01';

sub _sum {
    my $sum = 0;
    $sum += $_ for @_;
    return $sum
}

sub new {
    my ($class, %opt) = @_;
    $opt{size}  ||= 5;
    $opt{start} ||= 1;
    $opt{step}  ||= 1;
    croak "Size needs to be a positive, odd integer"
        unless $opt{size} > 0 and
               $opt{size} % 2 and
               $opt{size} == int($opt{size});
    my $self = [
        map {
            [ (undef) x $opt{size} ]
        } 1..$opt{size}
    ];
    my $value = $opt{start};
    my $halv = int(@$self / 2);
    for my $start_x (-$halv..$halv) {
        my $x = $start_x - 1;
        my $y = $x + @$self + 1;
        for (1 .. @$self) {
            $x = $x - @$self if ++$x > $#$self;
            $y = $y - @$self if --$y > $#$self;
            $self->[$y][$x] = $value;
            $value += $opt{step};
        }
    }
    return bless $self, $class;
}

sub hflip {
    my ($self) = @_;
    my $clone;
    push @$clone, [ reverse @$_ ] for @$self;
    return bless $clone, ref $self;
}

sub vflip {
    my ($self) = @_;
    my $clone;
    push @$clone, [ @$_ ] for reverse @$self;
    return bless $clone, ref $self;
}

sub sum {
    my ($self) = @_;
    return _sum( @{ $self->[0] } );
}

sub check {
    my ($self) = @_;
    my $sum = $self->sum;
    # Horizontals
    for (@$self[1..$#$self]) {
        return undef if @$_ > @$self; # undef if not square
        return undef if _sum(@$_) != $sum;
    }
    # Verticals
    for my $x (0..$#$self) {
        return undef if _sum(map $self->[$_][$x], 0..$#$self) != $sum;
    }
    # Diagonals
    return undef if _sum(map $self->[$_][$_],           0..$#$self) !=
+ $sum;
    return undef if _sum(map $self->[$#$self - $_][$_], 0..$#$self) !=
+ $sum;
    # Duplicates
    my %seen;
    $seen{$_}++ for map @$_, @$self;
    return undef if _sum(values %seen) != keys %seen;
    # Passed all tests!
    return $sum;
}

sub as_string {
    my ($self) = @_;
    my $max = 0;
    length > $max and $max = length for map @$_, @$self;
    return map { join(' ', map {' 'x($max - length) . $_} @$_) . "\n" 
+} @$self;
}

sub as_html {
    my ($self) = @_;
    return "<table>\n" . join("\n",
        map { '<tr><td>' . join('</td><td>', @$_) . '</td></tr>' } @$s
+elf) .
        "\n</table>\n";
}

sub as_csv {
    my ($self) = @_;
    return join("\n", map { join ',', @$_ } @$self) . "\n";
}

1;

__END__

=head1 NAME

Math::MagicSquare::Generator - Magic Square Generator

=head1 SYNOPSIS

    use Math::MagicSquare::Generator

    my $square = Math::MagicSquare::Generator->new(size => 5,
                                                   step => 3,
                                                   start=> 6);
    for ($square, $square->vflip, $square->hflip) {
        print $_->as_string;
        print "-----\n";
    }

    $square->[0][0] = -15; # Break magic :)
    print $square->check ? "Magic square\n" : "Just a square\n";

    print '<html><body>';
    print Math::MagicSquare::Generator->new->hflip->vflip->as_html;
    print '</body></html>';

=head1 DESCRIPTION

This module creates magic squares. A magic square is a square in which
all numbers are different and the sums of all rows, all columns and
the two diagonals are equal.
Math::MagicSquare::Generator cannot create panmagic squares, or square
+s
that have an even size. (A panmagic square is magic square where the
"wrapped" diagonals are also equal.)

=head1 EXAMPLE

     3 16  9 22 15  This square is the output of
    20  8 21 14  2  print Math::MagicSquare::Generator->new->as_string
+;
     7 25 13  1 19
    24 12  5 18  6
    11  4 17 10 23

    The sums of the rows are 65.
    The sums of the columns are 65.
    The sums of the diagonals are 65.

=head1 METHODS

=over 10

=item new

The constructor that generates the square immediately. It
creates an object using the given named arguments. Valid arguments are
C<size>, C<step> and C<start>. C<size> has to be positive, odd and
integer.

=item check

A checker - returns the common sum if the square is magic, or undef if
it's not. Because the sum can never be 0, you can use this as a boolea
+n
value. (Well, the sum in a 1x1 square can be 0, if the single number i
+s
0.) You can use this method to check if the square has been tampered w
+ith.

=item sum

Returns the common sum of the rows, columns and diagonals.

=item vflip, hflip

These methods return a vertically or horizontally flipped clone of the
square. The clone is a Math::MagicSquare::Generator, so stacking these
methods is possible.

=item as_string, as_html, as_csv

DWYM - return the square as a formatted string, piece of html or in
CSV format.

=back

=head1 THIS MODULE AND Math::MagicSquare

Math::MagicSquare is a module that checks if a square is magical. It
takes a list in its C<new> method, so you'll have to dereference the
generated square:

    use Math::MagicSquare;
    use Math::MagicSquare::Generator;

    my $square = Math::MagicSquare::Generator->new;
    print Math::MagicSquare->new( @$square )->check, "\n"; # 2

Its C<check> will always return 2 for squares generated using this
module (or 3 if it's a 1x1 square.

=head1 KNOWN BUGS

None yet.

=head1 AUTHOR

Juerd <juerd@juerd.nl>

=cut

Comment on Math::MagicSquare::Generator
Download Code
Re: Math::MagicSquare::Generator
by merlyn (Sage) on Jan 27, 2002 at 21:34 UTC
    (To be uploaded to CPAN soon)
    Did you miss Math::MagicSquare, already in the cpan? How is yours different? Can you just send patches to the original author or take it over instead? I don't see why we need two Magic Square generators in the CPAN!

    -- Randal L. Schwartz, Perl hacker


    Update: Argh. I could swear that when I first looked at Math::MagicSquare, that it was both a generator and a checker, because I noted it for potential material for a column. My mistake.
      I made Math::MagicSquare::Generator last night, just because I couldn't sleep and couldn't stop thinking about magic squares. Years ago, we had an assignment at school to create a simple 3x3 magic square. That hounted me for weeks. After several weeks, I gave my teacher a piece of paper with an 11x11 magic square and he was impressed. Last night, I created a 5x5 magic square in my head, with no paper. While doing that, I realised I was thinking in terms of Perl code. I then wrote a module called Math::MagicSquare (on my laptop - no Internet connection (yes, the same laptop that has a 802.11b connection, but the Internet connection was down :)) in one go. The next morning, I checked CPAN to see if there already was a module. And there was - Just like you did, I thought it could generate and check, and I was kind of disappointed that I had re-invented a wheel. Its source was a mess, so I decided to not read, but use the code. Reading the documentation gave me the answer: it was just a checker. I chose to name my module after what it does, hoping the difference would be obvious - I renamed it to Math::MagicSquare::Generator, added documentation and uploaded it to CPAN after checking thoroughly if a module already existed.

      We both made the same mistake, and perhaps Math::MagicSquare::Checker would have been a better name for the existing Math::MagicSquare module, but it's too late for that now.

      2;0 juerd@ouranos:~$ perl -e'undef christmas' Segmentation fault 2;139 juerd@ouranos:~$

Back to Code Catacombs

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: sourcecode [id://141932]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (8)
As of 2015-07-03 08:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (50 votes), past polls