capser
capser

Reputation: 2635

bash - finding coordinate pairs on x and y axis.

I had an interview test for a tech support position. Which I suppose I did not do well because they have not called back. I don’t care about the job, however one of the questions is bothering me, since I can only half figure it out. I have a robot that is able to read instructions where each line contains a single instruction. "L" for turn left, "R" for turn right, and a number for move #n steps forward.   The robot is facing north, the coordinates are (0,0) These are the instructions:

R
L
L
9
4
1
7
9
R
2

So the instructions say - turn right 90 degrees (which leaves it facing east), then turn left and left again (which leaves it facing west) walk 30 steps forward, then turn clockwise 90 degrees (which leaves it facing north) and move two steps forward. They wanted me to write a script that would compute the maximum distance and the coordinate pair (the x and y value)
They gave me the answer in the instructions above, the maximum distance the robot walks from the start is 30.07 and the coordinate pair is (-30, 2)

I understood that the robot movements visually and wrote a bash array which adds 10 to the value of x for a clockwise movement and subtracts 10 from the value of x for a counter counter clock wise movement. the cheap bash array (bash 3.2 does not have array support) matches the x VALUE with a KEY which is a 360 position or one of four arrow keys. The script saves the last instruction before moving n# steps forward. I have no idea how to script in the max distance and the coordinate pairs. It is a algebraic x and y axis function - but can’t figure out how to add it in bash.

#!/bin/bash 
#set -x 


dial_map=("up_1:10"    "right_1:20"    "down_1:30"    "left_1:40" 
      "up_2:50"    "right_2:60"    "down_2:70"    "left_2:80"
      "up_3:90"    "right_3:100"   "down_3:110"   "left_3:120")
x=50 # the robot starts at the up or north position
     # once clockwise click adds ten, one counter clockwise minus 10 from x value. 

IFS=$'\n' read -d"" -r -a directives < directions.txt

for i in "${directives[@]}" 
    do
    if   [ "$i" == "R" ] ; then
      #echo "R is clockwise"
      x=$(( $x + 10 ))
      #echo "x is $x" 
      for click in "${dial_map[@]}" ; do 
          KEY=${click%%:*}
          VALUE=${click#*:}
          if [ "$x" -eq "$VALUE" ] ; then 
              keytab=$KEY
              #echo "the keyboard command is $keytab" 
      fi 
      #sleep 1
      done  
    elif [ "$i" == "L" ] ; then 
      #echo "L is counterclock"
      x=$(( $x - 10 ))
      #echo "x is $x"
      for click in "${dial_map[@]}" ; do 
           KEY=${click%%:*}
           VALUE=${click#*:}
          if [ "$x" -eq "$VALUE" ] ; then 
              keytab=$KEY
              #echo "the keyboard command is $keytab" 
      fi 
      #sleep 1
      done  
    else 
      echo "Please move the cursor $i times $keytab" 
      sleep 1 
    fi  
done 

Finding the x and y coordinates in bash

Upvotes: 2

Views: 679

Answers (3)

Chet
Chet

Reputation: 1225

with awk:

awk -f script.awk file

script.awk

#! /bin/awk -f
BEGIN{
    codX=0;
    codY=1;
    X=0;
    Y=0
}
$1~"R"{
    tmp = codX;
    codX = codY;
    codY = tmp*-1
}
$1~"L"{
    tmp=codX;
    codX=codY*-1;
    codY=tmp
}
$1 ~ /[[:digit:]]/{
    X += codX*$1;
    Y += codY*$1
}
END{
    print "Co-ordinate(X,Y) : ("X","Y")";
    print "Distance : " sqrt(X^2+Y^2)
}

output

Co-ordinate(X,Y) : (-30,2)
Distance : 30.0666

Upvotes: 0

David C. Rankin
David C. Rankin

Reputation: 84599

Taking another approach, (using the same coordinates), the case statement and bc are basically all your need:

#!/bin/bash

dir=${1:-n}             ## (n) north (s) south (e) east (w) west
fn=${2:-/dev/stdin}     ## input file name (stdin default)

declare -i posx=0
declare -i posy=0

while read -r cmd || [ -n "$cmd" ]; do
    case "$cmd" in
        [0-9]* )
            case "$dir" in
                n ) ((posy += cmd)) ;;
                w ) ((posx -= cmd)) ;;
                s ) ((posy -= cmd)) ;;
                e ) ((posy += cmd)) ;;
            esac
            ;;
        R )
            case "$dir" in
                n ) dir='e' ;;
                w ) dir='n' ;;
                s ) dir='w' ;;
                e ) dir='s' ;;
            esac
            ;;
        L )
            case "$dir" in
                n ) dir='w' ;;
                w ) dir='s' ;;
                s ) dir='e' ;;
                e ) dir='n' ;;
            esac
            ;;
    esac
