예제 #1
0
class HWState:
    def __init__(
            self,
            nplayers=2,
            onmove=0,
            systems=None,
            stash=None,
            alive=None,  # list of player statuses, None for not yet created
    ):
        self.nplayers = nplayers
        self.onmove = onmove

        if systems is None:
            systems = []
            alive = [None] * nplayers
        self.systems = systems

        if stash is None:
            stash = Stash(nplayers + 1)
            for sys in systems:
                for m in sys.markers:
                    stash.request(m)
                for s in sys.ships:
                    stash.request(s.piece)
        self.stash = stash

        if alive is None:
            # This assumes that players with no home have been eliminated
            alive = [False] * nplayers
            for sys in systems:
                if not sys.home is None:
                    alive[sys.home] = True

        self.alive = alive
        # This turn will be built one event as a time
        self.curTurn = Turn(self)

    def deepCopy(self):
        systems = [s.deepCopy() for s in self.systems]
        stash = self.stash.deepCopy()
        alive = list(self.alive)
        return HWState(self.nplayers, self.onmove, systems, stash, alive)

    def creationOver(self):
        return self.alive.count(None) == 0

    def addSystem(self, system):
        self.systems.append(system)

    def removeSystem(self, system):
        self.systems.pop(self.systems.index(system))

    def findHome(self, player):
        for sys in self.systems:
            if sys.home == player:
                return sys
        # Player's home is missing
        return None

    def cancelTurn(self):
        self.curTurn.undoAll()

    def addEvent(self, e):
        # Event should be a Creation, Action, Catastrophe, or Pass
        # Fade and Elimination events are checked for and triggered here

        self.curTurn.addEvent(e)
        try:
            e.enact(self)
        except Exception as ex:
            # Signal the turn that the event is cancelled
            # This affects the turn's understanding of whether a sacrifice is occurring
            try:
                self.curTurn.undoLast()
            except:
                print(
                    'A problem occurred while resetting the turn. This could be a serious problem.'
                )
                print(str(ex))
            raise ex

        # Check for Fades (but not for home systems)
        sys = e.getThreatenedSystem()
        if (not sys is None) and (sys.home is None) and sys.isVoid():
            fade = sys.getFade()
            self.curTurn.addEvent(fade)
            fade.enact(self)

    def finishTurn(self):
        # Checks for home system fades and eliminations
        if not self.curTurn.isCompleted():
            self.cancelTurn()
            raise TurnNotOverException(
                'Turn is not complete. Did you forget to pass an unwanted sacrifice action?'
            )

        # Check for elimination
        # TODO this is better for huge numbers of players, but slower otherwise
#        for player in self.curTurn.getThreatenedPlayers()
        for player in range(self.nplayers):
            # Check if player has been eliminated
            if self.alive[player] != True:
                # Player is either already known to be dead or hasn't created a home
                continue
            # Player is believed to be alive but may have just been eliminated
            home = self.findHome(player)
            abandoned = not home.hasPresence(player)
            voided = home.isVoid()

            if abandoned or voided:
                # Player has been eliminated
                elim = event.Elimination(player, self.onmove)
                self.curTurn.addEvent(elim)
                elim.enact(self)
            if voided:
                fade = home.getFade()
                self.curTurn.addEvent(fade)
                fade.enact(self)

    def startNewTurn(self):
        if self.isEnd():
            raise Exception('State is at endpoint.')
        self.advanceOnmove()
        self.curTurn = Turn(self)

    def advanceOnmove(self, d=1):
        # set d=-1 to get previous player
        self.onmove = self.getNextPlayer(d)

    def getNextPlayer(self, d=1):
        # set d=-1 to get previous player
        i = (self.onmove + d) % self.nplayers
        # TODO if current player is somehow dead, this is an endless loop
        while self.alive[i] == False:
            i = (i + d) % self.nplayers
        return i

    def getScores(self):
        if not self.isEnd():
            raise Exception('Game is not over.')
        if self.alive.count(True) == 0:
            return [1 / self.nplayers] * self.nplayers
        scores = [0] * self.nplayers
        scores[self.alive.index(True)] = 1
        return scores

    def saveTuple(self):
        # Returns a tuple appropriate for saving the game
        # Systems are not sorted, so not appropriate for comparing states
        # Includes system names
        stuples = [s.saveTuple() for s in self.systems]
        return (self.onmove, tuple(self.alive), tuple(stuples))

    def tuplify(self):
        # Does not include system names, but systems are sorted
        # Appropriate for comparing states
        if not self.tupled is None:
            return self.tupled
        stuples = [s.tuplify() for s in self.systems]
        stuples.sort()
        self.tupled = (self.onmove, tuple(self.alive), tuple(stuples))
        return self.tupled

    def __hash__(self):
        return self.calcHash()

    def calcHash(self):
        return hash(self.tuplify())

    def isEnd(self):
        # TODO implement other win conditions
        if not self.creationOver():
            return False
        return self.alive.count(True) <= 1

    def __eq__(self, other):
        return self.tuplify() == other.tuplify()

    def __str__(self):
        divider = '/' * 30
        movestr = 'Player %s to move' % self.onmove
        stashStr = str(self.stash)
        sysStr = ('\n%s\n' % divideSysStr).join([str(s) for s in self.systems])
        return '%s\n%s\n%s\n%s\n%s' % (divider, movestr, stashStr, sysStr,
                                       divider)

    def buildStr(self):
        return '<{}>;\n{}'.format(
            self.onmove,
            ';\n'.join([sys.buildStr(self.nplayers) for sys in self.systems]))

    def getConnections(self, sys):
        # Return a list of systems connected to sys INCLUDING DISCOVERIES
        # TODO remember connections so this doesn't have to keep getting called
        connects = []
        # Existing systems
        for s in self.systems:
            if s.connectsTo(sys):
                connects.append(s)

        # Discoveries
        for size in piece.sizes:
            for m in sys.markers:
                if m.size == size:
                    break
            else:
                # Inner loop exited normally, discoveries may have this size
                for c in colors:
                    if self.stash.isAvailable(c, size):
                        p = piece.Piece(size, c)
                        connects.append(System([p]))
        return connects