Day 15
[advent-of-code-2019.git] / day15 / computer.sh
diff --git a/day15/computer.sh b/day15/computer.sh
new file mode 100644 (file)
index 0000000..8d7a56e
--- /dev/null
@@ -0,0 +1,166 @@
+#!/bin/bash
+
+declare -a data
+
+get_value() {
+    local -n da=$1
+    local index=$2
+
+    if [ $index -lt 0 ]; then
+        echo "Index negative, give up!"
+        exit 2
+    fi
+
+    if ! [ ${da[$index]+a} ]; then
+        da[$index]=0
+    fi
+
+    declare -g __comp_value="${da[$index]}"
+}
+
+get_param() {
+    local -n dap=$1
+    local pos=$2
+    local mode=$3
+    local relative_base=$4
+
+    get_param_loc dap $pos $mode $relative_base
+    loc=$__comp_param_loc
+
+    get_value dap $loc
+}
+
+get_param_loc() {
+    local -n dapl=$1
+    local pos=$2
+    local mode=$3
+    local relative_base=$4
+
+    case $mode in
+        0)
+            get_value dapl $pos
+            declare -g __comp_param_loc=$__comp_value
+            ;;
+        1)
+            declare -g __comp_param_loc=$pos
+            ;;
+        2)
+            get_value dapl $pos
+            move_by=$__comp_value
+            declare -g __comp_param_loc=$((relative_base+$move_by))
+            ;;
+        *)
+            echo "Unknown parameter mode: $mode"
+            exit 2
+            ;;
+    esac
+}
+
+run_program() {
+    local -n od=$1
+    local relative_base=0
+    data=("${od[@]}")
+    pos=0
+    declare -a immediate
+    while [ $pos -le ${#data[@]} ]; do
+        # first, printf the value to 5 digits
+        instruction=$(printf "%05d" ${data[$pos]})
+        for (( a=0; a<3; a++ )); do
+            immediate[$((3-a))]=${instruction:$a:1}
+        done
+        instruction=${instruction:3}
+        param_count=3
+        echo -n $'\r'"    "$'\r'$instruction >&2
+        case $instruction in
+            01|02)
+                # 01 - add, 02 multiply
+                symbol="+"
+                if [ $instruction == "02" ]; then
+                    symbol="*"
+                fi
+                get_param data $((pos+1)) ${immediate[1]} $relative_base
+                val1=$__comp_value
+                get_param data $((pos+2)) ${immediate[2]} $relative_base
+                val2=$__comp_value
+                get_param_loc data $((pos+3)) ${immediate[3]} $relative_base
+                res_loc=$__comp_param_loc
+                data[$res_loc]=$(($val1 $symbol $val2))
+                ;;
+            03)
+                # 03 - read input
+                echo -n $'\r'"    "$'\r' >&2
+                echo "input: "
+                read input
+                input=${input//\n/}
+                input=${input//\r/}
+                echo >&2
+                echo "$instruction: '$input'" >&2
+                echo >&2
+                get_param_loc data $((pos+1)) ${immediate[1]} $relative_base
+                res_loc=$__comp_param_loc
+                data[$res_loc]=$input
+                param_count=1
+                ;;
+            04)
+                # 04 - output
+                get_param data $((pos+1)) ${immediate[1]} $relative_base
+                val=$__comp_value
+                echo -n $'\r'"    "$'\r' >&2
+                echo $val
+                param_count=1
+                ;;
+            05|06)
+                # 05 - jump-if-true, 06 - jump-if-false
+                get_param data $((pos+1)) ${immediate[1]} $relative_base
+                val=$__comp_value
+                get_param data $((pos+2)) ${immediate[2]} $relative_base
+                jumpto=$__comp_value
+                param_count=2
+                if ( [ $val -ne 0 ] && [ "$instruction" == "05" ] ) ||
+                    ( [ $val -eq 0 ] && [ "$instruction" == "06" ] ); then
+                    pos=$jumpto
+                    continue
+                fi
+                ;;
+            07|08)
+                # 07 - is less than, 08 - is equal
+                get_param data $((pos+1)) ${immediate[1]} $relative_base
+                val1=$__comp_value
+                get_param data $((pos+2)) ${immediate[2]} $relative_base
+                val2=$__comp_value
+                echo >&2
+                echo "$instruction: '$val1' '$val2'" >&2
+                echo >&2
+                get_param_loc data $((pos+3)) ${immediate[3]} $relative_base
+                res_pos=$__comp_param_loc
+                data[$res_pos]=0
+                if [ "$instruction" == "07" ]; then
+                    if [ $val1 -lt $val2 ]; then
+                        data[$res_pos]=1
+                    fi
+                else
+                    if [ $val1 -eq $val2 ]; then
+                        data[$res_pos]=1
+                    fi
+                fi
+                ;;
+            09)
+                # adjust relative base
+                param_count=1
+                get_param data $((pos+1)) ${immediate[1]} $relative_base
+                val=$__comp_value
+                relative_base=$((relative_base+$val))
+                ;;
+            99)
+                break
+                ;;
+            *)
+                echo "Invalid opcode: $instruction at position $pos"
+                exit 1
+                ;;
+        esac
+        pos=$(($pos+$param_count+1))
+    done
+    echo -n $'\r'"    "$'\r' >&2
+}
+