2023 Advent of Code Day 7 Part 2

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use Hash::MultiKey;
use feature q/switch/;
no warnings q/deprecated/;

use constant CAMEL_CARDS => A => 12, K => 11, Q => 10, T => 9, 
                            9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 
                            1, J => 0;

sub camel_card_sorter{
    my %h = CAMEL_CARDS;
    my @a = split q//, $a;
    my @b = split q//, $b;
    {
        my $s = shift @a;
        my $t = shift @b;
        return $h{$s} <=> $h{$t} if $s ne $t;
        redo unless !@a || !@b;
    }
}

sub camel_hands{
    my $hand = shift; 
    my %h;
    tie %h, q/Hash::MultiKey/;
    $h{[5]}             = 6;
    $h{[1, 4]}          = 5;
    $h{[2, 3]}          = 4;
    $h{[1, 1, 3]}       = 3;
    $h{[1, 2, 2]}       = 2;
    $h{[1, 1, 1, 2]}    = 1;
    $h{[1, 1, 1, 1, 1]} = 0;    
    my %hand;
    my $jokers = $hand =~ tr/J//d; 
    do{$hand{$_}++} for split q//, $hand;
    my @cards_by_count = sort {$hand{$b} <=> $hand{$a}} keys %hand;
    given($jokers){
        when($_ == 0){
            break;
        }
        when($_ >= 1 && $_ < 5){
            my $card_most = $cards_by_count[0];
            my $s = $card_most x ($_ + 1);
            $hand =~ s/$card_most/$s/;
        }     
        when($_ == 5){
            $hand = q/A/ x 5;
        }
    }
    %hand = ();
    do{$hand{$_}++} for split q//, $hand;
    return $h{[sort {$a <=> $b} values %hand]};    
}

sub camel_hand_ranking{
    my $camel_hand_sorter = sub{ 
        my $c = camel_hands($a) <=> camel_hands($b); 
        return $c unless !$c;
        return camel_card_sorter $a, $b;
    };
    return sort $camel_hand_sorter @_;
}


MAIN:{
    open(DATA, q/data/);
    my @hands;
    my %hand_bids;
    do{
        chop; 
        my($hand, $bid) = split /\s/, $_;
        push @hands, $hand;
        $hand_bids{$hand} = $bid;
    } for <DATA>;
    my @hands_sorted = camel_hand_ranking(@hands); 
    my $total_winnings = 0;
    do{
        my $hand = $hands_sorted[$_ - 1]; 
        $total_winnings += $_ * $hand_bids{$hand};
    } for 1 .. @hands_sorted;
    say $total_winnings;
}

2023 Advent of Code Day 7 Part 1

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use Hash::MultiKey;

use constant CAMEL_CARDS => A => 12, K => 11, Q => 10, J => 9, T => 8, 
                            9, 7, 8, 6, 7, 5, 6, 4, 5, 3, 4, 2, 3, 1, 2, 
                            0;

sub camel_card_sorter{
    my %h = CAMEL_CARDS;
    my @a = split q//, $a;
    my @b = split q//, $b;
    {
        my $s = shift @a;
        my $t = shift @b;
        return $h{$s} <=> $h{$t} if $s ne $t;
        redo unless !@a || !@b;
    }
}

sub camel_hands{
    my $hand = shift; 
    my %h;
    tie %h, q/Hash::MultiKey/;
    $h{[5]}             = 6;
    $h{[1, 4]}          = 5;
    $h{[2, 3]}          = 4;
    $h{[1, 1, 3]}       = 3;
    $h{[1, 2, 2]}       = 2;
    $h{[1, 1, 1, 2]}    = 1;
    $h{[1, 1, 1, 1, 1]} = 0; 
    my %hand;
    do{$hand{$_}++} for split q//, $hand;
    return $h{[sort {$a <=> $b} values %hand]};    
}

sub camel_hand_ranking{
    my $camel_hand_sorter = sub{ 
        my $c = camel_hands($a) <=> camel_hands($b); 
        return $c unless !$c;
        return camel_card_sorter $a, $b;
    };
    return sort $camel_hand_sorter @_;
}


