implemented conveyor board elements, paying attention to conflicts and order of execution
This commit is contained in:
parent
003113cb89
commit
f33513644f
116
roborally.py
116
roborally.py
|
@ -89,16 +89,25 @@ class Robot:
|
||||||
# mark the tile on the board as occupied
|
# mark the tile on the board as occupied
|
||||||
self.board[(x,y)].occupant = self
|
self.board[(x,y)].occupant = self
|
||||||
|
|
||||||
|
def get_tile(self):
|
||||||
|
# return the tile the robot is standing on
|
||||||
|
return self.board[(self.x, self.y)]
|
||||||
|
|
||||||
|
def get_adjecent_tile(self, direction):
|
||||||
|
# get the tile adjecent to the robot in the given direction
|
||||||
|
current_tile = self.get_tile()
|
||||||
|
return self.board[current_tile.get_neighbor_coordinates(direction)]
|
||||||
|
|
||||||
def get_accessed_tiles(self, count, forward=True):
|
def get_accessed_tiles(self, count, forward=True):
|
||||||
# create a list of all tiles the robot would enter if it drives <count> steps forward
|
# create a list of all tiles the robot would enter if it drives <count> steps forward
|
||||||
tiles = []
|
tiles = []
|
||||||
current_tile = self.board[(self.x, self.y)]
|
current_tile = self.get_tile()
|
||||||
for i in range(1, count + 1):
|
for i in range(1, count + 1):
|
||||||
if forward:
|
if forward:
|
||||||
current_tile = self.board.get(current_tile.get_neighbor_coordinates(self.orientation))
|
current_tile = self.board.get(current_tile.get_neighbor_coordinates(self.orientation))
|
||||||
else:
|
else:
|
||||||
current_tile = self.board.get(current_tile.get_neighbor_coordinates(Robot.opposites[self.orientation]))
|
current_tile = self.board.get(current_tile.get_neighbor_coordinates(Robot.opposites[self.orientation]))
|
||||||
|
|
||||||
if current_tile is None:
|
if current_tile is None:
|
||||||
return tiles
|
return tiles
|
||||||
else:
|
else:
|
||||||
|
@ -108,7 +117,7 @@ class Robot:
|
||||||
def is_pushable(self, direction):
|
def is_pushable(self, direction):
|
||||||
# check if the robot can be pushed in the given direction
|
# check if the robot can be pushed in the given direction
|
||||||
# this is the case if there is a non-blocking tile next to the robot or if there is another robot that is pushable
|
# this is the case if there is a non-blocking tile next to the robot or if there is another robot that is pushable
|
||||||
robot_tile = self.board[(self.x, self.y)]
|
robot_tile = self.get_tile()
|
||||||
neighbor_tile = self.board.get(robot_tile.get_neighbor_coordinates(direction))
|
neighbor_tile = self.board.get(robot_tile.get_neighbor_coordinates(direction))
|
||||||
if neighbor_tile is None: # neighbor tile could not be found -> robot would be pushed out of the board
|
if neighbor_tile is None: # neighbor tile could not be found -> robot would be pushed out of the board
|
||||||
return False
|
return False
|
||||||
|
@ -140,9 +149,9 @@ class Robot:
|
||||||
def move(self, type):
|
def move(self, type):
|
||||||
# move the robot forward or backward
|
# move the robot forward or backward
|
||||||
# this involves
|
# this involves
|
||||||
tile = self.board[(self.x, self.y)]
|
tile = self.get_tile()
|
||||||
if type == 'forward':
|
if type == 'forward':
|
||||||
target_tile = self.board[tile.get_neighbor_coordinates(self.orientation)]
|
target_tile = self.get_adjecent_tile(self.orientation)
|
||||||
|
|
||||||
if target_tile.occupant is not None:
|
if target_tile.occupant is not None:
|
||||||
print("error: target tile is not empty")
|
print("error: target tile is not empty")
|
||||||
|
@ -157,7 +166,7 @@ class Robot:
|
||||||
return "{}, forward".format(self.id)
|
return "{}, forward".format(self.id)
|
||||||
elif type == 'backward':
|
elif type == 'backward':
|
||||||
opposite_orientation = self.get_opposite_orientation()
|
opposite_orientation = self.get_opposite_orientation()
|
||||||
target_tile = self.board[tile.get_neighbor_coordinates(opposite_orientation)]
|
target_tile = self.get_adjecent_tile(opposite_orientation)
|
||||||
|
|
||||||
if target_tile.occupant is not None:
|
if target_tile.occupant is not None:
|
||||||
print("error: target tile is not empty")
|
print("error: target tile is not empty")
|
||||||
|
@ -178,6 +187,19 @@ class Robot:
|
||||||
# do nothing command
|
# do nothing command
|
||||||
return "{}, nop".format(self.id)
|
return "{}, nop".format(self.id)
|
||||||
|
|
||||||
|
def board_element_processable(self):
|
||||||
|
# check if we can directly process the board element for the tile the current robot is located on
|
||||||
|
tile = self.get_tile()
|
||||||
|
|
||||||
|
if tile.modifier is None:
|
||||||
|
return True
|
||||||
|
elif tile.modifier in ['^', '>', 'v', '<']:
|
||||||
|
direction = tile.modifier
|
||||||
|
neighbor_tile = self.get_adjecent_tile(direction)
|
||||||
|
return neighbor_tile.occupant is None # if the adjacent tile the robot will be pushed into is empty
|
||||||
|
# we can execute the push
|
||||||
|
return True
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.id)
|
return str(self.id)
|
||||||
|
|
||||||
|
@ -242,12 +264,23 @@ class Board:
|
||||||
self.board[(x, y)] = Tile(x, y, 'v')
|
self.board[(x, y)] = Tile(x, y, 'v')
|
||||||
elif y == 4:
|
elif y == 4:
|
||||||
self.board[(x, y)] = Tile(x, y, '>')
|
self.board[(x, y)] = Tile(x, y, '>')
|
||||||
|
elif y == 1 and (x >= 2) and (x < 5):
|
||||||
|
self.board[(x, y)] = Tile(x, y, '>')
|
||||||
|
elif y == 1 and (x >= 6) and (x <= 8):
|
||||||
|
self.board[(x, y)] = Tile(x, y, '<')
|
||||||
else:
|
else:
|
||||||
self.board[(x,y)] = Tile(x,y)
|
self.board[(x,y)] = Tile(x,y)
|
||||||
|
|
||||||
|
self.board[(1, 1)].modifier = 'v'
|
||||||
|
self.board[(1, 2)].modifier = '>'
|
||||||
|
self.board[(2, 2)].modifier = '^'
|
||||||
|
self.board[(2, 1)].modifier = '<'
|
||||||
|
|
||||||
self.robots = {}
|
self.robots = {}
|
||||||
self.robots[0] = Robot(3, 1, '>', 0, self.board)
|
self.robots[0] = Robot(1, 1, '>', 0, self.board)
|
||||||
self.robots[1] = Robot(2, 1, 'v', 1, self.board)
|
self.robots[1] = Robot(1, 2, 'v', 1, self.board)
|
||||||
|
self.robots[2] = Robot(2, 1, '>', 2, self.board)
|
||||||
|
self.robots[3] = Robot(2, 2, 'v', 3, self.board)
|
||||||
|
|
||||||
def handle_push(self, direction, pushed_robot, forward=True, pushing_robot=None):
|
def handle_push(self, direction, pushed_robot, forward=True, pushing_robot=None):
|
||||||
cmd_list = []
|
cmd_list = []
|
||||||
|
@ -392,14 +425,57 @@ class Board:
|
||||||
|
|
||||||
cmd_list += self.handle_single_action(action, robot)
|
cmd_list += self.handle_single_action(action, robot)
|
||||||
|
|
||||||
# apply the actions caused by board elements at the end of the phase
|
|
||||||
for robot_id in self.robots:
|
|
||||||
robot = self.robots[robot_id]
|
|
||||||
cmd_list += self.handle_board_element(robot)
|
|
||||||
|
|
||||||
print(cmd_list)
|
|
||||||
print(self)
|
print(self)
|
||||||
pass
|
|
||||||
|
# apply the actions caused by board elements at the end of the phase
|
||||||
|
self.apply_board_element_actions()
|
||||||
|
|
||||||
|
return cmd_list
|
||||||
|
|
||||||
|
def apply_board_element_actions(self):
|
||||||
|
cmd_list = []
|
||||||
|
remaining_robots = set(self.robots.values())
|
||||||
|
processed_robots = set()
|
||||||
|
|
||||||
|
# first we compute all tiles the robots would enter as a result of board game elements
|
||||||
|
target_tiles = {} # get target tiles for each robot
|
||||||
|
for r in remaining_robots:
|
||||||
|
tile = r.get_tile()
|
||||||
|
if tile.modifier in ['^', '>', 'v', '<']: # tile would push the robot around
|
||||||
|
direction = tile.modifier
|
||||||
|
target_tiles[r] = r.get_adjecent_tile(direction) # save tile the robot would be pushed to
|
||||||
|
|
||||||
|
# now we check if there are any conflicts
|
||||||
|
conflicting_tiles = set([x for x in target_tiles.values() if list(target_tiles.values()).count(x) > 1])
|
||||||
|
if len(conflicting_tiles) > 0: # check if any robots would be pushed to the same tile
|
||||||
|
# there is a conflict -> skip the board element execution and mark those robots as processed
|
||||||
|
conflicting_robots = set(filter(lambda r: target_tiles[r] in conflicting_tiles, target_tiles.keys()))
|
||||||
|
processed_robots = processed_robots.union(conflicting_robots)
|
||||||
|
|
||||||
|
# Now we process the board game elements for the robots which have no conflicts.
|
||||||
|
# We have to pay attention to the order of the execution in order to avoid robots pushing other robots
|
||||||
|
# during this phase.
|
||||||
|
# This is done in a loop because we don't know yet which robot goes first. For instance, it may happen that
|
||||||
|
# multiple robots are queued on a conveyor belt. Then we first have to move the robot which is furthest down the
|
||||||
|
# line, then the second one and so on
|
||||||
|
# By doing this in a loop we can automatically determine the correct order by checking for each robot if it can
|
||||||
|
# move and then processing the robot such that the next robot can move
|
||||||
|
while len(processed_robots) < len(self.robots):
|
||||||
|
# update remaining robots to process
|
||||||
|
remaining_robots = set(self.robots.values()) - processed_robots
|
||||||
|
|
||||||
|
# check which robots can be moved around
|
||||||
|
processable_robots = list(filter(lambda r: r.board_element_processable(), remaining_robots))
|
||||||
|
|
||||||
|
if len(processable_robots) > 0:
|
||||||
|
# handle the board game elements for robots that can move
|
||||||
|
for current_robot in processable_robots:
|
||||||
|
cmd_list += self.handle_board_element(current_robot)
|
||||||
|
processed_robots.add(current_robot)
|
||||||
|
else:
|
||||||
|
# this happens if there is a deadlock that cannot be resolved (e.g. caused by a cyclical conveyor belt)
|
||||||
|
break
|
||||||
|
|
||||||
return cmd_list
|
return cmd_list
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -421,7 +497,7 @@ if __name__ == "__main__":
|
||||||
|
|
||||||
deck = CardDeck()
|
deck = CardDeck()
|
||||||
|
|
||||||
player_1_cards = random.sample(list(filter(lambda c: 'backward' in c.action, deck.deck.values())), n)
|
player_1_cards = random.sample(list(filter(lambda c: 'turn around' 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)
|
player_2_cards = random.sample(list(filter(lambda c: 'turn around' in c.action, deck.deck.values())), n)
|
||||||
#player_1_cards = deck.draw_cards(40)
|
#player_1_cards = deck.draw_cards(40)
|
||||||
#player_2_cards = deck.draw_cards(40)
|
#player_2_cards = deck.draw_cards(40)
|
||||||
|
@ -429,7 +505,15 @@ if __name__ == "__main__":
|
||||||
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]
|
||||||
|
|
||||||
chosen_cards = list(zip(cards_1, cards_2))
|
player_3_cards = random.sample(list(filter(lambda c: 'turn around' in c.action, deck.deck.values())), n)
|
||||||
|
player_4_cards = random.sample(list(filter(lambda c: 'turn around' in c.action, deck.deck.values())), n)
|
||||||
|
#player_1_cards = deck.draw_cards(40)
|
||||||
|
#player_2_cards = deck.draw_cards(40)
|
||||||
|
|
||||||
|
cards_3 = [(0, c) for c in player_3_cards]
|
||||||
|
cards_4 = [(1, c) for c in player_4_cards]
|
||||||
|
|
||||||
|
chosen_cards = list(zip(cards_1, cards_2, cards_3, cards_4))
|
||||||
|
|
||||||
b = Board()
|
b = Board()
|
||||||
print(b)
|
print(b)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user