Games-GUI-Battleship

 view release on metacpan or  search on metacpan

lib/Games/GUI/Battleship/AI/Parity.pm  view on Meta::CPAN

package Games::GUI::Battleship::AI::Parity;

use v5.38;
use experimental 'signatures';
use feature 'try';
no warnings 'experimental::try';
use parent 'Games::GUI::Battleship::AI';

use Logic::Relational ();
use Logic::Relational::Syntax;
use Logic::Relational::Goal::Call;

our $CURRENT_SHOTS = {};

logic ParityAITemplate {
    fact shot_hit( 0, 0 );
    fact shot_miss( 0, 0 );
    fact sunk_cell( 0, 0 );

    rule cell_unshot( $x, $y ) {
        guard(
            [ $x, $y ],
            sub ( $vx, $vy ) {
                return 0 if $vx < 1 || $vx > 10 || $vy < 1 || $vy > 10;
                return $Games::GUI::Battleship::AI::Parity::CURRENT_SHOTS->{
                    "$vx,$vy"} ? 0 : 1;
            }
        );
    }

    rule cell_valid( $x, $y ) {
        between( 1, 10, $x );
        between( 1, 10, $y );
        cell_unshot( $x, $y );
    }

    rule adjacent( $x1, $y1, $x2, $y2 ) {
        is_goal( $x2, [$x1], sub ($v) { $v + 1 } );
        unify( $y1, $y2 );
    }
    rule adjacent( $x1, $y1, $x2, $y2 ) {
        is_goal( $x2, [$x1], sub ($v) { $v - 1 } );
        unify( $y1, $y2 );
    }
    rule adjacent( $x1, $y1, $x2, $y2 ) {
        is_goal( $y2, [$y1], sub ($v) { $v + 1 } );
        unify( $x1, $x2 );
    }
    rule adjacent( $x1, $y1, $x2, $y2 ) {
        is_goal( $y2, [$y1], sub ($v) { $v - 1 } );
        unify( $x1, $x2 );
    }

    # Priority 1: Line Extension (Horizontal)
    rule select_target( $x, $y, $mode ) {
        fresh my ( $x1, $x2, $yh );
        shot_hit( $x1, $yh );
        is_goal( $x2, [$x1], sub ($v) { $v + 1 } );
        shot_hit( $x2, $yh );
        unify( $y, $yh );
        line_endpoint_h( $x1, $x2, $x );
        cell_valid( $x, $y );
        unify( $mode, 'Line Extension (Horizontal)' );
    }
    rule line_endpoint_h( $x1, $x2, $x ) {
        is_goal( $x, [$x2], sub ($v) { $v + 1 } );
    }
    rule line_endpoint_h( $x1, $x2, $x ) {
        is_goal( $x, [$x1], sub ($v) { $v - 1 } );
    }

    # Priority 1: Line Extension (Vertical)
    rule select_target( $x, $y, $mode ) {
        fresh my ( $y1, $y2, $xh );
        shot_hit( $xh, $y1 );
        is_goal( $y2, [$y1], sub ($v) { $v + 1 } );
        shot_hit( $xh, $y2 );
        unify( $x, $xh );
        line_endpoint_v( $y1, $y2, $y );
        cell_valid( $x, $y );
        unify( $mode, 'Line Extension (Vertical)' );
    }
    rule line_endpoint_v( $y1, $y2, $y ) {
        is_goal( $y, [$y2], sub ($v) { $v + 1 } );
    }
    rule line_endpoint_v( $y1, $y2, $y ) {
        is_goal( $y, [$y1], sub ($v) { $v - 1 } );
    }

    # Priority 2: Orthogonal Neighbor Probe
    rule select_target( $x, $y, $mode ) {
        fresh my ( $hx, $hy );
        shot_hit( $hx, $hy );
        adjacent( $hx, $hy, $x, $y );
        cell_valid( $x, $y );
        unify( $mode, 'Orthogonal Target Probe' );
    }

    # Priority 3: Checkerboard Parity Hunt
    rule select_target( $x, $y, $mode ) {
        between( 1, 10, $x );
        between( 1, 10, $y );
        cell_unshot( $x, $y );
        guard( [ $x, $y ],
            sub ( $vx, $vy ) { return ( $vx + $vy ) % 2 == 0; } );
        unify( $mode, 'Checkerboard Parity Hunt' );
    }

    # Priority 4: Fallback Search
    rule select_target( $x, $y, $mode ) {
        between( 1, 10, $x );
        between( 1, 10, $y );
        cell_unshot( $x, $y );
        unify( $mode, 'Fallback Grid Search' );
    }
}

sub new ( $class, %args ) {
    $args{name}       = 'ParityAI';
    $args{difficulty} = 'normal';
    return $class->SUPER::new(%args);
}

sub init_program ($self) {
    $self->{program} = $self->clone_program($ParityAITemplate::PROGRAM);
    return $self->{program};
}

sub _mode_tier ($mode) {
    return 1 if index( $mode, 'Line Extension' ) == 0;
    return 2 if $mode eq 'Orthogonal Target Probe';
    return 3 if $mode eq 'Checkerboard Parity Hunt';
    return 4;
}

sub select_target ($self) {
    local $CURRENT_SHOTS = $self->{shots_history};

    my $tx = Logic::Relational::variable('x');
    my $ty = Logic::Relational::variable('y');
    my $tm = Logic::Relational::variable('mode');

    my $q = $self->{program}->query(
        Logic::Relational::Goal::Call->new(
            'select_target', [ $tx, $ty, $tm ]
        )



( run in 0.577 second using v1.01-cache-2.11-cpan-cb04b646d8c )