Day 4
[advent-of-code-2021.git] / day04 / bingo.sh
1 #!/bin/bash
2
3 set -e
4 set -u
5
6 filename=${1:-example.txt}
7
8 bold=$(tput bold)
9 normal=$(tput sgr0)
10
11 # each board is a 5*5 grid, so that's handy, we'll store all the boards in one
12 # array, then create a second array that shows which matches we have, we can
13 # select any given board by limiting to a range of 25 values from the array
14
15 exec 3<"$filename"
16
17 # first line is the draws, so read that in to an array too
18 OLDIFS="$IFS"
19 IFS=","
20 read -u 3 -a draw
21
22 read -u 3 line
23
24 IFS="   "
25 line_count=0
26
27 declare -a boards
28 declare -a checked
29
30 while read -u 3 -a row; do
31     ((line_count+=1))
32     boards+=("${row[@]}")
33     if [ $line_count -eq 5 ]; then
34         read -u 3 line || true
35         line_count=0
36     fi
37 done
38
39 for x in ${boards[@]}; do
40     checked+=(0)
41 done
42
43 display_board() {
44     local board_number=$1
45     offset=$((25*$board_number))
46
47     count=0
48
49     for val in "${boards[@]:$offset:25}"; do
50         if [ ${checked[$((offset+$count))]} -eq 1 ]; then
51             echo -n "${bold}"
52         fi
53         ((count+=1))
54         printf "%2d " $val
55         echo -n "${normal}"
56         if [ $((count%5)) -eq 0 ]; then
57             echo
58         fi
59     done
60     echo
61 }
62
63 check_board() {
64     local board_number=$1
65     local drawn_number=$2
66     offset=$((25*$board_number))
67
68     count=0
69     for val in "${boards[@]:$offset:25}"; do
70         if [ $val -eq $drawn_number ]; then
71             checked[$((offset+$count))]=1
72         fi
73         ((count+=1))
74     done
75
76     row=0
77     col=0
78     declare -a row_matches=(0 0 0 0 0)
79     declare -a col_matches=(0 0 0 0 0)
80
81     for val in "${checked[@]:$offset:25}"; do
82         if [ $val -eq 1 ]; then
83             ((col_matches[$col]+=1))
84             ((row_matches[$row]+=1))
85         fi
86         ((col+=1))
87         if [ $col -eq 5 ]; then
88             col=0
89             ((row+=1))
90         fi
91     done
92
93     for row in "${row_matches[@]}"; do
94         if [ $row -eq 5 ]; then
95             return 0
96         fi
97     done
98
99     for col in "${col_matches[@]}"; do
100         if [ $col -eq 5 ]; then
101             return 0
102         fi
103     done
104
105     return 1
106 }
107
108 get_unmarked_count() {
109     local board_number=$1
110     local offset=$((board_number*25))
111     local count=0
112
113     for ((i=0; i<25; i++)); do
114         if [ ${checked[$(($offset+$i))]} -eq 0 ]; then
115             ((count+=${boards[$(($offset+$i))]}))
116         fi
117     done
118
119     echo $count
120 }
121
122 declare -a completed_boards=()
123
124 in_array() {
125     local value=$1
126     local -n array=$2
127
128     for val in "${array[@]}"; do
129         if [ $val -eq $value ]; then
130             return 0
131         fi
132     done
133
134     return 1
135 }
136
137 first_board=-1
138 first_board_last_number=-1
139 last_board_last_number=-1
140
141 for number in "${draw[@]}"; do
142     for (( board=0; board<$((${#boards[@]} / 25)); board++ )); do
143         if ( ! in_array $board completed_boards ); then
144             check_board $board $number && { if [ $first_board -eq -1 ]; then first_board=$board; first_board_last_number=$number; fi; completed_boards+=( $board ); } || true
145             if [ ${#completed_boards[@]} -eq $((${#boards[@]} / 25)) ]; then
146                 last_board_last_number=$number
147                 break 2
148             fi
149         fi
150     done
151 done
152
153 echo "Board matched: $first_board"
154 echo "Last number drawn: $first_board_last_number"
155 echo
156 display_board $first_board
157
158 unmarked=$(get_unmarked_count $first_board)
159
160 echo "$unmarked * $first_board_last_number = "$((unmarked*$first_board_last_number))
161
162 echo "Last board matched: "${completed_boards[-1]}
163 echo "Last number drawn: $last_board_last_number"
164 echo
165 display_board ${completed_boards[-1]}
166
167 unmarked=$(get_unmarked_count ${completed_boards[-1]})
168
169 echo "$unmarked * $last_board_last_number = "$((unmarked*$last_board_last_number))