Working day16 parser
[advent-of-code-2021.git] / day16 / decoder.sh
index fe67cbdfbc5a1976a55b1b2fe36c765a375948a2..cdf81667cd4c212ef28204c4a888f91955f56f82 100755 (executable)
@@ -19,6 +19,7 @@ done
 declare -g __versions_total=0
 
 declare __packet_offset=0
+declare -g __cur_level=0
 
 if [ $debug == "0"  ]; then
     echo_verbose() { true; }
@@ -27,18 +28,21 @@ else
 fi
 
 parse_version() {
+    local level="${1:- 1}"
     declare -g __packet_version=$((2#${__packets:$__packet_offset:3}))
     ((__packet_offset+=3))
     echo_verbose "Packet version: $__packet_version"
 }
 
 parse_type() {
+    local level="${1:- 1}"
     declare -g __packet_type=$((2#${__packets:$__packet_offset:3}))
     ((__packet_offset+=3))
     echo_verbose "Packet type: $__packet_type"
 }
 
 parse_literal() {
+    local level="${1:- 1}"
     local keep_going=${__packets:$__packet_offset:1}
     local binary_value=""
     ((__packet_offset+=1))
@@ -51,49 +55,278 @@ parse_literal() {
     # if we're here, we're on to the last group
     binary_value+=${__packets:$__packet_offset:4}
     ((__packet_offset+=4))
-    declare -g __packet_literal=$((2#$binary_value))
-    echo_verbose "Packet literal: $__packet_literal"
+    declare -g __packet_value=$((2#$binary_value))
+    echo_verbose "Packet literal: $__packet_value"
 }
 
 parse_operator() {
+    local level="${1:- 1}"
+    local ptype="${2:-}"
     local len_type=$((2#${__packets:$__packet_offset:1}))
+    local number=0
     ((__packet_offset+=1))
 
     case $len_type in
         0)
-            __packets_length=$((2#${__packets:$__packet_offset:15}))
+            number=$((2#${__packets:$__packet_offset:15}))
             ((__packet_offset+=15))
             ;;
         1)
-            __packets_count=$((2#${__packets:$__packet_offset:11}))
+            number=$((2#${__packets:$__packet_offset:11}))
             ((__packet_offset+=11))
             ;;
     esac
+
+    case $ptype in
+        0)
+            parse_sum $len_type $number
+            ;;
+        1)
+            parse_product $len_type $number
+            ;;
+        2)
+            parse_minimum $len_type $number
+            ;;
+        3)
+            parse_maximum $len_type $number
+            ;;
+        5)
+            parse_greaterthan $len_type $number
+            ;;
+        6)
+            parse_lessthan $len_type $number
+            ;;
+        7)
+            parse_equal $len_type $number
+            ;;
+    esac
+}
+
+parse_sum() {
+    local len_type=$1
+    local count=$2
+    local total=0
+    local id=$__packet_offset
+    local a=0
+
+    echo_verbose "[$id]: Doing a sum ($len_type - $count)"
+
+    case $len_type in
+        0)
+            local end_offset=$((__packet_offset+$count))
+            while [ $__packet_offset -lt $end_offset ]; do
+                echo_verbose "[$id]: Getting sum sub packet"
+                parse_packet
+                ((total+=$__packet_value))
+            done
+            ;;
+        1)
+            for (( a=0; a<$count; a++ )); do
+                echo_verbose "[$id]: Getting sum sub packet"
+                parse_packet
+                ((total+=$__packet_value))
+            done
+            ;;
+    esac
+
+    echo_verbose "[$id]: Sum total: $total (offset: $__packet_offset)"
+
+    declare -g __packet_value=$total
+}
+
+parse_product() {
+    local len_type=$1
+    local count=$2
+    local total=1
+    local id=$__packet_offset
+    local a=0
+
+    echo_verbose "[$id]: Doing a product ($len_type - $count)"
+
+    case $len_type in
+        0)
+            local end_offset=$((__packet_offset+$count))
+            while [ $__packet_offset -lt $end_offset ]; do
+                echo_verbose "[$id]: Getting product sub packet"
+                parse_packet
+                ((total*=$__packet_value))
+            done
+            ;;
+        1)
+            for (( a=0; a<$count; a++ )); do
+                echo_verbose "[$id]: Getting product sub packet"
+                parse_packet
+                ((total*=$__packet_value))
+            done
+            ;;
+    esac
+
+    echo_verbose "[$id]: Product total: $total (offset: $__packet_offset)"
+
+    declare -g __packet_value=$total
+}
+
+parse_minimum() {
+    local len_type=$1
+    local count=$2
+    local minimum=
+    local id=$__packet_offset
+    local a=0
+
+    echo_verbose "[$id]: Doing mimimum"
+
+    case $len_type in
+        0)
+            local end_offset=$((__packet_offset+$count))
+            while [ $__packet_offset -lt $end_offset ]; do
+                echo_verbose "[$id]: Getting minimum sub packet"
+                parse_packet
+                if [ -z $minimum ] || [ $__packet_value -lt $minimum ]; then
+                    minimum=$__packet_value
+                fi
+            done
+            ;;
+        1)
+            for (( a=0; a<$count; a++ )); do
+                echo_verbose "[$id]: Getting minimum sub packet"
+                parse_packet
+                if [ -z $minimum ] || [ $__packet_value -lt $minimum ]; then
+                    minimum=$__packet_value
+                fi
+            done
+            ;;
+    esac
+
+    echo_verbose "[$id]: Minimum value: $minimum (offset: $__packet_offset)"
+
+    declare -g __packet_value=$minimum
+}
+
+parse_maximum() {
+    local len_type=$1
+    local count=$2
+    local maximum=
+    local id=$__packet_offset
+    local a
+
+    echo_verbose "[$id]: Doing maximum ($len_type - $count)"
+
+    case $len_type in
+        0)
+            local end_offset=$((__packet_offset+$count))
+            while [ $__packet_offset -lt $end_offset ]; do
+                echo_verbose "[$id]: Getting maximum sub packet"
+                parse_packet
+                if [ -z $maximum ] || [ $__packet_value -gt $maximum ]; then
+                    maximum=$__packet_value
+                fi
+            done
+            ;;
+        1)
+            for (( a=0; a<$count; a++ )); do
+                echo_verbose "[$id]: Getting maximum sub packet"
+                parse_packet
+                if [ -z $maximum ] || [ $__packet_value -gt $maximum ]; then
+                    maximum=$__packet_value
+                fi
+            done
+            ;;
+    esac
+
+    echo_verbose "[$id]: Maximum value: $maximum (offset: $__packet_offset)"
+
+    declare -g __packet_value=$maximum
+}
+
+parse_greaterthan() {
+    local len_type=$1
+    local count=$2
+    local id=$__packet_offset
+
+    echo_verbose "[$id]: Doing >"
+
+    # we don't actually care, we know that it's exactly 2 sub packets that we want... so
+    parse_packet
+    local val1=$__packet_value
+    parse_packet
+    local val2=$__packet_value
+
+    if [ $val1 -gt $val2 ]; then
+        declare -g __packet_value=1
+    else
+        declare -g __packet_value=0
+    fi
+
+    echo_verbose "[$id]: Got $__packet_value (offset: $__packet_offset)"
+}
+
+parse_lessthan() {
+    local len_type=$1
+    local count=$2
+    local id=$__packet_offset
+
+    echo_verbose "[$id]: Doing <"
+
+    # we don't actually care, we know that it's exactly 2 sub packets that we want... so
+    parse_packet
+    local val1=$__packet_value
+    parse_packet
+    local val2=$__packet_value
+
+    if [ $val1 -lt $val2 ]; then
+        declare -g __packet_value=1
+    else
+        declare -g __packet_value=0
+    fi
+
+    echo_verbose "[$id]: Got $__packet_valuei (offset: $__packet_offset)"
+}
+
+parse_equal() {
+    local len_type=$1
+    local count=$2
+    local id=$__packet_offset
+
+    echo_verbose "[$id]: Doing ="
+
+    # we don't actually care, we know that it's exactly 2 sub packets that we want... so
+    parse_packet
+    local val1=$__packet_value
+    parse_packet
+    local val2=$__packet_value
+
+    if [ $val1 -eq $val2 ]; then
+        declare -g __packet_value=1
+    else
+        declare -g __packet_value=0
+    fi
+
+    echo_verbose "[$id]: Got $__packet_value (offset: $__packet_offset)"
+}
+
+parse_packet() {
+    parse_version && local pversion=$__packet_version
+    parse_type && local ptype=$__packet_type
+
+    ((__versions_total+=$pversion))
+
+    case $ptype in
+        4)
+            parse_literal
+            ;;
+        *)
+            parse_operator $__cur_level $ptype
+            ;;
+    esac
 }
 
 parse_packets() {
+    local level="${1:- 1}"
     # we'll first need the version and the type, so grab those
-    local pversion
-    local ptype
 
     while [ $__packet_offset -lt ${#__packets} ]; do
-        parse_version
-        parse_type
-        version=$__packet_version
-        ptype=$__packet_type
-
-        ((__versions_total+=$version))
-
-        echo_verbose "Remaining: ${__packets:$__packet_offset}"
 
-        case $ptype in
-            4)
-                parse_literal
-                ;;
-            *)
-                parse_operator
-                ;;
-        esac
+        parse_packet
 
         if [ $(($__packet_offset+7)) -ge ${#__packets} ]; then
             break
@@ -104,3 +337,4 @@ parse_packets() {
 parse_packets
 
 echo "Total: $__versions_total"
+echo "End value: $__packet_value"