Scrabble excercise

I suspected that those might be a problem.
I’ve always had trouble with looping through text one character at a time.

I’ve tried making input an array, as well as just a variable. Either way, I can’t get it to work.

The same for letters, I can’t loop through each individual character in the keys.

I’ve tried both type of for loops:

for i in "${!letters[@]}"
for (( i=0; i < ${!letters[@]}; i++ ))

Any hint would be appreciated

My suggestion here would be

  1. Make the input a string, not an array.
  2. Make the letter-to-score associative array just that – 26 entries that map one letter to one score.

Making those two changes should make things simpler.

Can you figure out how to loop over the individual characters in a single string/variable?

Ok, so this works, but the only thing is that I have to enter the input with spaces between each character or it fails: t e s t instead of test. Is there a way to allow the input to be entered naturally?

#!/bin/bash
   
  declare -A letters
   
  letters["a"]=1
  letters["b"]=3
  letters["c"]=3
  letters["d"]=2
  letters["e"]=1
  letters["f"]=4
  letters["g"]=2
  letters["h"]=4
  letters["i"]=1
  letters["j"]=8
  letters["k"]=5
  letters["l"]=1
  letters["m"]=3
  letters["n"]=1
  letters["o"]=1
  letters["p"]=3
  letters["q"]=10
  letters["r"]=1
  letters["s"]=1
  letters["t"]=1
  letters["u"]=1
  letters["v"]=4
  letters["w"]=4
  letters["x"]=8
  letters["y"]=4
  letters["z"]=10
  
  counter=0
  i=0
  
  #set -x
  echo "Enter a letter: "
  read input
  
  for x in $input
  
  do
     
     for i in "${!letters[@]}"
  
      do
        
        if [[ "$x" == "$i" ]]
  
           then
  
           (( counter+=${letters[$i]} ))
              break
        fi
  
      done
  
  done
  
  echo "counter is: $counter"

You’re splitting the input up into words. Can you find a way to iterate over the characters of a string? Given, string="Hello world", for each character, echo "<$character>". The output should be:

<H>
<e>
<l>
<l>
<l>
<o>
< >
<W>
<o>
...

Can you work that out? Or do you need a hint?

I’ve tried:

echo "Enter a letter/word: "
read -r input

for x in input
do
echo "${input:x:1}"
done

Using “test” as input, it only prints t, and doesn’t keep looping.

I also tried:

`for (( x=0; x < $input; x++ ))`
do
echo "$x"
done

That doesn’t get past the for statement:

Enter a letter/word: 
test 
+ (( x=0 ))
+ (( x < test ))

Probably because 0 can’t be less than t

So I’ll take that hint if you would.

This iterates over the string "input". for x in $input would expand $input and split it into words, then iterate over those words. Neither of those gives you the characters.

If x were a number, this would give you the xth character of "$input", which would allow you to print one character at a time!

If $input were the length of the input, this would give you a loop where x iterates over the numbers 0 to the length of the input. Combined with echo "${input:x:1}", that would echo out each character, one by one. So this is fairly close:

for (( x=0; x < $input; x++ )); do
    echo "${input:x:1}"
done

The only thing that needs fixing here is that you’re using the input string as the upper limit where that ought to be the length of the input, "${#input}". Making that change, this code should print out the characters of the input, one by one.

for (( x=0; x < ${#input}; x++ )); do
    echo "${input:x:1}"
done

A similar loop can be used to tally up the score of the characters of a string.

Let me know if I can explain anything else and/or if the above unblocks you.

Thanks, that all makes sense.

The problem I’m having is that if I change $input to ${#input}, it breaks the if comparison, because now x is a number instead of a letter.

I tried adding another nested for loop to make up for this:

 counter=0
 z=0
 temp=0
 
 echo "Enter a letter/word: "
 read -r input
 #set -x
 
 for (( x=0; x < ${#input}; x++ ))
                                                
  do
                                                
    for y in $input
 
    do
       temp="${input:y:1}"
                                                 
     for z in "${!letters[@]}"
 
       do
 
         if [[ "$temp" == "$z" ]]
 
            then
 
           (( counter+=${letters[$z]} ))
            break
 
         fi
 
      done
 
   done
 
  done
 
 echo "counter is: $counter"

The second for loop for y in $input doesn’t iterate past the first character. Using the word cabbage as a test, it assigns the character c to temp, but doesn’t go any further.

I’ve probably made it more complex than it needs to be, and I don’t really understand the way those nested loops move.

Well… yes. That’s exactly what I suggested. Using x as the character position to get to the xth character.

If you’re looping over x characters, one character at a time, you don’t need an inner loop. You only need one loop to iterate over x characters. x gives you the offset/index/position and you use x to get the xth character.

From my last response, you can use the following to print one character of input at a time.

You should use that same logic to get the xth character, but check its Scrabble value instead of printing it.

Got it.

Here is the finished product:

#!/bin/bash

declare -A letters
  
  letters["a"]=1
  letters["b"]=3
  letters["c"]=3
  letters["d"]=2
  letters["e"]=1
  letters["f"]=4
  letters["g"]=2
  letters["h"]=4
  letters["i"]=1
  letters["j"]=8
  letters["k"]=5
  letters["l"]=1
  letters["m"]=3
  letters["n"]=1
  letters["o"]=1
  letters["p"]=3
  letters["q"]=10
  letters["r"]=1
  letters["s"]=1
  letters["t"]=1
  letters["u"]=1
  letters["v"]=4
  letters["w"]=4
  letters["x"]=8
  letters["y"]=4
  letters["z"]=10
  
  counter=0
  
  echo "Enter a letter/word: "
  read -r input
  #set -x
  
  for (( x=0; x < ${#input}; x++ ))

   do
  
   temp="${input:x:1}"
  
          for z in "${!letters[@]}"
  
            do
  
              if [[ "$temp" == "$z" ]]
  
                 then
  
                (( counter+=${letters[$z]} ))
                 break
            
              fi
    
         done
 
      done
 
       echo "scrabble score is: $counter"

As always, thanks.

1 Like

Well solved!

Note, the purpose of an associative array is that you can look up a value in it without needing to loop.

1 Like