Inaccurate scanning of Bash array elements

username=cogiz
#!/bin/bash

shuffle() #@ USAGE: shuffle
{ #@ TODO: add options for multiple or partial decks
 Deck=$(
   printf "%s\n" {2,3,4,5,6,7,8,9,T,J,Q,K,A}{H,S,D,C} |
    awk '## Seed the random number generator
         BEGIN { srand() }

         ## Put a random number in front of each line
         { printf "%.0f\t%s\n", rand() * 99999, $0 }
    ' |
     sort -n |  ## Sort the lines numerically
      cut -f2  ## Remove the random numbers
  )
}

_deal() #@ USAGE: _deal [N] -- where N is no. of cards; defaults to 1
{       #@ RESULT: stored in $_DEAL
    local num=${1:-1}
    set -- $Deck
    _DEAL=${@:1:$num}
    shift "$num"
    cards_remaining=$#
    Deck=$*
}

deal() #@ USAGE: deal [N]
{      #@ RESULT: cards printed one to a line
    _deal "$@"
    printf "%s " $_DEAL
}

################################################

shuffle
declare -a PH
deal 8 > /home/cogiz/playerhand.txt           #  (7D 3H 4D 9H KC 2H 7C 9S)
deal 8 > /home/cogiz/computerhand.txt
deal 1 > /home/cogiz/activecard.txt            # (9C)
mapfile -t PH < /home/cogiz/playerhand.txt
X=$(</home/cogiz/activecard.txt)
A=( "${X[@]::1}" )                               # 9
B=( "${X[@]:1}" )                                # C
C=8                                                    # 8

for e in ${PH[@]}; do
  
   if [[ $e =~ $A|$B|8 ]]; then
      printf "$e " >> /home/cogiz/choices.txt
   else
      printf "0 " >> /home/cogiz/choices.txt
   fi
done

The above script is a rewrite of a terminal game of crazy 8's in Bash that I have been working on. The problem I am having is that the output file (choices.txt) is not showing me all of the results that it should. when the above script was run, the output in the choices.txt file was ( 0 0 0 9H 0 0 0 9S ).

It should have been ( 0 0 0 9H KC 0 7C 9S ).

Each time I run this script ( ./crazy.sh ) it gives me a different answer but always an incomplete one.

Can anyone explain to me why this is happening? I have been racking my brain for days trying to figure this out. Why does it show some valid card choices but not all? What am I missing?

Thank you in advance for any and all suggestions and advice.

Cogiz

To start with:

username=cogiz
#!/bin/bash

should be:

#!/bin/bash
username=cogiz

try:

A=${X[@]::1}
B=${X[@]:1}

or:

read X < /home/cogiz/activecard.txt    # instead of X=$(</home/cogiz/activecard.txt)
A=${X%?}
B=${X#?}

Otherwise there are spurious spaces in the variables A and B that mess up things with the regex comparison...

1 Like

Running your final for loop with the variables (not arrays!) set to the values indicated, I get 0 0 0 9H KC 0 7C 9S , so the logics seem to be OK. The inconsistent behaviour might be caused by some creative use of variables, arrays, separators, and assignments.
The way you assign them, A and B will be arrays with one single element only. Scrutinizer already proposed an alternative use.
mapfile will ( man bash ) "Read lines from the standard input into the indexed array variable array", so again array PH has just one element as the input file has all cards in one line separated by spaces.
While the logics should work nevertheless, I can't assess the ramifications of using arrays vs. variables. Wouldn't it be worthwhile to try simple variables?

BTW, would your system allow for this construct:

Deck=$(shuf -e {2,3,4,5,6,7,8,9,T,J,Q,K,A}{H,S,D,C})
3 Likes

Thank you very much for your suggestions. The following worked like a charm:

read X < /home/cogiz/activecard.txt
A=${X%?}
B=${X#?}
C=8

Now the scanning of ${PH[@]} for $A|$B|8 works perfectly.

Cogiz

---------- Post updated at 07:42 PM ---------- Previous update was at 07:21 PM ----------

This also worked like a charm:

Deck=$(shuf -e {2,3,4,5,6,7,8,9,T,J,Q,K,A}{H,S,D,C})

I like this much better....very streamlined, less complicated.

Thank you,

Cogiz

Well, talking of complexity: your entire script looks somewhat overcomplicated to me. Although I like subroutines and functions for repeating tasks, jumping to and fro between them just for the sake of it might overdo it. Consider the following - it should do exactly what you need (if I read the script correctly):

set -- $(shuf -e {2,3,4,5,6,7,8,9,T,J,Q,K,A}{H,S,D,C})
PH=$(echo ${@:1:8} | tee /home/cogiz/playerhand.txt); shift 8 
DU=$(echo ${@:1:8} | tee /home/cogiz/computerhand.txt); shift 8
XX=$(echo ${@:1:1} | tee /home/cogiz/activecard.txt); shift 1
for e in $PH
   do    if [[ $e =~ ${XX%?}|${XX#?}|8 ]]
           then  printf "$e "
           else  printf "0 "
         fi
   done
0 0 0 KS 8S 0 0 0
echo
echo $PH, $DU, $XX
5H QC QH KS 8S 9C 9D 3H, 6H 7S 7H 4H QS JH AD JS, AS