Пример #1
0
    def __init__(self):

        # Initialize world:
        self.world = World()
        self.world.load(AREAS, CONNECT)

        # Initialize player list:
        self.players = {}
        self.next_color = 1;

        # Initialize turn information:
        self.turn = 0
Пример #2
0
    def __init__(self, Q_network=None, buffer=None, **options):
        self.options = self.defaults.copy()
        self.options.update(options)

        self.world = World()
        self.world.load(self.options['areas'], self.options['connect'])

        self.board_state = None
        self.players = {}
        self.player_to_id = {}

        self.turn = 0
        self.turn_order = []

        # sot that AI can access the same DNN
        self.Q_network = Q_network
        self.buffer = buffer
Пример #3
0
    def __init__(self, **options):
        self.options = self.defaults.copy()
        self.options.update(options)

        self.world = World()
        self.world.load(self.options['areas'], self.options['connect'])

        self.players = {}

        self.turn = 0
        self.turn_order = []

        if self.options['curses']:
            self.display = CursesDisplay(self.options['screen'], self,
                                         self.options['cmap'], self.options['ckey'],
                                         self.options['color'], self.options['wait'])
        else:
            self.display = Display()
Пример #4
0
class Game(object):
    """
    This class represents an individual game, and contains the main game logic.
    """
    defaults = {
        "curses": True,  #whether to use ncurses for map display
        "color": True,  #whether to use color with ncurses
        "delay": 0.1,  #seconds to sleep after each (ncurses) display update
        "connect": CONNECT,  #the territory connection graph (see world.py)
        "areas": AREAS,  #the territory->continent mapping, and values
        "cmap": MAP,  #the ASCII art map to use
        "ckey": KEY,  #the territority->char mapping key for the map
        "screen":
        None,  #a curses.window (for use with the curses.wrapper function)
        "round": None,  #the round number
        "wait":
        False,  #whether to pause and wait for a keypress after each event
        "history":
        {},  #the win/loss history for each player, for multiple rounds
        "deal": False  #deal out territories rather than let players choose
    }

    def __init__(self, **options):
        self.options = self.defaults.copy()
        self.options.update(options)

        self.world = World()
        self.world.load(self.options['areas'], self.options['connect'])

        self.players = {}

        self.turn = 0
        self.turn_order = []

        if self.options['curses']:
            self.display = CursesDisplay(self.options['screen'], self,
                                         self.options['cmap'],
                                         self.options['ckey'],
                                         self.options['color'],
                                         self.options['wait'])
        else:
            self.display = Display()

    def add_player(self, name, ai_class, **ai_kwargs):
        assert name not in self.players
        player = Player(name, self, ai_class, ai_kwargs)
        self.players[name] = player

    @property
    def player(self):
        """Property that returns the correct player object for this turn."""
        return self.players[self.turn_order[self.turn % len(self.players)]]

    def aiwarn(self, *args):
        """Generate a warning message when an AI player tries to break the rules."""
        logging.getLogger("pyrisk.player.%s" %
                          self.player.ai.__class__.__name__).warn(*args)

    def event(self, msg, territory=None, player=None):
        """
        Record any game action.
        `msg` is a tuple describing what happened.
        `territory` is a list of territory objects to be highlighted, if any
        `player` is a list of player names to be highlighted, if any
        
        Calling this method triggers the display to be updated, and any AI
        players that have implemented event() to be notified.
        """

        self.display.update(msg, territory=territory, player=player)

        LOG.info([str(m) for m in msg])
        for p in self.players.values():
            p.ai.event(msg)

    def play(self):
        assert 2 <= len(self.players) <= 5
        self.turn_order = list(self.players)
        random.shuffle(self.turn_order)
        for i, name in enumerate(self.turn_order):
            self.players[name].color = i + 1
            self.players[name].ord = ord('\/-|+*'[i])
            self.players[name].ai.start()
        self.event(("start", ))
        live_players = len(self.players)
        self.initial_placement()

        while live_players > 1:
            if self.player.alive:
                choices = self.player.ai.reinforce(self.player.reinforcements)
                assert sum(choices.values()) == self.player.reinforcements
                for tt, ff in choices.items():
                    t = self.world.territory(tt)
                    f = int(ff)
                    if t is None:
                        self.aiwarn("reinforce invalid territory %s", tt)
                        continue
                    if t.owner != self.player:
                        self.aiwarn("reinforce unowned territory %s", t.name)
                        continue
                    if f < 0:
                        self.aiwarn("reinforce invalid count %s", f)
                        continue
                    t.forces += f
                    self.event(("reinforce", self.player, t, f),
                               territory=[t],
                               player=[self.player.name])

                for src, target, attack, move in self.player.ai.attack():
                    st = self.world.territory(src)
                    tt = self.world.territory(target)
                    if st is None:
                        self.aiwarn("attack invalid src %s", src)
                        continue
                    if tt is None:
                        self.aiwarn("attack invalid target %s", target)
                        continue
                    if st.owner != self.player:
                        self.aiwarn("attack unowned src %s", st.name)
                        continue
                    if tt.owner == self.player:
                        self.aiwarn("attack owned target %s", tt.name)
                        continue
                    if tt not in st.connect:
                        self.aiwarn("attack unconnected %s %s", st.name,
                                    tt.name)
                        continue
                    initial_forces = (st.forces, tt.forces)
                    opponent = tt.owner
                    victory = self.combat(st, tt, attack, move)
                    final_forces = (st.forces, tt.forces)
                    self.event(
                        ("conquer" if victory else "defeat", self.player,
                         opponent, st, tt, initial_forces, final_forces),
                        territory=[st, tt],
                        player=[self.player.name, tt.owner.name])
                freemove = self.player.ai.freemove()
                if freemove:
                    src, target, count = freemove
                    st = self.world.territory(src)
                    tt = self.world.territory(target)
                    f = int(count)
                    valid = True
                    if st is None:
                        self.aiwarn("freemove invalid src %s", src)
                        valid = False
                    if tt is None:
                        self.aiwarn("freemove invalid target %s", target)
                        valid = False
                    if st.owner != self.player:
                        self.aiwarn("freemove unowned src %s", st.name)
                        valid = False
                    if tt.owner != self.player:
                        self.aiwarn("freemove unowned target %s", tt.name)
                        valid = False
                    if not 0 <= f < st.forces:
                        self.aiwarn("freemove invalid count %s", f)
                        valid = False
                    if valid:
                        st.forces -= count
                        tt.forces += count
                        self.event(("move", self.player, st, tt, count),
                                   territory=[st, tt],
                                   player=[self.player.name])
                live_players = len(
                    [p for p in self.players.values() if p.alive])
            self.turn += 1
        winner = [p for p in self.players.values() if p.alive][0]
        self.event(("victory", winner), player=[self.player.name])
        for p in self.players.values():
            p.ai.end()
        return winner.name

    def combat(self, src, target, f_atk, f_move):
        n_atk = src.forces
        n_def = target.forces

        if f_atk is None:
            f_atk = lambda a, d: True
        if f_move is None:
            f_move = lambda a: a - 1

        while n_atk > 1 and n_def > 0 and f_atk(n_atk, n_def):
            atk_dice = min(n_atk - 1, 3)
            atk_roll = sorted([random.randint(1, 6) for i in range(atk_dice)],
                              reverse=True)
            def_dice = min(n_def, 2)
            def_roll = sorted([random.randint(1, 6) for i in range(def_dice)],
                              reverse=True)

            for a, d in zip(atk_roll, def_roll):
                if a > d:
                    n_def -= 1
                else:
                    n_atk -= 1

        if n_def == 0:
            move = f_move(n_atk)
            min_move = min(n_atk - 1, 3)
            max_move = n_atk - 1
            if move < min_move:
                self.aiwarn("combat invalid move request %s (%s-%s)", move,
                            min_move, max_move)
                move = min_move
            if move > max_move:
                self.aiwarn("combat invalid move request %s (%s-%s)", move,
                            min_move, max_move)
                move = max_move
            src.forces = n_atk - move
            target.forces = move
            target.owner = src.owner
            return True
        else:
            src.forces = n_atk
            target.forces = n_def
            return False

    def initial_placement(self):
        empty = list(self.world.territories.values())
        available = 35 - 2 * len(self.players)
        remaining = {p: available for p in self.players}

        if self.options['deal']:
            random.shuffle(empty)
            while empty:
                t = empty.pop()
                t.forces += 1
                remaining[self.player.name] -= 1
                t.owner = self.player
                self.event(("deal", self.player, t),
                           territory=[t],
                           player=[self.player.name])
                self.turn += 1
        else:
            while empty:
                choice = self.player.ai.initial_placement(
                    empty, remaining[self.player.name])
                t = self.world.territory(choice)
                if t is None:
                    self.aiwarn("invalid territory choice %s", choice)
                    self.turn += 1
                    continue
                if t not in empty:
                    self.aiwarn("initial invalid empty territory %s", t.name)
                    self.turn += 1
                    continue
                t.forces += 1
                t.owner = self.player
                remaining[self.player.name] -= 1
                empty.remove(t)
                self.event(("claim", self.player, t),
                           territory=[t],
                           player=[self.player.name])
                self.turn += 1

        while sum(remaining.values()) > 0:
            if remaining[self.player.name] > 0:
                choice = self.player.ai.initial_placement(
                    None, remaining[self.player.name])
                t = self.world.territory(choice)
                if t is None:
                    self.aiwarn("initial invalid territory %s", choice)
                    self.turn += 1
                    continue
                if t.owner != self.player:
                    self.aiwarn("initial unowned territory %s", t.name)
                    self.turn += 1
                    continue
                t.forces += 1
                remaining[self.player.name] -= 1
                self.event(("reinforce", self.player, t, 1),
                           territory=[t],
                           player=[self.player.name])
                self.turn += 1