MAIN:{
    open(DATA, q/data/);
    my @hands;
    my %hand_bids;
    do{
        chop; 
        my($hand, $bid) = split /\s/, $_;
        push @hands, $hand;
        $hand_bids{$hand} = $bid;
    } for <DATA>;
    my @hands_sorted = camel_hand_ranking(@hands); 
    my $total_winnings = 0;
    do{
        my $hand = $hands_sorted[$_ - 1]; 
        $total_winnings += $_ * $hand_bids{$hand};
    } for 1 .. @hands_sorted;
    say $total_winnings;
}

2023 Advent of Code Day 6 Part 2

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
package main{
    open(DATA, q/data/);
    my $line = <DATA>;
    my @times = split(/\s+/, (split(/Time:\s+/, $line))[1]); 
    $line = <DATA>;
    my @distances = split(/\s+/, (split(/Distance:\s+/, $line))[1]);
    my @winners;
    my $time = join q//, @times;
    my $distance = join q//, @distances;
    for my $t (0 .. $time){
        push @winners, $t if $distance < $t * ($time - $t);
    }
    say 0 + @winners;
}

2023 Advent of Code Day 6 Part 1

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
package main{
    open(DATA, q/data/);
    my $line = <DATA>;
    my @times = split(/\s+/, (split(/Time:\s+/, $line))[1]); 
    $line = <DATA>;
    my @distances = split(/\s+/, (split(/Distance:\s+/, $line))[1]);
    my @winners;
    for my $i (0 .. @times - 1){
        @winners[$i] = [];
        for my $time (0 .. $times[$i]){
            push @{$winners[$i]}, $time if $distances[$i] < $time * ($times[$i] - $time);
        }
    }
    my $winner_product = 1;
    do{
        $winner_product *= @{$_};
    } for @winners;
    say $winner_product;
}

2023 Advent of Code Day 5 Part 2

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use feature q/bareword_filehandles/;  
use constant SEED_SOIL => q/seed-to-soil map:/;  
use constant SOIL_FERTILIZER => q/soil-to-fertilizer map:/;  
use constant FERTILIZER_WATER => q/fertilizer-to-water map:/;  
use constant WATER_LIGHT => q/water-to-light map:/;   
use constant LIGHT_TEMPERATURE => q/light-to-temperature map:/;  
use constant TEMPERATURE_HUMIDITY => q/temperature-to-humidity map:/;  
use constant HUMIDITY_LOCATION => q/humidity-to-location map:/;  
 
package SeedSoil{
    use Class::Struct;
    struct(
        map_lines => q/@/,   
        map       => q/%/,   
        seed      => q/$/ 
    );  
    sub soil{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->seed() >= $start && $self->seed() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->seed() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->seed();   
    }  
}

package SoilFertilizer{
    use Class::Struct;
    struct(
        map_lines => q/@/,   
        map       => q/%/,   
        soil      => q/$/ 
    );  
    sub fertilizer{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->soil() >= $start && $self->soil() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->soil() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->soil();   
    }  
}

package FertilizerWater{
    use Class::Struct;
    struct(
        map_lines       => q/@/,   
        map             => q/%/,   
        fertilizer      => q/$/ 
    );  
    sub water{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->fertilizer() >= $start && $self->fertilizer() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->fertilizer() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->fertilizer();   
    }  
}

package WaterLight{
    use Class::Struct;
    struct(
        map_lines  => q/@/,   
        map        => q/%/,   
        water      => q/$/ 
    );  
    sub light{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->water() >= $start && $self->water() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->water() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->water();   
    }  
}

package LightTemperature{
    use Class::Struct;
    struct(
        map_lines  => q/@/,   
        map        => q/%/,   
        light      => q/$/ 
    );  
    sub temperature{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->light() >= $start && $self->light() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->light() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->light();   
    }  
}

package TemperatureHumidity{
    use Class::Struct;
    struct(
        map_lines        => q/@/,   
        map              => q/%/,   
        temperature      => q/$/ 
    );  
    sub humidity{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->temperature() >= $start && $self->temperature() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->temperature() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->temperature();   
    } 
}

package HumidityLocation{
    use Class::Struct;
    struct(
        map_lines     => q/@/,   
        map           => q/%/,   
        humidity      => q/$/ 
    );  
    sub location{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->humidity() >= $start && $self->humidity() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->humidity() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->humidity();   
    }  
}

