Perl Weekly Challenge 281: Let’s Play Chess!

This post presents my solutions to the Perl Weekly Challenge 281.
I keep doing the Perl Weekly Challenge in order to mantain my coding skills in good shape, as well as in order to learn new things, with particular regard to Raku, a language that I love.
This week, I solved the following tasks:

Raku Implementations

PWC 281 - Task 1 - Raku Implementation

The first task was about finding out if a given position on a chessboard was light or not.

sub MAIN( Str $coordinates where { $coordinates ~~ / ^ <[a .. h]> <[ 1 .. 8 ]> $/ } ) {
    my %chessboard;
    my $value = False;
    for 1 .. 8 -> $row {
		$value = $row %% 2 ?? True !! False;
		for 'a' .. 'h' -> $column {
			%chessboard{ $column ~ $row } = $value;
			$value = ! $value;
		}
    }

    %chessboard{ $coordinates }.say;

}



I lazily created a nested loop over %chessboard, which is initialized with True for light positions and False for black ones. Therefore, it becomes really simple to see if a given position is black or white.

PWC 281 - Task 2 - Raku Implementation

This has been a very complex task: find out the shortest path for a knight to move from a position to another.

sub compute-end-points( $start ) {
    my @end-points;
    my @valid-columns = 'a' .. 'h';

    $start ~~ / ^ $<column>=( <[a..h]> ) $<row>=( <[1..8]> ) $ /;
    my $row = $<row>.Int;
    my $column = $<column>.Str;

    return Nil if ( $row > 8 || $row < 1 || ! @valid-columns.grep( { $_ ~~ $column } ) );

    # at which index is the current column labeled with a letter?
    $column    = @valid-columns.grep( { $_ ~~ $column }, :k )[ 0 ];

    my @moves = [ +1, -2 ], [ +1, +2 ],
		[ +2, -1 ], [ +2, +1 ],
		[ -1, -2 ], [ -1, +2 ],
		[ -2, -1 ], [ -2, +1 ];

    @end-points = @moves.map(
	{  [ @valid-columns[ $_[ 0 ] + $column ], $_[ 1 ] + $row ] } )
		   .grep( { $_[ 0 ] && @valid-columns.grep( * ~~ $_[ 0 ] ) && 1 <= $_[ 1 ] <= 8 } )
		   .map( { $_[ 0 ] ~ $_[ 1 ] } );
		   ;
    return @end-points;
}


sub MAIN( Str $start,
	  Str $end,
	  :$verbose
			  where { $start !~~ $end
				  and $start ~~ / ^ <[a..h]> <[1..8]> $ /
				  and $end ~~ / ^ <[a..h]> <[1..8]> $ / }
			   ) {


    my @end-points = compute-end-points( $start );

    my @paths;
    @paths = @end-points.map( $start ~ '-' ~ * );

    my @new-paths;
    my $found = False;
    my $debug = 0;
    while ( ! $found ) {
		for @paths.sort -> $current-path  {
		    my $next = $current-path.split( '-' )[ * - 1 ];
		    my @ends = compute-end-points( $next ).grep( { $_ ne $start && $_ ne $next } );
		    @new-paths.push: $current-path ~ '-'  ~ $_ for @ends;
		}

		@paths = @new-paths;
		@new-paths = ();
		$found = @paths.grep( * ~~ / $end $ / );

    }



    my %possible-moves;
    for @paths.grep( * ~~ / $end $ / ) {
		%possible-moves{ $_.split( '-' ).elems }.push: $_;
    }

    my $min-moves = %possible-moves.keys.min;
    $min-moves.say;
    if ( $verbose ) {
		"possible moves:".say;
		%possible-moves{ $min-moves  }.sort.join( "\n" ).say;
    }
}




I define a function compute-end-points that, given a starting position, computes where the kight can be that, in a case where all the endings are still on the chessboard, means 8 possible positions. Then, in the MAIN, I start from the beginning position and compute the initial @end-points list. Then I do iterate until I find the final destination, appending to a list of positions, the ending points. So the list @new-paths is built as start-end1, start-end2, ... and then, in the next iteration, is expanded to start-end1-subend1, start-end1-subend2, ... , start-end8-subend1, .... Once I’ve the list of all possible places the knight can reach, I grep thos that end with the $end wanted position, splitting into places and computing the minimum value of moves. This can lead to more than one sequence, so I compare them into an hash keyed at the number of moves.

The article Perl Weekly Challenge 281: Let's Play Chess! has been posted by Luca Ferrari on August 9, 2024