Пример #5
0
class RiskBoard(object):

    def __init__(self):

        # Initialize world:
        self.world = World()
        self.world.load(AREAS, CONNECT)

        # Initialize player list:
        self.players = {}
        self.next_color = 1;

        # Initialize turn information:
        self.turn = 0

    # Method to add players to the board:
    def addPlayer(self, name, type, ai=None):
        assert name not in self.players
        new_player = RiskPlayer(name, self, type, ai)
        self.players[name] = new_player;
        self.players[name].color = self.next_color;
        self.next_color += 1;
        if(type == "AI"):
            self.players[name].ai.start()

    # Method to start the game board distribution once the players have been
    # added.
    def start(self):
        assert 2 <= len(self.players) <= 5  # Limit player count.
        self.turn_order = list(self.players)
        random.shuffle(self.turn_order)     # Change this to use seed?

        # Distribute territories between players:
        territories = list(self.world.territories.values())
        random.shuffle(territories)
        initial_troops_count = 35 - 2*len(self.players)
        self.initial_troops = {p: initial_troops_count for p in self.players}

        while territories:
            t = territories.pop()
            t.forces += 1
            self.initial_troops[self.player.name] -= 1
            t.owner = self.player
            self.info("Dealt player %s territory %s", self.player.name, t.name)
            self.turn += 1

        self.info("Board succesfully started!")

        # Now the board is all set for the game to start.
        # Remember, the game start with players choosing where to place their
        # remaining initial troops!

    # Properties:

    ## Property that returns the player whose turn is next.
    @property
    def player(self):
        return self.players[self.turn_order[self.turn % len(self.players)]]

    # General game methods:

    ## Attack method:
    def attack(self, src_c, target_c, f_atk=None, f_move=None):

        # Get the territories:
        src = self.world.territory(src_c)
        target = self.world.territory(target_c)

        # Check to see if the attack is valid:
        if src is None:
            self.warn("Attack invalid src %s", src)
            return -1
        if target is None:
            self.warn("Attack invalid target %s", target)
            return -1
        if src.owner != self.player:
            self.warn("Attack unowned src %s", src.name)
            return -1
        if target.owner == self.player:
            self.warn("Attack owned target %s", target.name)
            return -1
        if target not in src.connect:
            self.warn("Attack unconnected %s %s", src.name, target.name)
            return -1
        if src.forces == 1:
            self.warn("Not enought troops to attack from source %s", src.name)
            return -1

        # The attack seems valid, so we store the opponent's name...
        opponent = target.owner.name

        # ... and simulate the actual combat.
        victory = self.combat(src, target, f_atk, f_move)

        # Log the outcome of the attack:
        if(victory):
            self.info("%s conquered %s from %s", self.player.name, target.name,
                      opponent)
        else:
            self.info("%s defended %s from %s", opponent, target.name,
                      self.player.name)

        return 0;

    ## Method to clean up board:
    def cleanUpBoard(self):
        for p in self.players.values():
            if p.type == "AI":
                p.ai.end()

    ## Combat method:
    def combat(self, src, target, f_atk=None, f_move=None):
        n_atk = src.forces
        n_def = target.forces

        if f_atk is None:
            f_atk = lambda a, d: True
        if f_move is None:
            f_move = lambda a: a - 1

        while n_atk > 1 and n_def > 0 and f_atk(n_atk, n_def):
            atk_dice = min(n_atk - 1, 3)
            atk_roll = sorted([random.randint(1, 6) for i in range(atk_dice)], reverse=True)
            def_dice = min(n_def, 2)
            def_roll = sorted([random.randint(1, 6) for i in range(def_dice)], reverse=True)

            for a, d in zip(atk_roll, def_roll):
                if a > d:
                    n_def -= 1
                else:
                    n_atk -= 1

        if n_def == 0:
            move = f_move(n_atk)
            min_move = min(n_atk - 1, 3)
            max_move = n_atk - 1
            if move < min_move:
                self.warn("Combat invalid move request %s (%s-%s)", move, min_move, max_move)
                move = min_move
            if move > max_move:
                self.warn("Combat invalid move request %s (%s-%s)", move, min_move, max_move)
                move = max_move
            src.forces = n_atk - move
            target.forces = move
            target.owner = src.owner
            return True

        else:
            src.forces = n_atk
            target.forces = n_def
            return False

    ## Method to show if the initial placement ended:
    def finishedInitialPlacement(self):
        return sum(self.initial_troops.values()) == 0

    ## Method to freemove:
    def freemove(self, src, target, count):

        # Check to see if the freemove is valid:
        if src is None:
            self.warn("Freemove invalid src %s", src)
            return -1
        if target is None:
            self.warn("Freemove invalid target %s", target)
            return -1
        if src.owner != self.player:
            self.warn("Freemove unowned src %s", src.name)
            return -1
        if target.owner != self.player:
            self.warn("Freemove unowned target %s", target.name)
            return -1
        if not 0 <= count < src.forces:
            self.warn("Freemove invalid count %s", f)
            return -1

        src.forces -= count
        target.forces += count
        self.info("%s moved %s troops from %s to %s", self.player.name, count,
                  src.name, target.name)

        return 0

    ## Method to simulate a full turn:
    def fullTurn(self):
        # Check to see if the current player is alive:
        if self.player.alive:
            # Check to see if it is an AI:
            if self.player.type == "AI":

                # Reinforcement phase:
                choices = self.player.ai.reinforce(self.player.reinforcements)
                assert sum(choices.values()) == self.player.reinforcements
                for territory, forces in choices.items():
                    self.reinforce(territory, int(forces))

                # Combat phase:
                for src, target, f_attack, f_move in self.player.ai.attack():
                    self.attack(self.world.territory(src),
                                self.world.territory(target),
                                f_attack,
                                f_move)

                # Freemove phase:
                freemove_input = self.player.ai.freemove()

                if freemove_input:
                    src, target, count = freemove_input
                    self.freemove(self.world.territory(src),
                                  self.world.territory(target),
                                  int(count))


        # End the turn:
        self.turn += 1

    ## Method to check if the game has ended:
    def gameEnded(self):
        players_alive = [p for p in self.players.values() if p.alive]

        if len(players_alive) == 1:
            winner_name = players_alive[0].name
            self.info("Player %s won the game!", winner_name)
            self.cleanUpBoard()
            return (True, winner_name)

        else:
            return (False, None)

    ## Initial placement method:
    def initialPlacement(self, input=None):
        if self.finishedInitialPlacement():
            return 0

        else:
            if self.initial_troops[self.player.name] > 0:
                if(self.player.type == "AI"):
                    choice = self.player.ai.initial_placement(None,
                             self.initial_troops[self.player.name])

                elif input != None:
                    choice = input

                else:
                    self.warn("No initial place input")
                    return -1

                error = self.reinforce(choice, 1)

                if(error == 0):
                    self.initial_troops[self.player.name] -= 1

                self.turn += 1

                return 0 if not error else -1

    ## Reinforce method:
    def reinforce(self, choice, number):
        t = self.world.territory(choice)

        if t is None:
            self.warn("Initial invalid territory %s", choice)
            return -1
        elif t.owner != self.player:
            self.warn("Initial unowned territory %s", t.name)
            return -1
        elif number < 0:
            self.warn("Reinforce invalid count %s", number)
            return -1
        else:
            t.forces += number
            self.info("%s reinforced %s", self.player.name, t.name)
            return 0

    # Logger methods:
    def info(self, *args):
        logger.info(*args)

    def warn(self, *args):
        logger.warn(*args)