Reputation: 2635
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
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
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
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