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
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
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()
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
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)