Skip to content

Instantly share code, notes, and snippets.

@ReeceHub
Created June 22, 2017 11:05
Show Gist options
  • Save ReeceHub/d73468975157bcf4656543c2d455bb40 to your computer and use it in GitHub Desktop.
Save ReeceHub/d73468975157bcf4656543c2d455bb40 to your computer and use it in GitHub Desktop.
First attempt at Asteroid Robots challenge
import json
class Robot:
def __init__(self, asteroid, x, y, bearing):
self.x = x
self.y = y
self.bearing = bearing
self.asteroid = asteroid
def turn_left(self):
final_bearing_by_inital_bearing = {
'north': 'west',
'west': 'south',
'south': 'east',
'east': 'north',
}
self.bearing = final_bearing_by_inital_bearing[self.bearing]
def turn_right(self):
final_bearing_by_inital_bearing = {
'north': 'east',
'west': 'north',
'south': 'west',
'east': 'south',
}
self.bearing = final_bearing_by_inital_bearing[self.bearing]
def move_forward(self):
position_change_by_bearing = {
'north': {'x': 0, 'y': 1},
'west': {'x': -1, 'y': 0},
'south': {'x': 0, 'y': -1},
'east': {'x': 1, 'y': 0},
}
self.x += position_change_by_bearing[self.bearing]['x']
self.y += position_change_by_bearing[self.bearing]['y']
self.x = min(self.asteroid.width, self.x)
self.x = max(0, self.x)
self.y = min(self.asteroid.height, self.y)
self.y = max(0, self.y)
def __repr__(self):
return '{{"type": "robot" , "position": {{"x": {x}, "y": {y}}}, "bearing": "{b}"}}'.\
format(
x=self.x,
y=self.y,
b=self.bearing
)
class Asteroid:
def __init__(self, width, height):
self.width = width
self.height = height
class Parser:
def __init__(self):
self.asteroid_store = []
self.robot_store = []
def get_latest_robot(self):
return self.robot_store[-1]
def build_asteroid_from_input_line(self, input_json_line):
x, y = self.parse_asteroid_builder_json(input_json_line)
a = Asteroid(x, y)
self.asteroid_store.append(a)
def build_robot_from_input_line(self, input_json_line):
x, y, bearing = self.parse_robot_builder_json(input_json_line)
r = Robot(self.asteroid_store[0], x, y, bearing)
return r
@staticmethod
def parse_robot_builder_json(json_line):
data = json.loads(json_line)
x = data['position']['x']
y = data['position']['y']
bearing = data['bearing']
return x, y, bearing
@staticmethod
def parse_asteroid_builder_json(json_line):
data = json.loads(json_line)
x = data['size']['x']
y = data['size']['y']
return x, y
def do_movement_from_json_line(self, input_json_line):
r = self.get_latest_robot()
movement = json.loads(input_json_line)['movement']
if movement == 'turn-left':
r.turn_left()
elif movement == 'move-forward':
r.move_forward()
elif movement == 'turn-right':
r.turn_right()
def consume(self, line):
instruction_type = json.loads(line)['type']
if instruction_type == 'asteroid':
self.build_asteroid_from_input_line(line)
elif instruction_type == 'new-robot':
r = self.build_robot_from_input_line(line)
self.robot_store.append(r)
elif instruction_type == 'move':
self.do_movement_from_json_line(line)
def read(self, input_json_file):
with open(input_json_file, 'r') as f:
for line in f:
self.consume(line)
def produce_output_file(self, name):
state = []
for robot in self.robot_store:
state.append(str(robot))
with open(name, 'w') as f:
f.write('\n'.join(state))
return name
{"type": "asteroid", "size": {"x": 5, "y": 5}}
{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}
{"type": "move", "movement": "turn-left"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-left"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-left"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-left"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "move-forward"}
{"type": "new-robot", "position": {"x": 3, "y": 3}, "bearing": "east"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-right"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-right"}
{"type": "move", "movement": "move-forward"}
{"type": "move", "movement": "turn-right"}
{"type": "move", "movement": "turn-right"}
{"type": "move", "movement": "move-forward"}
{"type": "robot" , "position": {"x": 1, "y": 3}, "bearing": "north"}
{"type": "robot" , "position": {"x": 5, "y": 1}, "bearing": "east"}
import pytest
from asteroids2 import (
Robot,
Asteroid,
Parser,
)
def test_build_a_robot():
a = Asteroid(10, 10)
r = Robot(a, 1, 2, 'north')
assert r
assert r.x == 1
assert r.y == 2
assert r.bearing == 'north'
def test_turn_left_from_north():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'north')
r.turn_left()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'west'
def test_turn_left_from_west():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'west')
r.turn_left()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'south'
def test_turn_left_from_south():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'south')
r.turn_left()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'east'
def test_turn_left_from_east():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'east')
r.turn_left()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'north'
def test_turn_right_from_north():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'north')
r.turn_right()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'east'
def test_turn_right_from_west():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'west')
r.turn_right()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'north'
def test_turn_right_from_south():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'south')
r.turn_right()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'west'
def test_turn_right_from_east():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'east')
r.turn_right()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'south'
def test_move_forward_from_north():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'north')
r.move_forward()
assert r.x == 0
assert r.y == 1
assert r.bearing == 'north'
def test_move_forward_from_west():
a = Asteroid(10, 10)
r = Robot(a, 5, 5, 'west')
r.move_forward()
assert r.x == 4
assert r.y == 5
assert r.bearing == 'west'
def test_move_forward_from_south():
a = Asteroid(10, 10)
r = Robot(a, 0, 1, 'south')
r.move_forward()
assert r.x == 0
assert r.y == 0
assert r.bearing == 'south'
def test_move_forward_from_east():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'east')
r.move_forward()
assert r.x == 1
assert r.y == 0
assert r.bearing == 'east'
def test_define_asteroid_limits():
a = Asteroid(10, 15)
assert a.width == 10
assert a.height == 15
def test_robot_knows_what_asteroid_its_on():
a = Asteroid(10, 10)
r = Robot(a, 0, 0, 'north')
assert r.asteroid == a
def test_robot_doesnt_move_forward_north_off_asteroid():
a = Asteroid(10, 1)
r = Robot(a, 5, 1, 'north')
assert r.x == 5
assert r.y == 1
def test_robot_doesnt_move_forward_south_off_asteroid():
a = Asteroid(10, 1)
r = Robot(a, 5, 0, 'south')
assert r.x == 5
assert r.y == 0
def test_robot_doesnt_move_forward_east_off_asteroid():
a = Asteroid(10, 1)
r = Robot(a, 10, 1, 'east')
assert r.x == 10
assert r.y == 1
def test_robot_doesnt_move_forward_west_off_asteroid():
a = Asteroid(10, 1)
r = Robot(a, 0, 1, 'west')
assert r.x == 0
assert r.y == 1
def test_input_for_new_robot_builds_a_robot():
a = Asteroid(10, 10)
input_line = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
parser = Parser()
parser.asteroid_store.append(a)
r = parser.build_robot_from_input_line(input_line)
assert r.x == 1
assert r.y == 2
assert r.bearing == 'north'
def test_reading_asteroid_json_line_builds_and_stores_an_asteroid():
input_line = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
parser = Parser()
a = parser.build_asteroid_from_input_line(input_line)
assert len(parser.asteroid_store) == 1
assert parser.asteroid_store[0].width == 5
assert parser.asteroid_store[0].height == 5
def test_reading_movement_line_moves_a_robot():
a = Asteroid(10, 20)
r = Robot(a, 5, 10, 'east')
parser = Parser()
parser.asteroid_store.append(a)
parser.robot_store.append(r)
input_line = '{"type": "move", "movement": "turn-left"}'
parser.do_movement_from_json_line(input_line)
assert r.x == 5
assert r.y == 10
assert r.bearing == 'north'
def test_building_two_robots_adds_them_both_to_the_robot_store():
a = Asteroid(20, 40)
parser = Parser()
parser.asteroid_store.append(a)
input_line = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
r1 = parser.build_robot_from_input_line(input_line)
input_line = '{"type": "new-robot", "position": {"x": 4, "y": 6}, "bearing": "south"}'
r2 = parser.build_robot_from_input_line(input_line)
parser.robot_store.append(r1)
parser.robot_store.append(r2)
assert r1.asteroid == a
assert r2.asteroid == a
assert len(parser.robot_store) == 2
def test_able_to_consume_asteroid_input():
i1 = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
parser = Parser()
parser.consume(i1)
assert len(parser.asteroid_store) == 1
assert len(parser.robot_store) == 0
assert parser.asteroid_store[0].width == 5
assert parser.asteroid_store[0].height == 5
def test_able_to_consume_asteroid_then_new_robot_input():
i1 = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
i2 = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
parser = Parser()
parser.consume(i1)
parser.consume(i2)
assert len(parser.asteroid_store) == 1
assert len(parser.robot_store) == 1
assert parser.asteroid_store[0].width == 5
assert parser.asteroid_store[0].height == 5
assert parser.robot_store[0].x == 1
assert parser.robot_store[0].y == 2
assert parser.robot_store[0].bearing == 'north'
def test_reading_three_lines_then_requesting_output_gives_correct_output():
i1 = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
i2 = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
i3 = '{"type": "move", "movement": "turn-left"}'
parser = Parser()
parser.consume(i1)
parser.consume(i2)
parser.consume(i3)
assert len(parser.asteroid_store) == 1
assert len(parser.robot_store) == 1
assert parser.asteroid_store[0].width == 5
assert parser.asteroid_store[0].height == 5
assert parser.robot_store[0].x == 1
assert parser.robot_store[0].y == 2
assert parser.robot_store[0].bearing == 'west'
def test_creating_multiple_robots_works_ok():
i1 = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
i2 = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
i3 = '{"type": "move", "movement": "turn-left"}'
i4 = '{"type": "new-robot", "position": {"x": 3, "y": 3}, "bearing": "east"}'
i5 = '{"type": "move", "movement": "move-forward"}'
parser = Parser()
parser.consume(i1)
parser.consume(i2)
parser.consume(i3)
parser.consume(i4)
parser.consume(i5)
assert len(parser.asteroid_store) == 1
assert len(parser.robot_store) == 2
assert parser.asteroid_store[0].width == 5
assert parser.asteroid_store[0].height == 5
assert parser.robot_store[0].x == 1
assert parser.robot_store[0].y == 2
assert parser.robot_store[0].bearing == 'west'
assert parser.robot_store[1].x == 4
assert parser.robot_store[1].y == 3
assert parser.robot_store[1].bearing == 'east'
def test_that_we_can_ask_a_robot_for_its_current_condition_as_json():
i1 = '{"type": "asteroid", "size": {"x": 5, "y": 5}}'
i2 = '{"type": "new-robot", "position": {"x": 1, "y": 2}, "bearing": "north"}'
parser = Parser()
parser.consume(i1)
parser.consume(i2)
assert str(parser.robot_store[0]) == \
'{"type": "robot" , "position": {"x": 1, "y": 2}, "bearing": "north"}'
def test_that_we_can_read_from_input_json():
parser = Parser()
parser.read('asteroids_example_input.json')
assert parser.robot_store[0].x == 1
assert parser.robot_store[0].y == 3
assert parser.robot_store[0].bearing == 'north'
assert parser.robot_store[1].x == 5
assert parser.robot_store[1].y == 1
assert parser.robot_store[1].bearing == 'east'
def test_that_we_can_spit_out_final_robot_conditions_to_output_json():
parser = Parser()
parser.read('asteroids_example_input.json')
output_file_name = parser.produce_output_file('output_from_test.json')
with open('asteroids_example_output.json', 'r') as f1:
with open(output_file_name, 'r') as f2:
assert f1.read() == f2.read()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment