Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create and populate an array of Raku objects?

Tags:

raku

I chose to redesign a portion of the previous code of mine, in this case, a chessboard, in Perl 6. The first two classes went well (or at least worked, I know so little that I can't speak to their correctness), but I'm stuck with the third. Here is the code:

#!/home/hsmyers/rakudo741/bin/perl6
# board.p6 - Beginnings of a PGN toolset. And place to start learning
#            Perl 6/Raku.
use v6d;

#!___________________________________________________________

constant $size = 4;

class Piece {
    my Str @namesOfPieces[$size] = <
        white-rook white-knight white-bishop white-queen
    >;
    my Str @abrevsOfPieces[$size] = <
        R N B Q K B N R
    >;
    my Str @symbolsOfPieces[$size] = <
        &#9814; &#9816; &#9815; &#9813; &#9812; &#9815; &#9816; &#9814;
    >;
    my Str @codeptsOfPieces[$size] = (
        "\x2656", "\x2658", "\x2657", "\x2655",
    );
    has Str $.name;
    has Str $.abrev;
    has Str $.symbol;
    has Uni $.codept;

    submethod BUILD( :$i ) {
        $!name   = @namesOfPieces[$i];
        $!abrev  = @abrevsOfPieces[$i];
        $!symbol = @symbolsOfPieces[$i];
        $!codept = @codeptsOfPieces[$i].NFC;
    }
}

class Square {
    my Int @colors[$size] = <
        1 0 1 0 1 0 1 0
    >;
    my Str @names[$size] = <
        a1 b1 c1 d1 e1 f1 g1 h1
    >;
    has Int   $.color;
    has Int   $.index;
    has Str   $.name;
    has Piece $.piece;

    submethod BUILD( :$i ) {
        $!color = @colors[$i];
        $!index = $i;
        $!name  = @names[$i];
        $!piece = Piece.new(:i($i));
    }
}

class Board is Array {
}

my $p = Piece.new(:i(0));
$p.say;
my $s = Square.new(:i(0));
$s.say;

#!___________________________________________________________

my @b := Board.new(
    Square.new(:i(0)),
    Square.new(:i(1)),
    Square.new(:i(2))
);
say @b;
say @b.WHAT;

When run at the cli, results in:

Piece.new(name => "white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)
Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name => "white- 
rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC))
[Square.new(color => IntStr.new(1, "1"), index => 0, name => "a1", piece => Piece.new(name => 
"white-rook", abrev => "R", symbol => "♖", codept => Uni.new(0x2656).NFC)) Square.new(color => 
IntStr.new(0, "0"), index => 1, name => "b1", piece => Piece.new(name => "white-knight", abrev => 
"N", symbol => "♘", codept => Uni.new(0x2658).NFC)) Square.new(color => IntStr.new(1, "1"), index => 
2, name => "c1", piece => Piece.new(name => "white-bishop", abrev => "B", symbol => "♗", codept => 
Uni.new(0x2657).NFC))]
(Board)

The Board class (empty as it is) is all that is left from my attempts so far. Amazingly (at least to me), it provides a degree of workability. It has variously had a "new" and a "BUILD," neither provided a working solution. The current approach doesn't work, considering that the actual count will be 64 and not 4.

My current notion is that I need to build an array of 64 Squares, which in turn will create the necessary pieces. I've tried to add to self with nothing working. Suggestions?

like image 411
hsmyers Avatar asked Nov 18 '19 18:11

hsmyers


1 Answers

Inheriting from Array is probably not the best design choice here; it reveals and commits to the underlying representation of the Board, which will present refactoring challenges as the code evolves. Rather, I'd suggest that a Board has an Array of Square, which is initialized with Square objects.

Assuming the board is meant to have $size squared places, then you could do something like:

class Board {
    has @.squares[$size ** 2];

    method TWEAK() {
        @!squares = map { Square.new(i => $_ % $size) }, ^($size ** 2);
    }
}

That is, take the range from 0 up to but excluding $size squared, and then map each value into a Square instance. (We modulo the index to avoid an index out of bounds in one of the other classes.)

A 2D array may be preferable:

class Board {
    has @.squares[$size;$size];

    method TWEAK() {
        @!squares = (map -> $i { Square.new(:$i) }, ^$size) xx $size;
    }
}

Here, we map again, but this time since we're just doing one dimension we drop the modulo. The use of a named $i parameter means we can use the :$i convenience, which is short for :i($i) (there's an opportunity to do that in the code you posted also). We then take that expression producing one row, and use xx to run it $size times in order to get data for every column.

Ultimately, it will probably not be quite so simple as this; perhaps Square should take two constructor arguments, both a numeric and a letter, to form its name. That's probably best done as a map of map. Further, the initialization of Piece instances probably wants to happen in Board too; while it's been a quarter of a century since I last played chess, I'm quite sure not every square has a piece on it at the start of the game.

like image 92
Jonathan Worthington Avatar answered Oct 06 '22 22:10

Jonathan Worthington