package main{
    use Thread;
    sub build_map{
        my @map_lines = @{shift @_};   
        my %map;
        do{
            my($destination_start, $source_start, $range) = split(/\s/, $_); 
            $map{qq/$source_start:$range/} = $destination_start; 
        } for @map_lines;
        return \%map;  
    }  

    open(DATA, q/data/);
    my @seeds = split(/\s/, (split(/:\s/, <DATA>))[1]);
    my @seed_soil;
    my @soil_fertilizer; 
    my @fertilizer_water; 
    my @water_light; 
    my @light_temperature; 
    my @temperature_humidity; 
    my @humidity_location; 
    while(<DATA>){
        chop;  
        my $line; 
        next if m/^$/;  
        my $r = SEED_SOIL; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @seed_soil, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }  
        } 
        $r = SOIL_FERTILIZER; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @soil_fertilizer, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = FERTILIZER_WATER; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @fertilizer_water, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = WATER_LIGHT; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @water_light, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = LIGHT_TEMPERATURE; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @light_temperature, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = TEMPERATURE_HUMIDITY; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @temperature_humidity, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = HUMIDITY_LOCATION; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @humidity_location, $line;
                $line = <DATA>;
                redo unless !$line || $line =~ m/^$/;
            }
        }
    } 
    my $ss = SeedSoil->new(map_lines => [@seed_soil]); 
    my $sf = SoilFertilizer->new(map_lines => [@soil_fertilizer]); 
    my $fw = FertilizerWater->new(map_lines => [@fertilizer_water]); 
    my $wl = WaterLight->new(map_lines => [@water_light]); 
    my $lt = LightTemperature->new(map_lines => [@light_temperature]); 
    my $th = TemperatureHumidity->new(map_lines => [@temperature_humidity]); 
    my $tl = HumidityLocation->new(map_lines => [@humidity_location]); 
    my %seed_range;
    my @threads;
    while(@seeds){ 
        my $seed = shift @seeds;
        my $range = shift @seeds;    
		push @threads, Thread->new(
            sub{
                my @locations;
                for my $i (0 .. $range - 1){ 
                    $seed++;
    	            $ss->seed($seed); 
		            $sf->soil($ss->soil());
		            $fw->fertilizer($sf->fertilizer());
		            $wl->water($fw->water());
		            $lt->light($wl->light()); 
		            $th->temperature($lt->temperature());
		            $tl->humidity($th->humidity());
		            push @locations, $tl->location(); 		            
		        }
		        return ((sort {$a <=> $b} @locations)[0]);
		    }
        );
    }
    my @locations;
    do{
	    push @locations, $_ -> join();
	} for @threads;
    say ((sort {$a <=> $b} @locations)[0]);
}

2023 Advent of Code Day 5 Part 1

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use feature q/bareword_filehandles/;  
use constant SEED_SOIL => q/seed-to-soil map:/;  
use constant SOIL_FERTILIZER => q/soil-to-fertilizer map:/;  
use constant FERTILIZER_WATER => q/fertilizer-to-water map:/;  
use constant WATER_LIGHT => q/water-to-light map:/;   
use constant LIGHT_TEMPERATURE => q/light-to-temperature map:/;  
use constant TEMPERATURE_HUMIDITY => q/temperature-to-humidity map:/;  
use constant HUMIDITY_LOCATION => q/humidity-to-location map:/;  
 
use Data::Dump q/pp/; 

package SeedSoil{
    use Class::Struct;
    struct(
        map_lines => q/@/,   
        map       => q/%/,   
        seed      => q/$/ 
    );  
    sub soil{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->seed() >= $start && $self->seed() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->seed() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->seed();   
    }  
}

package SoilFertilizer{
    use Class::Struct;
    struct(
        map_lines => q/@/,   
        map       => q/%/,   
        soil      => q/$/ 
    );  
    sub fertilizer{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->soil() >= $start && $self->soil() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->soil() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->soil();   
    }  
}

package FertilizerWater{
    use Class::Struct;
    struct(
        map_lines       => q/@/,   
        map             => q/%/,   
        fertilizer      => q/$/ 
    );  
    sub water{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->fertilizer() >= $start && $self->fertilizer() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->fertilizer() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->fertilizer();   
    }  
}

package WaterLight{
    use Class::Struct;
    struct(
        map_lines  => q/@/,   
        map        => q/%/,   
        water      => q/$/ 
    );  
    sub light{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->water() >= $start && $self->water() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->water() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->water();   
    }  
}

package LightTemperature{
    use Class::Struct;
    struct(
        map_lines  => q/@/,   
        map        => q/%/,   
        light      => q/$/ 
    );  
    sub temperature{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->light() >= $start && $self->light() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->light() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->light();   
    }  
}

