#!/usr/bin/env bash adv () { # OPCODE 0 $DEBUG && printf "OPCODE : 0 OP: adv OPERAND: %s\n" "$1" >&2 OPERAND=$( get_combo "$1" ) #REG_A=$(( REG_A / (2 ** OPERAND) )) REG_A=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc ) } bxl () { # OPCODE 1 $DEBUG && printf "OPCODE : 1 OP: bxl OPERAND: %s\n" "$1" >&2 OPERAND=$1 REG_B=$(( REG_B ^ OPERAND )) } bst () { # OPCODE 2 $DEBUG && printf "OPCODE : 2 OP: bst OPERAND: %s\n" "$1" >&2 OPERAND=$( get_combo "$1" ) REG_B=$(( OPERAND % 8 )) } jnz () { # OPCODE 3 $DEBUG && printf "OPCODE : 3 OP: jnz OPERAND: %s\n" "$1" >&2 OPERAND=$1 if [[ $REG_A -eq 0 ]] then return 0 else POINTER=$OPERAND fi } bxc () { # OPCODE 4 $DEBUG && printf "OPCODE : 4 OP: bxc OPERAND: %s\n" "$1" >&2 # OPERAND=$1 # Deprecated for legacy reasons REG_B=$(( REG_B ^ REG_C )) } out () { # OPCODE 5 $DEBUG && printf "OPCODE : 5 OP: out OPERAND: %s\n" "$1" >&2 OPERAND=$( get_combo "$1" ) printf "%s\n" "$(( OPERAND % 8 ))" } bdv () { # OPCODE 6 $DEBUG && printf "OPCODE : 6 OP: bdv OPERAND: %s\n" "$1" >&2 OPERAND=$( get_combo "$1" ) #REG_B=$(( REG_A / (2 ** OPERAND) )) REG_B=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc ) } cdv () { # OPCODE 7 $DEBUG && printf "OPCODE : 7 OP: cdv OPERAND: %s\n" "$1" >&2 OPERAND=$( get_combo "$1" ) #REG_C=$(( REG_A / (2 ** OPERAND) )) REG_C=$( printf "%s / (2 ^ %s) \n" "$REG_A" "$OPERAND" | bc ) } # Get combo operand get_combo () { if [[ $OPERAND -eq 0 ]] then printf "$1" elif [[ $OPERAND -eq 1 ]] then printf "$1" elif [[ $OPERAND -eq 2 ]] then printf "$1" elif [[ $OPERAND -eq 3 ]] then printf "$1" elif [[ $OPERAND -eq 4 ]] then printf "%s" "$REG_A" elif [[ $OPERAND -eq 5 ]] then printf "%s" "$REG_B" elif [[ $OPERAND -eq 6 ]] then printf "%s" "$REG_C" elif [[ $OPERAND -eq 7 ]] then printf "Reserved operand. Exiting.\n" >&2 exit 1 fi } # Check if the program is a quine check_quine () { $DEBUG && printf "%s," "${INPUT[@]}" $DEBUG && printf "%s," "${OUTPUT[@]}" for (( i=0; i<${#INPUT[@]}; i++ )) do if [[ ${INPUT[$i]} != "${OUTPUT[$i]}" ]] then return 1 fi done } # Check digits check_tail () { $DEBUG && printf "%s," "${INPUT[@]}" $DEBUG && printf "%s," "${OUTPUT[@]}" for (( i=${#INPUT[@]}; i>=${#INPUT[@]}-$1; i-- )) do if [[ ${INPUT[$i]} != "${OUTPUT[$i]}" ]] then return 1 fi done } # Interpret opcode read_opcode () { local OPCODE=$1 local OPERAND=$2 if [[ $OPCODE -eq 0 ]] then adv "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 1 ]] then bxl "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 2 ]] then bst "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 3 ]] then # jnz itself moves the pointer jnz "$OPERAND" elif [[ $OPCODE -eq 4 ]] then bxc "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 5 ]] then out "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 6 ]] then bdv "$OPERAND" (( POINTER+=2 )) elif [[ $OPCODE -eq 7 ]] then cdv "$OPERAND" (( POINTER+=2 )) fi } execute_machine () { POINTER=0 while [[ $POINTER -lt $INPUT_LEN ]] do OPCODE=${INPUT[$POINTER]} OPERAND=${INPUT[$POINTER+1]} read_opcode "$OPCODE" "$OPERAND" $DEBUG && printf "Registers A : %s B: %s C : %s\n" "$REG_A" "$REG_B" "$REG_C" >&2 $DEBUG && printf "Pointer : %s\n" "$POINTER" >&2 (( MACHINE_ITER-- )) if [[ $MACHINE_ITER -eq 0 ]] ; then break ; fi done | paste -s -d " " }