class LocalGameService:
    def __init__(self, width: int, height: int, player_count: int):

        self.board = Board(width, height)

        start_point_distance = self.board.cell_count // (player_count + 1)
        self.players = []

        # Init Player
        y_start_positions = list(range(1, self.board.width - 1))
        x_start_positions = list(range(1, self.board.height - 1))
        del y_start_positions[::2]
        del x_start_positions[::2]

        start_positions = list(
            itertools.product(y_start_positions, x_start_positions))
        random.shuffle(start_positions)

        for player_id in range(1, player_count + 1):
            start_cell = start_positions.pop()
            player = Player(
                player_id,
                PlayerState(random.choice(list(PlayerDirection)), 1,
                            start_cell[0], start_cell[1]))
            self.board.set_cell(player.current_state.position_x,
                                player.current_state.position_y,
                                player.player_id)
            self.players.append(player)

        self.is_started = False
        self.deadline = None
        self.on_round_start = Event()
        self.all_player_moved = False

    # Starts the Game and sends first Notification to the Player
    def start(self):
        self.is_started = True
        self.__reset_deadline()
        self.__notify_player()
        self.__wait_and_end_round()

    # processes an User interaction
    def do_action(self, player: int, player_action):
        if not self.is_started:
            raise Exception('Game is not started')
        elif self.players[player - 1].next_action is None:
            self.players[player - 1].next_action = player_action
        else:
            self.players[player - 1].is_active = False

        if self.__is_running() and all(
                not p.is_active or p.next_action is not None
                for p in self.players):
            self.all_player_moved = True

    def __notify_player(self):
        self.on_round_start.notify(
            json.dumps({
                "width":
                self.board.width,
                "height":
                self.board.height,
                "cells":
                self.board.cells,
                "players": {
                    player.player_id: player.to_dict()
                    for player in self.players
                },
                "you":
                1,
                "running":
                self.__is_running(),
                "deadline":
                self.deadline.replace(microsecond=0).isoformat("T") + "Z"
            }))

    def __reset_deadline(self):
        deadline_seconds = SIMULATION_DEADLINE

        if not deadline_seconds:
            # default deadline is 1 Day
            deadline_seconds = 60 * 60 * 24

        self.deadline = datetime.utcnow() + timedelta(seconds=deadline_seconds)

    def __is_running(self) -> bool:
        return self.is_started and sum(p.is_active for p in self.players) > 1

    # is running in extra thread: checks the deadline and ends round
    def __wait_and_end_round(self):
        while self.__is_running():
            time.sleep(0.1)

            if self.all_player_moved or self.deadline and self.deadline < datetime.utcnow(
            ):
                self.__reset_deadline()

                for player in self.players:
                    if player.is_active:
                        player.do_action_and_move()
                        for point in player.current_state.steps_to_this_point:
                            self.board.set_cell(point[0], point[1],
                                                player.player_id)
                        player.is_active &= player.current_state.verify_state(
                            self.board)

                for player in self.players:
                    if player.is_active:
                        for point in player.current_state.steps_to_this_point:
                            if self.board[point[1]][point[0]] == -1:
                                player.is_active = False

                self.all_player_moved = False
                self.__notify_player()
Ejemplo n.º 2
0
class Connection:
	"""The component that handles managing the connection to the TA server."""
	
	def __init__(self):
		self._state = STATE_DISCONNECTED
		self._proto = None
		self._host = None
		self._port = None
		self._socket = None
		self.stateChanged = Event()
		self.lineReceived = Event()
		self.xmlReceived = Event()
		self.callback = lambda:None
		self._sockets = []
		self._parser = None
		
	def connect(self, host, port):
		"""Start connecting to a given host/port, terminating current connection if necessary."""
		if not self.isDisconnected():
			self.disconnect()
		self._host = host
		self._port = port
		#reactor.connectTCP(host, port, self)
		self._socket = TiberiaSocket(self)
		self._socket.callback = self.callback
		self._sockets.append(self._socket)
		self._socket.connect(host,port)
		self._parser = ta2.LineParser()
		self._enterState(STATE_CONNECTING)
			
	def disconnect(self):
		"""Disconnect from the current server if connected."""
		if not self.isDisconnected():
			# TODO: make this not warn if you cancel during a connection
			#self._proto.transport.loseConnection()
			self._socket.close()
			self._enterDisconnectedState()
		
	def getHost(self):
		return self._host
		
	def getPort(self):
		return self._port
		
	def getState(self):
		return self._state
		
	def isDisconnected(self):
		return self._state == STATE_DISCONNECTED
		
	def isConnecting(self):
		return self._state == STATE_CONNECTING
		
	def isConnected(self):
		return self._state == STATE_CONNECTED
			
	def sendRaw(self, data):
		"""Send byte data to the server."""
		if self.isConnected():
			self._socket.send(data)
		
	def _enterDisconnectedState(self, reason=REASON_USER):
		self._proto = None
		self._host = None
		self._port = None
		self._socket = None
		self._enterState(STATE_DISCONNECTED, reason)
		self._parser = None
			
	def _enterState(self, state, reason=None):
		oldState = self._state
		self._state = state
		if state != oldState:
			self.stateChanged.notify(state, reason)
		
	def _disconnected(self, sock):
		if sock is self._socket:
			self._enterDisconnectedState(REASON_CONNECTION_LOST)

	def _connectionFailed(self, sock):
		if sock is self._socket:
			self._enterDisconnectedState(REASON_CONNECTION_FAILED)

	def _connected(self, sock):
		if sock is self._socket:
			self._enterState(STATE_CONNECTED)
		
	def _dataReceived(self, sock, data):
		if sock is not self._socket:
			return
			
		self._parser.queueData(data)
		
		# TODO: This is rather ugly! :(
		while True:
			lineChunks = self._parser.getLine()
			if lineChunks is None:
				break
			textChunks = []
			xmlChunks = []
			for c in lineChunks:
				if isinstance(c, ta2.XmlChunk):
					self.xmlReceived.notify(c.xml)
					xmlChunks.append(c)
				else:
					textChunks.append(c)
			if (len(textChunks) > 0 or len(xmlChunks) == 0):
				self.lineReceived.notify(textChunks)
			
	def update(self):
		#print 'Connection.update()'
		socketDied = False
		for s in self._sockets:
			s.update()
			if s.state == SocketWrapper.ST_DISCONNECTED:
				socketDied = True
		if socketDied:
			self._sockets = [x for x in self._sockets if x.state != SocketWrapper.ST_DISCONNECTED]