Handle door navigation and turnarounds
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import unittest
|
||||
from munch import Munch
|
||||
|
||||
from mosfet.game import Game
|
||||
from mosfet.info import blocks
|
||||
from mosfet.path import Pathfinder
|
||||
|
||||
|
||||
class FakeChunks:
|
||||
def __init__(self, states):
|
||||
self.states = states
|
||||
|
||||
def get_block_at(self, *position):
|
||||
return self.states.get(tuple(position))
|
||||
|
||||
|
||||
class DoorNavigationTests(unittest.TestCase):
|
||||
@staticmethod
|
||||
def door_state(name, half='lower', opened=False, powered=False):
|
||||
data = blocks.MCD_BLOCKS[name]
|
||||
return next(
|
||||
state for state in range(data['minStateId'], data['maxStateId'] + 1)
|
||||
if blocks.PROPS[state].get('half') == half and
|
||||
blocks.PROPS[state].get('open') == str(opened).lower() and
|
||||
blocks.PROPS[state].get('powered') == str(powered).lower())
|
||||
|
||||
def test_pathfinder_treats_openable_doors_as_passable_but_not_iron(self):
|
||||
wooden = self.door_state('oak_door')
|
||||
iron = self.door_state('iron_door')
|
||||
opened_iron = self.door_state('iron_door', opened=True)
|
||||
positions = {(1, 64, 0): wooden, (2, 64, 0): iron,
|
||||
(3, 64, 0): opened_iron}
|
||||
g = Munch(chunks=FakeChunks(positions), crawling=False,
|
||||
unopenable_doors=set())
|
||||
finder = Pathfinder(g)
|
||||
|
||||
self.assertTrue(finder.bair((1, 64, 0)))
|
||||
self.assertFalse(finder.bair((2, 64, 0)))
|
||||
self.assertTrue(finder.bair((3, 64, 0)))
|
||||
|
||||
g.unopenable_doors.add((1, 64, 0))
|
||||
finder = Pathfinder(g)
|
||||
self.assertFalse(finder.bair((1, 64, 0)))
|
||||
|
||||
def test_pressure_plate_opened_door_is_not_toggled_or_closed(self):
|
||||
door = (1, 64, 0)
|
||||
after = (2, 64, 0)
|
||||
closed = self.door_state('oak_door', powered=True)
|
||||
opened = self.door_state('oak_door', opened=True, powered=True)
|
||||
states = {door: closed}
|
||||
|
||||
class FakeWorld:
|
||||
@staticmethod
|
||||
def block_at(*position):
|
||||
return states.get(tuple(position))
|
||||
|
||||
toggles = []
|
||||
game = Game.__new__(Game)
|
||||
game.g = Munch(
|
||||
world=FakeWorld(), pos=(0.5, 64, 0.5), yaw=-90.0,
|
||||
path=[door, after], unopenable_doors=set(), look_at=None)
|
||||
game.door_navigation = None
|
||||
game._toggle_door = lambda position, state: toggles.append(
|
||||
(position, state))
|
||||
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
states[door] = opened # the bot steps on a pressure plate
|
||||
self.assertFalse(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'crossing')
|
||||
self.assertFalse(game.door_navigation['opened_by_bot'])
|
||||
|
||||
game.g.pos = (2.5, 64, 0.5)
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_to_close')
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_back')
|
||||
self.assertEqual(toggles, [])
|
||||
|
||||
states[door] = closed
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertFalse(game.handle_path_doors())
|
||||
self.assertIsNone(game.door_navigation)
|
||||
|
||||
def test_powered_door_is_left_to_pressure_plate_after_crossing(self):
|
||||
door = (1, 64, 0)
|
||||
after = (2, 64, 0)
|
||||
closed = self.door_state('oak_door', powered=False)
|
||||
powered_open = self.door_state('oak_door', opened=True, powered=True)
|
||||
released_closed = self.door_state('oak_door', powered=False)
|
||||
states = {door: closed}
|
||||
|
||||
class FakeWorld:
|
||||
@staticmethod
|
||||
def block_at(*position):
|
||||
return states.get(tuple(position))
|
||||
|
||||
toggles = []
|
||||
game = Game.__new__(Game)
|
||||
game.g = Munch(
|
||||
world=FakeWorld(), pos=(0.5, 64, 0.5), yaw=-90.0,
|
||||
path=[door, after], unopenable_doors=set(), look_at=None)
|
||||
game.door_navigation = None
|
||||
game._toggle_door = lambda position, state: toggles.append(
|
||||
(position, state))
|
||||
|
||||
game.handle_path_doors()
|
||||
game.door_navigation['deadline'] = 0
|
||||
game.handle_path_doors()
|
||||
states[door] = powered_open
|
||||
game.handle_path_doors()
|
||||
game.g.pos = (2.5, 64, 0.5)
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_to_close')
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_back')
|
||||
self.assertEqual(toggles, [(door, closed)])
|
||||
|
||||
states[door] = released_closed
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertFalse(game.handle_path_doors())
|
||||
self.assertIsNone(game.door_navigation)
|
||||
|
||||
def test_opens_door_before_crossing_and_closes_it_after(self):
|
||||
door = (1, 64, 0)
|
||||
after = (2, 64, 0)
|
||||
closed = self.door_state('oak_door', powered=False)
|
||||
opened = self.door_state('oak_door', opened=True, powered=False)
|
||||
states = {door: closed}
|
||||
|
||||
class FakeWorld:
|
||||
@staticmethod
|
||||
def block_at(*position):
|
||||
return states.get(tuple(position))
|
||||
|
||||
@staticmethod
|
||||
def path_to_place(_start, _goal):
|
||||
return None
|
||||
|
||||
toggles = []
|
||||
game = Game.__new__(Game)
|
||||
game.g = Munch(
|
||||
world=FakeWorld(), pos=(0.5, 64, 0.5), yaw=-90.0,
|
||||
path=[door, after], unopenable_doors=set(), look_at=None)
|
||||
game.door_navigation = None
|
||||
game._toggle_door = lambda position, state: toggles.append(
|
||||
(position, state))
|
||||
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'checking')
|
||||
self.assertEqual(toggles, [])
|
||||
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'opening')
|
||||
self.assertEqual(toggles, [(door, closed)])
|
||||
|
||||
states[door] = opened
|
||||
self.assertFalse(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'crossing')
|
||||
|
||||
game.g.pos = (2.5, 64, 0.5)
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_to_close')
|
||||
self.assertEqual(game.g.look_at, door)
|
||||
self.assertEqual(len(toggles), 1)
|
||||
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'closing')
|
||||
self.assertEqual(toggles[-1], (door, opened))
|
||||
|
||||
states[door] = closed
|
||||
self.assertTrue(game.handle_path_doors())
|
||||
self.assertEqual(game.door_navigation['phase'], 'turning_back')
|
||||
self.assertEqual(game.g.look_at, (4, 64, 0))
|
||||
game.door_navigation['deadline'] = 0
|
||||
self.assertFalse(game.handle_path_doors())
|
||||
self.assertIsNone(game.door_navigation)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -2,8 +2,8 @@ import unittest
|
||||
|
||||
from munch import Munch
|
||||
|
||||
from mosfet.bot import look_direction_from_eye, update_pitch
|
||||
from mosfet.vector import Point3D
|
||||
from mosfet.bot import look_direction_from_eye, update_pitch, update_yaw
|
||||
from mosfet.vector import Point3D, Vector3D
|
||||
|
||||
|
||||
class LookDirection:
|
||||
@@ -38,6 +38,26 @@ class TestFarmingPitch(unittest.TestCase):
|
||||
update_pitch(state, directly_below)
|
||||
self.assertEqual(state.pitch, 45.0)
|
||||
|
||||
def test_fixed_yaw_turn_returns_over_the_same_180_degree_arc(self):
|
||||
state = Munch(yaw=-90.0)
|
||||
no_look_direction = Vector3D((0.0, 0.0, 0.0))
|
||||
|
||||
for _ in range(6):
|
||||
update_yaw(state, no_look_direction, fixed_target=-270.0)
|
||||
self.assertEqual(state.yaw, -270.0)
|
||||
|
||||
for _ in range(6):
|
||||
update_yaw(state, no_look_direction, fixed_target=-90.0)
|
||||
self.assertEqual(state.yaw, -90.0)
|
||||
|
||||
def test_yaw_turns_across_wrap_without_unbounded_rotation(self):
|
||||
state = Munch(yaw=630.0)
|
||||
update_yaw(state, Vector3D((0, 0, -1)))
|
||||
|
||||
self.assertEqual(state.yaw, -60.0)
|
||||
self.assertGreaterEqual(state.yaw, -180.0)
|
||||
self.assertLess(state.yaw, 180.0)
|
||||
|
||||
def test_pitch_tracks_look_target_instead_of_a_fixed_farming_angle(self):
|
||||
state = Munch(pitch=0.0)
|
||||
direction_to_crop = LookDirection(45.0)
|
||||
|
||||
Reference in New Issue
Block a user