Viewing file: main.py (24.75 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- coding: utf-8 -*- """ """
__author__ = 'Robert Ancell <bob27@users.sourceforge.net>' __license__ = 'GNU General Public License Version 2' __copyright__ = 'Copyright 2005-2006 Robert Ancell'
__all__ = ['Application']
import sys import os import errno from gettext import gettext as _ import traceback import time
import config import ui import gtkui import game import player import chess.board import chess.lan import chess.pgn import ai import network import display import history
from defaults import * class PlayerTimer(ui.TimerFeedback): """ """
def __init__(self, game, colour, duration): self.game = game self.colour = colour self.duration = duration self.controller = game.application.ui.controller.addTimer(self, duration) def onTick(self, t): """Called by ui.TimerFeedback""" if self.colour is chess.board.WHITE: self.game.view.controller.setWhiteTime(self.duration, t) else: self.game.view.controller.setBlackTime(self.duration, t)
def onExpired(self): """Called by ui.TimerFeedback""" if self.colour is chess.board.WHITE: self.game.getWhite().outOfTime() else: self.game.getBlack().outOfTime()
class ChessGame(game.ChessGame): """ """
# Mapping between piece names and promotion types __promotionMapping = {'queen': chess.board.QUEEN, 'knight': chess.board.KNIGHT, 'bishop': chess.board.BISHOP, 'rook': chess.board.ROOK}
def __init__(self, application, name): """Constructor for a chess game. 'application' is a reference to the glChess application. 'name' is the name of the game (string). """ self.application = application self.name = name self.__aiPlayers = [] # TEMP self.duration = 0 self.wT = None self.bT = None self.fileName = None self.inHistory = False self.needsSaving = False # Call parent constructor self.view = None game.ChessGame.__init__(self)
self.view = display.View(self) self.view.controller = application.ui.controller.setView(name, self.view) self.view.updateRotation(animate = False)
self.view.showMoveHints(config.get('show_move_hints') is True) self.view.showBoardNumbering(config.get('show_numbering') is True) self.view.showSmooth(config.get('show_3d_smooth') is True) # Watch for piece moves with a player self.__movePlayer = player.MovePlayer(self) self.addSpectator(self.__movePlayer) self.date = time.strftime('%Y.%m.%d')
def addAIPlayer(self, name, profile, level): """Create an AI player. 'name' is the name of the player to create (string). 'profile' is the the AI profile to use (ai.Profile). 'level' is the difficulty level to use (string). Returns an AI player to use (game.ChessPlayer). """ # Translators: Description of an AI player used in log window. %(name)s is replaced with # the name of the AI player. %(game)s is replaced with the name of the game the AI player # is in. description = _("'%(name)s' in '%(game)s'") % {'name': name, 'game': self.name} p = player.AIPlayer(self.application, name, profile, level, description) self.__aiPlayers.append(p) self.application.watchAIPlayer(p) return p
def addHumanPlayer(self, name): """Create a human player. 'name' is the name of the player to create. Returns a human player to use (game.ChessPlayer). """ return player.HumanPlayer(self, name)
def setTimer(self, duration, whiteTime, blackTime): self.duration = duration if duration <= 0: return
self.view.controller.setWhiteTime(duration, whiteTime) self.view.controller.setBlackTime(duration, blackTime)
self.wT = PlayerTimer(self, chess.board.WHITE, whiteTime) self.bT = PlayerTimer(self, chess.board.BLACK, blackTime) self.wT.controller.run() self.setTimers(self.wT, self.bT) def getHumanPlayer(self): """Get the human player. Returns the human player (HumanPlayer) or None if no human players. If both players are human the current player is returned. """ c = self.getCurrentPlayer() if isinstance(c, player.HumanPlayer): return c white = self.getWhite() black = self.getWhite() if c is white: opponent = black else: opponent = white if isinstance(opponent, player.HumanPlayer): return opponent return None
def currentPlayerIsHuman(self): """Test if the player to move is human.
Returns True if the current player is human and able to move. """ p = self.getCurrentPlayer() return isinstance(p, player.HumanPlayer) and p.isReadyToMove()
def squareIsFriendly(self, coord): """ """ owner = self.getSquareOwner(coord) if owner is None: return False return owner is self.getCurrentPlayer() def moveHuman(self, start, end): """ """ assert(self.currentPlayerIsHuman()) p = self.getCurrentPlayer() if p is self.getWhite(): colour = chess.board.WHITE else: colour = chess.board.BLACK
# Use configured promotion type try: promotionType = self.__promotionMapping[config.get('promotion_type')] except KeyError: promotionType = chess.board.QUEEN
# Make the move move = chess.lan.encode(colour, start, end, promotionType = promotionType) p.move(move)
# Notify move self.view.controller.setAttention(False)
def toPGN(self, pgnGame): """Write the properties of this game into a PGN game. 'pgnGame' is the game to write into (pgn.PGNGame). All the tags should be unset. """ white = self.getWhite() black = self.getBlack() pgnGame.setTag(chess.pgn.TAG_EVENT, self.name) pgnGame.setTag(chess.pgn.TAG_WHITE, white.getName()) pgnGame.setTag(chess.pgn.TAG_BLACK, black.getName()) pgnGame.setTag(chess.pgn.TAG_DATE, self.date)
results = {game.RESULT_WHITE_WINS: chess.pgn.RESULT_WHITE_WIN, game.RESULT_BLACK_WINS: chess.pgn.RESULT_BLACK_WIN, game.RESULT_DRAW: chess.pgn.RESULT_DRAW} try: value = results[self.result] except KeyError: pass else: pgnGame.setTag(chess.pgn.TAG_RESULT, value)
rules = {game.RULE_ABANDONMENT: chess.pgn.TERMINATE_ABANDONED, game.RULE_TIMEOUT: chess.pgn.TERMINATE_TIME_FORFEIT, game.RULE_DEATH: chess.pgn.TERMINATE_DEATH} try: value = rules[self.rule] except KeyError: pass else: pgnGame.setTag(chess.pgn.TAG_TERMINATION, value)
if self.duration > 0: pgnGame.setTag(chess.pgn.TAG_TIME_CONTROL, str(self.duration)) if self.wT is not None: pgnGame.setTag('WhiteTime', str(self.wT.controller.getRemaining())) if self.bT is not None: pgnGame.setTag('BlackTime', str(self.bT.controller.getRemaining()))
# FIXME: AI levels if isinstance(white, ai.Player): (profile, level) = white.getProfile() pgnGame.setTag('WhiteAI', profile) pgnGame.setTag('WhiteLevel', level) if isinstance(black, ai.Player): (profile, level) = black.getProfile() pgnGame.setTag('BlackAI', profile) pgnGame.setTag('BlackLevel', level)
moves = self.getMoves() for m in moves: pgnMove = chess.pgn.PGNMove() pgnMove.move = m.sanMove pgnMove.nag = m.nag pgnMove.comment = m.comment pgnGame.addMove(pgnMove)
def animate(self, timeStep): """ """ return self.view.scene.controller.animate(timeStep) def endMove(self, p): game.ChessGame.endMove(self, p) self.view.updateRotation()
def remove(self): """Remove this game""" # Remove AI player windows for p in self.__aiPlayers: p.window.close() self.application.unwatchAIPlayer(p)
# Stop the game self.abort() # Remove the game from the UI self.application._removeGame(self) self.view.controller.close()
def setNeedsSaving(self, needsSaving): """ """ # Autosaved games don't need saving if self.inHistory: needsSaving = False
if self.needsSaving == needsSaving: return self.needsSaving = needsSaving self.view.controller.setNeedsSaving(needsSaving)
def save(self): """Save this game""" pgnGame = chess.pgn.PGNGame() self.toPGN(pgnGame) if self.inHistory: # Don't bother if haven't made any significant moves if len(self.getMoves()) < 2: return self.application.history.save(pgnGame, self.fileName) else: try: f = file(self.fileName, 'w') lines = pgnGame.getLines() for line in lines: f.write(line + '\n') f.write('\n') f.close() except IOError, e: return e.strerror
self.setNeedsSaving(False) self.application.logger.addLine('Saved game %s to %s' % (repr(self.name), self.fileName)) class UI(ui.UIFeedback): """ """
def __init__(self, application): """ """ self.controller = gtkui.GtkUI(self) self.application = application self.splashscreen = display.Splashscreen(self) self.splashscreen.controller = self.controller.setView('', self.splashscreen, isPlayable = False)
self.ggzConfig = network.GGZConfig() dialog = network.GGZNetworkDialog(self) self.networkDialog = dialog.controller = self.controller.addNetworkDialog(dialog) for server in self.ggzConfig.getServers(): dialog.controller.addProfile(server, server.name)
def onAnimate(self, timeStep): """Called by ui.UIFeedback""" return self.application.animate(timeStep) def onReadFileDescriptor(self, fd): """Called by ui.UIFeedback""" try: handler = self.application.ioHandlers[fd] except KeyError: return False else: result = handler.read() if result is False: self.application.ioHandlers.pop(fd) return result
def onWriteFileDescriptor(self, fd): """Called by ui.UIFeedback""" try: handler = self.application.ioHandlers[fd] except KeyError: return False else: result = handler.write() if result is False: self.application.ioHandlers.pop(fd) return result
def onGameStart(self, game): """Called by ui.UIFeedback""" if game.white.type == '': w = None else: w = (game.white.type, game.white.level) if game.black.type == '': b = None else: b = (game.black.type, game.black.level) g = self.application.addLocalGame(game.name, game.white.name, w, game.black.name, b) if g is None: return g.inHistory = True self.application.logger.addLine('Starting game %s between %s (%s) and %s (%s). (%i moves)' % \ (game.name, game.white.name, str(game.white.type), game.black.name, str(game.black.type), len(game.moves)))
g.setTimer(game.duration, game.duration, game.duration) g.start(game.moves) def loadGame(self, path, configure): """Called by ui.UI""" try: p = chess.pgn.PGN(path, 1) except chess.pgn.Error, e: return str(e) except IOError, e: return e.strerror # Use the first game self.application.addPGNGame(p[0], path, configure) return None
def onNewNetworkGame(self): """Called by ui.UIFeedback""" self.networkDialog.setVisible(True) def onQuit(self): """Called by ui.UIFeedback""" self.application.quit()
class Application: """ """
def __init__(self): """Constructor for glChess application""" self.__aiProfiles = {} # The AI types self.ioHandlers = {} # Objects with IO keyed by file descriptor self.networkConnections = {} # Network connections keyed by file descriptor self.__game = None # The game in progress self.ui = UI(self) self.history = history.GameHistory() # Translators: Name of the log that displays application events title = _('Application Log') self.logger = self.ui.controller.addLogWindow(title, '', '')
def addAIProfile(self, profile): """Add a new AI profile into glChess. 'profile' is the profile to add (ai.Profile). """ name = profile.name assert(self.__aiProfiles.has_key(name) is False) self.__aiProfiles[name] = profile self.ui.controller.addAIEngine(name)
def getAIProfile(self, name): """Get an installed AI profile. 'name' is the name of the profile to get (string). Return the profile (ai.Profile) or None if it does not exist. """ try: return self.__aiProfiles[name] except KeyError: return None def watchAIPlayer(self, p): """ """ fd = p.fileno() if fd is not None: self.ioHandlers[fd] = p self.ui.controller.watchFileDescriptor(fd)
def unwatchAIPlayer(self, p): """ """ fd = p.fileno() if fd is not None: self.ioHandlers.pop(fd) def addGame(self, name): if self.__game is not None: # Save the current game to the history if self.__game.inHistory: response = ui.SAVE_YES elif self.__game.needsSaving: response = self.ui.controller.requestSave('Save current game?') else: response = ui.SAVE_NO
if response is ui.SAVE_YES: self.__game.save() elif response is ui.SAVE_ABORT: return None self.__game = ChessGame(self, name) return self.__game
def addLocalGame(self, name, whiteName, whiteType, blackName, blackType): """Add a chess game into glChess. 'name' is the name of the game (string). 'whiteName' is the name of the white player (string). 'whiteType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players. 'blackName' is the name of the black player (string). 'blackType' is a 2-tuple containing the AI profile name and difficulty level (str, str) or None for human players. Returns the game object. Use game.start() to start the game. """ # FIXME: Replace arguments with player objects # Create the game g = self.addGame(name) if g is None: return None
msg = '' if whiteType is None: p = g.addHumanPlayer(whiteName) else: (profile, level) = whiteType p = g.addAIPlayer(whiteName, self.__aiProfiles[profile], level) g.setWhite(p)
if blackType is None: p = g.addHumanPlayer(blackName) else: (profile, level) = blackType p = g.addAIPlayer(blackName, self.__aiProfiles[profile], level) g.setBlack(p)
return g def addPGNGame(self, pgnGame, path, configure = False): """Add a PGN game. 'pgnGame' is the game to add (chess.pgn.PGNGame). 'path' is the path this game was loaded from (string or None). Returns the game object. Use game.start() to start the game. """ gameProperties = ui.Game()
gameProperties.path = path gameProperties.name = pgnGame.getTag(chess.pgn.TAG_EVENT) gameProperties.white.name = pgnGame.getTag(chess.pgn.TAG_WHITE) gameProperties.black.name = pgnGame.getTag(chess.pgn.TAG_BLACK) moves = [] for pgnMove in pgnGame.getMoves(): moves.append(pgnMove.move) gameProperties.moves = moves
missingEngines = False gameProperties.white.type = pgnGame.getTag('WhiteAI', '') if gameProperties.white.type == '': w = None else: if not self.__aiProfiles.has_key(gameProperties.white.type): missingEngines = True gameProperties.white.level = pgnGame.getTag('WhiteLevel') if gameProperties.white.level is None: gameProperties.white.level = 'normal' w = (gameProperties.white.type, gameProperties.white.level)
gameProperties.black.type = pgnGame.getTag('BlackAI', '') if gameProperties.black.type == '': b = None else: if not self.__aiProfiles.has_key(gameProperties.black.type): missingEngines = True gameProperties.black.level = pgnGame.getTag('BlackLevel') if gameProperties.black.level is None: gameProperties.black.level = 'normal' b = (gameProperties.black.type, gameProperties.black.level)
# If some of the properties were invalid display the new game dialog if missingEngines or configure: self.ui.controller.reportGameLoaded(gameProperties) return
newGame = self.addLocalGame(gameProperties.name, gameProperties.white.name, w, gameProperties.black.name, b) if newGame is None: return None newGame.date = pgnGame.getTag(chess.pgn.TAG_DATE) newGame.fileName = path if gameProperties.moves: newGame.start(gameProperties.moves) else: newGame.start() # Comment on each move # FIXME: This should be done through a method so the UI can update better moves = newGame.getMoves() pgnMoves = pgnGame.getMoves() for i in xrange(len(moves)): moves[i].comment = pgnMoves[i].comment moves[i].nag = pgnMoves[i].nag
# Get the last player to resign if the file specifies it result = pgnGame.getTag(chess.pgn.TAG_RESULT, None) loser = None if result == chess.pgn.RESULT_DRAW: newGame.claimDraw() if newGame.result != game.RESULT_DRAW: newGame.endGame(game.RESULT_DRAW, game.RULE_AGREEMENT) elif result == chess.pgn.RESULT_INCOMPLETE: if newGame.result != game.RESULT_IN_PROGRESS: print "WARNING: PGN file specifies game in progress, glChess does't..." elif result == chess.pgn.RESULT_WHITE_WIN: loser = newGame.getBlack() elif result == chess.pgn.RESULT_BLACK_WIN: loser = newGame.getWhite() if newGame.result == game.RESULT_IN_PROGRESS and loser is not None: loser.resign()
duration = 0 value = pgnGame.getTag(chess.pgn.TAG_TIME_CONTROL) if value is not None: timers = value.split(':') try: duration = int(timers[0]) except ValueError: print 'Unknown time control: ' + value value = pgnGame.getTag('WhiteTime', duration * 1000) try: whiteTime = int(value) except ValueError: whiteTime = duration value = pgnGame.getTag('BlackTime', duration * 1000) try: blackTime = int(value) except ValuError: blackTime = duration newGame.setTimer(duration, whiteTime / 1000, blackTime / 1000) # No need to save freshly loaded game newGame.setNeedsSaving(False)
return newGame
def start(self): """Run glChess. This method does not return. """ self.logger.addLine('This is glChess %s' % VERSION) # Load AI profiles profiles = ai.loadProfiles()
for p in profiles: p.detect() if p.path is not None: self.logger.addLine('Detected AI: %s at %s' % (p.name, p.path)) self.addAIProfile(p)
nArgs = len(sys.argv)
# Load existing games if nArgs == 1: self.__autoload() # Load requested game elif nArgs == 2: path = sys.argv[1] import time self.logger.addLine('loading...') s = time.time() try: p = chess.pgn.PGN(path, 1) except chess.pgn.Error, e: # TODO: Pop-up dialog self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e))) except IOError, e: self.logger.addLine('Unable to open PGN file %s: %s' % (path, str(e))) else: # Use the first game if len(p) > 0: g = self.addPGNGame(p[0], path) self.logger.addLine('loaded in %f seconds' % (time.time() - s))
else: # FIXME: Should be in a dialog # Translators: Text displayed on the command-line if an unknown argument is passed print _('Usage: %s [game]') % sys.argv[0] sys.exit(0)
# Start default game if no game present if self.__game is None and len(self.__aiProfiles) > 0: # Try the previously used AIs first, then any other AI on easy mode possibleAIs = [(config.get('new_game_dialog/black/type'), config.get('new_game_dialog/black/difficulty')), (config.get('new_game_dialog/white/type'), config.get('new_game_dialog/white/difficulty'))] for p in profiles: possibleAIs.append((p.name, 'easy')) for (name, difficulty) in possibleAIs: if self.__aiProfiles.has_key(name): black = (name, difficulty) break # Translators: Name of a human versus AI game. The %s is replaced with the name of the AI player gameName = _('Human versus %s') % black[0] # Translators: Name of white player in a default game whiteName = _('White') # Translators: Name of black player in a default game blackName = _('Black') g = self.addLocalGame(gameName, whiteName, None, blackName, black) g.inHistory = True g.start()
# Start UI (does not return) try: self.ui.controller.run() except: # FIXME: Isn't this done by bug-buddy? print _("""glChess has crashed. Please report this bug to http://bugzilla.gnome.org Debug output:""") print traceback.format_exc() self.quit() sys.exit(1) def animate(self, timeStep): """ """ return self.__game.animate(timeStep)
def quit(self): """Quit glChess""" if self.__game is not None: if self.__game.inHistory: response = ui.SAVE_YES elif self.__game.needsSaving: response = self.ui.controller.requestSave(_('Save game before closing?')) else: response = ui.SAVE_NO
if response == ui.SAVE_YES: self.__game.save() elif response == ui.SAVE_ABORT: return
# Abort current game (will delete AIs etc) self.__game.abort()
# Notify the UI self.ui.controller.close()
# Exit the application sys.exit() # Private methods
def __autoload(self): """Restore games from the autosave file""" (pgnGame, fileName, inHistory) = self.history.getUnfinishedGame() if pgnGame is not None: g = self.addPGNGame(pgnGame, fileName) if g is not None: g.inHistory = inHistory
if __name__ == '__main__': app = Application() app.start()
|