Day 4
[advent-of-code-2021.git] / day04 / bingo.sh
diff --git a/day04/bingo.sh b/day04/bingo.sh
new file mode 100755 (executable)
index 0000000..4348325
--- /dev/null
@@ -0,0 +1,169 @@
+#!/bin/bash
+
+set -e
+set -u
+
+filename=${1:-example.txt}
+
+bold=$(tput bold)
+normal=$(tput sgr0)
+
+# each board is a 5*5 grid, so that's handy, we'll store all the boards in one
+# array, then create a second array that shows which matches we have, we can
+# select any given board by limiting to a range of 25 values from the array
+
+exec 3<"$filename"
+
+# first line is the draws, so read that in to an array too
+OLDIFS="$IFS"
+IFS=","
+read -u 3 -a draw
+
+read -u 3 line
+
+IFS="  "
+line_count=0
+
+declare -a boards
+declare -a checked
+
+while read -u 3 -a row; do
+    ((line_count+=1))
+    boards+=("${row[@]}")
+    if [ $line_count -eq 5 ]; then
+        read -u 3 line || true
+        line_count=0
+    fi
+done
+
+for x in ${boards[@]}; do
+    checked+=(0)
+done
+
+display_board() {
+    local board_number=$1
+    offset=$((25*$board_number))
+
+    count=0
+
+    for val in "${boards[@]:$offset:25}"; do
+        if [ ${checked[$((offset+$count))]} -eq 1 ]; then
+            echo -n "${bold}"
+        fi
+        ((count+=1))
+        printf "%2d " $val
+        echo -n "${normal}"
+        if [ $((count%5)) -eq 0 ]; then
+            echo
+        fi
+    done
+    echo
+}
+
+check_board() {
+    local board_number=$1
+    local drawn_number=$2
+    offset=$((25*$board_number))
+
+    count=0
+    for val in "${boards[@]:$offset:25}"; do
+        if [ $val -eq $drawn_number ]; then
+            checked[$((offset+$count))]=1
+        fi
+        ((count+=1))
+    done
+
+    row=0
+    col=0
+    declare -a row_matches=(0 0 0 0 0)
+    declare -a col_matches=(0 0 0 0 0)
+
+    for val in "${checked[@]:$offset:25}"; do
+        if [ $val -eq 1 ]; then
+            ((col_matches[$col]+=1))
+            ((row_matches[$row]+=1))
+        fi
+        ((col+=1))
+        if [ $col -eq 5 ]; then
+            col=0
+            ((row+=1))
+        fi
+    done
+
+    for row in "${row_matches[@]}"; do
+        if [ $row -eq 5 ]; then
+            return 0
+        fi
+    done
+
+    for col in "${col_matches[@]}"; do
+        if [ $col -eq 5 ]; then
+            return 0
+        fi
+    done
+
+    return 1
+}
+
+get_unmarked_count() {
+    local board_number=$1
+    local offset=$((board_number*25))
+    local count=0
+
+    for ((i=0; i<25; i++)); do
+        if [ ${checked[$(($offset+$i))]} -eq 0 ]; then
+            ((count+=${boards[$(($offset+$i))]}))
+        fi
+    done
+
+    echo $count
+}
+
+declare -a completed_boards=()
+
+in_array() {
+    local value=$1
+    local -n array=$2
+
+    for val in "${array[@]}"; do
+        if [ $val -eq $value ]; then
+            return 0
+        fi
+    done
+
+    return 1
+}
+
+first_board=-1
+first_board_last_number=-1
+last_board_last_number=-1
+
+for number in "${draw[@]}"; do
+    for (( board=0; board<$((${#boards[@]} / 25)); board++ )); do
+        if ( ! in_array $board completed_boards ); then
+            check_board $board $number && { if [ $first_board -eq -1 ]; then first_board=$board; first_board_last_number=$number; fi; completed_boards+=( $board ); } || true
+            if [ ${#completed_boards[@]} -eq $((${#boards[@]} / 25)) ]; then
+                last_board_last_number=$number
+                break 2
+            fi
+        fi
+    done
+done
+
+echo "Board matched: $first_board"
+echo "Last number drawn: $first_board_last_number"
+echo
+display_board $first_board
+
+unmarked=$(get_unmarked_count $first_board)
+
+echo "$unmarked * $first_board_last_number = "$((unmarked*$first_board_last_number))
+
+echo "Last board matched: "${completed_boards[-1]}
+echo "Last number drawn: $last_board_last_number"
+echo
+display_board ${completed_boards[-1]}
+
+unmarked=$(get_unmarked_count ${completed_boards[-1]})
+
+echo "$unmarked * $last_board_last_number = "$((unmarked*$last_board_last_number))