Compare commits
4 Commits
003113cb89
...
845c6b1cf1
Author | SHA1 | Date | |
---|---|---|---|
845c6b1cf1 | |||
5359dfc4ac | |||
e73c4f8f63 | |||
f33513644f |
53
app.py
53
app.py
|
@ -47,36 +47,41 @@ class Game:
|
|||
l.append(zip([p] * len(self.action_stack[p]), self.action_stack[p]))
|
||||
chosen_cards = list(zip(*l))
|
||||
|
||||
# apply the chosen commands to the board which generates a list of movement commands to send to the control program
|
||||
# process the chosen commands and generate a list of robot commands to send to the controller program
|
||||
cmd_list = self.board.apply_actions(chosen_cards)
|
||||
|
||||
if False:
|
||||
# send movements to the program
|
||||
for c in current_actions:
|
||||
if c[0] == 0:
|
||||
print("{}, {}\n".format(c[1].action, 11))
|
||||
self.comm_socket.sendall("{}, {}\n".format(c[1].action, 11).encode())
|
||||
elif c[0] == 1:
|
||||
print("{}, {}\n".format(c[1].action, 14))
|
||||
self.comm_socket.sendall("{}, {}\n".format(c[1].action, 14).encode())
|
||||
# send movements to the controller program
|
||||
for c in cmd_list:
|
||||
self.comm_socket.sendall(c.encode())
|
||||
data = self.comm_socket.recv(32)
|
||||
|
||||
if data == b'OK\n':
|
||||
print("an error occured while processing the commands")
|
||||
if data != b'OK\n':
|
||||
print("an error occurred while processing the commands")
|
||||
self.processing_done = True
|
||||
self.action_stack = {}
|
||||
return
|
||||
# for c in current_actions:
|
||||
# if c[0] == 0:
|
||||
# print("{}, {}\n".format(c[1].action, 11))
|
||||
# self.comm_socket.sendall("{}, {}\n".format(c[1].action, 11).encode())
|
||||
# elif c[0] == 1:
|
||||
# print("{}, {}\n".format(c[1].action, 14))
|
||||
# self.comm_socket.sendall("{}, {}\n".format(c[1].action, 14).encode())
|
||||
# data = self.comm_socket.recv(32)
|
||||
#
|
||||
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
#self.comm_socket.send()
|
||||
|
||||
|
||||
# clear the action stack for the next round
|
||||
self.action_stack = {}
|
||||
|
||||
self.processing_done = True
|
||||
|
||||
players = {}
|
||||
game = Game()
|
||||
|
||||
class Player:
|
||||
MAX_PLAYERS = 3
|
||||
player_counter = 0
|
||||
|
@ -93,15 +98,17 @@ class Player:
|
|||
self.action_count = 5
|
||||
self.action_chosen = False
|
||||
|
||||
self.robot = None
|
||||
|
||||
else:
|
||||
print("max players reached!")
|
||||
|
||||
def initialize_robot(self, x, y, orientation, marker_id):
|
||||
self.robot = game.board.create_robot(x, y, orientation, self.id, marker_id)
|
||||
|
||||
def draw_new_cards(self):
|
||||
self.player_hand += deck.draw_cards(self.max_cards - len(self.player_hand))
|
||||
|
||||
players = {}
|
||||
game = Game()
|
||||
|
||||
|
||||
@app.route('/send_cmds', methods=['POST', 'GET'])
|
||||
def send_cmds():
|
||||
|
@ -110,6 +117,11 @@ def send_cmds():
|
|||
player_id = session['player_id']
|
||||
p = players[player_id]
|
||||
|
||||
if p.robot is None:
|
||||
x = int(request.form.get('x'))
|
||||
y = int(request.form.get('y'))
|
||||
p.initialize_robot(x, y, '>', 11)
|
||||
|
||||
if game.register_actions(p.id, p.player_hand[0:p.action_count]):
|
||||
p.player_hand = p.player_hand[p.action_count:] # discard used cards
|
||||
p.draw_new_cards()
|
||||
|
@ -149,7 +161,12 @@ def hello_world():
|
|||
|
||||
|
||||
if request.method == 'GET':
|
||||
return render_template('drag_example.html', cmds=player_hand, player_id=player_id)
|
||||
robot = players[player_id].robot
|
||||
if robot is not None:
|
||||
robot_pos = (robot.x, robot.y)
|
||||
else:
|
||||
robot_pos = None
|
||||
return render_template('drag_example.html', cmds=player_hand, player_id=player_id, robot_pos=robot_pos)
|
||||
elif request.method == 'POST':
|
||||
#print(request.form)
|
||||
|
||||
|
|
206
roborally.py
206
roborally.py
|
@ -78,27 +78,38 @@ class Robot:
|
|||
# dictionary mapping an orientation to its opposite
|
||||
opposites = {'^': 'v', '>': '<', 'v': '^', '<': '>'}
|
||||
|
||||
def __init__(self, x, y, orientation, id, board):
|
||||
def __init__(self, x, y, orientation, marker_id, board):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.orientation = orientation
|
||||
self.id = id
|
||||
self.marker_id = marker_id
|
||||
self.damage = 0
|
||||
self.collected_flags = set()
|
||||
|
||||
self.board = board
|
||||
|
||||
# mark the tile on the board as occupied
|
||||
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):
|
||||
# create a list of all tiles the robot would enter if it drives <count> steps forward
|
||||
tiles = []
|
||||
current_tile = self.board[(self.x, self.y)]
|
||||
current_tile = self.get_tile()
|
||||
for i in range(1, count + 1):
|
||||
if forward:
|
||||
current_tile = self.board.get(current_tile.get_neighbor_coordinates(self.orientation))
|
||||
else:
|
||||
current_tile = self.board.get(current_tile.get_neighbor_coordinates(Robot.opposites[self.orientation]))
|
||||
|
||||
if current_tile is None:
|
||||
return tiles
|
||||
else:
|
||||
|
@ -108,7 +119,7 @@ class Robot:
|
|||
def is_pushable(self, 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
|
||||
robot_tile = self.board[(self.x, self.y)]
|
||||
robot_tile = self.get_tile()
|
||||
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
|
||||
return False
|
||||
|
@ -135,14 +146,14 @@ class Robot:
|
|||
# change the orientation of the robot
|
||||
self.orientation = Robot.resulting_orientation[self.orientation][type]
|
||||
|
||||
return "{}, {}".format(self.id, type)
|
||||
return "{}, {}".format(self.marker_id, type)
|
||||
|
||||
def move(self, type):
|
||||
# move the robot forward or backward
|
||||
# this involves
|
||||
tile = self.board[(self.x, self.y)]
|
||||
tile = self.get_tile()
|
||||
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:
|
||||
print("error: target tile is not empty")
|
||||
|
@ -154,10 +165,10 @@ class Robot:
|
|||
self.y = target_tile.y
|
||||
|
||||
# return the move for sending to the controller
|
||||
return "{}, forward".format(self.id)
|
||||
return "{}, forward".format(self.marker_id)
|
||||
elif type == 'backward':
|
||||
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:
|
||||
print("error: target tile is not empty")
|
||||
|
@ -169,24 +180,50 @@ class Robot:
|
|||
self.y = target_tile.y
|
||||
|
||||
# return the move for sending to the controller
|
||||
return "{}, backward".format(self.id)
|
||||
return "{}, backward".format(self.marker_id)
|
||||
else:
|
||||
print("error: invalid move")
|
||||
sys.exit(1)
|
||||
|
||||
def nop(self):
|
||||
# do nothing command
|
||||
return "{}, nop".format(self.id)
|
||||
return "{}, nop".format(self.marker_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 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 take_damage(self, count):
|
||||
self.damage = min(self.damage + count, 10)
|
||||
|
||||
def heal_damage(self, count):
|
||||
self.damage = max(self.damage - count, 0)
|
||||
|
||||
def pick_up_flag(self, flag):
|
||||
self.collected_flags.add(flag)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.id)
|
||||
return str(self.marker_id)
|
||||
|
||||
|
||||
class Tile:
|
||||
# possible modifiers:
|
||||
# conveyors: <, >, ^, v
|
||||
# repair station: r
|
||||
# flag: f<number>
|
||||
# # : wall (robot is blocked from moving there)
|
||||
# [<, >, ^, v] : conveyors (robot is pushed to the next tile)
|
||||
# + : rotation in positive direction (robot is rotated ccw)
|
||||
# - : rotation in negative direction (robot is rotated cw)
|
||||
# p : pit (robot takes damage)
|
||||
# r : repair station (robot heals damage)
|
||||
# [a,b,c,d] : flag (robot scores)
|
||||
#
|
||||
# occupant: Robot that is standing on the tile
|
||||
def __init__(self, x, y, modifier=None):
|
||||
self.modifier = modifier
|
||||
self.occupant = None
|
||||
|
@ -238,16 +275,46 @@ class Board:
|
|||
if (x == 0) or (x == Board.x_dims + 1) or (y == 0) or (y == Board.y_dims + 1):
|
||||
# place walls around the board
|
||||
self.board[(x, y)] = Tile(x, y, '#')
|
||||
elif y > 2 and y < 6 and x == 7:
|
||||
self.board[(x, y)] = Tile(x, y, '#')
|
||||
elif x == 1 and (y >= 1) and (y < 4):
|
||||
self.board[(x, y)] = Tile(x, y, 'v')
|
||||
elif y == 4:
|
||||
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:
|
||||
self.board[(x,y)] = Tile(x,y)
|
||||
|
||||
self.board[(5, 1)].modifier = '+'
|
||||
self.board[(5, 4)].modifier = '-'
|
||||
self.board[(2, 2)].modifier = 'p'
|
||||
self.board[(3, 3)].modifier = 'r'
|
||||
|
||||
# place flags near the corners of the board
|
||||
self.board[(2,2)].modifier = 'a'
|
||||
self.board[(Board.x_dims-1, 2)].modifier = 'b'
|
||||
self.board[(Board.x_dims-1, Board.y_dims-1)].modifier = 'c'
|
||||
self.board[(2, Board.y_dims-1)].modifier = 'd'
|
||||
|
||||
|
||||
# self.board[(2, 2)].modifier = '^'
|
||||
# self.board[(2, 1)].modifier = '<'
|
||||
|
||||
self.robots = {}
|
||||
self.robots[0] = Robot(3, 1, '>', 0, self.board)
|
||||
self.robots[1] = Robot(2, 1, 'v', 1, self.board)
|
||||
#self.robots[0] = Robot(1, 1, 'v', 0, 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)
|
||||
#self.create_robot(1,1,'>', 7, 11)
|
||||
|
||||
def create_robot(self, x, y, orientation, player_id, marker_id):
|
||||
new_robot = Robot(x, y, orientation, marker_id, self.board)
|
||||
self.robots[player_id] = new_robot
|
||||
return new_robot
|
||||
|
||||
|
||||
def handle_push(self, direction, pushed_robot, forward=True, pushing_robot=None):
|
||||
cmd_list = []
|
||||
|
@ -366,12 +433,24 @@ class Board:
|
|||
def handle_board_element(self, robot):
|
||||
cmd_list = []
|
||||
tile = self.board[(robot.x, robot.y)]
|
||||
if tile.modifier in ['^', '>', 'v', '<']:
|
||||
if tile.modifier is None:
|
||||
return cmd_list
|
||||
elif tile.modifier in ['^', '>', 'v', '<']:
|
||||
# board element pushes the robot to next tile
|
||||
if robot.is_pushable(tile.modifier):
|
||||
cmd_list += self.handle_push(direction=tile.modifier, pushed_robot=robot, forward=True)
|
||||
else:
|
||||
cmd_list.append(robot.nop())
|
||||
elif tile.modifier == '+':
|
||||
cmd_list.append(robot.turn('turn left'))
|
||||
elif tile.modifier == '-':
|
||||
cmd_list.append(robot.turn('turn right'))
|
||||
elif tile.modifier == 'p':
|
||||
robot.take_damage(1)
|
||||
elif tile.modifier == 'r':
|
||||
robot.heal_damage(1)
|
||||
elif tile.modifier in 'abcd':
|
||||
robot.pick_up_flag(tile.modifier)
|
||||
return cmd_list
|
||||
|
||||
def apply_actions(self, cards):
|
||||
|
@ -392,14 +471,59 @@ class Board:
|
|||
|
||||
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)
|
||||
pass
|
||||
|
||||
# apply the actions caused by board elements at the end of the phase
|
||||
self.apply_board_element_actions()
|
||||
|
||||
print(self)
|
||||
|
||||
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
|
||||
|
||||
def __str__(self):
|
||||
|
@ -407,29 +531,41 @@ class Board:
|
|||
output = ''
|
||||
for y in range(Board.y_dims+2):
|
||||
for x in range(Board.x_dims+2):
|
||||
if any((r.x, r.y) == (x,y) for r in self.robots.values()):
|
||||
r = list(filter(lambda r: (r.x,r.y) == (x,y), self.robots.values()))[0]
|
||||
output += str(r.id)
|
||||
if any((r.x, r.y) == (x,y) for (r_id, r) in self.robots.items()):
|
||||
r = next(filter(lambda r: (r[1].x,r[1].y) == (x,y), self.robots.items()))
|
||||
output += str(r[0])
|
||||
else:
|
||||
output += str(self.board[(x, y)])
|
||||
output += '\n'
|
||||
#output += '#' * (Board.x_dims + 2)
|
||||
for r_id, r in self.robots.items():
|
||||
output += "Robot {}: {}\n".format(r_id, r.orientation)
|
||||
return output
|
||||
|
||||
if __name__ == "__main__":
|
||||
n = 5
|
||||
|
||||
deck = CardDeck()
|
||||
deck = CardDeck(n=1000)
|
||||
|
||||
player_1_cards = random.sample(list(filter(lambda c: 'backward' 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_2_cards = deck.draw_cards(40)
|
||||
player_1_cards = deck.draw_cards(200)
|
||||
#player_2_cards = deck.draw_cards(200)
|
||||
#player_3_cards = deck.draw_cards(200)
|
||||
#player_4_cards = deck.draw_cards(200)
|
||||
|
||||
#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_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)
|
||||
|
||||
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]
|
||||
#cards_3 = [(2, c) for c in player_3_cards]
|
||||
#cards_4 = [(3, c) for c in player_4_cards]
|
||||
|
||||
chosen_cards = list(zip(cards_1, cards_2))
|
||||
|
||||
|
||||
#chosen_cards = list(zip(cards_1, cards_2, cards_3, cards_4))
|
||||
chosen_cards = list(zip(cards_1))
|
||||
|
||||
b = Board()
|
||||
print(b)
|
||||
|
|
Loading…
Reference in New Issue
Block a user