package TemperatureHumidity{
    use Class::Struct;
    struct(
        map_lines        => q/@/,   
        map              => q/%/,   
        temperature      => q/$/ 
    );  
    sub humidity{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->temperature() >= $start && $self->temperature() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->temperature() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->temperature();   
    } 
}

package HumidityLocation{
    use Class::Struct;
    struct(
        map_lines     => q/@/,   
        map           => q/%/,   
        humidity      => q/$/ 
    );  
    sub location{
        my($self) = @_; 
        $self->map(main::build_map($self->map_lines())) if %{$self->map()} == 0;  
        do{
            my($start, $range) = split(/:/, $_); 
            if($self->humidity() >= $start && $self->humidity() <= $start + $range){
                my $destination = $self->map()->{$_};
                return $destination + ($self->humidity() - $start); 
            }  
        } for keys %{$self->map()}; 
        return $self->humidity();   
    }  
}

package main{
    sub build_map{
        my @map_lines = @{shift @_};   
        my %map;
        do{
            my($destination_start, $source_start, $range) = split(/\s/, $_); 
            $map{qq/$source_start:$range/} = $destination_start;  
        } for @map_lines;
        return \%map;  
    }  

    open(DATA, q/data/);
    my @seeds = split(/\s/, (split(/:\s/, <DATA>))[1]);
    my @seed_soil;
    my @soil_fertilizer; 
    my @fertilizer_water; 
    my @water_light; 
    my @light_temperature; 
    my @temperature_humidity; 
    my @humidity_location; 
    while(<DATA>){
        chop;  
        my $line; 
        next if m/^$/;  
        my $r = SEED_SOIL; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @seed_soil, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }  
        } 
        $r = SOIL_FERTILIZER; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @soil_fertilizer, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = FERTILIZER_WATER; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @fertilizer_water, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = WATER_LIGHT; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @water_light, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = LIGHT_TEMPERATURE; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @light_temperature, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = TEMPERATURE_HUMIDITY; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @temperature_humidity, $line;
                $line = <DATA>;
                redo unless $line =~ m/^$/;
            }
        }
        $r = HUMIDITY_LOCATION; 
        if(m/$r/){
            $line = <DATA>;
            {
                chop($line);
                push @humidity_location, $line;
                $line = <DATA>;
                redo unless !$line || $line =~ m/^$/;
            }
        }
    } 
    my $ss = SeedSoil->new(map_lines => [@seed_soil]); 
    my $sf = SoilFertilizer->new(map_lines => [@soil_fertilizer]); 
    my $fw = FertilizerWater->new(map_lines => [@fertilizer_water]); 
    my $wl = WaterLight->new(map_lines => [@water_light]); 
    my $lt = LightTemperature->new(map_lines => [@light_temperature]); 
    my $th = TemperatureHumidity->new(map_lines => [@temperature_humidity]); 
    my $tl = HumidityLocation->new(map_lines => [@humidity_location]); 
    my @locations; 
    for my $seed (@seeds){ 
        $ss->seed($seed); 
        $sf->soil($ss->soil());
        $fw->fertilizer($sf->fertilizer());
        $wl->water($fw->water());
        $lt->light($wl->light()); 
        $th->temperature($lt->temperature());
        $tl->humidity($th->humidity());
        push @locations, $tl->location(); 
    }    
    say ((sort {$a <=> $b} @locations)[0]);
} 

2023 Advent of Code Day 4 Part 2

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use English;
use feature q/bareword_filehandles/;
##
# Advent of Code Day 4. 
# Part 2.
##
package ScratchCard{
    use Class::Struct;
    struct(
        id              => q/$/,
        player_numbers  => q/@/,
        winning_numbers => q/@/
    );
    sub matches{
        my $self = shift @_;
        my %winning;
        do{$winning{$_}=undef} for @{$self->winning_numbers()};
        my $matches = 0;
        do{
            $matches++ if(exists($winning{$_}));
        } for @{$self->player_numbers()};
        return $matches;
    }
}

