#!/usr/bin/env perl use strict; use warnings; use v5.22; sub get_filehandle { if (@ARGV !=1) { die "Usage: $0 [input-filename]"; } my $input_filename = $ARGV[0]; open my $filehandle, '<', $input_filename or die "Could not open input file $input_filename: $!"; return $filehandle; } sub get_numbers { my $fh = shift; chomp( my $numbers = ( <$fh> ) ); return (split( ",", $numbers)); } sub initialize_boards { my $fh = shift; chomp( my @input = ( <$fh> ) ); my (@boards, @new_board); foreach (@input) { if (/\A\s*\Z/) { push(@boards, [@new_board]) if (@new_board); @new_board = (); } else { push(@new_board, [split]); } } push(@boards, \@new_board); return (@boards); } sub initialize_state { my %state; my $boards = shift; foreach my $board (@$boards) { foreach my $row (@$board) { foreach my $column (@$row) { $state{$board}{$row}{$column} = 0; } } } return %state; } sub play_turn { my ($number, $boards, $state) = @_; printf "playing number %2d\n", $number; foreach my $board (@$boards) { # $board is ref to board array foreach my $row (@$board) { # $row is ref to board row array foreach my $column (@$row) { # $column is element of row array if ($column == $number) { $state->{$board}{$row}{$column} = 1; } } } } } # for debugging purposes only sub print_board_state { my ($boards, $state) = @_; foreach my $board (@$boards) { # $board is ref to board array foreach my $row (@$board) { # $row is ref to board row array foreach my $column (@$row) { # $column is element of row array print "$board\t$row\t"; if ($state->{$board}{$row}{$column} == 1) { printf "-> %2d <-\n", $column; } else { printf " %2d\n", $column; } } } } } sub check_for_winner { my ($boards, $state) = @_; # check for row wins foreach my $board (@$boards) { # $board is ref to board array foreach my $row (@$board) { # $row is ref to board row array my $total = 0; foreach my $column (@$row) { # $column is the actual value of # the number in @$row $total += $state->{$board}{$row}{$column}; } if ($total == 5) { #say "found row winner"; return $board; } } # for each board, iterate through each column (0 .. 4) foreach (0 .. 4) { my $total = 0; foreach my $row (@$board) { # here, $_ represents the index within the array @$row. Because the hash key # stores the actual value, we need to deference $row (which is an array # reference) and then look up the value stored in the index $_ $total += $state->{$board}{$row}{$row->[$_]}; } if ($total == 5) { #say "found column winner"; return $board; } } } return 0; # no winner found } sub calculate_score { my ($number, $board, $state) = @_; my $score = 0; foreach my $row (@$board) { # $row is ref to board row array foreach my $column (@$row) { # $column is element of row array if ($state->{$board}{$row}{$column} == 0) { $score += $column; } } } return $score *= $number; } # Advent of Code 2021 Day 04 Part 1 # initialize game variables my $filehandle = get_filehandle(); my @numbers = get_numbers($filehandle); my $number; my @boards = initialize_boards($filehandle); my %state = initialize_state(\@boards); my $turn = 0; # $winner_found plays double duty. When equal to 0, it means that # we haven't yet found a winning board. When we do, the subroutine # check_for_winner returns a reference to the array of the winning # board which we can then submit to calculate_score. my $winner_found = 0; until ($winner_found) { $turn++; printf "turn: %2d, ", $turn; $number = shift( @numbers ); play_turn($number, \@boards, \%state); $winner_found = check_for_winner(\@boards, \%state); unless (@numbers) { # if we run out of numbers say "Out of numbers. No winner found."; last; } } my $score = calculate_score($number, $winner_found, \%state); say "winning score is $score";