Search code examples
arraysbashcounterassociative

Digit counter in Bash improvement needed


I made a simple digit counter with associative arrays. Is there a better way to declare and initialize those arrays especially "segments" array to make a 2d array I went with key and value combination. Or this is the only way to do it in Bash?

I would like to hear feedback on the code side too if its not too horrible. It's a simple counter.

#!/bin/bash
#set -x
declare -r MAX_DIGITS=10
declare -i position=0

declare -A digits

declare -A segments
segments["0,0"]=1
segments["0,1"]=1
segments["0,2"]=1
segments["0,3"]=1
segments["0,4"]=1
segments["0,5"]=1
segments["0,6"]=0
segments["1,0"]=0
segments["1,1"]=1
segments["1,2"]=1
segments["1,3"]=0
segments["1,4"]=0
segments["1,5"]=0
segments["1,6"]=0
segments["2,0"]=1
segments["2,1"]=1
segments["2,2"]=0
segments["2,3"]=1
segments["2,4"]=1
segments["2,5"]=0
segments["2,6"]=1
segments["3,0"]=1
segments["3,1"]=1
segments["3,2"]=1
segments["3,3"]=1
segments["3,4"]=0
segments["3,5"]=0
segments["3,6"]=1
segments["4,0"]=0
segments["4,1"]=1
segments["4,2"]=1
segments["4,3"]=0
segments["4,4"]=0
segments["4,5"]=1
segments["4,6"]=1
segments["5,0"]=1
segments["5,1"]=0
segments["5,2"]=1
segments["5,3"]=1
segments["5,4"]=0
segments["5,5"]=1
segments["5,6"]=1
segments["6,0"]=1
segments["6,1"]=0
segments["6,2"]=1
segments["6,3"]=1
segments["6,4"]=1
segments["6,5"]=1
segments["6,6"]=1
segments["7,0"]=1
segments["7,1"]=1
segments["7,2"]=1
segments["7,3"]=0
segments["7,4"]=0
segments["7,5"]=0
segments["7,6"]=0
segments["8,0"]=1
segments["8,1"]=1
segments["8,2"]=1
segments["8,3"]=1
segments["8,4"]=1
segments["8,5"]=1
segments["8,6"]=1
segments["9,0"]=1
segments["9,1"]=1
segments["9,2"]=1
segments["9,3"]=1
segments["9,4"]=0
segments["9,5"]=1
segments["9,6"]=1

declare -A segment_grid
segment_grid["0,0"]=0
segment_grid["0,1"]=1
segment_grid["1,0"]=1
segment_grid["1,1"]=2
segment_grid["2,0"]=2
segment_grid["2,1"]=2
segment_grid["3,0"]=2
segment_grid["3,1"]=1
segment_grid["4,0"]=2
segment_grid["4,1"]=0
segment_grid["5,0"]=1
segment_grid["5,1"]=0
segment_grid["6,0"]=1
segment_grid["6,1"]=1

clear_digits_array() {
   local -i i=0
   local -i j=0

   while [ "$i" -lt 4 ]; do
   j=0
       while [ "$j" -lt "$((MAX_DIGITS * 4))" ]; do
           digits[$i,$j]=' '
       ((j++))
       done
   ((i++))
   done
}

process_digit() {
    local -i i=0
    local -i row
    local -i col

    while [ "$i" -lt 7 ]; do
        if [ "${segments[$1,$i]}" == 1 ]; then
            row="${segment_grid[$i,0]}"
            col="${segment_grid[$i,1]}"
            col="$((col + $2))"
            if [ "$((i % 3))" -eq 0 ]; then
                digits[$row,$col]='_'
            else
                digits[$row,$col]='|'
            fi
        fi
    ((i++))
    done
}

print_digits_array() {
     local -i i=0
     local -i j=0

     while [ "$i" -lt 3 ]; do
     j=0
         while [ "$j" -lt "$((MAX_DIGITS * 4))" ]; do
             echo -n "${digits[$i,$j]}"
         ((j++))
         done
     echo
     ((i++))
     done
}


for number in {0..9}; do
    clear
    clear_digits_array

       process_digit "$number" "$position"
#       ((position+=4))

    print_digits_array
    sleep 0.5
done

exit 0

Solution

  • Initializing indexed arrays is easier than associative arrays. To simplify a bit you could try something like:

    declare -a arr=( 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 )
    declare -A segment
    for (( i=0; i<"${#arr[@]}"; i++ )); do
        k="$(( i/7 )),$(( i%7 ))"
        segment["$k"]="${arr[i]}"
    done
    

    But as the index in ${parameter[index]} is treated as an arithmetic expression you could simply replace your associative array with an indexed array and emulate the 2-dimensions with some arithmetic:

    declare -a segment=( 1 1 1 1 1 1 0 0 1 1 0 0 0 0 1 1 0 1 1 0 1 1 1 1 1 0 0 1 0 1 1 0 0 1 1 1 0 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 0 1 1 )
    for (( i=0; i<=9; i++ )); do
        for (( j=0; j<=6; j++ )); do
            printf '%s: %s\n' "$i,$j" "${segment[7*i+j]}"
        done
    done
    0,0: 1
    0,1: 1
    0,2: 1
    ...
    9,4: 0
    9,5: 1
    9,6: 1
    

    Note that you could use a string instead of an indexed array because the offset and length in "${parameter:offset:length}" are also arithmetic expressions:

    declare -r segment="1111110011000011011011111001011001110110111011111111000011111111111011"
    for (( i=0; i<=9; i++ )); do
        for (( j=0; j<=6; j++ )); do
            printf '%s: %s\n' "$i,$j" "${segment:7*i+j:1}"
        done
    done
    0,0: 1
    0,1: 1
    0,2: 1
    ...
    9,4: 0
    9,5: 1
    9,6: 1