5

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?

Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
hsmyers
  • 665
  • 7
  • 10
  • If you want to go super OO, you could have a row/column class, but realistically, just a single array of 64 objects would be fine using a `for ^63` loop. I'll try to post a longer answer though on the rest of the your coding style for Raku later tonight – user0721090601 Nov 18 '19 at 20:32

1 Answers1

9

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.

Jonathan Worthington
  • 29,104
  • 2
  • 97
  • 136
  • Thank you for your time and patience. As you say it is just as reasonable to contain an array of squares—that was part of some of the earlier designs. Easy enough to re-group! – hsmyers Nov 19 '19 at 00:04