Rename days to 2 digits always
[advent-of-code-2019.git] / day09 / computer.sh
1 #!/bin/bash
2
3 declare -a data
4
5 get_value() {
6     local -n da=$1
7     local index=$2
8
9     if [ $index -lt 0 ]; then
10         echo "Index negative, give up!"
11         exit 2
12     fi
13
14     if ! [ ${da[$index]+a} ]; then
15         da[$index]=0
16     fi
17
18     echo "${da[$index]}"
19 }
20
21 get_param() {
22     local -n dap=$1
23     local pos=$2
24     local mode=$3
25     local relative_base=$4
26
27     loc=$(get_param_loc dap $pos $mode $relative_base)
28
29     get_value dap $loc
30 }
31
32 get_param_loc() {
33     local -n dapl=$1
34     local pos=$2
35     local mode=$3
36     local relative_base=$4
37
38     case $mode in
39         0)
40             get_value dapl $pos
41             ;;
42         1)
43             echo $pos
44             ;;
45         2)
46             move_by=$(get_value dapl $pos)
47             echo $((relative_base+$move_by))
48             ;;
49         *)
50             echo "Unknown parameter mode: $mode"
51             exit 2
52             ;;
53     esac
54 }
55
56 run_program() {
57     local -n od=$1
58     local relative_base=0
59     data=("${od[@]}")
60     pos=0
61     declare -a immediate
62     while [ $pos -le ${#data[@]} ]; do
63         # first, printf the value to 5 digits
64         instruction=$(printf "%05d" ${data[$pos]})
65         for (( a=0; a<3; a++ )); do
66             immediate[$((3-a))]=${instruction:$a:1}
67         done
68         instruction=${instruction:3}
69         param_count=3
70         echo -n $'\r'"    "$'\r'$instruction >&2
71         case $instruction in
72             01|02)
73                 # 01 - add, 02 multiply
74                 symbol="+"
75                 if [ $instruction == "02" ]; then
76                     symbol="*"
77                 fi
78                 val1=$(get_param data $((pos+1)) ${immediate[1]} $relative_base)
79                 val2=$(get_param data $((pos+2)) ${immediate[2]} $relative_base)
80                 res_loc=$(get_param_loc data $((pos+3)) ${immediate[3]} $relative_base)
81                 data[$res_loc]=$(($val1 $symbol $val2))
82                 ;;
83             03)
84                 # 03 - read input
85                 echo -n $'\r'"    "$'\r' >&2
86                 read -p "input: " input
87                 res_loc=$(get_param_loc data $((pos+1)) ${immediate[1]} $relative_base)
88                 data[$res_loc]=$input
89                 param_count=1
90                 ;;
91             04)
92                 # 04 - output
93                 val=$(get_param data $((pos+1)) ${immediate[1]} $relative_base)
94                 echo -n $'\r'"    "$'\r' >&2
95                 echo $val
96                 param_count=1
97                 ;;
98             05|06)
99                 # 05 - jump-if-true, 06 - jump-if-false
100                 val=$(get_param data $((pos+1)) ${immediate[1]} $relative_base)
101                 jumpto=$(get_param data $((pos+2)) ${immediate[2]} $relative_base)
102                 param_count=2
103                 if ( [ $val -ne 0 ] && [ "$instruction" == "05" ] ) ||
104                     ( [ $val -eq 0 ] && [ "$instruction" == "06" ] ); then
105                     pos=$jumpto
106                     continue
107                 fi
108                 ;;
109             07|08)
110                 # 07 - is less than, 08 - is equal
111                 val1=$(get_param data $((pos+1)) ${immediate[1]} $relative_base)
112                 val2=$(get_param data $((pos+2)) ${immediate[2]} $relative_base)
113                 res_pos=$(get_param_loc data $((pos+3)) ${immediate[3]} $relative_base)
114                 data[$res_pos]=0
115                 if [ "$instruction" == "07" ]; then
116                     if [ $val1 -lt $val2 ]; then
117                         data[$res_pos]=1
118                     fi
119                 else
120                     if [ $val1 -eq $val2 ]; then
121                         data[$res_pos]=1
122                     fi
123                 fi
124                 ;;
125             09)
126                 # adjust relative base
127                 param_count=1
128                 val=$(get_param data $((pos+1)) ${immediate[1]} $relative_base)
129                 relative_base=$((relative_base+$val))
130                 ;;
131             99)
132                 break
133                 ;;
134             *)
135                 echo "Invalid opcode: $instruction at position $pos"
136                 exit 1
137                 ;;
138         esac
139         pos=$(($pos+$param_count+1))
140     done
141     echo -n $'\r'"    "$'\r'
142 }
143