Created
June 22, 2017 11:05
-
-
Save ReeceHub/d73468975157bcf4656543c2d455bb40 to your computer and use it in GitHub Desktop.
First attempt at Asteroid Robots challenge
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"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"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{"type": "robot" , "position": {"x": 1, "y": 3}, "bearing": "north"} | |
{"type": "robot" , "position": {"x": 5, "y": 1}, "bearing": "east"} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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