#!/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) = @_; 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) = @_; my @winning_board_ref; # 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) { push (@winning_board_ref, $board); last; } } # 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) { push (@winning_board_ref, $board); last; } } } return @winning_board_ref; } 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; my @winner_found; my $final_board; while ( scalar ( @boards ) ) { @winner_found = (); $turn++; $number = shift( @numbers ); printf "turn: %2d, playing number %2d\n", $turn, $number; play_turn($number, \@boards, \%state); @winner_found = check_for_winner(\@boards, \%state); if ( @winner_found ) { say "found a winning board"; say "we had ", scalar(@boards), " boards"; foreach my $winning_board (@winner_found) { @boards = grep { $_ ne $winning_board } @boards; } say "we now have ", scalar(@boards), " boards"; } unless (@numbers) { # if we run out of numbers say "Out of numbers. No winner found."; exit; } $final_board = $boards[0] if ( scalar( @boards ) == 1 ); } my $score = calculate_score($number, $final_board, \%state); say "winning score is $score";