implemented core game logic with generated commands

master
Simon Pirkelmann 2020-09-20 20:20:40 +02:00
parent ca5c0d7083
commit ca11f3476c
1 changed files with 136 additions and 65 deletions

View File

@ -62,6 +62,25 @@ deck = CardDeck()
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):
self.x = x
self.y = y
@ -71,7 +90,7 @@ class Robot:
self.board = board
# 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):
@ -104,25 +123,57 @@ class Robot:
return (self.orientation, direction) in opposites
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
directions = {
('^', '>'): 'turn right',
('^', 'v'): 'turn around',
('^', '<'): 'turn left',
('>', 'v'): 'turn right',
('>', '<'): 'turn around',
('>', '^'): 'turn left',
('v', '<'): 'turn right',
('v', '^'): 'turn around',
('v', '>'): 'turn left',
('<', '^'): 'turn right',
('<', '>'): 'turn around',
('<', 'v'): 'turn left',
}
return directions[(self.orientation, target_orienation)]
return Robot.necessary_turn[self.orientation][target_orienation]
def get_opposite_orientation(self):
return Robot.opposites[self.orientation]
def turn(self, type):
# change the orientation of the robot
self.orientation = Robot.resulting_orientation[self.orientation][type]
return "{}, {}".format(self.id, 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):
return str(self.id)
@ -164,7 +215,13 @@ class Tile:
else:
return self.modifier
else:
return str(self.occupant)
if self.occupant is None:
return self.modifier
else:
return str(self.occupant)
def __repr__(self):
return "({}, {}) occ: {} mod: {}".format(self.x, self.y, self.occupant, self.modifier)
class Board:
@ -185,10 +242,42 @@ class Board:
self.robots = {}
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):
cmd_list = []
if 'forward' in action: # driving forward
if "x2" in action:
move_count = 2
@ -199,58 +288,38 @@ class Board:
accessed_tiles = robot.get_accessed_tiles(move_count)
for tile in accessed_tiles:
if tile.modifier == '#': # robot hits a wall -> stop the robot
pass
elif tile.modifier == 'X': # robot drives into a pit -> take damage
pass
if tile is None:
# this case should not happen
print("error: unknown state occured")
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
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()))
if pushed_robot.is_pushable(robot.orientation): # check if robot is pushable in the given direction
if pushed_robot.orientation == robot.orientation:
# 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:
# 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(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:
print("error: invalid turn direction")
sys.exit(1)
self.handle_single_action(turn_back_direction, pushed_robot)
else: # robot is not pushable -> do not move
pass
cmd_list += self.handle_push(pushing_robot=robot, pushed_robot=pushed_robot)
else:
cmd_list.append(robot.nop())
return cmd_list
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
# this case should not happen
print("error: unknown state occured")
sys.exit(1)
elif action == 'backward':
# basically do the same as with forward
pass
elif action == 'turn left':
pass
elif action == 'turn right':
pass
elif action == 'turn around':
pass
else: # this means we have a turn action
cmd_list.append(robot.turn(action))
return cmd_list
def apply_actions(self, cards):
cmd_list = []
# apply the actions to the board and generate a list of movement commands
for i, phase in enumerate(cards): # process register phases
@ -265,7 +334,9 @@ class Board:
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
pass
@ -286,8 +357,8 @@ class Board:
if __name__ == "__main__":
n = 5
player_1_cards = random.sample(list(filter(lambda c: 'forward' in c.action, deck.deck.values())), 3)
player_2_cards = random.sample(list(filter(lambda c: 'turn around' 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())), n)
cards_1 = [(0, c) for c in player_1_cards]
cards_2 = [(1, c) for c in player_2_cards]