done <"$fn"

dist=$(printf "scale=2; sqrt($posx*$posx+$posy*$posy)\n" | bc)

printf "final coordinates (%d, %d)\n" "$posx" "$posy"
printf "distance traveled: %s units.\n" "$dist"

Example Use/Output

$ bash robotstat.sh <dat/robot.txt
final coordinates (-30, 2)
distance traveled: 30.06 units.

Upvotes: 2

Argonauts
Argonauts

Reputation: 199

Here is a method to do it without associative arrays, which if I read correctly was a requirement - actually I initially read no arrays at all; which is why I used so many variables. You could use an associative array instead of the case statement that I used. I hard coded the instructions, but it's trivial to read them in from a file instead. Between the comments and the echo statements it's hopefully self explanatory.

#!/bin/bash - 

inst=(R L L 9 4 1 7 9 R 2)
# compass to cartesian translation
#              x = 1 : N
#                  ^
#                  |
# y = -1 : W  <--------->  y = 1 : E
#                  |
#                  v
#             x = -1 : S

startloc_x=0
startloc_y=0
newloc_x=0
newloc_y=0
dir_x=1
dir_y=0
a=0;
for i in ${inst[@]}; do
  ((a++))
  echo "[$a] Next Instruction: $i"
  slct="${i}${dir_x}${dir_y}"

#   lookup table for case statement
#
#   X  Y     turning Right     turning left
#   1  0         0  1               0  -1
#   0  1        -1  0               1   0
#  -1  0         0 -1               0   1
#   0 -1         1  0              -1   0
  case $slct in
    R10|L-10)
            dir_x=0
            dir_y=1
            ;;
    R01|L0-1)
            dir_x=-1
            dir_y=0
            ;;
    R-10|L10)
            dir_x=0
            dir_y=-1
            ;;
    R0-1|L01)
            dir_x=1
            dir_y=0
            ;;
           *)
             (( newloc_x += $i * dir_x )) 
             (( newloc_y += $i * dir_y )) 
             ;;
  esac
  echo "[$a] Current location (x,y) = ($newloc_x, $newloc_y)"
  echo "[$a] Current direction (x,y) = ($dir_x, $dir_y)"
  echo
done
echo;echo "---"
echo "Finished processing $a instructions"
echo "Starting Location: (x,y) ($startloc_x, $startloc_y)"
echo "Ending Location:   (x,y) ($newloc_x, $newloc_y)"
echo "Final Direction:   (x,y) ($dir_x, $dir_y)"            
delta_x=0
delta_y=0
((delta_x = $newloc_x - $startloc_x ))
((delta_y = $newloc_y - $startloc_y ))
distance=`echo "sqrt( (${delta_x}.000)^2 + (${delta_y}.000)^2 )" | bc`
echo "Distance traveled = $distance"
echo

Final output:

Finished processing 10 instructions
Starting Location: (x,y) (0, 0)
Ending Location:   (x,y) (2, -30)
Final Direction:   (x,y) (1, 0)
Distance traveled = 30.066

Upvotes: 2

Related Questions