package ScratchCardPile{
    use Class::Struct; 
    struct(
        cards => q/@/
    );
    sub score{
        my $self = shift @_;
        my @cards = @{$self->cards()};
        my $number_cards = (sort {$b <=> $a} map {$_->id()} @cards)[0];
        my @updated_card_counts = (1) x $number_cards; 
        for(my $i = 1; $i <= $number_cards; $i++){ 
            my $card = (grep {$_->id() == $i} @cards)[0]; 
            for(1 .. $updated_card_counts[$card->id() - 1]){
				for my $match (1 .. $card->matches()){ 
					$updated_card_counts[$card->id() + $match - 1]++;
				}
            }
        }
        return unpack(q/%32I*/, pack(q/I*/, @updated_card_counts)); ;
    }
}

package main{
	open(DATA, q/data/);
	my @cards;
	while(<DATA>){
		chop;
		my @card = split(q/\s\|\s/, (split(/:\s+/, $_))[1]);
		my @winning_numbers = split(/\s+/, $card[0]); 
		my @player_numbers  = split(/\s+/, $card[1]); 
		my $scratchcard = ScratchCard->new(
		    id => $INPUT_LINE_NUMBER,
		    player_numbers => [@player_numbers],
		    winning_numbers => [@winning_numbers]
		);
		push @cards, $scratchcard;
	}
	my $scratchcards = ScratchCardPile->new(cards => [@cards]);
	say $scratchcards->score();
}

2023 Advent of Code Day 4 Part 1

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use feature q/bareword_filehandles/;
##
# Advent of Code Day 4. 
# Part 1.
##
package ScratchCard{
    use Class::Struct;
    struct(
        player_numbers  => q/@/,
        winning_numbers => q/@/
    );
    sub score{
        my $self = shift @_;
        my %winning;
        do{$winning{$_}=undef} for @{$self->winning_numbers()};
        my $score = 0;
        do{
            if(exists($winning{$_})){
                $score *= 2 if $score;
                $score = 1 if !$score;
            }
        } for @{$self->player_numbers()};
        return $score;
    }
}

package main{
	open(DATA, q/data/);
	my $total_score = 0;
	while(<DATA>){
		chop;
		my @card = split(q/\s\|\s/, (split(/:\s+/, $_))[1]);
		my @winning_numbers = split(/\s+/, $card[0]); 
		my @player_numbers  = split(/\s+/, $card[1]); 
		my $scorecard = ScratchCard->new(
		    player_numbers => [@player_numbers],
		    winning_numbers => [@winning_numbers]
		);
		$total_score += $scorecard->score();
	}
	say $total_score;
}

