"Exercise: RobotSimulator"

robotSimulator: facing atPosition: aPoint instructions: instructionsString
"Write a robot simulator.
    A robot factory's test facility needs a program to verify robot movements.
    The robots have three possible movements:
    - turn right
    - turn left
    - advance
    Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of {x,y} coordinates, e.g., {3,8}, with coordinates increasing to the north and east.
    The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing."
"(self new robotSimulator: 'north' atPosition: 7@3 instructions: 'RAALAL') >>> ((Dictionary new) add: ('direction'->'west'); add: ('position'->9@4"
| directions dirIndex x y |
	directions := #( 'north' 'east' 'south' 'west' ).
	dirIndex := directions
		            indexOf: facing
		            ifAbsent: [ self error: 'invalid direction' ].
	x := aPoint x.
	y := aPoint y.
	instructionsString do: [ :inst |
			inst = $R ifTrue: [
					dirIndex := dirIndex = directions size
						            ifTrue: [ 1 ]
						            ifFalse: [ dirIndex + 1 ] ].
			inst = $L ifTrue: [
					dirIndex := dirIndex = 1
						            ifTrue: [ directions size ]
						            ifFalse: [ dirIndex - 1 ] ].
			inst = $A ifTrue: [
					| dirName dx dy |
					dirName := directions at: dirIndex.
					dx := 0.
					dy := 0.
					dirName = 'north' ifTrue: [
							dx := 0.
							dy := 1 ].
					dirName = 'east' ifTrue: [
							dx := 1.
							dy := 0 ].
					dirName = 'south' ifTrue: [
							dx := 0.
							dy := -1 ].
					dirName = 'west' ifTrue: [
							dx := -1.
							dy := 0 ].
					x := x + dx.
					y := y + dy ] ].
	^ Dictionary new
		  add: 'direction' -> (directions at: dirIndex);
		  add: 'position' -> (x @ y);
		  yourself


"Tests"
testRobotSimulator
self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 0 @ 0
				 instructions: '')
		equals: (Dictionary new
				 add: 'direction' -> 'north';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'south'
				 atPosition: -1 @ -1
				 instructions: '')
		equals: (Dictionary new
				 add: 'direction' -> 'south';
				 add: 'position' -> (-1 @ -1);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 0 @ 0
				 instructions: 'R')
		equals: (Dictionary new
				 add: 'direction' -> 'east';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'east'
				 atPosition: 0 @ 0
				 instructions: 'R')
		equals: (Dictionary new
				 add: 'direction' -> 'south';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'south'
				 atPosition: 0 @ 0
				 instructions: 'R')
		equals: (Dictionary new
				 add: 'direction' -> 'west';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'west'
				 atPosition: 0 @ 0
				 instructions: 'R')
		equals: (Dictionary new
				 add: 'direction' -> 'north';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 0 @ 0
				 instructions: 'L')
		equals: (Dictionary new
				 add: 'direction' -> 'west';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'west'
				 atPosition: 0 @ 0
				 instructions: 'L')
		equals: (Dictionary new
				 add: 'direction' -> 'south';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'south'
				 atPosition: 0 @ 0
				 instructions: 'L')
		equals: (Dictionary new
				 add: 'direction' -> 'east';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'east'
				 atPosition: 0 @ 0
				 instructions: 'L')
		equals: (Dictionary new
				 add: 'direction' -> 'north';
				 add: 'position' -> (0 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 0 @ 0
				 instructions: 'A')
		equals: (Dictionary new
				 add: 'direction' -> 'north';
				 add: 'position' -> (0 @ 1);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'south'
				 atPosition: 0 @ 0
				 instructions: 'A')
		equals: (Dictionary new
				 add: 'direction' -> 'south';
				 add: 'position' -> (0 @ -1);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'east'
				 atPosition: 0 @ 0
				 instructions: 'A')
		equals: (Dictionary new
				 add: 'direction' -> 'east';
				 add: 'position' -> (1 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'west'
				 atPosition: 0 @ 0
				 instructions: 'A')
		equals: (Dictionary new
				 add: 'direction' -> 'west';
				 add: 'position' -> (-1 @ 0);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 7 @ 3
				 instructions: 'RAALAL')
		equals: (Dictionary new
				 add: 'direction' -> 'west';
				 add: 'position' -> (9 @ 4);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'north'
				 atPosition: 0 @ 0
				 instructions: 'LAAARALA')
		equals: (Dictionary new
				 add: 'direction' -> 'west';
				 add: 'position' -> (-4 @ 1);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'east'
				 atPosition: 2 @ -7
				 instructions: 'RRAAAAALA')
		equals: (Dictionary new
				 add: 'direction' -> 'south';
				 add: 'position' -> (-3 @ -8);
				 yourself).

	self
		assert: (task
				 robotSimulator: 'south'
				 atPosition: 8 @ 4
				 instructions: 'LAAARRRALLLL')
		equals: (Dictionary new
				 add: 'direction' -> 'north';
				 add: 'position' -> (11 @ 5);
				 yourself)