def newGame(self): """asks user for players and ruleset for a new game and returns that new game""" Players.load() # we want to make sure we have the current definitions selectDialog = SelectPlayers(self.game) if not selectDialog.exec_(): return return ScoringGame(selectDialog.names, selectDialog.cbRuleset.current)
def __init__(self): SelectRuleset.__init__(self) Players.load() decorateWindow(self, i18n('Select four players')) self.names = None self.nameWidgets = [] for idx, wind in enumerate(Wind.all4): cbName = QComboBox() cbName.manualSelect = False # increase width, we want to see the full window title cbName.setMinimumWidth(350) # is this good for all platforms? cbName.addItems(list(Players.humanNames.values())) self.grid.addWidget(cbName, idx + 1, 1) self.nameWidgets.append(cbName) self.grid.addWidget(WindLabel(wind), idx + 1, 0) cbName.currentIndexChanged.connect(self.slotValidate) query = Query( "select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)" ) if len(query.records): with BlockSignals(self.nameWidgets): for cbName, playerId in zip(self.nameWidgets, query.records[0]): try: playerName = Players.humanNames[playerId] playerIdx = cbName.findText(playerName) if playerIdx >= 0: cbName.setCurrentIndex(playerIdx) except KeyError: logError( 'database is inconsistent: player with id %d is in game but not in player' % playerId) self.slotValidate()
def __init__(self, game): SelectRuleset.__init__(self) self.game = game Players.load() self.setWindowTitle(m18n('Select four players') + ' - Kajongg') self.names = None self.nameWidgets = [] for idx, wind in enumerate(WINDS): cbName = QComboBox() cbName.manualSelect = False # increase width, we want to see the full window title cbName.setMinimumWidth(350) # is this good for all platforms? cbName.addItems(Players.humanNames.values()) self.grid.addWidget(cbName, idx+1, 1) self.nameWidgets.append(cbName) self.grid.addWidget(WindLabel(wind), idx+1, 0) cbName.currentIndexChanged.connect(self.slotValidate) query = Query("select p0,p1,p2,p3 from game where seed is null and game.id = (select max(id) from game)") if len(query.records): for pidx, playerId in enumerate(query.records[0]): try: playerName = Players.humanNames[playerId] cbName = self.nameWidgets[pidx] playerIdx = cbName.findText(playerName) if playerIdx >= 0: cbName.setCurrentIndex(playerIdx) except KeyError: logError('database is inconsistent: player with id %d is in game but not in player' \ % playerId) self.slotValidate()
def assignPlayers(self, playerNames): """the server tells us the seating order and player names""" pairs = [] for idx, pair in enumerate(playerNames): if isinstance(pair, basestring): wind, name = WINDS[idx], pair else: wind, name = pair pairs.append((wind, name)) field = Internal.field if not self.players: if field: self.players = field.genPlayers() else: self.players = Players([Player(self) for idx in range(4)]) for idx, pair in enumerate(pairs): wind, name = pair player = self.players[idx] Players.createIfUnknown(name) player.wind = wind player.name = name else: for idx, pair in enumerate(playerNames): wind, name = pair self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers()
def eric(config_file): ardUniverse = ArdUniverse(config_file) ardUniverse.connect_all() players = Players(config) while True: for sensor in ardUniverse.sensors: status = sensor.get_status() player = players.find_player_for_rfid(status) for event in sensor.events: handle_event(event, player, sensor)
def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) # Set the working directory (where we expect to find files) to the same # directory this .py file is in. You can leave this out of your own # code, but it is needed to easily run the examples using "python -m" # as mentioned at the top of this program. file_path = os.path.dirname(os.path.abspath(__file__)) os.chdir(file_path) arcade.set_background_color(arcade.color.AMAZON) self.players = Players()
def sortPlayers(self): """sort by wind order. Place ourself at bottom (idx=0)""" self.players.sort(key=lambda x: x.wind) self.activePlayer = self.players[East] if Internal.scene: if self.belongsToHumanPlayer(): while self.players[0] != self.myself: self.players = Players(self.players[1:] + self.players[:1]) for idx, player in enumerate(self.players): player.front = self.wall[idx] player.sideText.board = player.front # we want names to move simultaneously self.players[1].sideText.refreshAll()
def __init__(self, use_network=False): self.players = None if use_network: self.players = NetworkPlayers() else: self.players = Players()
def __init__(self, names, ruleset, gameid=None, wantedGame=None, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set. Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=too-many-statements assert self.__class__ != Game, 'Do not directly instantiate Game' for wind, name in names: assert isinstance(wind, Wind), 'Game.__init__ expects Wind objects' assert isinstance(name, (str, unicode)), 'Game.__init__: name must be string and not {}'.format(type(name)) self.players = Players() # if we fail later on in init, at least we can still close the program self.myself = None # the player using this client instance for talking to the server self.__shouldSave = False self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.randomGenerator = CountingRandom(self) self.wantedGame = wantedGame self._setHandSeed() self.activePlayer = None self.__winner = None self.moves = [] self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.__lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' self.wall = self.wallClass(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() self._scanGameOption() for player in self.players: player.clearHand()
def selectScoringGame(self): """show all games, select an existing game or create a new game""" Players.load() if len(Players.humanNames) < 4: logWarning(m18n('Please define four players in <interface>Settings|Players</interface>')) return False gameSelector = Games(self) if gameSelector.exec_(): selected = gameSelector.selectedGame if selected is not None: ScoringGame.loadFromDB(selected) else: self.newGame() if self.game: self.game.throwDices() gameSelector.close() self.updateGUI() return bool(self.game)
def sortPlayers(self): """sort by wind order. Place ourself at bottom (idx=0)""" self.players.sort(key=lambda x: x.wind) self.activePlayer = self.players[East] if Internal.scene: if self.belongsToHumanPlayer(): while self.players[0] != self.myself: self.players = Players(self.players[1:] + self.players[:1]) for idx, player in enumerate(self.players): player.front = self.wall[idx]
def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' qGame = Query("select p0,p1,p2,p3,ruleset,seed from game where id = %d" % gameid) if not qGame.records: return None rulesetId = qGame.records[0][4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions game = cls(Game.__getNames(qGame.records[0]), ruleset, gameid=gameid, client=client, wantedGame=qGame.records[0][5]) qLastHand = Query("select hand,rotated from score where game=%d and hand=" "(select max(hand) from score where game=%d)" % (gameid, gameid)) if qLastHand.records: (game.handctr, game.rotated) = qLastHand.records[0] qScores = Query("select player, wind, balance, won, prevailing from score " "where game=%d and hand=%d" % (gameid, game.handctr)) # default value. If the server saved a score entry but our client did not, # we get no record here. Should we try to fix this or exclude such a game from # the list of resumable games? prevailing = 'E' for record in qScores.records: playerid = record[0] wind = str(record[1]) player = game.players.byId(playerid) if not player: logError( 'game %d inconsistent: player %d missing in game table' % \ (gameid, playerid)) else: player.getsPayment(record[2]) player.wind = wind if record[3]: game.winner = player prevailing = record[4] game.roundsFinished = WINDS.index(prevailing) game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() game.wall.decorate() return game
def scoreGame(): """show all games, select an existing game or create a new game""" Players.load() if len(Players.humanNames) < 4: logWarning( m18n('Please define four players in <interface>Settings|Players</interface>')) return gameSelector = Games(Internal.mainWindow) selected = None if not gameSelector.exec_(): return selected = gameSelector.selectedGame gameSelector.close() if selected is not None: return ScoringGame.loadFromDB(selected) else: selectDialog = SelectPlayers() if not selectDialog.exec_(): return return ScoringGame(list(zip(Wind.all4, selectDialog.names)), selectDialog.cbRuleset.current)
def assignPlayers(self, playerNames): """ The server tells us the seating order and player names. @param playerNames: A list of 4 tuples. Each tuple holds wind and name. @type playerNames: The tuple contents must be C{str} @todo: Can we pass L{Players} instead of that tuple list? """ if not self.players: self.players = Players() for idx in range(4): # append each separately: Until they have names, the current length of players # is used to assign one of the four walls to the player self.players.append(self.playerClass(self, playerNames[idx][1])) for wind, name in playerNames: self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers()
def selectScoringGame(self): """show all games, select an existing game or create a new game""" Players.load() if len(Players.humanNames) < 4: logWarning( m18n( 'Please define four players in <interface>Settings|Players</interface>' )) return False gameSelector = Games(self) if gameSelector.exec_(): selected = gameSelector.selectedGame if selected is not None: ScoringGame.loadFromDB(selected) else: self.newGame() if self.game: self.game.throwDices() gameSelector.close() self.updateGUI() return bool(self.game)
def scoreGame(): """show all games, select an existing game or create a new game""" Players.load() if len(Players.humanNames) < 4: logWarning( i18n( 'Please define four players in <interface>Settings|Players</interface>' )) return gameSelector = Games(Internal.mainWindow) selected = None if not gameSelector.exec_(): return selected = gameSelector.selectedGame gameSelector.close() if selected is not None: return ScoringGame.loadFromDB(selected) else: selectDialog = SelectPlayers() if not selectDialog.exec_(): return return ScoringGame(list(zip(Wind.all4, selectDialog.names)), selectDialog.cbRuleset.current)
def assignPlayers(self, playerNames): """ The server tells us the seating order and player names. @param playerNames: A list of 4 tuples. Each tuple holds wind and name. @type playerNames: The tuple contents must be C{unicode} @todo: Can we pass L{Players} instead of that tuple list? """ if not self.players: self.players = Players(self.playerClass( self, playerNames[x][1]) for x in range(4)) for wind, name in playerNames: self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers()
class MyGame(arcade.Window): """ Main application class. """ def __init__(self): """ Initializer """ # Call the parent class initializer super().__init__(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) # Set the working directory (where we expect to find files) to the same # directory this .py file is in. You can leave this out of your own # code, but it is needed to easily run the examples using "python -m" # as mentioned at the top of this program. file_path = os.path.dirname(os.path.abspath(__file__)) os.chdir(file_path) arcade.set_background_color(arcade.color.AMAZON) self.players = Players() def setup(self): """ Set up the game and initialize the variables. """ self.ball = Ball(200, 200) self.players.setup() self.map = Map() arcade.set_background_color(arcade.color.AMAZON) self.timer = 2 self.score_red = 0 self.score_blue = 0 def on_draw(self): """ Render the screen. """ # This command has to happen before we start drawing arcade.start_render() self.ball.draw() self.players.draw() self.map.draw() arcade.draw_text(str(round(self.timer)), 10, 20, arcade.color.BLACK, 14) arcade.draw_text(str(round(self.players.score_red)), self.map.gate_r.center_x, self.map.gate_r.center_y + 200, arcade.color.RED, 30) arcade.draw_text(str(round(self.players.score_blue)), self.map.gate_b.center_x, self.map.gate_b.center_y + 200, arcade.color.BLUE, 30) arcade.draw_text(str(self.players.pointer), 40, 20, arcade.color.BLACK, 14) def on_mouse_press(self, x, y, button, modifiers): """ Called whenever the mouse button is clicked. """ pass def on_update(self, delta_time): """ Movement and game logic """ self.timer -= delta_time if self.timer < 0: self.setup() self.players.update(delta_time, self.ball, self.map)
class Game: """the game without GUI""" # pylint: disable=too-many-instance-attributes playerClass = Player wallClass = Wall def __init__(self, names, ruleset, gameid=None, wantedGame=None, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set. Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=too-many-statements assert self.__class__ != Game, 'Do not directly instantiate Game' for wind, name in names: assert isinstance(wind, Wind), 'Game.__init__ expects Wind objects' assert isinstance( name, str), 'Game.__init__: name must be string and not {}'.format( type(name)) self.players = Players() # if we fail later on in init, at least we can still close the program self.myself = None # the player using this client instance for talking to the server self.__shouldSave = False self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.wantedGame = wantedGame self.moves = [] self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.__lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self.randomGenerator = CountingRandom(self) self._setHandSeed() self.activePlayer = None self.__winner = None self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' self.wall = self.wallClass(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() self._scanGameOption() for player in self.players: player.clearHand() @property def shouldSave(self): """as a property""" return self.__shouldSave @shouldSave.setter def shouldSave(self, value): """if activated, save start time""" if value and not self.__shouldSave: self.saveStartTime() self.__shouldSave = value @property def handId(self): """current position in game""" result = HandId(self) if result != self._currentHandId: self._prevHandId = self._currentHandId self._currentHandId = result return result @property def client(self): """hide weakref""" if self._client: return self._client() @client.setter def client(self, value): """hide weakref""" if value: self._client = weakref.ref(value) else: self._client = None def clearHand(self): """empty all data""" if self.moves: for move in self.moves: del move self.moves = [] for player in self.players: player.clearHand() self.__winner = None self.__activePlayer = None self.prevActivePlayer = None self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 def _scanGameOption(self): """this is only done for PlayingGame""" pass @property def lastDiscard(self): """hide weakref""" return self.__lastDiscard @lastDiscard.setter def lastDiscard(self, value): """hide weakref""" self.__lastDiscard = value if value is not None: assert isinstance(value, Tile), value if value.isExposed: raise Exception('lastDiscard is exposed:%s' % value) @property def winner(self): """the name of the game server this game is attached to""" return self.__winner @property def roundWind(self): """the round wind for Hand""" return Wind.all[self.roundsFinished % 4] @winner.setter def winner(self, value): """the name of the game server this game is attached to""" if self.__winner != value: if self.__winner: self.__winner.invalidateHand() self.__winner = value if value: value.invalidateHand() def addCsvTag(self, tag, forAllPlayers=False): """tag will be written to tag field in csv row""" if forAllPlayers or self.belongsToHumanPlayer(): self.csvTags.append('%s/%s' % (tag, self.handId.prompt(withSeed=False))) def isFirstHand(self): """as the name says""" return self.roundHandCount == 0 and self.roundsFinished == 0 def _setGameId(self): """virtual""" assert not self # we want it to fail, and quieten pylint def close(self): """log off from the server and return a Deferred""" self.wall = None self.lastDiscard = None def playerByName(self, playerName): """return None or the matching player""" if playerName is None: return None for myPlayer in self.players: if myPlayer.name == playerName: return myPlayer logException('Move references unknown player %s' % playerName) def losers(self): """the 3 or 4 losers: All players without the winner""" return list([x for x in self.players if x is not self.__winner]) def belongsToRobotPlayer(self): """does this game instance belong to a robot player?""" return self.client and self.client.isRobotClient() def belongsToHumanPlayer(self): """does this game instance belong to a human player?""" return self.client and self.client.isHumanClient() def belongsToGameServer(self): """does this game instance belong to the game server?""" return self.client and self.client.isServerClient() @staticmethod def isScoringGame(): """are we scoring a manual game?""" return False def belongsToPlayer(self): """does this game instance belong to a player (as opposed to the game server)?""" return self.belongsToRobotPlayer() or self.belongsToHumanPlayer() def assignPlayers(self, playerNames): """ The server tells us the seating order and player names. @param playerNames: A list of 4 tuples. Each tuple holds wind and name. @type playerNames: The tuple contents must be C{str} @todo: Can we pass L{Players} instead of that tuple list? """ if not self.players: self.players = Players() for idx in range(4): # append each separately: Until they have names, the current length of players # is used to assign one of the four walls to the player self.players.append(self.playerClass(self, playerNames[idx][1])) for wind, name in playerNames: self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers() def __shufflePlayers(self): """assign random seats to the players and assign winds""" self.players.sort(key=lambda x: x.name) self.randomGenerator.shuffle(self.players) for player, wind in zip(self.players, Wind.all4): player.wind = wind def __exchangeSeats(self): """execute seat exchanges according to the rules""" winds = list( x for x in self.shiftRules.split(',')[(self.roundsFinished - 1) % 4]) players = list(self.players[Wind(x)] for x in winds) pairs = list(players[x:x + 2] for x in range(0, len(winds), 2)) for playerA, playerB in self._mustExchangeSeats(pairs): playerA.wind, playerB.wind = playerB.wind, playerA.wind def _mustExchangeSeats(self, pairs): """filter: which player pairs should really swap places?""" # pylint: disable=no-self-use return pairs def sortPlayers(self): """sort by wind order. Place ourself at bottom (idx=0)""" self.players.sort(key=lambda x: x.wind) self.activePlayer = self.players[East] if Internal.scene: if self.belongsToHumanPlayer(): while self.players[0] != self.myself: self.players = Players(self.players[1:] + self.players[:1]) for idx, player in enumerate(self.players): player.front = self.wall[idx] player.sideText.board = player.front # we want names to move simultaneously self.players[1].sideText.refreshAll() @staticmethod def _newGameId(): """write a new entry in the game table and returns the game id of that new entry""" return Query("insert into game(seed) values(0)").cursor.lastrowid def saveStartTime(self): """save starttime for this game""" starttime = datetime.datetime.now().replace(microsecond=0).isoformat() args = list( [starttime, self.seed, int(self.autoPlay), self.ruleset.rulesetId]) args.extend([p.nameid for p in self.players]) args.append(self.gameid) Query( "update game set starttime=?,seed=?,autoplay=?," "ruleset=?,p0=?,p1=?,p2=?,p3=? where id=?", tuple(args)) def __useRuleset(self, ruleset): """use a copy of ruleset for this game, reusing an existing copy""" self.ruleset = ruleset self.ruleset.load() if Internal.db: # only if we have a DB open. False in scoringtest.py query = Query('select id from ruleset where id>0 and hash=?', (self.ruleset.hash, )) if query.records: # reuse that ruleset self.ruleset.rulesetId = query.records[0][0] else: # generate a new ruleset self.ruleset.save() @property def seed(self): # TODO: move this to PlayingGame """extract it from wantedGame. Set wantedGame if empty.""" if not self.wantedGame: self.wantedGame = str(int(self.randomGenerator.random() * 10**9)) return int(self.wantedGame.split('/')[0]) def _setHandSeed(self): # TODO: move this to PlayingGame """set seed to a reproducible value, independent of what happened in previous hands/rounds. This makes it easier to reproduce game situations in later hands without having to exactly replay all previous hands""" seedFactor = ((self.roundsFinished + 1) * 10000 + self.rotated * 1000 + self.notRotated * 100) self.randomGenerator.seed(self.seed * seedFactor) def prepareHand(self): """prepare a game hand""" self.clearHand() if self.finished(): if Options.rounds: self.close().addCallback(Internal.mainWindow.close) else: self.close() def initHand(self): """directly before starting""" self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 if Internal.scene: # TODO: why not self.scene? Internal.scene.prepareHand() self._setHandSeed() def saveHand(self): """save hand to database, update score table and balance in status line""" self.__payHand() self._saveScores() self.handctr += 1 self.notRotated += 1 self.roundHandCount += 1 self.handDiscardCount = 0 def _saveScores(self): """save computed values to database, update score table and balance in status line""" scoretime = datetime.datetime.now().replace(microsecond=0).isoformat() logMessage = '' for player in self.players: if player.hand: manualrules = '||'.join(x.rule.name for x in player.hand.usedRules) else: manualrules = i18n('Score computed manually') Query( "INSERT INTO SCORE " "(game,hand,data,manualrules,player,scoretime,won,prevailing," "wind,points,payments, balance,rotated,notrotated) " "VALUES(%d,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" % (self.gameid, self.handctr, player.nameid, scoretime, int(player == self.__winner), self.roundWind.char, player.wind, player.handTotal, player.payment, player.balance, self.rotated, self.notRotated), (player.hand.string, manualrules)) logMessage += '{player:<12} {hand:>4} {total:>5} {won} | '.format( player=str(player)[:12], hand=player.handTotal, total=player.balance, won='WON' if player == self.winner else ' ') for usedRule in player.hand.usedRules: rule = usedRule.rule if rule.score.limits: self.addCsvTag(rule.name.replace(' ', '')) if Debug.scores: self.debug(logMessage) def maybeRotateWinds(self): """rules which make winds rotate""" result = list(x for x in self.ruleset.filterRules('rotate') if x.rotate(self)) if result: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug(','.join(x.name for x in result), prevHandId=True) self.rotateWinds() return bool(result) def rotateWinds(self): """rotate winds, exchange seats. If finished, update database""" self.rotated += 1 self.notRotated = 0 if self.rotated == 4: self.roundsFinished += 1 self.rotated = 0 self.roundHandCount = 0 if self.finished(): endtime = datetime.datetime.now().replace( microsecond=0).isoformat() with Internal.db as transaction: transaction.execute( 'UPDATE game set endtime = "%s" where id = %d' % (endtime, self.gameid)) elif not self.belongsToPlayer(): # the game server already told us the new placement and winds winds = [player.wind for player in self.players] winds = winds[3:] + winds[0:3] for idx, newWind in enumerate(winds): self.players[idx].wind = newWind if self.roundsFinished % 4 and self.rotated == 0: # exchange seats between rounds self.__exchangeSeats() if Internal.scene: with AnimationSpeed(Speeds.windMarker): self.wall.showWindMarkers() def debug(self, msg, btIndent=None, prevHandId=False): """ Log a debug message. @param msg: The message. @type msg: A string. @param btIndent: If given, message is indented by depth(backtrace)-btIndent @type btIndent: C{int} @param prevHandId: If True, do not use current handId but previous @type prevHandId: C{bool} """ if self.belongsToRobotPlayer(): prefix = 'R' elif self.belongsToHumanPlayer(): prefix = 'C' elif self.belongsToGameServer(): prefix = 'S' else: logDebug(msg, btIndent=btIndent) return handId = self._prevHandId if prevHandId else self.handId handId = handId.prompt(withMoveCount=True) logDebug('%s%s: %s' % (prefix, handId, msg), withGamePrefix=False, btIndent=btIndent) @staticmethod def __getName(playerid): """get name for playerid """ try: return Players.allNames[playerid] except KeyError: return i18n('Player %1 not known', playerid) @classmethod def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' records = Query( "select p0,p1,p2,p3,ruleset,seed from game where id = ?", (gameid, )).records if not records: return None qGameRecord = records[0] rulesetId = qGameRecord[4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions records = Query( "select hand,rotated from score where game=? and hand=" "(select max(hand) from score where game=?)", (gameid, gameid)).records if records: qLastHandRecord = records[0] else: qLastHandRecord = tuple([0, 0]) qScoreRecords = Query( "select player, wind, balance, won, prevailing from score " "where game=? and hand=?", (gameid, qLastHandRecord[0])).records if not qScoreRecords: # this should normally not happen qScoreRecords = list([ tuple([qGameRecord[wind], wind.char, 0, False, East.char]) for wind in Wind.all4 ]) if len(qScoreRecords) != 4: logError('game %d inconsistent: There should be exactly ' '4 score records for the last hand' % gameid) # after loading SQL, prepare values. # default value. If the server saved a score entry but our client # did not, we get no record here. Should we try to fix this or # exclude such a game from the list of resumable games? if len(set(x[4] for x in qScoreRecords)) != 1: logError('game %d inconsistent: All score records for the same ' 'hand must have the same prevailing wind' % gameid) players = list( tuple([Wind(x[1]), Game.__getName(x[0])]) for x in qScoreRecords) # create the game instance. game = cls(players, ruleset, gameid=gameid, client=client, wantedGame=qGameRecord[5]) game.handctr, game.rotated = qLastHandRecord for record in qScoreRecords: playerid = record[0] player = game.players.byId(playerid) if not player: logError( 'game %d inconsistent: player %d missing in game table' % (gameid, playerid)) else: player.getsPayment(record[2]) if record[3]: game.winner = player game.roundsFinished = Wind(qScoreRecords[0][4]).__index__() game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() with AnimationSpeed(Speeds.windMarker): animateAndDo(game.wall.decorate4) return game def finished(self): """The game is over after minRounds completed rounds. Also, check if we reached the second handId defined by --game. If we did, the game is over too""" last = HandId(self, self.wantedGame, 1) if self.handId > last: return True if Options.rounds: return self.roundsFinished >= Options.rounds elif self.ruleset: # while initialising Game, ruleset might be None return self.roundsFinished >= self.ruleset.minRounds def __payHand(self): """pay the scores""" # pylint: disable=too-many-branches # too many branches winner = self.__winner if winner: winner.wonCount += 1 guilty = winner.usedDangerousFrom if guilty: payAction = self.ruleset.findUniqueOption('payforall') if guilty and payAction: if Debug.dangerousGame: self.debug('%s: winner %s. %s pays for all' % (self.handId, winner, guilty)) guilty.hand.usedRules.append((payAction, None)) score = winner.handTotal score = score * 6 if winner.wind == East else score * 4 guilty.getsPayment(-score) winner.getsPayment(score) return for player1 in self.players: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug('%s: %s' % (player1, player1.hand.string)) for line in player1.hand.explain(): self.debug(' %s' % (line)) for player2 in self.players: if id(player1) != id(player2): if player1.wind == East or player2.wind == East: efactor = 2 else: efactor = 1 if player2 != winner: player1.getsPayment(player1.handTotal * efactor) if player1 != winner: player1.getsPayment(-player2.handTotal * efactor) def lastMoves(self, only=None, without=None, withoutNotifications=False): """filters and yields the moves in reversed order""" for idx in range(len(self.moves) - 1, -1, -1): move = self.moves[idx] if withoutNotifications and move.notifying: continue if only: if move.message in only: yield move elif without: if move.message not in without: yield move else: yield move def throwDices(self): """sets random living and kongBox sets divideAt: an index for the wall break""" breakWall = self.randomGenerator.randrange(4) sideLength = len(self.wall.tiles) // 4 # use the sum of four dices to find the divide self.divideAt = breakWall * sideLength + \ sum(self.randomGenerator.randrange(1, 7) for idx in range(4)) if self.divideAt % 2 == 1: self.divideAt -= 1 self.divideAt %= len(self.wall.tiles)
target_host = "localhost" target_port = 1212 # create a socket object client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((target_host, target_port)) INSTRUKCJA = pygame.image.load('Instrukcje.png') TEXT = pygame.image.load('txt.png') pygame.font.init() myfont = pygame.font.SysFont('monospace', 25) small_font = pygame.font.SysFont('monospace', 15) while (1): done = False p = Players(pygame) b = Bullets() m = Meteors(pygame) e = Enemies(pygame) bo = Bonus(pygame) g = Game() while (1): menu_ret = Menu() if menu_ret == 1: break elif menu_ret == 2: instrukcja() elif menu_ret == 3: exit()
def __init__(self): self.tables = {} self.suspendedTables = {} # key is gameid self.users = list() Players.load()
def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' records = Query( "select p0,p1,p2,p3,ruleset,seed from game where id = ?", (gameid,)).records if not records: return None qGameRecord = records[0] rulesetId = qGameRecord[4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions records = Query( "select hand,rotated from score where game=? and hand=" "(select max(hand) from score where game=?)", (gameid, gameid)).records if records: qLastHandRecord = records[0] else: qLastHandRecord = tuple([0, 0]) qScoreRecords = Query( "select player, wind, balance, won, prevailing from score " "where game=? and hand=?", (gameid, qLastHandRecord[0])).records if not qScoreRecords: # this should normally not happen qScoreRecords = list([ tuple([qGameRecord[wind], wind.char, 0, False, East.char]) for wind in Wind.all4]) if len(qScoreRecords) != 4: logError(u'game %d inconsistent: There should be exactly ' '4 score records for the last hand' % gameid) # after loading SQL, prepare values. # default value. If the server saved a score entry but our client # did not, we get no record here. Should we try to fix this or # exclude such a game from the list of resumable games? if len(set(x[4] for x in qScoreRecords)) != 1: logError(u'game %d inconsistent: All score records for the same ' 'hand must have the same prevailing wind' % gameid) players = list(tuple([Wind(x[1]), Game.__getName(x[0])]) for x in qScoreRecords) # create the game instance. game = cls(players, ruleset, gameid=gameid, client=client, wantedGame=qGameRecord[5]) game.handctr, game.rotated = qLastHandRecord for record in qScoreRecords: playerid = record[0] player = game.players.byId(playerid) if not player: logError( u'game %d inconsistent: player %d missing in game table' % (gameid, playerid)) else: player.getsPayment(record[2]) if record[3]: game.winner = player game.roundsFinished = Wind(qScoreRecords[0][4]).__index__() game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() game.wall.decorate() return game
from player import Player from player import Players from teamroster import TeamRoster from draft import Draft from team import Teams #max year should be current -1 minYear = 2010 maxYear = 2020 keeperDraftDate = "2021-09-19" playerList = Players() teamRoster = TeamRoster() draftPick = Draft() #teams = Teams() #min 2007, max 2017 # should get season 2017-2018 to 2008-2009 # should get draft 2008 for currentYear in range(maxYear, minYear, -1): season = str(currentYear) + "" + str(currentYear + 1) draft = str(currentYear + 1) print("Season : " + season + " draft: " + str(draft)) teamRoster.getTeamRoster(season, keeperDraftDate, playerList) draftPick.getDraftPlayers(draft, keeperDraftDate, playerList) playerListFile = ".\playerList.txt" open(playerListFile, 'w').close() playerList.writePlayerList(playerListFile) print("Done")
#everyWhere snakeCase import random from dealer import Dealer from player import Players random.seed(0) if __name__ == '__main__': numPlayers = 2 players = Players(numPlayers) dealer = Dealer() dealer.initialiseCards() dealer.shuffleCards() # print(len(dealer.drawPile), set(dealer.drawPile)) # print(dealer.discardedCards) dealer.distributeCards(players.players) playerChance = 0 player = players.players[playerChance] while (not player.hasWon()): print('+++++++++++++++++++++++++++++') print(f'Player - {player.id}') print('+++++++++++++++++++++++++++++') # print(player.handCards,player.propertyCollection, player.bankCollection) player.chance = True player.drawCards(dealer) print(len(dealer.drawPile), len(player.handCards)) player.chanceNo = 1 while player.chanceNo <= player.cardsToPlay:
def genPlayers(self): """generate four default VisiblePlayers""" return Players([VisiblePlayer(self.game, idx) for idx in range(4)])
room['outside'].n_to = room['foyer'] room['foyer'].s_to = room['outside'] room['foyer'].n_to = room['overlook'] room['foyer'].e_to = room['narrow'] room['overlook'].s_to = room['foyer'] room['narrow'].w_to = room['foyer'] room['narrow'].n_to = room['treasure'] room['treasure'].s_to = room['narrow'] # # # Main # # Make a new player object that is currently in the 'outside' room. player = Players(input('Player name:'), room['outside']) print(f"Hello {player.name}") # Write a loop that: # # * Prints the current room name # * Prints the current description (the textwrap module might be useful here). # * Waits for user input and decides what to do. # # If the user enters a cardinal direction, attempt to move to the room there. # Print an error message if the movement isn't allowed. # # If the user enters "q", quit the game. # LOOP # READ
class Game(object): """the game without GUI""" # pylint: disable=R0902 # pylint we need more than 10 instance attributes def __del__(self): """break reference cycles""" self.clearHand() if self.players: for player in self.players[:]: self.players.remove(player) del player self.players = [] self.__activePlayer = None self.prevActivePlayer = None self.__winner = None self.myself = None if self.client: self.client.game = None self.client = None def __init__(self, names, ruleset, gameid=None, wantedGame=None, shouldSave=True, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=R0915 # pylint we need more than 50 statements self.players = Players() # if we fail later on in init, at least we can still close the program self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.seed = 0 self.randomGenerator = CountingRandom(self) if self.isScoringGame(): self.wantedGame = str(wantedGame) self.seed = wantedGame else: self.wantedGame = wantedGame _ = int(wantedGame.split('/')[0]) if wantedGame else 0 self.seed = _ or int(self.randomGenerator.random() * 10**9) self.shouldSave = shouldSave self.__setHandSeed() self.activePlayer = None self.__winner = None self.moves = [] self.myself = None # the player using this client instance for talking to the server self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' field = Internal.field if field: field.game = self field.startingGame = False field.showWall() # sets self.wall else: self.wall = Wall(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() if not self.isScoringGame() and '/' in self.wantedGame: roundsFinished, rotations, notRotated = self.__scanGameOption(self.wantedGame) for _ in range(roundsFinished * 4 + rotations): self.rotateWinds() self.notRotated = notRotated if self.shouldSave: self.saveNewGame() if field: self.__initVisiblePlayers() field.updateGUI() self.wall.decorate() @property def client(self): """hide weakref""" if self._client is not None: return self._client() @client.setter def client(self, value): """hide weakref""" if value is None: self._client = None else: self._client = weakref.ref(value) def __scanGameOption(self, wanted): """scan the --game option. Return roundsFinished, rotations, notRotated""" part = wanted.split('/')[1] roundsFinished = 'ESWN'.index(part[0]) if roundsFinished > self.ruleset.minRounds: logWarning('Ruleset %s has %d minimum rounds but you want round %d(%s)' % ( self.ruleset.name, self.ruleset.minRounds, roundsFinished + 1, part[0])) return self.ruleset.minRounds, 0 rotations = int(part[1]) - 1 notRotated = 0 if rotations > 3: logWarning('You want %d rotations, reducing to maximum of 3' % rotations) return roundsFinished, 3, 0 for char in part[2:]: if char < 'a': logWarning('you want %s, changed to a' % char) char = 'a' if char > 'z': logWarning('you want %s, changed to z' % char) char = 'z' notRotated = notRotated * 26 + ord(char) - ord('a') + 1 return roundsFinished, rotations, notRotated @property def winner(self): """the name of the game server this game is attached to""" return self.__winner @winner.setter def winner(self, value): """the name of the game server this game is attached to""" if self.__winner != value: if self.__winner: self.__winner.invalidateHand() self.__winner = value if value: value.invalidateHand() def addCsvTag(self, tag, forAllPlayers=False): """tag will be written to tag field in csv row""" if forAllPlayers or self.belongsToHumanPlayer(): self.csvTags.append('%s/%s' % (tag, self.handId())) def isFirstHand(self): """as the name says""" return self.roundHandCount == 0 and self.roundsFinished == 0 def handId(self, withAI=True, withMoveCount=False): """identifies the hand for window title and scoring table""" aiVariant = '' if withAI and self.belongsToHumanPlayer(): aiName = self.client.intelligence.name() if aiName != 'Default': aiVariant = aiName + '/' num = self.notRotated charId = '' while num: charId = chr(ord('a') + (num-1) % 26) + charId num = (num-1) / 26 if self.finished(): wind = 'X' else: wind = WINDS[self.roundsFinished] result = '%s%s/%s%s%s' % (aiVariant, self.seed, wind, self.rotated + 1, charId) if withMoveCount: result += '/moves:%d' % len(self.moves) if result != self._currentHandId: self._prevHandId = self._currentHandId self._currentHandId = result return result def _setGameId(self): """virtual""" assert not self # we want it to fail, and quiten pylint def close(self): """log off from the server and return a Deferred""" Internal.autoPlay = False # do that only for the first game self.__hideGame() if self.client: client = self.client self.client = None result = client.logout() client.delete() else: result = succeed(None) return result def __hideGame(self): """remove all visible traces of the current game""" field = Internal.field if isAlive(field): field.setWindowTitle('Kajongg') if field: field.discardBoard.hide() field.selectorBoard.tiles = [] field.selectorBoard.allSelectorTiles = [] if isAlive(field.centralScene): field.centralScene.removeTiles() field.clientDialog = None for player in self.players: if player.handBoard: player.clearHand() player.handBoard.hide() if self.wall: self.wall.hide() self.wall = None self.lastDiscard = None if field: field.actionAutoPlay.setChecked(False) field.startingGame = False field.game = None field.updateGUI() def __initVisiblePlayers(self): """make players visible""" for idx, player in enumerate(self.players): player.front = self.wall[idx] player.clearHand() player.handBoard.setVisible(True) scoring = self.isScoringGame() player.handBoard.setEnabled(scoring or \ (self.belongsToHumanPlayer() and player == self.myself)) player.handBoard.showMoveHelper(scoring) Internal.field.adjustView() def setConcealedTiles(self, allPlayerTiles): """when starting the hand. tiles is one string""" with Animated(False): for playerName, tileNames in allPlayerTiles: player = self.playerByName(playerName) player.addConcealedTiles(self.wall.deal(tileNames)) def playerByName(self, playerName): """return None or the matching player""" if playerName is None: return None for myPlayer in self.players: if myPlayer.name == playerName: return myPlayer logException('Move references unknown player %s' % playerName) def losers(self): """the 3 or 4 losers: All players without the winner""" return list([x for x in self.players if x is not self.__winner]) def belongsToRobotPlayer(self): """does this game instance belong to a robot player?""" return self.client and self.client.isRobotClient() def belongsToHumanPlayer(self): """does this game instance belong to a human player?""" return self.client and self.client.isHumanClient() def belongsToGameServer(self): """does this game instance belong to the game server?""" return self.client and self.client.isServerClient() @staticmethod def isScoringGame(): """are we scoring a manual game?""" return False def belongsToPlayer(self): """does this game instance belong to a player (as opposed to the game server)?""" return self.belongsToRobotPlayer() or self.belongsToHumanPlayer() def assignPlayers(self, playerNames): """the server tells us the seating order and player names""" pairs = [] for idx, pair in enumerate(playerNames): if isinstance(pair, basestring): wind, name = WINDS[idx], pair else: wind, name = pair pairs.append((wind, name)) field = Internal.field if not self.players: if field: self.players = field.genPlayers() else: self.players = Players([Player(self) for idx in range(4)]) for idx, pair in enumerate(pairs): wind, name = pair player = self.players[idx] Players.createIfUnknown(name) player.wind = wind player.name = name else: for idx, pair in enumerate(playerNames): wind, name = pair self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers() def assignVoices(self): """now we have all remote user voices""" assert self.belongsToHumanPlayer() available = Voice.availableVoices()[:] # available is without transferred human voices for player in self.players: if player.voice and player.voice.oggFiles(): # remote human player sent her voice, or we are human and have a voice if Debug.sound and player != self.myself: logDebug('%s got voice from opponent: %s' % (player.name, player.voice)) else: player.voice = Voice.locate(player.name) if player.voice: if Debug.sound: logDebug('%s has own local voice %s' % (player.name, player.voice)) if player.voice: for voice in Voice.availableVoices(): if voice in available and voice.md5sum == player.voice.md5sum: # if the local voice is also predefined, # make sure we do not use both available.remove(voice) # for the other players use predefined voices in preferred language. Only if # we do not have enough predefined voices, look again in locally defined voices predefined = [x for x in available if x.language() != 'local'] predefined.extend(available) for player in self.players: if player.voice is None and predefined: player.voice = predefined.pop(0) if Debug.sound: logDebug('%s gets one of the still available voices %s' % (player.name, player.voice)) def __shufflePlayers(self): """assign random seats to the players and assign winds""" self.players.sort(key=lambda x:x.name) self.randomGenerator.shuffle(self.players) for player, wind in zip(self.players, WINDS): player.wind = wind def __exchangeSeats(self): """execute seat exchanges according to the rules""" windPairs = self.shiftRules.split(',')[(self.roundsFinished-1) % 4] while len(windPairs): windPair = windPairs[0:2] windPairs = windPairs[2:] swappers = list(self.players[windPair[x]] for x in (0, 1)) if self.belongsToPlayer(): # we are a client in a remote game, the server swaps and tells us the new places shouldSwap = False elif self.isScoringGame(): # we play a manual game and do only the scoring shouldSwap = Internal.field.askSwap(swappers) else: # we are the game server. Always swap in remote games. # do not do assert self.belongsToGameServer() here because # self.client might not yet be set - this code is called for all # suspended games but self.client is assigned later shouldSwap = True if shouldSwap: swappers[0].wind, swappers[1].wind = swappers[1].wind, swappers[0].wind self.sortPlayers() def sortPlayers(self): """sort by wind order. If we are in a remote game, place ourself at bottom (idx=0)""" players = self.players if Internal.field: fieldAttributes = list([(p.handBoard, p.front) for p in players]) players.sort(key=lambda x: 'ESWN'.index(x.wind)) if self.belongsToHumanPlayer(): myName = self.myself.name while players[0].name != myName: values0 = players[0].values for idx in range(4, 0, -1): this, prev = players[idx % 4], players[idx - 1] this.values = prev.values players[1].values = values0 self.myself = players[0] if Internal.field: for idx, player in enumerate(players): player.handBoard, player.front = fieldAttributes[idx] player.handBoard.player = player self.activePlayer = self.players['E'] @staticmethod def _newGameId(): """write a new entry in the game table and returns the game id of that new entry""" with Transaction(): query = Query("insert into game(seed) values(0)") gameid, gameidOK = query.query.lastInsertId().toInt() assert gameidOK return gameid def saveNewGame(self): """write a new entry in the game table with the selected players""" if self.gameid is None: return if not self.isScoringGame(): records = Query("select seed from game where id=?", list([self.gameid])).records assert records if not records: return seed = records[0][0] if not Internal.isServer and self.client: host = self.client.connection.url else: host = None if self.isScoringGame() or seed == 'proposed' or seed == host: # we reserved the game id by writing a record with seed == hostname starttime = datetime.datetime.now().replace(microsecond=0).isoformat() args = list([starttime, self.seed, int(self.autoPlay), self.ruleset.rulesetId]) args.extend([p.nameid for p in self.players]) args.append(self.gameid) with Transaction(): Query("update game set starttime=?,seed=?,autoplay=?," \ "ruleset=?,p0=?,p1=?,p2=?,p3=? where id=?", args) if not Internal.isServer: Query('update server set lastruleset=? where url=?', list([self.ruleset.rulesetId, host])) def __useRuleset(self, ruleset): """use a copy of ruleset for this game, reusing an existing copy""" self.ruleset = ruleset self.ruleset.load() query = Query('select id from ruleset where id>0 and hash="%s"' % \ self.ruleset.hash) if query.records: # reuse that ruleset self.ruleset.rulesetId = query.records[0][0] else: # generate a new ruleset self.ruleset.save(copy=True, minus=False) def __setHandSeed(self): """set seed to a reproducable value, independent of what happend in previous hands/rounds. This makes it easier to reproduce game situations in later hands without having to exactly replay all previous hands""" if self.seed is not None: seedFactor = (self.roundsFinished + 1) * 10000 + self.rotated * 1000 + self.notRotated * 100 self.randomGenerator.seed(self.seed * seedFactor) def clearHand(self): """empty all data""" if self.moves: for move in self.moves: del move self.moves = [] for player in self.players: player.clearHand() self.__winner = None self.__activePlayer = None self.prevActivePlayer = None Hand.clearCache(self) self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 def prepareHand(self): """prepares the next hand""" self.clearHand() if self.finished(): self.close() else: if not self.isScoringGame(): self.sortPlayers() self.hidePopups() self.__setHandSeed() self.wall.build() def initHand(self): """directly before starting""" Hand.clearCache(self) self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 if Internal.field: Internal.field.prepareHand() self.__setHandSeed() def hidePopups(self): """hide all popup messages""" for player in self.players: player.hidePopup() def saveHand(self): """save hand to database, update score table and balance in status line""" self.__payHand() self.__saveScores() self.handctr += 1 self.notRotated += 1 self.roundHandCount += 1 self.handDiscardCount = 0 def __needSave(self): """do we need to save this game?""" if self.isScoringGame(): return True elif self.belongsToRobotPlayer(): return False else: return self.shouldSave # as the server told us def __saveScores(self): """save computed values to database, update score table and balance in status line""" if not self.__needSave(): return scoretime = datetime.datetime.now().replace(microsecond=0).isoformat() for player in self.players: if player.hand: manualrules = '||'.join(x.rule.name for x in player.hand.usedRules) else: manualrules = m18n('Score computed manually') Query("INSERT INTO SCORE " "(game,hand,data,manualrules,player,scoretime,won,prevailing,wind," "points,payments, balance,rotated,notrotated) " "VALUES(%d,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" % \ (self.gameid, self.handctr, player.nameid, scoretime, int(player == self.__winner), WINDS[self.roundsFinished % 4], player.wind, player.handTotal, player.payment, player.balance, self.rotated, self.notRotated), list([player.hand.string, manualrules])) if Debug.scores: self.debug('%s: handTotal=%s balance=%s %s' % ( player, player.handTotal, player.balance, 'won' if player == self.winner else '')) for usedRule in player.hand.usedRules: rule = usedRule.rule if rule.score.limits: tag = rule.function.__class__.__name__ if hasattr(rule.function, 'limitHand'): tag = rule.function.limitHand.__class__.__name__ self.addCsvTag(tag) def savePenalty(self, player, offense, amount): """save computed values to database, update score table and balance in status line""" if not self.__needSave(): return scoretime = datetime.datetime.now().replace(microsecond=0).isoformat() with Transaction(): Query("INSERT INTO SCORE " "(game,penalty,hand,data,manualrules,player,scoretime," "won,prevailing,wind,points,payments, balance,rotated,notrotated) " "VALUES(%d,1,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" % \ (self.gameid, self.handctr, player.nameid, scoretime, int(player == self.__winner), WINDS[self.roundsFinished % 4], player.wind, 0, amount, player.balance, self.rotated, self.notRotated), list([player.hand.string, offense.name])) if Internal.field: Internal.field.updateGUI() def maybeRotateWinds(self): """rules which make winds rotate""" result = list(x for x in self.ruleset.filterFunctions('rotate') if x.rotate(self)) if result: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug(result, prevHandId=True) self.rotateWinds() return bool(result) def rotateWinds(self): """rotate winds, exchange seats. If finished, update database""" self.rotated += 1 self.notRotated = 0 if self.rotated == 4: if not self.finished(): self.roundsFinished += 1 self.rotated = 0 self.roundHandCount = 0 if self.finished(): endtime = datetime.datetime.now().replace(microsecond=0).isoformat() with Transaction(): Query('UPDATE game set endtime = "%s" where id = %d' % \ (endtime, self.gameid)) elif not self.belongsToPlayer(): # the game server already told us the new placement and winds winds = [player.wind for player in self.players] winds = winds[3:] + winds[0:3] for idx, newWind in enumerate(winds): self.players[idx].wind = newWind if self.roundsFinished % 4 and self.rotated == 0: # exchange seats between rounds self.__exchangeSeats() def debug(self, msg, btIndent=None, prevHandId=False): """prepend game id""" if self.belongsToRobotPlayer(): prefix = 'R' elif self.belongsToHumanPlayer(): prefix = 'C' elif self.belongsToGameServer(): prefix = 'S' else: logDebug(msg, btIndent=btIndent) return logDebug('%s%s: %s' % (prefix, self._prevHandId if prevHandId else self.handId(), msg), withGamePrefix=False, btIndent=btIndent) @staticmethod def __getNames(record): """get name ids from record and return the names""" names = [] for idx in range(4): nameid = record[idx] try: name = Players.allNames[nameid] except KeyError: name = m18n('Player %1 not known', nameid) names.append(name) return names @classmethod def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' qGame = Query("select p0,p1,p2,p3,ruleset,seed from game where id = %d" % gameid) if not qGame.records: return None rulesetId = qGame.records[0][4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions game = cls(Game.__getNames(qGame.records[0]), ruleset, gameid=gameid, client=client, wantedGame=qGame.records[0][5]) qLastHand = Query("select hand,rotated from score where game=%d and hand=" "(select max(hand) from score where game=%d)" % (gameid, gameid)) if qLastHand.records: (game.handctr, game.rotated) = qLastHand.records[0] qScores = Query("select player, wind, balance, won, prevailing from score " "where game=%d and hand=%d" % (gameid, game.handctr)) # default value. If the server saved a score entry but our client did not, # we get no record here. Should we try to fix this or exclude such a game from # the list of resumable games? prevailing = 'E' for record in qScores.records: playerid = record[0] wind = str(record[1]) player = game.players.byId(playerid) if not player: logError( 'game %d inconsistent: player %d missing in game table' % \ (gameid, playerid)) else: player.getsPayment(record[2]) player.wind = wind if record[3]: game.winner = player prevailing = record[4] game.roundsFinished = WINDS.index(prevailing) game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() game.wall.decorate() return game def finished(self): """The game is over after minRounds completed rounds""" if self.ruleset: # while initialising Game, ruleset might be None return self.roundsFinished >= self.ruleset.minRounds def __payHand(self): """pay the scores""" # pylint: disable=R0912 # too many branches winner = self.__winner if winner: winner.wonCount += 1 guilty = winner.usedDangerousFrom if guilty: payAction = self.ruleset.findUniqueOption('payforall') if guilty and payAction: if Debug.dangerousGame: self.debug('%s: winner %s. %s pays for all' % \ (self.handId(), winner, guilty)) guilty.hand.usedRules.append((payAction, None)) score = winner.handTotal score = score * 6 if winner.wind == 'E' else score * 4 guilty.getsPayment(-score) winner.getsPayment(score) return for player1 in self.players: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug('%s: %s' % (player1, player1.hand.string)) for line in player1.hand.explain(): self.debug(' %s' % (line)) for player2 in self.players: if id(player1) != id(player2): if player1.wind == 'E' or player2.wind == 'E': efactor = 2 else: efactor = 1 if player2 != winner: player1.getsPayment(player1.handTotal * efactor) if player1 != winner: player1.getsPayment(-player2.handTotal * efactor) def lastMoves(self, only=None, without=None, withoutNotifications=False): """filters and yields the moves in reversed order""" for idx in range(len(self.moves)-1, -1, -1): move = self.moves[idx] if withoutNotifications and move.notifying: continue if only: if move.message in only: yield move elif without: if move.message not in without: yield move else: yield move def throwDices(self): """sets random living and kongBox sets divideAt: an index for the wall break""" if self.belongsToGameServer(): self.wall.tiles.sort(key=tileKey) self.randomGenerator.shuffle(self.wall.tiles) breakWall = self.randomGenerator.randrange(4) sideLength = len(self.wall.tiles) // 4 # use the sum of four dices to find the divide self.divideAt = breakWall * sideLength + \ sum(self.randomGenerator.randrange(1, 7) for idx in range(4)) if self.divideAt % 2 == 1: self.divideAt -= 1 self.divideAt %= len(self.wall.tiles) def dangerousFor(self, forPlayer, tile): """returns a list of explaining texts if discarding tile would be Dangerous game for forPlayer. One text for each reason - there might be more than one""" if isinstance(tile, Tile): tile = tile.element tile = tile.lower() result = [] for dang, txt in self.dangerousTiles: if tile in dang: result.append(txt) for player in forPlayer.others(): for dang, txt in player.dangerousTiles: if tile in dang: result.append(txt) return result def computeDangerous(self, playerChanged=None): """recompute gamewide dangerous tiles. Either for playerChanged or for all players""" self.dangerousTiles = list() if playerChanged: playerChanged.findDangerousTiles() else: for player in self.players: player.findDangerousTiles() self._endWallDangerous() def _endWallDangerous(self): """if end of living wall is reached, declare all invisible tiles as dangerous""" if len(self.wall.living) <=5: allTiles = [x for x in defaultdict.keys(elements.occurrence) if x[0] not in 'fy'] # see http://www.logilab.org/ticket/23986 invisibleTiles = set(x for x in allTiles if x not in self.visibleTiles) msg = m18n('Short living wall: Tile is invisible, hence dangerous') self.dangerousTiles = list(x for x in self.dangerousTiles if x[1] != msg) self.dangerousTiles.append((invisibleTiles, msg)) def appendMove(self, player, command, kwargs): """append a Move object to self.moves""" self.moves.append(Move(player, command, kwargs))
class Game(object): """the game without GUI""" # pylint: disable=too-many-instance-attributes playerClass = Player wallClass = Wall def __init__(self, names, ruleset, gameid=None, wantedGame=None, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set. Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=too-many-statements assert self.__class__ != Game, 'Do not directly instantiate Game' for wind, name in names: assert isinstance(wind, Wind), 'Game.__init__ expects Wind objects' assert isinstance(name, (str, unicode)), 'Game.__init__: name must be string and not {}'.format(type(name)) self.players = Players() # if we fail later on in init, at least we can still close the program self.myself = None # the player using this client instance for talking to the server self.__shouldSave = False self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.randomGenerator = CountingRandom(self) self.wantedGame = wantedGame self._setHandSeed() self.activePlayer = None self.__winner = None self.moves = [] self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.__lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' self.wall = self.wallClass(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() self._scanGameOption() for player in self.players: player.clearHand() @property def shouldSave(self): """as a property""" return self.__shouldSave @shouldSave.setter def shouldSave(self, value): """if activated, save start time""" if value and not self.__shouldSave: self.saveStartTime() self.__shouldSave = value @property def handId(self): """current position in game""" result = HandId(self) if result != self._currentHandId: self._prevHandId = self._currentHandId self._currentHandId = result return result @property def client(self): """hide weakref""" if self._client: return self._client() @client.setter def client(self, value): """hide weakref""" if value: self._client = weakref.ref(value) else: self._client = None def clearHand(self): """empty all data""" if self.moves: for move in self.moves: del move self.moves = [] for player in self.players: player.clearHand() self.__winner = None self.__activePlayer = None self.prevActivePlayer = None self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 def _scanGameOption(self): """this is only done for PlayingGame""" pass @property def lastDiscard(self): """hide weakref""" return self.__lastDiscard @lastDiscard.setter def lastDiscard(self, value): """hide weakref""" self.__lastDiscard = value if value is not None: assert isinstance(value, Tile), value if value.isExposed: raise Exception('lastDiscard is exposed:%s' % value) @property def winner(self): """the name of the game server this game is attached to""" return self.__winner @property def roundWind(self): """the round wind for Hand""" return Wind.all[self.roundsFinished % 4] @winner.setter def winner(self, value): """the name of the game server this game is attached to""" if self.__winner != value: if self.__winner: self.__winner.invalidateHand() self.__winner = value if value: value.invalidateHand() def addCsvTag(self, tag, forAllPlayers=False): """tag will be written to tag field in csv row""" if forAllPlayers or self.belongsToHumanPlayer(): self.csvTags.append('%s/%s' % (tag, self.handId.prompt(withSeed=False))) def isFirstHand(self): """as the name says""" return self.roundHandCount == 0 and self.roundsFinished == 0 def _setGameId(self): """virtual""" assert not self # we want it to fail, and quieten pylint def close(self): """log off from the server and return a Deferred""" self.wall = None self.lastDiscard = None def playerByName(self, playerName): """return None or the matching player""" if playerName is None: return None for myPlayer in self.players: if myPlayer.name == playerName: return myPlayer logException('Move references unknown player %s' % playerName) def losers(self): """the 3 or 4 losers: All players without the winner""" return list([x for x in self.players if x is not self.__winner]) def belongsToRobotPlayer(self): """does this game instance belong to a robot player?""" return self.client and self.client.isRobotClient() def belongsToHumanPlayer(self): """does this game instance belong to a human player?""" return self.client and self.client.isHumanClient() def belongsToGameServer(self): """does this game instance belong to the game server?""" return self.client and self.client.isServerClient() @staticmethod def isScoringGame(): """are we scoring a manual game?""" return False def belongsToPlayer(self): """does this game instance belong to a player (as opposed to the game server)?""" return self.belongsToRobotPlayer() or self.belongsToHumanPlayer() def assignPlayers(self, playerNames): """ The server tells us the seating order and player names. @param playerNames: A list of 4 tuples. Each tuple holds wind and name. @type playerNames: The tuple contents must be C{unicode} @todo: Can we pass L{Players} instead of that tuple list? """ if not self.players: self.players = Players(self.playerClass( self, playerNames[x][1]) for x in range(4)) for wind, name in playerNames: self.players.byName(name).wind = wind if self.client and self.client.name: self.myself = self.players.byName(self.client.name) self.sortPlayers() def __shufflePlayers(self): """assign random seats to the players and assign winds""" self.players.sort(key=lambda x: x.name) self.randomGenerator.shuffle(self.players) for player, wind in zip(self.players, Wind.all4): player.wind = wind def __exchangeSeats(self): """execute seat exchanges according to the rules""" winds = list(x for x in self.shiftRules.split(',')[(self.roundsFinished - 1) % 4]) players = list(self.players[Wind(x)] for x in winds) pairs = list(players[x:x + 2] for x in range(0, len(winds), 2)) for playerA, playerB in self._mustExchangeSeats(pairs): playerA.wind, playerB.wind = playerB.wind, playerA.wind def _mustExchangeSeats(self, pairs): """filter: which player pairs should really swap places?""" # pylint: disable=no-self-use return pairs def sortPlayers(self): """sort by wind order. Place ourself at bottom (idx=0)""" self.players.sort(key=lambda x: x.wind) self.activePlayer = self.players[East] if Internal.scene: if self.belongsToHumanPlayer(): while self.players[0] != self.myself: self.players = Players(self.players[1:] + self.players[:1]) for idx, player in enumerate(self.players): player.front = self.wall[idx] @staticmethod def _newGameId(): """write a new entry in the game table and returns the game id of that new entry""" return Query("insert into game(seed) values(0)").cursor.lastrowid def saveStartTime(self): """save starttime for this game""" starttime = datetime.datetime.now().replace(microsecond=0).isoformat() args = list([starttime, self.seed, int(self.autoPlay), self.ruleset.rulesetId]) args.extend([p.nameid for p in self.players]) args.append(self.gameid) Query("update game set starttime=?,seed=?,autoplay=?," "ruleset=?,p0=?,p1=?,p2=?,p3=? where id=?", tuple(args)) def __useRuleset(self, ruleset): """use a copy of ruleset for this game, reusing an existing copy""" self.ruleset = ruleset self.ruleset.load() if Internal.db: # only if we have a DB open. False in scoringtest.py query = Query( 'select id from ruleset where id>0 and hash=?', (self.ruleset.hash,)) if query.records: # reuse that ruleset self.ruleset.rulesetId = query.records[0][0] else: # generate a new ruleset self.ruleset.save() @property def seed(self): # TODO: move this to PlayingGame """extract it from wantedGame. Set wantedGame if empty.""" if not self.wantedGame: self.wantedGame = str(int(self.randomGenerator.random() * 10 ** 9)) return int(self.wantedGame.split('/')[0]) def _setHandSeed(self): # TODO: move this to PlayingGame """set seed to a reproducable value, independent of what happend in previous hands/rounds. This makes it easier to reproduce game situations in later hands without having to exactly replay all previous hands""" seedFactor = ((self.roundsFinished + 1) * 10000 + self.rotated * 1000 + self.notRotated * 100) self.randomGenerator.seed(self.seed * seedFactor) def prepareHand(self): """prepare a game hand""" self.clearHand() if self.finished(): if Options.rounds: self.close().addCallback(Internal.mainWindow.close) else: self.close() def initHand(self): """directly before starting""" self.dangerousTiles = list() self.discardedTiles.clear() assert self.visibleTiles.count() == 0 if Internal.scene: # TODO: why not self.scene? Internal.scene.prepareHand() self._setHandSeed() def saveHand(self): """save hand to database, update score table and balance in status line""" self.__payHand() self._saveScores() self.handctr += 1 self.notRotated += 1 self.roundHandCount += 1 self.handDiscardCount = 0 def _saveScores(self): """save computed values to database, update score table and balance in status line""" scoretime = datetime.datetime.now().replace(microsecond=0).isoformat() logMessage = u'' for player in self.players: if player.hand: manualrules = '||'.join(x.rule.name for x in player.hand.usedRules) else: manualrules = m18n('Score computed manually') Query( "INSERT INTO SCORE " "(game,hand,data,manualrules,player,scoretime,won,prevailing," "wind,points,payments, balance,rotated,notrotated) " "VALUES(%d,%d,?,?,%d,'%s',%d,'%s','%s',%d,%d,%d,%d,%d)" % (self.gameid, self.handctr, player.nameid, scoretime, int(player == self.__winner), self.roundWind.char, player.wind, player.handTotal, player.payment, player.balance, self.rotated, self.notRotated), (player.hand.string, manualrules)) logMessage += u'{player:<12} {hand:>4} {total:>5} {won} | '.format( player=unicode(player)[:12], hand=player.handTotal, total=player.balance, won='WON' if player == self.winner else ' ') for usedRule in player.hand.usedRules: rule = usedRule.rule if rule.score.limits: self.addCsvTag(rule.name.replace(' ', '')) if Debug.scores: self.debug(logMessage) def maybeRotateWinds(self): """rules which make winds rotate""" result = list(x for x in self.ruleset.filterRules('rotate') if x.rotate(self)) if result: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug(u','.join(x.name for x in result), prevHandId=True) self.rotateWinds() return bool(result) def rotateWinds(self): """rotate winds, exchange seats. If finished, update database""" self.rotated += 1 self.notRotated = 0 if self.rotated == 4: self.roundsFinished += 1 self.rotated = 0 self.roundHandCount = 0 if self.finished(): endtime = datetime.datetime.now().replace( microsecond=0).isoformat() with Internal.db as transaction: transaction.execute( 'UPDATE game set endtime = "%s" where id = %d' % (endtime, self.gameid)) elif not self.belongsToPlayer(): # the game server already told us the new placement and winds winds = [player.wind for player in self.players] winds = winds[3:] + winds[0:3] for idx, newWind in enumerate(winds): self.players[idx].wind = newWind if self.roundsFinished % 4 and self.rotated == 0: # exchange seats between rounds self.__exchangeSeats() def debug(self, msg, btIndent=None, prevHandId=False): """ Log a debug message. @param msg: The message. @type msg: A string. @param btIndent: If given, message is indented by depth(backtrace)-btIndent @type btIndent: C{int} @param prevHandId: If True, do not use current handId but previous @type prevHandId: C{bool} """ if self.belongsToRobotPlayer(): prefix = u'R' elif self.belongsToHumanPlayer(): prefix = u'C' elif self.belongsToGameServer(): prefix = u'S' else: logDebug(msg, btIndent=btIndent) return handId = self._prevHandId if prevHandId else self.handId handId = unicodeString(handId.prompt(withMoveCount=True)) logDebug( u'%s%s: %s' % (prefix, handId, unicodeString(msg)), withGamePrefix=False, btIndent=btIndent) @staticmethod def __getName(playerid): """get name for playerid """ try: return Players.allNames[playerid] except KeyError: return m18n('Player %1 not known', playerid) @classmethod def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' records = Query( "select p0,p1,p2,p3,ruleset,seed from game where id = ?", (gameid,)).records if not records: return None qGameRecord = records[0] rulesetId = qGameRecord[4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions records = Query( "select hand,rotated from score where game=? and hand=" "(select max(hand) from score where game=?)", (gameid, gameid)).records if records: qLastHandRecord = records[0] else: qLastHandRecord = tuple([0, 0]) qScoreRecords = Query( "select player, wind, balance, won, prevailing from score " "where game=? and hand=?", (gameid, qLastHandRecord[0])).records if not qScoreRecords: # this should normally not happen qScoreRecords = list([ tuple([qGameRecord[wind], wind.char, 0, False, East.char]) for wind in Wind.all4]) if len(qScoreRecords) != 4: logError(u'game %d inconsistent: There should be exactly ' '4 score records for the last hand' % gameid) # after loading SQL, prepare values. # default value. If the server saved a score entry but our client # did not, we get no record here. Should we try to fix this or # exclude such a game from the list of resumable games? if len(set(x[4] for x in qScoreRecords)) != 1: logError(u'game %d inconsistent: All score records for the same ' 'hand must have the same prevailing wind' % gameid) players = list(tuple([Wind(x[1]), Game.__getName(x[0])]) for x in qScoreRecords) # create the game instance. game = cls(players, ruleset, gameid=gameid, client=client, wantedGame=qGameRecord[5]) game.handctr, game.rotated = qLastHandRecord for record in qScoreRecords: playerid = record[0] player = game.players.byId(playerid) if not player: logError( u'game %d inconsistent: player %d missing in game table' % (gameid, playerid)) else: player.getsPayment(record[2]) if record[3]: game.winner = player game.roundsFinished = Wind(qScoreRecords[0][4]).__index__() game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() game.wall.decorate() return game def finished(self): """The game is over after minRounds completed rounds. Also, check if we reached the second handId defined by --game. If we did, the game is over too""" last = HandId(self, self.wantedGame, 1) if self.handId > last: return True if Options.rounds: return self.roundsFinished >= Options.rounds elif self.ruleset: # while initialising Game, ruleset might be None return self.roundsFinished >= self.ruleset.minRounds def __payHand(self): """pay the scores""" # pylint: disable=too-many-branches # too many branches winner = self.__winner if winner: winner.wonCount += 1 guilty = winner.usedDangerousFrom if guilty: payAction = self.ruleset.findUniqueOption('payforall') if guilty and payAction: if Debug.dangerousGame: self.debug('%s: winner %s. %s pays for all' % (self.handId, winner, guilty)) guilty.hand.usedRules.append((payAction, None)) score = winner.handTotal score = score * 6 if winner.wind == East else score * 4 guilty.getsPayment(-score) winner.getsPayment(score) return for player1 in self.players: if Debug.explain: if not self.belongsToRobotPlayer(): self.debug('%s: %s' % (player1, player1.hand.string)) for line in player1.hand.explain(): self.debug(' %s' % (line)) for player2 in self.players: if id(player1) != id(player2): if player1.wind == East or player2.wind == East: efactor = 2 else: efactor = 1 if player2 != winner: player1.getsPayment(player1.handTotal * efactor) if player1 != winner: player1.getsPayment(-player2.handTotal * efactor) def lastMoves(self, only=None, without=None, withoutNotifications=False): """filters and yields the moves in reversed order""" for idx in range(len(self.moves) - 1, -1, -1): move = self.moves[idx] if withoutNotifications and move.notifying: continue if only: if move.message in only: yield move elif without: if move.message not in without: yield move else: yield move def throwDices(self): """sets random living and kongBox sets divideAt: an index for the wall break""" breakWall = self.randomGenerator.randrange(4) sideLength = len(self.wall.tiles) // 4 # use the sum of four dices to find the divide self.divideAt = breakWall * sideLength + \ sum(self.randomGenerator.randrange(1, 7) for idx in range(4)) if self.divideAt % 2 == 1: self.divideAt -= 1 self.divideAt %= len(self.wall.tiles)
def __init__(self, names, ruleset, gameid=None, wantedGame=None, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set. Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=too-many-statements assert self.__class__ != Game, 'Do not directly instantiate Game' for wind, name in names: assert isinstance(wind, Wind), 'Game.__init__ expects Wind objects' assert isinstance( name, str), 'Game.__init__: name must be string and not {}'.format( type(name)) self.players = Players() # if we fail later on in init, at least we can still close the program self.myself = None # the player using this client instance for talking to the server self.__shouldSave = False self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.wantedGame = wantedGame self.moves = [] self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.__lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self.randomGenerator = CountingRandom(self) self._setHandSeed() self.activePlayer = None self.__winner = None self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' self.wall = self.wallClass(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() self._scanGameOption() for player in self.players: player.clearHand()
def player(): return Players()
def __init__(self, names, ruleset, gameid=None, wantedGame=None, shouldSave=True, client=None): """a new game instance. May be shown on a field, comes from database if gameid is set Game.lastDiscard is the tile last discarded by any player. It is reset to None when a player gets a tile from the living end of the wall or after he claimed a discard. """ # pylint: disable=R0915 # pylint we need more than 50 statements self.players = Players() # if we fail later on in init, at least we can still close the program self._client = None self.client = client self.rotated = 0 self.notRotated = 0 # counts hands since last rotation self.ruleset = None self.roundsFinished = 0 self._currentHandId = None self._prevHandId = None self.seed = 0 self.randomGenerator = CountingRandom(self) if self.isScoringGame(): self.wantedGame = str(wantedGame) self.seed = wantedGame else: self.wantedGame = wantedGame _ = int(wantedGame.split('/')[0]) if wantedGame else 0 self.seed = _ or int(self.randomGenerator.random() * 10**9) self.shouldSave = shouldSave self.__setHandSeed() self.activePlayer = None self.__winner = None self.moves = [] self.myself = None # the player using this client instance for talking to the server self.gameid = gameid self.playOpen = False self.autoPlay = False self.handctr = 0 self.roundHandCount = 0 self.handDiscardCount = 0 self.divideAt = None self.lastDiscard = None # always uppercase self.visibleTiles = IntDict() self.discardedTiles = IntDict(self.visibleTiles) # tile names are always lowercase self.dangerousTiles = list() self.csvTags = [] self._setGameId() self.__useRuleset(ruleset) # shift rules taken from the OEMC 2005 rules # 2nd round: S and W shift, E and N shift self.shiftRules = 'SWEN,SE,WE' field = Internal.field if field: field.game = self field.startingGame = False field.showWall() # sets self.wall else: self.wall = Wall(self) self.assignPlayers(names) if self.belongsToGameServer(): self.__shufflePlayers() if not self.isScoringGame() and '/' in self.wantedGame: roundsFinished, rotations, notRotated = self.__scanGameOption(self.wantedGame) for _ in range(roundsFinished * 4 + rotations): self.rotateWinds() self.notRotated = notRotated if self.shouldSave: self.saveNewGame() if field: self.__initVisiblePlayers() field.updateGUI() self.wall.decorate()
def loadFromDB(cls, gameid, client=None): """load game by game id and return a new Game instance""" Internal.logPrefix = 'S' if Internal.isServer else 'C' records = Query( "select p0,p1,p2,p3,ruleset,seed from game where id = ?", (gameid, )).records if not records: return None qGameRecord = records[0] rulesetId = qGameRecord[4] or 1 ruleset = Ruleset.cached(rulesetId) Players.load() # we want to make sure we have the current definitions records = Query( "select hand,rotated from score where game=? and hand=" "(select max(hand) from score where game=?)", (gameid, gameid)).records if records: qLastHandRecord = records[0] else: qLastHandRecord = tuple([0, 0]) qScoreRecords = Query( "select player, wind, balance, won, prevailing from score " "where game=? and hand=?", (gameid, qLastHandRecord[0])).records if not qScoreRecords: # this should normally not happen qScoreRecords = list([ tuple([qGameRecord[wind], wind.char, 0, False, East.char]) for wind in Wind.all4 ]) if len(qScoreRecords) != 4: logError('game %d inconsistent: There should be exactly ' '4 score records for the last hand' % gameid) # after loading SQL, prepare values. # default value. If the server saved a score entry but our client # did not, we get no record here. Should we try to fix this or # exclude such a game from the list of resumable games? if len(set(x[4] for x in qScoreRecords)) != 1: logError('game %d inconsistent: All score records for the same ' 'hand must have the same prevailing wind' % gameid) players = list( tuple([Wind(x[1]), Game.__getName(x[0])]) for x in qScoreRecords) # create the game instance. game = cls(players, ruleset, gameid=gameid, client=client, wantedGame=qGameRecord[5]) game.handctr, game.rotated = qLastHandRecord for record in qScoreRecords: playerid = record[0] player = game.players.byId(playerid) if not player: logError( 'game %d inconsistent: player %d missing in game table' % (gameid, playerid)) else: player.getsPayment(record[2]) if record[3]: game.winner = player game.roundsFinished = Wind(qScoreRecords[0][4]).__index__() game.handctr += 1 game.notRotated += 1 game.maybeRotateWinds() game.sortPlayers() with AnimationSpeed(Speeds.windMarker): animateAndDo(game.wall.decorate4) return game
def __init__(self): self.tables = {} self.srvUsers = list() Players.load() self.lastPing = datetime.datetime.now() self.checkPings()