implemented core game logic with generated commands
This commit is contained in:
parent
ca5c0d7083
commit
ca11f3476c
195
roborally.py
195
roborally.py
|
@ -62,6 +62,25 @@ deck = CardDeck()
|
||||||
|
|
||||||
|
|
||||||
class Robot:
|
class Robot:
|
||||||
|
# dictionary mapping the current orientation and a turn command to the resulting orientation
|
||||||
|
resulting_orientation = {
|
||||||
|
'^': {'turn left': '<', 'turn right': '>', 'turn around': 'v'},
|
||||||
|
'>': {'turn left': '^', 'turn right': 'v', 'turn around': '<'},
|
||||||
|
'v': {'turn left': '>', 'turn right': '<', 'turn around': '^'},
|
||||||
|
'<': {'turn left': 'v', 'turn right': '^', 'turn around': '>'},
|
||||||
|
}
|
||||||
|
|
||||||
|
# dictionary mapping the current orientation and the target orientation to the necessary turn command
|
||||||
|
necessary_turn = {
|
||||||
|
'^': {'>': 'turn right', 'v': 'turn around', '<': 'turn left'},
|
||||||
|
'>': {'v': 'turn right', '<': 'turn around', '^': 'turn left'},
|
||||||
|
'v': {'<': 'turn right', '^': 'turn around', '>': 'turn left'},
|
||||||
|
'<': {'^': 'turn right', '>': 'turn around', 'v': 'turn left'},
|
||||||
|
}
|
||||||
|
|
||||||
|
# dictionary mapping an orientation to its opposite
|
||||||
|
opposites = {'^': 'v', '>': '<', 'v': '^', '<': '>'}
|
||||||
|
|
||||||
def __init__(self, x, y, orientation, id, board):
|
def __init__(self, x, y, orientation, id, board):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
|
@ -71,7 +90,7 @@ class Robot:
|
||||||
self.board = board
|
self.board = board
|
||||||
|
|
||||||
# mark the tile on the board as occupied
|
# mark the tile on the board as occupied
|
||||||
self.board[(x,y)].occupier = self
|
self.board[(x,y)].occupant = self
|
||||||
|
|
||||||
|
|
||||||
def get_accessed_tiles(self, count):
|
def get_accessed_tiles(self, count):
|
||||||
|
@ -104,25 +123,57 @@ class Robot:
|
||||||
return (self.orientation, direction) in opposites
|
return (self.orientation, direction) in opposites
|
||||||
|
|
||||||
def get_turn_direction(self, target_orienation):
|
def get_turn_direction(self, target_orienation):
|
||||||
# get the direction to turn to in order to face in the same direction as the given orientation
|
return Robot.necessary_turn[self.orientation][target_orienation]
|
||||||
directions = {
|
|
||||||
('^', '>'): 'turn right',
|
def get_opposite_orientation(self):
|
||||||
('^', 'v'): 'turn around',
|
return Robot.opposites[self.orientation]
|
||||||
('^', '<'): 'turn left',
|
|
||||||
('>', 'v'): 'turn right',
|
def turn(self, type):
|
||||||
('>', '<'): 'turn around',
|
# change the orientation of the robot
|
||||||
('>', '^'): 'turn left',
|
self.orientation = Robot.resulting_orientation[self.orientation][type]
|
||||||
('v', '<'): 'turn right',
|
|
||||||
('v', '^'): 'turn around',
|
return "{}, {}".format(self.id, type)
|
||||||
('v', '>'): 'turn left',
|
|
||||||
('<', '^'): 'turn right',
|
|
||||||
('<', '>'): 'turn around',
|
|
||||||
('<', 'v'): 'turn left',
|
|
||||||
}
|
|
||||||
return directions[(self.orientation, target_orienation)]
|
|
||||||
|
|
||||||
def move(self, type):
|
def move(self, type):
|
||||||
pass
|
# move the robot forward or backward
|
||||||
|
# this involves
|
||||||
|
tile = self.board[(self.x, self.y)]
|
||||||
|
if type == 'forward':
|
||||||
|
target_tile = self.board[tile.get_neighbor_coordinates(self.orientation)]
|
||||||
|
|
||||||
|
if target_tile.occupant is not None:
|
||||||
|
print("error: target tile is not empty")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tile.occupant = None # delete the robot from the current tile
|
||||||
|
target_tile.occupant = self # place the robot in the next tile
|
||||||
|
self.x = target_tile.x
|
||||||
|
self.y = target_tile.y
|
||||||
|
|
||||||
|
# return the move for sending to the controller
|
||||||
|
return "{}, forward".format(self.id)
|
||||||
|
elif type == 'backward':
|
||||||
|
opposite_orientation = self.get_opposite_orientation()
|
||||||
|
target_tile = tile.get_neighbor_coordinates(opposite_orientation)
|
||||||
|
|
||||||
|
if target_tile.occupant is not None:
|
||||||
|
print("error: target tile is not empty")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tile.occupant = None # delete the robot from the current tile
|
||||||
|
target_tile.occupant = self # place the robot in the next tile
|
||||||
|
self.x = target_tile.x
|
||||||
|
self.y = target_tile.y
|
||||||
|
|
||||||
|
# return the move for sending to the controller
|
||||||
|
return "{}, backward".format(self.id)
|
||||||
|
else:
|
||||||
|
print("error: invalid move")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
def nop(self):
|
||||||
|
# do nothing command
|
||||||
|
return "{}, nop".format(self.id)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.id)
|
return str(self.id)
|
||||||
|
@ -163,9 +214,15 @@ class Tile:
|
||||||
return ' '
|
return ' '
|
||||||
else:
|
else:
|
||||||
return self.modifier
|
return self.modifier
|
||||||
|
else:
|
||||||
|
if self.occupant is None:
|
||||||
|
return self.modifier
|
||||||
else:
|
else:
|
||||||
return str(self.occupant)
|
return str(self.occupant)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "({}, {}) occ: {} mod: {}".format(self.x, self.y, self.occupant, self.modifier)
|
||||||
|
|
||||||
|
|
||||||
class Board:
|
class Board:
|
||||||
x_dims = 12 # number of tiles in x direction
|
x_dims = 12 # number of tiles in x direction
|
||||||
|
@ -185,10 +242,42 @@ class Board:
|
||||||
|
|
||||||
self.robots = {}
|
self.robots = {}
|
||||||
self.robots[0] = Robot(3, 1, '<', 0, self.board)
|
self.robots[0] = Robot(3, 1, '<', 0, self.board)
|
||||||
self.robots[1] = Robot(1, 1, 'v', 1, self.board)
|
self.robots[1] = Robot(2, 1, 'v', 1, self.board)
|
||||||
|
|
||||||
|
def handle_push(self, pushing_robot, pushed_robot):
|
||||||
|
cmd_list = []
|
||||||
|
# push robot out of the way
|
||||||
|
if pushed_robot.orientation == pushing_robot.orientation:
|
||||||
|
# the pushed robot can just drive forward
|
||||||
|
cmd_list += self.handle_single_action('forward', pushed_robot)
|
||||||
|
elif pushed_robot.has_opposite_orientation(pushing_robot.orientation):
|
||||||
|
# the pushed robot can drive backward
|
||||||
|
cmd_list += self.handle_single_action('backward', pushed_robot)
|
||||||
|
else:
|
||||||
|
# we first have to turn the pushed robot s.t. it faces in the same orientation as the
|
||||||
|
# pushing robot
|
||||||
|
turn_direction = pushed_robot.get_turn_direction(pushing_robot.orientation)
|
||||||
|
cmd_list += self.handle_single_action(turn_direction, pushed_robot)
|
||||||
|
|
||||||
|
# then the pushed robot drives one step forward
|
||||||
|
cmd_list += self.handle_single_action('forward', pushed_robot)
|
||||||
|
|
||||||
|
# afterwards we turn the robot back to the original orientation
|
||||||
|
if turn_direction == 'turn left':
|
||||||
|
turn_back_direction = 'turn right'
|
||||||
|
elif turn_direction == 'turn right':
|
||||||
|
turn_back_direction = 'turn left'
|
||||||
|
else:
|
||||||
|
print("error: invalid turn direction")
|
||||||
|
sys.exit(1)
|
||||||
|
cmd_list += self.handle_single_action(turn_back_direction, pushed_robot)
|
||||||
|
|
||||||
|
# now the tile should be empty so the pushing robot can move into the tile
|
||||||
|
cmd_list.append(pushing_robot.move('forward'))
|
||||||
|
return cmd_list
|
||||||
|
|
||||||
def handle_single_action(self, action, robot):
|
def handle_single_action(self, action, robot):
|
||||||
|
cmd_list = []
|
||||||
if 'forward' in action: # driving forward
|
if 'forward' in action: # driving forward
|
||||||
if "x2" in action:
|
if "x2" in action:
|
||||||
move_count = 2
|
move_count = 2
|
||||||
|
@ -199,58 +288,38 @@ class Board:
|
||||||
accessed_tiles = robot.get_accessed_tiles(move_count)
|
accessed_tiles = robot.get_accessed_tiles(move_count)
|
||||||
|
|
||||||
for tile in accessed_tiles:
|
for tile in accessed_tiles:
|
||||||
if tile.modifier == '#': # robot hits a wall -> stop the robot
|
if tile is None:
|
||||||
pass
|
# this case should not happen
|
||||||
elif tile.modifier == 'X': # robot drives into a pit -> take damage
|
print("error: unknown state occured")
|
||||||
pass
|
sys.exit(1)
|
||||||
|
elif tile.is_empty():
|
||||||
|
# if the tile is empty we can just move there
|
||||||
|
cmd_list.append(robot.move('forward'))
|
||||||
|
elif tile.modifier == '#': # robot hits a wall -> stop the robot
|
||||||
|
cmd_list.append(robot.nop())
|
||||||
|
return cmd_list
|
||||||
elif any([(tile.x, tile.y) == (r.x, r.y) for r in
|
elif any([(tile.x, tile.y) == (r.x, r.y) for r in
|
||||||
self.robots.values()]): # robots hits a tile occupied by another robot
|
self.robots.values()]): # robots hits a tile occupied by another robot
|
||||||
pushed_robot = next(filter(lambda r: (tile.x, tile.y) == (r.x, r.y), self.robots.values()))
|
pushed_robot = next(filter(lambda r: (tile.x, tile.y) == (r.x, r.y), self.robots.values()))
|
||||||
|
|
||||||
if pushed_robot.is_pushable(robot.orientation): # check if robot is pushable in the given direction
|
if pushed_robot.is_pushable(robot.orientation): # check if robot is pushable in the given direction
|
||||||
if pushed_robot.orientation == robot.orientation:
|
cmd_list += self.handle_push(pushing_robot=robot, pushed_robot=pushed_robot)
|
||||||
# the pushed robot can just drive forward
|
|
||||||
self.handle_single_action('forward', pushed_robot)
|
|
||||||
elif pushed_robot.has_opposite_orientation(robot.orientation):
|
|
||||||
# the pushed robot can drive backward
|
|
||||||
self.handle_single_action('backward', pushed_robot)
|
|
||||||
else:
|
else:
|
||||||
# we first have to turn the pushed robot s.t. it faces in the same orientation as the
|
cmd_list.append(robot.nop())
|
||||||
# pushing robot
|
return cmd_list
|
||||||
turn_direction = pushed_robot.get_turn_direction(robot.orientation)
|
|
||||||
self.handle_single_action(turn_direction, pushed_robot)
|
|
||||||
|
|
||||||
# then the pushed robot drives one step forward
|
|
||||||
self.handle_single_action('forward', pushed_robot)
|
|
||||||
|
|
||||||
# afterwards we turn the robot back to the original orientation
|
|
||||||
if turn_direction == 'turn left':
|
|
||||||
turn_back_direction = 'turn right'
|
|
||||||
elif turn_direction == 'turn right':
|
|
||||||
turn_back_direction = 'turn left'
|
|
||||||
else:
|
else:
|
||||||
print("error: invalid turn direction")
|
# this case should not happen
|
||||||
|
print("error: unknown state occured")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
self.handle_single_action(turn_back_direction, pushed_robot)
|
|
||||||
else: # robot is not pushable -> do not move
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
# now the tile should be empty so the robot can move into the tile
|
|
||||||
# TODO: possible problem: what happens when robot cannot be pushed out of the way (e.g. because it is
|
|
||||||
# blocked by a wall) -> check if robot is pushable beforehand
|
|
||||||
# -> register move action to process
|
|
||||||
pass
|
|
||||||
elif action == 'backward':
|
elif action == 'backward':
|
||||||
# basically do the same as with forward
|
# basically do the same as with forward
|
||||||
pass
|
pass
|
||||||
elif action == 'turn left':
|
else: # this means we have a turn action
|
||||||
pass
|
cmd_list.append(robot.turn(action))
|
||||||
elif action == 'turn right':
|
|
||||||
pass
|
return cmd_list
|
||||||
elif action == 'turn around':
|
|
||||||
pass
|
|
||||||
|
|
||||||
def apply_actions(self, cards):
|
def apply_actions(self, cards):
|
||||||
|
cmd_list = []
|
||||||
# apply the actions to the board and generate a list of movement commands
|
# apply the actions to the board and generate a list of movement commands
|
||||||
|
|
||||||
for i, phase in enumerate(cards): # process register phases
|
for i, phase in enumerate(cards): # process register phases
|
||||||
|
@ -265,7 +334,9 @@ class Board:
|
||||||
|
|
||||||
print("robot {} action {}".format(robot, action))
|
print("robot {} action {}".format(robot, action))
|
||||||
|
|
||||||
self.handle_single_action(action, robot)
|
cmd_list += self.handle_single_action(action, robot)
|
||||||
|
print(cmd_list)
|
||||||
|
pass
|
||||||
|
|
||||||
# apply the actions caused by board elements at the end of the phase
|
# apply the actions caused by board elements at the end of the phase
|
||||||
pass
|
pass
|
||||||
|
@ -286,8 +357,8 @@ class Board:
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
n = 5
|
n = 5
|
||||||
player_1_cards = random.sample(list(filter(lambda c: 'forward' in c.action, deck.deck.values())), 3)
|
player_1_cards = random.sample(list(filter(lambda c: 'forward' in c.action, deck.deck.values())), n)
|
||||||
player_2_cards = random.sample(list(filter(lambda c: 'turn around' in c.action, deck.deck.values())), 3)
|
player_2_cards = random.sample(list(filter(lambda c: 'turn around' in c.action, deck.deck.values())), n)
|
||||||
|
|
||||||
cards_1 = [(0, c) for c in player_1_cards]
|
cards_1 = [(0, c) for c in player_1_cards]
|
||||||
cards_2 = [(1, c) for c in player_2_cards]
|
cards_2 = [(1, c) for c in player_2_cards]
|
||||||
|
|
Loading…
Reference in New Issue
Block a user