#!/usr/bin/env perl ############################################# # Solution for Advent of Code 2023 day four # # 2023-12-04 # # # # Called with `perl aoc-42.pl input.txt` # ############################################# use strict; use warnings; use v5.16; use Getopt::Long; use Array::Utils qw(intersect); my $debug; GetOptions( 'debug' => \$debug, ); ############################################################################### ############################################################################### # Read all the lines in from $ARGV[0] or STDIN my @lines = readline(); my $total = 0; my $x = {}; foreach my $line (@lines) { if ($line =~ m/Card\s+(\d+): (.+?)\|(.+)/) { my $game_num = $1; my @winners = split(' ', $2); my @our_nums = split(' ', $3); $x->{$game_num}++; my $matches = get_matches($game_num, \@our_nums, \@winners); my $copies = $x->{$game_num}; earn_cards($game_num, $matches, $copies); if ($debug) { k("Game #$game_num had $matches matches"); } # Something went way wrong with our input } else { kd($line); } } # Go back through the counts for each card and build a total foreach my $card (sort keys %$x) { my $copies = $x->{$card}; $total += $copies; } k($total); ############################################################################### ############################################################################### sub get_matches { my ($num, $our, $win) = @_; my @matches = intersect(@$our, @$win); my $matches = scalar(@matches); return $matches; } sub earn_cards { my ($card_num, $wins, $copies) = @_; my $end = ($card_num + $wins); for (my $i = $card_num + 1; $i <= $end; $i++) { $x->{$i} += $copies; } } sub is_numeric { my $str = shift(); my $ret = $str =~ /^[0-9]+$/; $ret = int($ret); return $ret; } sub trim { my ($s) = (@_, $_); # Passed in var, or default to $_ $s =~ s/^\s*//; $s =~ s/\s*$//; return $s; } # String format: '115', '165_bold', '10_on_140', 'reset', 'on_173', 'red', 'white_on_blue' sub color { my ($str, $txt) = @_; # If we're NOT connected to a an interactive terminal don't do color if (-t STDOUT == 0) { return ''; } # No string sent in, so we just reset if (!length($str) || $str eq 'reset') { return "\e[0m"; } # Some predefined colors my %color_map = qw(red 160 blue 27 green 34 yellow 226 orange 214 purple 93 white 15 black 0); $str =~ s|([A-Za-z]+)|$color_map{$1} // $1|eg; # Get foreground/background and any commands my ($fc,$cmd) = $str =~ /^(\d{1,3})?_?(\w+)?$/g; my ($bc) = $str =~ /on_(\d{1,3})$/g; # Some predefined commands my %cmd_map = qw(bold 1 italic 3 underline 4 blink 5 inverse 7); my $cmd_num = $cmd_map{$cmd // 0}; my $ret = ''; if ($cmd_num) { $ret .= "\e[${cmd_num}m"; } if (defined($fc)) { $ret .= "\e[38;5;${fc}m"; } if (defined($bc)) { $ret .= "\e[48;5;${bc}m"; } if ($txt) { $ret .= $txt . "\e[0m"; } return $ret; } sub file_get_contents { open (my $fh, "<", $_[0]) or return undef; my $array_mode = ($_[1]) || (!defined($_[1]) && wantarray); if ($array_mode) { # Line mode return my @lines = readline($fh); } else { # String mode local $/ = undef; # Input rec separator (slurp) return my $ret = readline($fh); } } sub file_put_contents { my ($file, $data) = @_; open (my $fh, ">", $file) or return undef; print $fh $data; return length($data); } # Creates methods k() and kd() to print, and print & die respectively BEGIN { if (eval { require Data::Dump::Color }) { *k = sub { Data::Dump::Color::dd(@_) }; } else { require Data::Dumper; *k = sub { print Data::Dumper::Dumper(\@_) }; } sub kd { k(@_); printf("Died at %2\$s line #%3\$s\n",caller()); exit(15); } } sub in_array { my ($needle, @haystack) = @_; foreach my $l (@haystack) { if ($l eq $needle) { return 1; } } return 0; } # vim: tabstop=4 shiftwidth=4 noexpandtab autoindent softtabstop=4