2023 Advent of Code Day 3 Part 2

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use feature q/bareword_filehandles/;
##
# Advent of Code Day 3. 
# Part 2.
##
sub gear_ratios{
    my @schematic = @_;
    my %part_locations;
    my %symbol_locations;
    my $line_counter = 0;
    do{
        my $line = $_;
        my @parts = $line =~ m/(\d+)/g;
        my @symbols = $line =~ m/(\*)/g;
        my $location = 0;
        while(@parts){
            my $part = shift @parts;
            my $index = index($line, $part, $location);
            $part_locations{qq/$line_counter:$index/} = $part;
            $location = $index + length($part);
        }
        $location = 0;
        while(@symbols){
            my $symbol = shift @symbols;
            my $index = index($line, $symbol, $location);
            $symbol_locations{qq/$line_counter:$index/} = $symbol;
            $location = $index + length($symbol);
        }   
        $line_counter++;     
    } for @schematic; 
    my $gear_ratio = 0;
    my %gears_seen;
    do{
        my $part = $part_locations{$_};
        my($x, $y) = split(/:/, $_); 
        for(my $i = 0; $i < length($part); $i++){
            if(exists($symbol_locations{($x - 1) . q/:/ . ($y - 1)})){
                if(!$gears_seen{($x - 1) . q/:/ . ($y - 1)}){
                    $gears_seen{($x - 1) . q/:/ . ($y - 1)} = [$part];
                }
                else{
                    push @{$gears_seen{($x - 1) . q/:/ . ($y - 1)}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{($x - 1) . q/:/ . $y})){
                if(!$gears_seen{($x - 1) . q/:/ . $y}){
                    $gears_seen{($x - 1) . q/:/ . $y} = [$part];
                }
                else{
                    push @{$gears_seen{($x - 1) . q/:/ . $y}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{($x - 1) . q/:/ . ($y + 1)})){
                if(!$gears_seen{($x - 1) . q/:/ . ($y + 1)}){
                    $gears_seen{($x - 1) . q/:/ . ($y + 1)} = [$part];
                }
                else{
                    push @{$gears_seen{($x - 1) . q/:/ . ($y + 1)}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{$x . q/:/ . ($y - 1)})){
                if(!$gears_seen{$x . q/:/ . ($y - 1)}){
                    $gears_seen{$x . q/:/ . ($y - 1)} = [$part];
                }
                else{
                    push @{$gears_seen{$x . q/:/ . ($y - 1)}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{$x . q/:/ . ($y + 1)})){
                if(!$gears_seen{$x . q/:/ . ($y + 1)}){
                    $gears_seen{$x . q/:/ . ($y + 1)} = [$part];
                }
                else{
                    push @{$gears_seen{$x . q/:/ . ($y + 1)}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{($x + 1) . q/:/ . ($y - 1)})){
                if(!$gears_seen{($x + 1) . q/:/ . ($y - 1)}){
                    $gears_seen{($x + 1) . q/:/ . ($y - 1)} = [$part];
                }
                else{
                    push @{$gears_seen{($x + 1) . q/:/ . ($y - 1)}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{($x + 1) . q/:/ . $y})){
                if(!$gears_seen{($x + 1) . q/:/ . $y}){
                    $gears_seen{($x + 1) . q/:/ . $y} = [$part];
                }
                else{
                    push @{$gears_seen{($x + 1) . q/:/ . $y}}, $part;
                }
                last;
            }
            if(exists($symbol_locations{($x + 1) . q/:/ . ($y + 1)})){
                if(!$gears_seen{($x + 1) . q/:/ . ($y + 1)}){
                    $gears_seen{($x + 1) . q/:/ . ($y + 1)} = [$part];
                }
                else{
                    push @{$gears_seen{($x + 1) . q/:/ . ($y + 1)}}, $part;
                }
                last;
            }
            $y++;
        }        
    } for keys %part_locations;
    do{
        my @gears = @{$gears_seen{$_}};
        if(@gears == 2){
            $gear_ratio += ($gears[0] * $gears[1]);
        }
    } for keys %gears_seen;
    return $gear_ratio;
}

open(DATA, q/data/);
my @schematic;
while(<DATA>){
    chop;
    push @schematic, $_;
}
say gear_ratios @schematic;

2023 Advent of Code Day 3 Part 1

Perl. Developed and tested with Perl 5.38.0.

use v5.38;
use feature q/bareword_filehandles/;
##
# Advent of Code Day 3. 
# Part 1.
##
sub gear_ratios{
    my @schematic = @_;
    my %part_locations;
    my %symbol_locations;
    my $line_counter = 0;
    do{
        my $line = $_;
        my @parts = $line =~ m/(\d+)/g;
        my @symbols = $line =~ m/([^\d\.])/g;
        my $location = 0;
        while(@parts){
            my $part = shift @parts;
            my $index = index($line, $part, $location);
            $part_locations{qq/$line_counter:$index/} = $part;
            $location = $index + length($part);
        }
        $location = 0;
        while(@symbols){
            my $symbol = shift @symbols;
            my $index = index($line, $symbol, $location);
            $symbol_locations{qq/$line_counter:$index/} = $symbol;
            $location = $index + length($symbol);
        }   
        $line_counter++;     
    } for @schematic; 
    my $parts_sum = 0;
    do{
        my $part = $part_locations{$_};
        my($x, $y) = split(/:/, $_); 
        for(my $i = 0; $i < length($part); $i++){
            if(exists($symbol_locations{($x - 1) . q/:/ . ($y - 1)}) ||
               exists($symbol_locations{($x - 1) . q/:/ . $y})       ||
               exists($symbol_locations{($x - 1) . q/:/ . ($y + 1)}) ||
               exists($symbol_locations{$x . q/:/ . ($y - 1)})       ||
               exists($symbol_locations{$x . q/:/ . ($y + 1)})       ||
               exists($symbol_locations{($x + 1) . q/:/ . ($y - 1)}) ||
               exists($symbol_locations{($x + 1) . q/:/ . $y})       ||
               exists($symbol_locations{($x + 1) . q/:/ . ($y + 1)}) 
            ){
                $parts_sum += $part;
                last;
            }
            $y++;
        }        
    } for keys %part_locations;
    return $parts_sum;
}

open(DATA, q/data/);
my @schematic;
while(<DATA>){
    chop;
    push @schematic, $_;
}
say gear_ratios @schematic;