def reinit(self, includeFlags = 6): # ------------------------------------ # Initialize the persistent parameters # ------------------------------------ if includeFlags & 2: self.rules = [] Game.reinit(self, includeFlags)
def validateStatus(self): for power in [x for x in self.powers if x.orders]: # ------------------------------------------------------- # Make sure all units are ordered (in non-NO_CHECK games) # ------------------------------------------------------- if 'DEFAULT_UNORDERED' not in self.rules: [self.error.append( 'UNIT LEFT UNORDERED: %s, ' % power.name + unit) for unit in power.units if unit not in power.orders] for unit, order in power.orders.items(): # ----------------------------------------------------- # Convert NO_CHECK "ORDER"s to "INVALID" as appropriate # ----------------------------------------------------- if unit[:5] == 'ORDER': word = self.expandOrder(order.split()) word = self.addUnitTypes(word) word = self.map.defaultCoast(word) valid = self.validOrder(power, ' '.join(word[:2]), ' '.join(word[2:]), report=0) if not valid: power.orders['INVALID ' + unit[6:]] = order del power.orders[unit] # ------------------------------------------------------- # Non-NO_CHECK. Validate the order and report all errors # ------------------------------------------------------- else: valid = self.validOrder(power, unit, order) if valid == -1 and not order.endswith(' ?'): power.orders[unit] += ' ?' elif valid == 1 and order.endswith(' ?'): power.orders[unit] = power.orders[unit][:-2] # ------------------------------------------------------------- # Go validate the rest of the data read in from the status file # ------------------------------------------------------------- Game.validateStatus(self)
def validateStatus(self): sizes = [len(x.centers) for x in self.powers if x.centers] if sizes: self.smallest, self.largest = min(sizes), max(sizes) for rule in ['NO_CHECK']: if rule in self.rules: self.error += [rule + ' RULE IS INVALID IN CRYSTAL BALL'] Game.validateStatus(self)
def validateStatus(self): for power in [x for x in self.powers if x.orders]: # ------------------------------------------------------- # Make sure all units are ordered (in non-NO_CHECK games) # ------------------------------------------------------- if 'DEFAULT_UNORDERED' not in self.rules: [self.error.append( 'UNIT LEFT UNORDERED: %s, ' % power.name + unit) for unit in power.units if unit not in power.orders] for unit, order in power.orders.items(): # ----------------------------------------------------- # Convert NO_CHECK "ORDER"s to "INVALID" as appropriate # ----------------------------------------------------- if unit[:5] == 'ORDER': word = self.expandOrder(order.split()) word = self.addUnitTypes(word) word = self.map.defaultCoast(word) if not self.validOrder(power, ' '.join(word[:2]), ' '.join(word[2:]), report=0): power.orders['INVALID ' + unit[6:]] = order del power.orders[unit] # ------------------------------------------------------- # Non-NO_CHECK. Validate the order and report all errors # ------------------------------------------------------- else: self.validOrder(power, unit, order) # ------------------------------------------------------------- # Go validate the rest of the data read in from the status file # ------------------------------------------------------------- Game.validateStatus(self)
def processIncomePhase(self): if self.findNextPhase().endswith('ADJUSTMENTS'): list = Game.captureCenters(self, self.parseSupplyCount) else: Game.powerSizes(self, None, self.parseSupplyCount) list = [] return (list + [self.phase.title() + ' has been distributed.\n'] ['NO_LEDGERS' in self.rules or self.phase in (None, 'COMPLETED'):])
def reinit(self, includeFlags=6): # ------------------------------------ # Initialize the persistent parameters # ------------------------------------ if includeFlags & 2: self.rules = ['FICTIONAL_OK', 'PROXY_OK'] # ----------------------------------- # Initialize the transient parameters # ----------------------------------- if includeFlags & 4: self.largest = self.smallest = None Game.reinit(self, includeFlags)
def reinit(self, includeFlags = 6): # ------------------------------------ # Initialize the persistent parameters # ------------------------------------ if includeFlags & 2: self.rules = ['FICTIONAL_OK', 'PROXY_OK'] # ----------------------------------- # Initialize the transient parameters # ----------------------------------- if includeFlags & 4: self.largest = self.smallest = None Game.reinit(self, includeFlags)
def reinit(self, includeFlags = 6): # ------------------------------------ # Initialize the persistent parameters # ------------------------------------ if includeFlags & 2: self.rules = ['ORDER_ANY'] self.taxes, self.tax, self.cap = {}, 0, 0 # ----------------------------------- # Initialize the transient parameters # ----------------------------------- if includeFlags & 4: self.offers, self.orders = {}, {} Game.reinit(self, includeFlags)
def preMoveUpdate(self): if not self.skip: self.openMail('Xtalball orders', 'lists') if 'PUBLIC_LISTS' in self.rules: self.mail.write( 'OFFICIAL Crystal Ball orders %s %.1s%s%.1s\n' % tuple([self.name] + self.phase.split()), 0) self.mail.write('BROADCAST\n', 0) else: self.mail.write('SIGNOFF\n', 0) self.mail.write('%s ORDERS\n%s\n' % (self.phase, '=' * (len(self.phase) + 7))) for player in self.map.powers: for guy in [x for x in self.powers if x.name == player and x.units]: for count, order in enumerate(guy.list['SOONER']): if ('LIMIT_LISTS' in self.rules and count > self.largest): break self.mail.write('%-10s[%s] %s\n' % (player.title() + ':', guy.notes[count], order)) self.mail.write('\n') break self.mail.write('ENDPRESS\nSIGNOFF\n', 0) self.mail.close() self.mail = None # --------------------------- # Move the order sheets ahead # --------------------------- for power in self.powers: if power.list['SOONER'] and not power.list['LATER']: power.cd = 1 power.list = {'SOONER': power.list['LATER'] or (power.units and [power.units[0] + ' H']) or []} return Game.preMoveUpdate(self)
def parsePowerData(self, power, word, includeFlags): parsed = Game.parsePowerData(self, power, word, includeFlags) if parsed: return parsed word = [x.upper() for x in word] upline = ' '.join(word) # ------------------------------------------- # Power-specific data (SOONER and/or LATER) # ------------------------------------------- # Note that SOONER are orders entered during # a previous turn and thus should be included # even if includeFlags & 1 is 0, because the # latter is only concerned with LATER orders. # ------------------------------------------- if word[0] in ('SOONER', 'LATER') and len(word) == 1: self.mode, self.modeRequiresEnd = word[0], None elif self.mode == 'LATER' and not includeFlags & 1: return -1 elif self.mode and word[0] in ('A', 'F'): word = self.expandOrder(word) if len(word[-1]) == 1 and not word[-1].isalpha(): word = word[:-1] upline = upline[:-2] if len(word) < 3: return self.error.append('BAD ORDER: ' + upline) unit, order = ' '.join(word[:2]), ' '.join(word[2:]) valid = self.validOrder(power, unit, order) if valid != None: power.list[self.mode] += [ upline + ' ?' * (valid == -1 and not upline.endswith(' ?')) ] if self.mode == 'LATER': power.held = 1 else: return 0 return 1
def rollback(self, phase = None, includeFlags = 0): error = Game.rollback(self, phase, includeFlags) if error: return error # ----------------------- # Truncate the chart file # ----------------------- phase = self.phaseType == 'M' and self.phase or self.findNextPhase('M') if len(phase.split()) == 3 and os.path.isfile(self.file('chart')): file = open(self.file('chart'), 'r', 'latin-1') lines = file.readlines() file.close() for num, text in enumerate(lines): if phase == text.strip(): break else: num = len(lines) if num == len(lines): pass elif num < 3: try: os.unlink(self.file('chart')) except: pass else: file = open(self.file('chart'), 'w', 'latin-1') file.writelines(lines[:num-2]) file.close() try: os.chmod(self.file('chart'), 0666) except: pass
def parseGameData(self, word, includeFlags): parsed = Game.parseGameData(self, word, includeFlags) if parsed: return parsed word = [x.upper() for x in word] upline = ' '.join(word) # ----- # Modes # ----- if self.mode: return 0 # -------------------------------------- # Game-specific information (persistent) # -------------------------------------- if not includeFlags & 2: return 0 # ---------------------------------------------- # Center tax income values (completely optional) # ---------------------------------------------- elif word[0] == 'TAX': self.tax = -1 if len(word) == 3 and self.map.areatype(word[1]) or len(word) < 3: try: self.tax = int(word[-1]) except: pass if self.tax < 0: self.error += ['BAD TAX: ' + upline] elif len(word) == 3: self.taxes[word[1]] = self.tax elif word[0] == 'CAP': try: self.cap = int('$'.join(word[1:])) if self.cap < 1: raise except: self.error += ['BAD CAP: ' + upline] else: return 0 return 1
def parsePowerData(self, power, word, includeFlags): parsed = Game.parsePowerData(self, power, word, includeFlags) if parsed: return parsed word = [x.upper() for x in word] upline = ' '.join(word) # ------------------------------------------- # Power-specific data (SOONER and/or LATER) # ------------------------------------------- # Note that SOONER are orders entered during # a previous turn and thus should be included # even if includeFlags & 1 is 0, because the # latter is only concerned with LATER orders. # ------------------------------------------- if word[0] in ('SOONER', 'LATER') and len(word) == 1: self.mode, self.modeRequiresEnd = word[0], None elif self.mode == 'LATER' and not includeFlags & 1: return -1 elif self.mode and word[0] in ('A', 'F'): word = self.expandOrder(word) if len(word[-1]) == 1 and not word[-1].isalpha(): word = word[:-1] upline = upline[:-2] if len(word) < 3: return self.error.append('BAD ORDER: ' + upline) unit, order = ' '.join(word[:2]), ' '.join(word[2:]) valid = self.validOrder(power, unit, order) if valid != None: power.list[self.mode] += [upline + ' ?' * (valid == -1)] if self.mode == 'LATER': power.held = 1 else: return 0 return 1
def checkPhase(self, text): if self.phaseType == 'I': text += self.processIncomePhase() elif self.phaseType == 'D': text += self.processExchangeDividendsPhase() elif self.phaseType == 'Y': text += self.processExchangeReportsPhase() else: return Game.checkPhase(self, text) return 1
def otherResults(self): if self.phaseType == 'A' and 'GARRISON' in self.rules: # Add HOLD orders for incoming GARRISON builds for power in self.powers: if not power.list and power.adjust and 'SC?' in power.centers: power.list = {'SOONER': ['%s %s H' % tuple(x.split()[1:]) for x in power.adjust]} return Game.otherResults(self)
def __repr__(self): text = Game.__repr__(self).decode('latin-1') if self.taxes: for center, value in self.taxes.items(): text += '\nTAX %s %d' % (center, value) if self.tax: text += '\nTAX %d' % self.tax if self.cap: text += '\nCAP %d' % self.cap return '\n'.join([x for x in text.split('\n') if x not in self.directives]).encode('latin-1')
def transferCenter(self, loser, gainer, sc): if 'ZEROSUM' in self.rules: # -------------------------------------- # Add to the "gained" and "lost" lists. # Each entry is an SC and an amount that # is to be moved from one treasury to # another. The amount is the current # (pre-income) balance of the losing # power divided by the number of SC's he # held AT THE BEGINNING OF THE YEAR. # -------------------------------------- if loser: amt = ((loser.balance + loser.funds.get('+', 0)) / (len(loser.centers) - len(loser.gained) + len(loser.lost))) loser.lost += [(sc, amt)] else: amt = 10 gainer.gained += [(sc, amt)] Game.transferCenter(self, loser, gainer, sc)
def otherResults(self): if self.phaseType == 'A' and 'GARRISON' in self.rules: # Add HOLD orders for incoming GARRISON builds for power in self.powers: if not power.list and power.adjust and 'SC?' in power.centers: power.list = { 'SOONER': [ '%s %s H' % tuple(x.split()[1:]) for x in power.adjust ] } return Game.otherResults(self)
def checkPhase(self, text): if 'LAST_MAN_STANDING' in self.rules: if self.phase in (None, 'FORMING', 'COMPLETED'): return if self.phaseType in 'MA': if sum([len(x.units) for x in self.powers]) == 1: power = [x for x in self.powers if x.units][0] text += ['The %s %s is the Last Man Standing.' % (self.anglify(self.map.ownWord[power.name]), self.anglify(power.units[0], retreating = 1))] self.finish([power.name]) return return Game.checkPhase(self, text)
def validateStatus(self): # ------------------------------------------------------------ # Parsing offers needs to be done after all powers are loaded, # thus not in finishPowerData(), because it checks whether an # ordered unit has an owner. # ------------------------------------------------------------ for power in self.powers: for offer in power.sheet: self.parseOffer(power, offer) self.validateOffers(power) self.map = self.map or Map.Map() # ------------------------------------------------- # If the map's flow already holds any INCOME phase, # leave it alone. Otherwise, add a single INCOME # phase into the flow after the first ADJUSTMENTS. # ------------------------------------------------- if self.map.flow: for item in [x.split(':')[1] for x in self.map.flow]: if 'INCOME' in item.split(','): break else: for flow, item in enumerate(self.map.flow): if 'ADJUSTMENTS' in item.split(':')[1].split(','): self.map.flow[flow] = item.replace( 'ADJUSTMENTS', 'ADJUSTMENTS,INCOME') break (where, what) = [(x+1,y) for (x,y) in enumerate(self.map.seq) if y.endswith('ADJUSTMENTS')][0] self.map.seq.insert(where, what.replace('ADJUSTMENTS','INCOME')) if self.rotate: self.error += ['CONTROL ROTATION IS INVALID IN PAYOLA'] self.error += [rule + ' RULE IS INVALID IN PAYOLA' for rule in ('PROXY_OK', 'NO_CHECK') if rule in self.rules] for power in self.powers: if power.centers: if type(power.accept) not in (str, unicode): power.initAccept() else: self.checkAccept(power) if power.balance is None and not power.isEliminated(False, True): self.error += ['NO BALANCE FOR ' + power.name] # for subvar in ('ZEROSUM', 'EXCHANGE', 'FLAT_TAX'): # if subvar in self.rules: # self.variant = subvar.lower() + ' ' + self.variant Game.validateStatus(self)
def parsePowerData(self, power, word, includeFlags): parsed = Game.parsePowerData(self, power, word, includeFlags) if parsed: return parsed word = [x.upper() for x in word] upline = ' '.join(word) # ----- # Modes # ----- if self.mode: # ---------------------------- # Power-specific data (orders) # ---------------------------- if self.mode == 'ORDERS': if not includeFlags & 1: return -1 # ------------------------------------------------------ # Even NO_CHECK games check that the order contains only # recognized tokens, and announce this error immediately # ------------------------------------------------------ word = self.expandOrder(word) if len(word) < 3 and (len(word) == 1 or word[1] != 'H'): return self.error.append('BAD ORDER: ' + upline) # -------------------------------- # Now parse and validate the order # -------------------------------- unit, order = ' '.join(word[:2]), ' '.join(word[2:]) if unit in power.orders: return self.error.append('UNIT REORDERED: ' + upline) # ---------------------------------------------------- # NO_CHECK games take the raw order text and keep it # internally under the name "ORDER 1", "ORDER 2", etc. # In validateStatus(), each may change from "ORDER" to # "INVALID". The addOrder() method can also change it # from "ORDER" to "REORDER" (if to a twice-ordered # unit). The Game.moveResults() method knows to take # any orders thus marked "INVALID" or "REORDER" and # include them in the results file with annotation. # ---------------------------------------------------- if 'NO_CHECK' in self.rules: unit, order = 'ORDER ' + `len(power.orders) + 1`, upline power.orders[unit], power.held = order, 1 else: return 0 return 1 # -------------------------------------- # Power-specific information (transient) # -------------------------------------- elif word[0] == 'ORDERS' and len(word) == 1: self.mode, self.modeRequiresEnd = word[0], None else: return 0 return 1
def unitOwner(self, unit, power = None): owner = Game.unitOwner(self, unit) # -------------------------------------------------- # See if we've been passed a specific power. If so, # check any pending "adjust" orders (build, remove, # and retreat) to see if this unit is mentioned. # This method is called this way when displaying the # locked-in orders for a certain power. # -------------------------------------------------- if power: if 'BUILD ' + unit in power.adjust: return power if 'REMOVE ' + unit in power.adjust: return for word in [x.split() for x in power.adjust]: if word[1] + ' ' + word[-1] == unit: return power return owner
def unitOwner(self, unit, power=None): owner = Game.unitOwner(self, unit) # -------------------------------------------------- # See if we've been passed a specific power. If so, # check any pending "adjust" orders (build, remove, # and retreat) to see if this unit is mentioned. # This method is called this way when displaying the # locked-in orders for a certain power. # -------------------------------------------------- if power: if 'BUILD ' + unit in power.adjust: return power if 'REMOVE ' + unit in power.adjust: return for word in [x.split() for x in power.adjust]: if word[1] + ' ' + word[-1] == unit: return power return owner
def process(self, now = 0, email = None, roll = 0): # ------------------------------------------------------------- # Convert all raw movement phase "ORDER"s in a NO_CHECK game to # standard orders before calling Game.process(). All "INVALID" # and "REORDER" orders are left raw -- the Game.moveResults() # method knows how to detect and report them. # ------------------------------------------------------------- if ('NO_CHECK' in self.rules and self.phaseType == 'M' and now and (self.preview or self.ready(now))): for power in self.powers: orders, power.orders, cd = power.orders, {}, power.cd for status, order in orders.items(): if status[:5] != 'ORDER': power.orders[status] = order else: self.addOrder(power, order.split()) power.cd = cd return Game.process(self, now, email, roll)
def preMoveUpdate(self): if 'BLIND' in self.rules or 'FICTIONAL_OK' in self.rules: self.error = [x for x in self.error if not x.startswith('IMPOSSIBLE ORDER')] if self.error: print 'ERRORS IMPEDING RESOLUTION:', self.error return self.writeChart() if 'NO_LEDGERS' not in self.rules: self.sendLedgers() # ---------------------- # Empty the offer sheets # ---------------------- for power in self.powers: if not power.offers and not power.isEliminated(False, True): power.cd = 1 power.sheet = power.offers = [] return Game.preMoveUpdate(self)
def parsePowerData(self, power, word, includeFlags): parsed = Game.parsePowerData(self, power, word, includeFlags) if parsed: return parsed word = [x.upper() for x in word] upline = ' '.join(word) # ----- # Modes # ----- if self.mode: return 0 # ------------------- # Offers and comments # ------------------- elif word[0][0] in '0123456789%' or word[0] in ('A', 'F'): if not includeFlags & 1: return -1 power.sheet += [upline] if word[0][0] != '%': power.held = 1 return 1 # ------------------------------- # Power-specific data (transient) # ------------------------------- found = 0 if includeFlags & 4: found = 1 if word[0] == 'SENT': # (one_transfer) try: power.sent += [word[1]] except: self.error += ['BAD SENT FOR ' + power.name] elif word[0] == 'ELECT': # (exchange) for item in word[1:]: company, candidate = item.split(':') power.elect[company] = candidate elif word[0] == 'STATE': # (exchange, undocumented?) if len(word) == 2: power.state = word[1] else: error += ['BAD STATE FOR ' + power.name] elif word[0] == 'ACCEPT': if power.accept: self.error += ['TWO ACCEPTS FOR ' + power.name] elif len(word) != 2: self.error += ['BAD ACCEPT FOR ' + power.name] else: power.accept = word[1] else: found = 0 return found
def preMoveUpdate(self): if not self.skip: self.openMail('Xtalball orders', 'lists') if 'PUBLIC_LISTS' in self.rules: self.mail.write( 'OFFICIAL Crystal Ball orders %s %.1s%s%.1s\n' % tuple([self.name] + self.phase.split()), 0) self.mail.write('BROADCAST\n', 0) else: self.mail.write('SIGNOFF\n', 0) self.mail.write('%s ORDERS\n%s\n' % (self.phase, '=' * (len(self.phase) + 7))) for player in self.map.powers: for guy in [ x for x in self.powers if x.name == player and x.units ]: for count, order in enumerate(guy.list['SOONER']): if ('LIMIT_LISTS' in self.rules and count > self.largest): break self.mail.write( '%-10s[%s] %s\n' % (player.title() + ':', guy.notes[count], order)) self.mail.write('\n') break self.mail.write('ENDPRESS\nSIGNOFF\n', 0) self.mail.close() self.mail = None # --------------------------- # Move the order sheets ahead # --------------------------- for power in self.powers: if power.list['SOONER'] and not power.list['LATER']: power.cd = 1 power.list = { 'SOONER': power.list['LATER'] or (power.units and [power.units[0] + ' H']) or [] } return Game.preMoveUpdate(self)
def findStartPhase(self): Game.findStartPhase(self, 1)
def __init__(self, gameName, fileName = 'status'): self.variant, self.powerType = 'payola', PayolaPower Game.__init__(self, gameName, fileName)
def __init__(self, gameName, fileName='status'): self.variant, self.powerType = 'xtalball', XtalballPower Game.__init__(self, gameName, fileName)
def determineWin(self, lastYear, func = None): if 'LAST_MAN_STANDING' in self.rules: return [] return Game.determineWin(self, lastYear, func)
def finishPowerData(self, power): Game.finishPowerData(self, power) power.liquid = power.balance
def __init__(self, gameName, fileName = 'status'): self.powerType = StandardPower Game.__init__(self, gameName, fileName)
def __init__(self, gameName, fileName = 'status'): self.variant, self.powerType = 'xtalball', XtalballPower Game.__init__(self, gameName, fileName)
def postMoveUpdate(self): if 'NO_DONATIONS' in self.rules: self.findGoners(phase = 0) for power in self.powers: if power.goner: power.balance = 0 return Game.postMoveUpdate(self)
def resolvePhase(self): if self.phaseType == 'I': return self.processIncomePhase() if self.phaseType == 'D': return self.processExchangeDividendsPhase() if self.phaseType == 'Y': return self.processExchangeReportsPhase() return Game.resolvePhase(self)
def captureCenters(self): return Game.captureCenters(self, self.parseSupplyCount)
def postMoveUpdate(self): for power in self.powers: power.orders, power.cd = {}, 0 return Game.postMoveUpdate(self)