removed tabs, added game history
svn path=/trunk/; revision=6842
This commit is contained in:
parent
687bbccb2e
commit
f18a39ba49
12 changed files with 882 additions and 979 deletions
6
TODO
6
TODO
|
@ -4,10 +4,7 @@
|
|||
Have pop-up menu on tab headers (i.e. game menu).
|
||||
Advanced save options (i.e. PGN fields).
|
||||
Supply application/x-chess-pgn icons
|
||||
Add numbering to the board
|
||||
LAN game support
|
||||
Gnome Gaming Zone support
|
||||
Crash dialog so users can easily make bug reports
|
||||
Make configuration dialog (with AI config too)
|
||||
Don't autosave games that have finished (move into a history file)
|
||||
Reflect chess pieces in board
|
||||
|
@ -15,10 +12,11 @@ Chess piece shadows
|
|||
Anti-aliasing
|
||||
Drag and drop 2D pieces
|
||||
Drag and drop application/x-chess-pgn files
|
||||
Add icons for human/network/ai players. Use Red Robot for the AI!
|
||||
Use Red Robot for the AI player icon!
|
||||
Share textures and display lists between games
|
||||
Allow resigning
|
||||
Unifiy save format with KDE's Knights interface
|
||||
Allow multiple chess windows using dbus to make an app singleton - this means the history database can be synchronised
|
||||
|
||||
1.2+ Features
|
||||
--------------
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
<accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image60">
|
||||
<widget class="GtkImage" id="image64">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-network</property>
|
||||
<property name="icon_size">1</property>
|
||||
|
@ -103,29 +103,6 @@
|
|||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkImageMenuItem" id="menu_end_game_item">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="label" translatable="yes">_End Game</property>
|
||||
<property name="use_underline">True</property>
|
||||
<signal name="activate" handler="_on_end_game_button_clicked" last_modification_time="Sun, 14 May 2006 14:07:07 GMT"/>
|
||||
<accelerator key="W" modifiers="GDK_CONTROL_MASK" signal="activate"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image61">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-close</property>
|
||||
<property name="icon_size">1</property>
|
||||
<property name="xalign">0.5</property>
|
||||
<property name="yalign">0.5</property>
|
||||
<property name="xpad">0</property>
|
||||
<property name="ypad">0</property>
|
||||
</widget>
|
||||
</child>
|
||||
</widget>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
|
||||
<property name="visible">True</property>
|
||||
|
@ -163,7 +140,7 @@
|
|||
<accelerator key="F11" modifiers="0" signal="activate"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image62">
|
||||
<widget class="GtkImage" id="image65">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-fullscreen</property>
|
||||
<property name="icon_size">1</property>
|
||||
|
@ -184,7 +161,7 @@
|
|||
<accelerator key="F11" modifiers="0" signal="activate"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image63">
|
||||
<widget class="GtkImage" id="image66">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-leave-fullscreen</property>
|
||||
<property name="icon_size">1</property>
|
||||
|
@ -271,7 +248,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_White Side</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="active">False</property>
|
||||
<signal name="activate" handler="_on_board_view_changed" last_modification_time="Sun, 18 Mar 2007 02:42:01 GMT"/>
|
||||
</widget>
|
||||
</child>
|
||||
|
@ -281,7 +258,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Black Side</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="active">False</property>
|
||||
<property name="group">menu_side_white</property>
|
||||
<signal name="activate" handler="_on_board_view_changed" last_modification_time="Sun, 18 Mar 2007 02:42:01 GMT"/>
|
||||
</widget>
|
||||
|
@ -303,7 +280,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Current Player</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="active">False</property>
|
||||
<property name="group">menu_side_white</property>
|
||||
<signal name="activate" handler="_on_board_view_changed" last_modification_time="Sun, 18 Mar 2007 02:42:01 GMT"/>
|
||||
</widget>
|
||||
|
@ -338,7 +315,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Long Algebraic</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="active">False</property>
|
||||
<property name="group">menu_movef_human</property>
|
||||
<signal name="activate" handler="_on_menu_movef_lan_activate" last_modification_time="Sat, 10 Feb 2007 03:06:49 GMT"/>
|
||||
</widget>
|
||||
|
@ -349,7 +326,7 @@
|
|||
<property name="visible">True</property>
|
||||
<property name="label" translatable="yes">_Standard Algebraic</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="active">True</property>
|
||||
<property name="active">False</property>
|
||||
<property name="group">menu_movef_human</property>
|
||||
<signal name="activate" handler="_on_menu_movef_san_activate" last_modification_time="Sat, 10 Feb 2007 03:06:49 GMT"/>
|
||||
</widget>
|
||||
|
@ -448,7 +425,7 @@
|
|||
<signal name="activate" handler="_on_help_clicked" last_modification_time="Fri, 11 Aug 2006 12:43:28 GMT"/>
|
||||
|
||||
<child internal-child="image">
|
||||
<widget class="GtkImage" id="image64">
|
||||
<widget class="GtkImage" id="image67">
|
||||
<property name="visible">True</property>
|
||||
<property name="stock">gtk-help</property>
|
||||
<property name="icon_size">1</property>
|
||||
|
@ -596,34 +573,16 @@
|
|||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkToolButton" id="surrender_button">
|
||||
<widget class="GtkToolButton" id="resign_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="label" translatable="yes">Surrender</property>
|
||||
<property name="label" translatable="yes">Resign</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-dialog-warning</property>
|
||||
<property name="visible_horizontal">True</property>
|
||||
<property name="visible_vertical">True</property>
|
||||
<property name="is_important">False</property>
|
||||
<signal name="clicked" handler="_on_surrender_button_clicked" last_modification_time="Sun, 15 Jan 2006 18:21:18 GMT"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="homogeneous">True</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<widget class="GtkToolButton" id="end_game_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="sensitive">False</property>
|
||||
<property name="tooltip" translatable="yes">End the current game</property>
|
||||
<property name="label" translatable="yes">End Game</property>
|
||||
<property name="use_underline">True</property>
|
||||
<property name="stock_id">gtk-close</property>
|
||||
<property name="visible_horizontal">True</property>
|
||||
<property name="visible_vertical">True</property>
|
||||
<property name="is_important">False</property>
|
||||
<signal name="clicked" handler="_on_end_game_button_clicked" last_modification_time="Sun, 15 Jan 2006 18:21:10 GMT"/>
|
||||
<signal name="clicked" handler="_on_resign_button_clicked" last_modification_time="Sun, 15 Jan 2006 18:21:18 GMT"/>
|
||||
</widget>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
|
@ -6,9 +6,12 @@ glchess_PYTHON = \
|
|||
cecp.py \
|
||||
config.py \
|
||||
defaults.py \
|
||||
display.py \
|
||||
game.py \
|
||||
glchess.py \
|
||||
history.py \
|
||||
__init__.py \
|
||||
main.py \
|
||||
network.py \
|
||||
player.py \
|
||||
uci.py
|
||||
|
|
|
@ -28,6 +28,44 @@ f3 Bc8 34. Kf2 Bf5 35. Ra7 g6 36. Ra6+ Kc5 37. Ke1 Nf4 38. g3 Nxh3 39. Kd2 Kb5
|
|||
40. Rd6 Kc5 41. Ra6 Nf2 42. g4 Bd3 43. Re6 1/2-1/2
|
||||
"""
|
||||
|
||||
RESULT_INCOMPLETE = '*'
|
||||
RESULT_WHITE_WIN = '1-0'
|
||||
RESULT_BLACK_WIN = '0-1'
|
||||
RESULT_DRAW = '1/2-1/2'
|
||||
|
||||
"""The required tags in a PGN file (the seven tag roster, STR)"""
|
||||
TAG_EVENT = 'Event'
|
||||
TAG_SITE = 'Site'
|
||||
TAG_DATE = 'Date'
|
||||
TAG_ROUND = 'Round'
|
||||
TAG_WHITE = 'White'
|
||||
TAG_BLACK = 'Black'
|
||||
TAG_RESULT = 'Result'
|
||||
|
||||
"""Optional tags"""
|
||||
TAG_TIME = 'Time'
|
||||
TAG_FEN = 'FEN'
|
||||
TAG_WHITE_TYPE = 'WhiteType'
|
||||
TAG_WHITE_ELO = 'WhiteElo'
|
||||
TAG_BLACK_TYPE = 'BlackType'
|
||||
TAG_BLACK_ELO = 'BlackElo'
|
||||
TAG_TIME_CONTROL = 'TimeControl'
|
||||
TAG_TERMINATION = 'Termination'
|
||||
|
||||
# Values for the WhiteType and BlackType tag
|
||||
PLAYER_HUMAN = 'human'
|
||||
PLAYER_AI = 'program'
|
||||
|
||||
# Values for the Termination tag
|
||||
TERMINATE_ABANDONED = 'abandoned'
|
||||
TERMINATE_ADJUDICATION = 'adjudication'
|
||||
TERMINATE_DEATH = 'death'
|
||||
TERMINATE_EMERGENCY = 'emergency'
|
||||
TERMINATE_NORMAL = 'normal'
|
||||
TERMINATE_RULES_INFRACTION = 'rules infraction'
|
||||
TERMINATE_TIME_FORFEIT = 'time forfeit'
|
||||
TERMINATE_UNTERMINATED = 'unterminated'
|
||||
|
||||
# Comments are bounded by ';' to '\n' or '{' to '}'
|
||||
# Lines starting with '%' are ignored and are used as an extension mechanism
|
||||
# Strings are bounded by '"' and '"' and quotes inside the strings are escaped with '\"'
|
||||
|
@ -82,11 +120,6 @@ class PGNToken:
|
|||
SYMBOL_CONTINUATION_CHARACTERS = SYMBOL_START_CHARACTERS + '_+#=:-' + '/' # Not in spec but required from game draw and imcomplete
|
||||
NAG_CONTINUATION_CHARACTERS = '0123456789'
|
||||
|
||||
GAME_TERMINATE_INCOMPLETE = '*'
|
||||
GAME_TERMINATE_WHITE_WIN = '1-0'
|
||||
GAME_TERMINATE_BLACK_WIN = '0-1'
|
||||
GAME_TERMINATE_DRAW = '1/2-1/2'
|
||||
|
||||
data = None
|
||||
|
||||
lineNumber = -1
|
||||
|
@ -288,10 +321,10 @@ class PGNGameParser:
|
|||
|
||||
elif token.type is PGNToken.SYMBOL:
|
||||
# See if this is a game terminate
|
||||
if token.data == PGNToken.GAME_TERMINATE_INCOMPLETE or \
|
||||
token.data == PGNToken.GAME_TERMINATE_WHITE_WIN or \
|
||||
token.data == PGNToken.GAME_TERMINATE_BLACK_WIN or \
|
||||
token.data == PGNToken.GAME_TERMINATE_DRAW:
|
||||
if token.data == RESULT_INCOMPLETE or \
|
||||
token.data == RESULT_WHITE_WIN or \
|
||||
token.data == RESULT_BLACK_WIN or \
|
||||
token.data == RESULT_DRAW:
|
||||
game = self.__game
|
||||
self.__game = None
|
||||
|
||||
|
@ -418,42 +451,9 @@ class PGNMove:
|
|||
class PGNGame:
|
||||
"""
|
||||
"""
|
||||
|
||||
"""The required tags in a PGN file (the seven tag roster, STR)"""
|
||||
PGN_TAG_EVENT = 'Event'
|
||||
PGN_TAG_SITE = 'Site'
|
||||
PGN_TAG_DATE = 'Date'
|
||||
PGN_TAG_ROUND = 'Round'
|
||||
PGN_TAG_WHITE = 'White'
|
||||
PGN_TAG_BLACK = 'Black'
|
||||
PGN_TAG_RESULT = 'Result'
|
||||
|
||||
"""Optional tags"""
|
||||
PGN_TAG_TIME = 'Time'
|
||||
PGN_TAG_FEN = 'FEN'
|
||||
PGN_TAG_WHITE_TYPE = 'WhiteType'
|
||||
PGN_TAG_WHITE_ELO = 'WhiteElo'
|
||||
PGN_TAG_BLACK_TYPE = 'BlackType'
|
||||
PGN_TAG_BLACK_ELO = 'BlackElo'
|
||||
PGN_TAG_TIME_CONTROL = 'TimeControl'
|
||||
PGN_TAG_TERMINATION = 'Termination'
|
||||
|
||||
# Values for the WhiteType and BlackType tag
|
||||
PGN_TYPE_HUMAN = 'human'
|
||||
PGN_TYPE_AI = 'program'
|
||||
|
||||
# Values for the Termination tag
|
||||
PGN_TERMINATE_ABANDONED = 'abandoned'
|
||||
PGN_TERMINATE_ADJUDICATION = 'adjudication'
|
||||
PGN_TERMINATE_DEATH = 'death'
|
||||
PGN_TERMINATE_EMERGENCY = 'emergency'
|
||||
PGN_TERMINATE_NORMAL = 'normal'
|
||||
PGN_TERMINATE_RULES_INFRACTION = 'rules infraction'
|
||||
PGN_TERMINATE_TIME_FORFEIT = 'time forfeit'
|
||||
PGN_TERMINATE_UNTERMINATED = 'unterminated'
|
||||
|
||||
|
||||
# The seven tag roster in the required order (REFERENCE)
|
||||
__strTags = [PGN_TAG_EVENT, PGN_TAG_SITE, PGN_TAG_DATE, PGN_TAG_ROUND, PGN_TAG_WHITE, PGN_TAG_BLACK, PGN_TAG_RESULT]
|
||||
__strTags = [TAG_EVENT, TAG_SITE, TAG_DATE, TAG_ROUND, TAG_WHITE, TAG_BLACK, TAG_RESULT]
|
||||
|
||||
# The tags in this game
|
||||
__tagsByName = None
|
||||
|
@ -463,13 +463,13 @@ class PGNGame:
|
|||
def __init__(self):
|
||||
# Set the default STR tags
|
||||
self.__tagsByName = {}
|
||||
self.setTag(self.PGN_TAG_EVENT, '?')
|
||||
self.setTag(self.PGN_TAG_SITE, '?')
|
||||
self.setTag(self.PGN_TAG_DATE, '????.??.??')
|
||||
self.setTag(self.PGN_TAG_ROUND, '?')
|
||||
self.setTag(self.PGN_TAG_WHITE, '?')
|
||||
self.setTag(self.PGN_TAG_BLACK, '?')
|
||||
self.setTag(self.PGN_TAG_RESULT, '*')
|
||||
self.setTag(TAG_EVENT, '?')
|
||||
self.setTag(TAG_SITE, '?')
|
||||
self.setTag(TAG_DATE, '????.??.??')
|
||||
self.setTag(TAG_ROUND, '?')
|
||||
self.setTag(TAG_WHITE, '?')
|
||||
self.setTag(TAG_BLACK, '?')
|
||||
self.setTag(TAG_RESULT, '*')
|
||||
|
||||
self.__moves = []
|
||||
|
||||
|
@ -501,7 +501,7 @@ class PGNGame:
|
|||
tokens.append('{' + m.comment + '}')
|
||||
|
||||
# Add result token to the end
|
||||
tokens.append(self.__tagsByName[self.PGN_TAG_RESULT])
|
||||
tokens.append(self.__tagsByName[TAG_RESULT])
|
||||
|
||||
# Print moves keeping the line length to less than 256 characters (PGN requirement)
|
||||
line = ''
|
||||
|
|
|
@ -2,19 +2,20 @@
|
|||
|
||||
import os, os.path
|
||||
import errno
|
||||
import gettext
|
||||
import gettext
|
||||
|
||||
APP_DATA_DIR = os.path.join('@prefix@', 'share')
|
||||
ICON_DIR = os.path.join(APP_DATA_DIR, 'pixmaps')
|
||||
TEXTURE_DIR = os.path.join(ICON_DIR, 'glchess')
|
||||
GLADE_DIR = os.path.join(APP_DATA_DIR, 'glchess')
|
||||
BASE_DIR = os.path.join(APP_DATA_DIR, 'glchess')
|
||||
LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
|
||||
DATA_DIR = os.path.expanduser('~/.gnome2/glchess/')
|
||||
LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
CONFIG_FILE = os.path.join(DATA_DIR, 'config.xml')
|
||||
AUTOSAVE_FILE = os.path.join(DATA_DIR, 'autosave.pgn')
|
||||
LOCAL_AI_CONFIG = os.path.join(DATA_DIR, 'ai.xml')
|
||||
APP_DATA_DIR = os.path.join('@prefix@', 'share')
|
||||
ICON_DIR = os.path.join(APP_DATA_DIR, 'pixmaps')
|
||||
TEXTURE_DIR = os.path.join(ICON_DIR, 'glchess')
|
||||
GLADE_DIR = os.path.join(APP_DATA_DIR, 'glchess')
|
||||
BASE_DIR = os.path.join(APP_DATA_DIR, 'glchess')
|
||||
LOCALEDIR = os.path.join(APP_DATA_DIR, 'locale')
|
||||
DATA_DIR = os.path.expanduser('~/.gnome2/glchess/')
|
||||
LOG_DIR = os.path.join(DATA_DIR, 'logs')
|
||||
CONFIG_FILE = os.path.join(DATA_DIR, 'config.xml')
|
||||
HISTORY_DIR = os.path.join(DATA_DIR, 'history')
|
||||
UNFINISHED_FILE = os.path.join(HISTORY_DIR, 'unfinished')
|
||||
LOCAL_AI_CONFIG = os.path.join(DATA_DIR, 'ai.xml')
|
||||
|
||||
DOMAIN = 'gnome-games'
|
||||
gettext.bindtextdomain(DOMAIN, LOCALEDIR)
|
||||
|
|
438
src/lib/display.py
Normal file
438
src/lib/display.py
Normal file
|
@ -0,0 +1,438 @@
|
|||
__author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
|
||||
__license__ = 'GNU General Public License Version 2'
|
||||
__copyright__ = 'Copyright 2005-2006 Robert Ancell'
|
||||
|
||||
import config
|
||||
import player
|
||||
import scene
|
||||
import scene.cairo
|
||||
import scene.opengl
|
||||
import scene.human
|
||||
import ui
|
||||
import chess.board
|
||||
|
||||
class CairoPiece(scene.ChessPieceFeedback):
|
||||
"""
|
||||
"""
|
||||
|
||||
model = None
|
||||
|
||||
location = ''
|
||||
|
||||
def __init__(self, scene, piece):
|
||||
self.scene = scene
|
||||
self.piece = piece
|
||||
|
||||
def onDeleted(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
self.scene.pieces.pop(self.piece)
|
||||
|
||||
def onMoved(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
# If waiting for this piece then end players turn
|
||||
if self.scene.waitingPiece is self:
|
||||
self.scene.game.view.pieceMoved()
|
||||
|
||||
class SceneCairo(scene.SceneFeedback, scene.human.SceneHumanInput):
|
||||
"""
|
||||
"""
|
||||
controller = None
|
||||
|
||||
# The game this scene is rendering
|
||||
game = None
|
||||
|
||||
# TODO
|
||||
pieces = None
|
||||
|
||||
# FIXME: Abort when scenes changed
|
||||
waitingPiece = None
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""
|
||||
"""
|
||||
self.controller = scene.cairo.Scene(self)
|
||||
self.game = chessGame
|
||||
self.pieces = {}
|
||||
scene.human.SceneHumanInput.__init__(self)
|
||||
|
||||
def getPieces(self):
|
||||
return self.pieces.values()
|
||||
|
||||
def movePiece(self, piece, location, delete, animate):
|
||||
"""
|
||||
"""
|
||||
# Get the model for this piece creating one if it doesn't exist
|
||||
try:
|
||||
p = self.pieces[piece]
|
||||
except KeyError:
|
||||
# Make the new model
|
||||
pieceName = {chess.board.PAWN: 'pawn', chess.board.ROOK: 'rook', chess.board.KNIGHT: 'knight',
|
||||
chess.board.BISHOP: 'bishop', chess.board.QUEEN: 'queen', chess.board.KING: 'king'}[piece.getType()]
|
||||
chessSet = {chess.board.WHITE: 'white', chess.board.BLACK: 'black'}[piece.getColour()]
|
||||
p = CairoPiece(self, piece)
|
||||
p.model = self.controller.addChessPiece(chessSet, pieceName, location, p)
|
||||
self.pieces[piece] = p
|
||||
|
||||
# Move the model
|
||||
p.location = location
|
||||
p.model.move(location, delete, animate)
|
||||
|
||||
return p
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.cairo.Scene"""
|
||||
if self.game.view.controller is not None:
|
||||
self.game.view.controller.render()
|
||||
|
||||
def startAnimation(self):
|
||||
"""Called by scene.cairo.Scene"""
|
||||
self.game.application.ui.controller.startAnimation()
|
||||
|
||||
def getSquare(self, x, y):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.controller.getSquare(x, y)
|
||||
|
||||
def setBoardHighlight(self, coords):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.controller.setBoardHighlight(coords)
|
||||
|
||||
def playerIsHuman(self):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.game.currentPlayerIsHuman()
|
||||
|
||||
def squareIsFriendly(self, coord):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.squareIsFriendly(coord)
|
||||
|
||||
def canMove(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.getCurrentPlayer().canMove(start, end) # FIXME: Promotion type
|
||||
|
||||
def moveHuman(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.game.moveHuman(start, end)
|
||||
|
||||
class OpenGLPiece(scene.ChessPieceFeedback):
|
||||
"""
|
||||
"""
|
||||
|
||||
model = None
|
||||
|
||||
location = ''
|
||||
|
||||
def __init__(self, scene, piece):
|
||||
self.scene = scene
|
||||
self.piece = piece
|
||||
|
||||
def onDeleted(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
self.scene.pieces.pop(self.piece)
|
||||
|
||||
def onMoved(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
# If waiting for this piece then end players turn
|
||||
if self.scene.waitingPiece is self:
|
||||
self.scene.waitingPiece = None
|
||||
self.scene.game.getCurrentPlayer().endMove()
|
||||
|
||||
class SceneOpenGL(scene.SceneFeedback, scene.human.SceneHumanInput):
|
||||
"""
|
||||
"""
|
||||
# The game this scene is rendering
|
||||
game = None
|
||||
|
||||
# TODO
|
||||
pieces = None
|
||||
|
||||
# FIXME: Abort when scenes changed
|
||||
waitingPiece = None
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""Constructor for a glChess scene.
|
||||
|
||||
'chessGame' is the game the scene is rendering (game.ChessGame).
|
||||
"""
|
||||
self.game = chessGame
|
||||
self.pieces = {}
|
||||
|
||||
# Call parent constructors
|
||||
scene.human.SceneHumanInput.__init__(self)
|
||||
self.controller = scene.opengl.Scene(self)
|
||||
|
||||
def getPieces(self):
|
||||
return self.pieces.values()
|
||||
|
||||
def movePiece(self, piece, location, delete, animate):
|
||||
"""
|
||||
"""
|
||||
# Get the model for this piece creating one if it doesn't exist
|
||||
try:
|
||||
p = self.pieces[piece]
|
||||
except KeyError:
|
||||
# Make the new model
|
||||
pieceName = {chess.board.PAWN: 'pawn', chess.board.ROOK: 'rook', chess.board.KNIGHT: 'knight',
|
||||
chess.board.BISHOP: 'bishop', chess.board.QUEEN: 'queen', chess.board.KING: 'king'}[piece.getType()]
|
||||
chessSet = {chess.board.WHITE: 'white', chess.board.BLACK: 'black'}[piece.getColour()]
|
||||
p = OpenGLPiece(self, piece)
|
||||
p.model = self.controller.addChessPiece(chessSet, pieceName, location, p)
|
||||
self.pieces[piece] = p
|
||||
|
||||
# Move the model
|
||||
p.location = location
|
||||
p.model.move(location, delete)
|
||||
|
||||
return p
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.opengl.Scene"""
|
||||
if self.game.view.controller is not None:
|
||||
self.game.view.controller.render()
|
||||
|
||||
def startAnimation(self):
|
||||
"""Called by scene.opengl.Scene"""
|
||||
self.game.application.ui.controller.startAnimation()
|
||||
|
||||
def getSquare(self, x, y):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.controller.getSquare(x, y)
|
||||
|
||||
def setBoardHighlight(self, coords):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.controller.setBoardHighlight(coords)
|
||||
|
||||
def playerIsHuman(self):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.game.currentPlayerIsHuman()
|
||||
|
||||
def squareIsFriendly(self, coord):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.squareIsFriendly(coord)
|
||||
|
||||
def canMove(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.getCurrentPlayer().canMove(start, end) # FIXME: Promotion type
|
||||
|
||||
def moveHuman(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.game.moveHuman(start, end)
|
||||
|
||||
class Splashscreen(ui.ViewFeedback):
|
||||
"""
|
||||
"""
|
||||
application = None
|
||||
scene = None
|
||||
|
||||
def __init__(self, application):
|
||||
"""Constructor.
|
||||
|
||||
'application' is ???
|
||||
"""
|
||||
self.application = application
|
||||
self.cairoScene = scene.cairo.Scene(self)
|
||||
self.scene = scene.opengl.Scene(self)
|
||||
|
||||
def updateRotation(self, animate = True):
|
||||
boardView = config.get('board_view')
|
||||
if boardView == 'black':
|
||||
rotation = 180.0
|
||||
else:
|
||||
rotation = 0.0
|
||||
self.cairoScene.controller.setBoardRotation(rotation, animate)
|
||||
self.scene.controller.setBoardRotation(rotation, animate)
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.SceneFeedback"""
|
||||
self.controller.render()
|
||||
|
||||
def showBoardNumbering(self, showNumbering):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.showBoardNumbering(showNumbering)
|
||||
self.cairoScene.showBoardNumbering(showNumbering)
|
||||
|
||||
def renderGL(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.render()
|
||||
|
||||
def renderCairoStatic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.cairoScene.renderStatic(context)
|
||||
|
||||
def renderCairoDynamic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.cairoScene.renderDynamic(context)
|
||||
|
||||
def reshape(self, width, height):
|
||||
"""Called by ui.View"""
|
||||
self.scene.reshape(width, height)
|
||||
self.cairoScene.reshape(width, height)
|
||||
|
||||
class View(ui.ViewFeedback):
|
||||
"""
|
||||
"""
|
||||
# The game this view is rendering
|
||||
game = None
|
||||
|
||||
# The scene for this view
|
||||
scene = None
|
||||
|
||||
# The controller object for this view
|
||||
controller = None
|
||||
|
||||
moveNumber = -1
|
||||
|
||||
def __init__(self, game):
|
||||
"""Constructor.
|
||||
|
||||
'game' is ???
|
||||
"""
|
||||
self.game = game
|
||||
|
||||
# Use a Cairo scene by default - it will be replaced by an OpenGL one if that is the requested view
|
||||
# I wanted to remove this but then scene is None and there are a number of issues...
|
||||
# This should be cleaned up
|
||||
self.scene = SceneCairo(game)
|
||||
config.watch('board_view', self.__onBoardViewChanged)
|
||||
|
||||
def __onBoardViewChanged(self, key, value):
|
||||
self.updateRotation()
|
||||
|
||||
def updateRotation(self, animate = True):
|
||||
"""
|
||||
"""
|
||||
# Get the angle to face
|
||||
p = self.game.getCurrentPlayer()
|
||||
if p is self.game.getWhite():
|
||||
rotation = 0.0
|
||||
elif p is self.game.getBlack():
|
||||
rotation = 180.0
|
||||
|
||||
# Decide if we should face this angle
|
||||
boardView = config.get('board_view')
|
||||
if boardView == 'white':
|
||||
rotation = 0.0
|
||||
elif boardView == 'black':
|
||||
rotation = 180.0
|
||||
elif boardView == 'human':
|
||||
if not isinstance(p, player.HumanPlayer):
|
||||
return
|
||||
|
||||
self.scene.controller.setBoardRotation(rotation, animate)
|
||||
|
||||
def pieceMoved(self):
|
||||
"""
|
||||
"""
|
||||
if self.scene.waitingPiece is None:
|
||||
return
|
||||
self.scene.waitingPiece = None
|
||||
self.game.getCurrentPlayer().endMove()
|
||||
|
||||
def showMoveHints(self, showHints):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.showMoveHints(showHints)
|
||||
|
||||
def showBoardNumbering(self, showNumbering):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.controller.showBoardNumbering(showNumbering)
|
||||
|
||||
def updateScene(self, sceneClass):
|
||||
"""
|
||||
"""
|
||||
if isinstance(self.scene, sceneClass):
|
||||
return
|
||||
self.pieceMoved()
|
||||
self.scene = sceneClass(self.game)
|
||||
self.reshape(self.width, self.height)
|
||||
self.setMoveNumber(self.moveNumber)
|
||||
self.updateRotation(animate = False)
|
||||
|
||||
def renderGL(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneOpenGL)
|
||||
self.scene.controller.render()
|
||||
|
||||
def renderCairoStatic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneCairo)
|
||||
return self.scene.controller.renderStatic(context)
|
||||
|
||||
def renderCairoDynamic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneCairo)
|
||||
self.scene.controller.renderDynamic(context)
|
||||
|
||||
def reshape(self, width, height):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.scene.controller.reshape(width, height)
|
||||
|
||||
def select(self, x, y):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.select(x, y)
|
||||
|
||||
def deselect(self, x, y):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.deselect(x, y)
|
||||
|
||||
def setMoveNumber(self, moveNumber):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.moveNumber = moveNumber
|
||||
|
||||
# Lock the scene if not tracking the game
|
||||
self.scene.enableHumanInput(moveNumber == -1)
|
||||
|
||||
# Get the state of this scene
|
||||
piecesByLocation = self.game.getAlivePieces(moveNumber)
|
||||
|
||||
# Remove any models not present
|
||||
requiredPieces = piecesByLocation.values()
|
||||
for piece in self.scene.getPieces():
|
||||
try:
|
||||
requiredPieces.index(piece.piece)
|
||||
except ValueError:
|
||||
piece.model.move(piece.location, True)
|
||||
|
||||
# Move the models in the scene
|
||||
for (location, piece) in piecesByLocation.iteritems():
|
||||
self.scene.movePiece(piece, location, False, True)
|
||||
|
||||
# Can't wait for animation if not looking at the latest move
|
||||
if moveNumber != -1:
|
||||
self.pieceMoved()
|
||||
|
||||
def save(self, fileName = None):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
if fileName is None:
|
||||
fileName = self.game.fileName
|
||||
assert(fileName is not None)
|
||||
|
||||
try:
|
||||
f = file(fileName, 'w')
|
||||
except IOError, e:
|
||||
return e.args[1]
|
||||
|
||||
self.game.application.logger.addLine('Saving game %s to %s' % (repr(self.game.name), fileName))
|
||||
|
||||
pgnGame = chess.pgn.PGNGame()
|
||||
self.game.toPGN(pgnGame)
|
||||
|
||||
lines = pgnGame.getLines()
|
||||
for line in lines:
|
||||
f.write(line + '\n')
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
||||
self.game.fileName = fileName
|
||||
self.game.needsSaving = False
|
||||
|
||||
def getFileName(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.game.fileName
|
||||
|
||||
def needsSaving(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.game.needsSaving and (self.game.fileName is not None)
|
|
@ -54,7 +54,7 @@ class GtkViewArea(gtk.DrawingArea):
|
|||
|
||||
# Make openGL drawable
|
||||
if hasattr(gtk, 'gtkgl'):
|
||||
gtk.gtkgl.widget_set_gl_capability(self, self.view.ui.notebook.glConfig)# FIXME:, share_list=glContext)
|
||||
gtk.gtkgl.widget_set_gl_capability(self, self.view.ui.glConfig)# FIXME:, share_list=glContext)
|
||||
|
||||
# Connect signals
|
||||
self.connect('realize', self.__init)
|
||||
|
@ -369,13 +369,13 @@ class GtkView(glchess.ui.ViewController):
|
|||
def setWhiteTime(self, total, current):
|
||||
"""Extends glchess.ui.ViewController"""
|
||||
self.whiteTime = (total, current)
|
||||
if self.ui.notebook.getView() is self:
|
||||
if self.ui.view is self:
|
||||
self.ui.setTimers(self.whiteTime, self.blackTime)
|
||||
|
||||
def setBlackTime(self, total, current):
|
||||
"""Extends glchess.ui.ViewController"""
|
||||
self.blackTime = (total, current)
|
||||
if self.ui.notebook.getView() is self:
|
||||
if self.ui.view is self:
|
||||
self.ui.setTimers(self.whiteTime, self.blackTime)
|
||||
|
||||
def setAttention(self, requiresAttention):
|
||||
|
|
|
@ -51,109 +51,6 @@ gtk.window_set_default_icon_name(ICON_NAME)
|
|||
def loadGladeFile(name, root = None):
|
||||
return gtk.glade.XML(os.path.join(GLADE_DIR, name), root, domain = DOMAIN)
|
||||
|
||||
class GtkGameNotebook(gtk.Notebook):
|
||||
"""
|
||||
"""
|
||||
|
||||
glConfig = None
|
||||
|
||||
defaultView = None
|
||||
views = None
|
||||
viewsByWidget = None
|
||||
|
||||
def __init__(self, ui):
|
||||
"""
|
||||
"""
|
||||
self.ui = ui
|
||||
self.views = []
|
||||
self.viewsByWidget = {}
|
||||
|
||||
gtk.Notebook.__init__(self)
|
||||
self.set_show_border(False)
|
||||
|
||||
# Make the tabs scrollable so the area is not resized
|
||||
self.set_scrollable(True)
|
||||
|
||||
# Configure openGL
|
||||
try:
|
||||
gtk.gdkgl
|
||||
except AttributeError:
|
||||
self.glConfig = None
|
||||
else:
|
||||
display_mode = (gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE)
|
||||
try:
|
||||
self.glConfig = gtk.gdkgl.Config(mode = display_mode)
|
||||
except gtk.gdkgl.NoMatches:
|
||||
display_mode &= ~gtk.gdkgl.MODE_DOUBLE
|
||||
self.glConfig = gtk.gdkgl.Config(mode = display_mode)
|
||||
|
||||
self.set_show_tabs(False)
|
||||
|
||||
def setDefault(self, feedback):
|
||||
"""
|
||||
"""
|
||||
assert(self.defaultView is None)
|
||||
self.defaultView = chessview.GtkView(self.ui, '', feedback)
|
||||
page = self.append_page(self.defaultView.widget)
|
||||
self.set_current_page(page)
|
||||
|
||||
self.__updateTabVisibleState()
|
||||
|
||||
return self.defaultView
|
||||
|
||||
def addView(self, title, feedback):
|
||||
"""
|
||||
"""
|
||||
moveFormat = glchess.config.get('move_format')
|
||||
showComments = glchess.config.get('show_comments')
|
||||
view = chessview.GtkView(self.ui, title, feedback, moveFormat = moveFormat, showComments = showComments)
|
||||
self.views.append(view)
|
||||
self.viewsByWidget[view.widget] = view
|
||||
page = self.append_page(view.widget)
|
||||
self.set_tab_label_text(view.widget, title)
|
||||
self.set_current_page(page)
|
||||
|
||||
self.__updateTabVisibleState()
|
||||
|
||||
return view
|
||||
|
||||
def getView(self):
|
||||
"""Get the currently selected view.
|
||||
|
||||
Returns the view (GtkView) on this page or None if no view here.
|
||||
"""
|
||||
# If splashscreen present then there is no view
|
||||
if len(self.viewsByWidget) == 0:
|
||||
return None
|
||||
|
||||
num = self.get_current_page()
|
||||
if num < 0:
|
||||
return None
|
||||
widget = self.get_nth_page(num)
|
||||
return self.viewsByWidget[widget]
|
||||
|
||||
def removeView(self, view):
|
||||
"""Remove a view from the notebook.
|
||||
|
||||
'view' is the view to remove.
|
||||
"""
|
||||
self.remove_page(self.page_num(view.widget))
|
||||
self.views.remove(view)
|
||||
self.viewsByWidget.pop(view.widget)
|
||||
self.__updateTabVisibleState()
|
||||
|
||||
def __updateTabVisibleState(self):
|
||||
"""
|
||||
"""
|
||||
# Only show tabs if there is more than one game
|
||||
self.set_show_tabs(len(self.viewsByWidget) > 1)
|
||||
|
||||
# Show/hide the default view
|
||||
if len(self.viewsByWidget) == 0:
|
||||
self.defaultView.widget.show()
|
||||
else:
|
||||
self.defaultView.widget.hide()
|
||||
|
||||
class GLibTimer(glchess.ui.Timer):
|
||||
"""
|
||||
"""
|
||||
|
@ -271,9 +168,6 @@ class GtkUI(glchess.ui.UI):
|
|||
__lastTime = None
|
||||
__animationTimer = None
|
||||
|
||||
# The notebook containing games
|
||||
notebook = None
|
||||
|
||||
# The Gtk+ list model of the available player types
|
||||
__playerModel = None
|
||||
|
||||
|
@ -303,6 +197,8 @@ class GtkUI(glchess.ui.UI):
|
|||
height = None
|
||||
isFullscreen = False
|
||||
isMaximised = False
|
||||
|
||||
view = None
|
||||
|
||||
def __init__(self, feedback):
|
||||
"""Constructor for a GTK+ glChess GUI"""
|
||||
|
@ -314,6 +210,8 @@ class GtkUI(glchess.ui.UI):
|
|||
self.__networkGames = {}
|
||||
self.__saveGameDialogs = {}
|
||||
self.__joinGameDialogs = []
|
||||
|
||||
self._initOpenGL()
|
||||
|
||||
# Set the message panel to the tooltip style
|
||||
# (copied from Gedit)
|
||||
|
@ -346,12 +244,6 @@ class GtkUI(glchess.ui.UI):
|
|||
self.__boardViewTypeByRadio[widget] = name
|
||||
self.__boardViewRadioByType[name] = widget
|
||||
|
||||
# Make a notebook for the games
|
||||
self.notebook = GtkGameNotebook(self)
|
||||
self.notebook.connect_after('switch-page', self._on_view_changed)
|
||||
self.__getWidget('game_viewport').add(self.notebook)
|
||||
self.notebook.show_all()
|
||||
|
||||
# Create the model for the player types
|
||||
self.__playerModel = gtk.ListStore(str, str, str)
|
||||
iconTheme = gtk.icon_theme_get_default()
|
||||
|
@ -371,14 +263,10 @@ class GtkUI(glchess.ui.UI):
|
|||
combo.pack_start(cell, False)
|
||||
combo.add_attribute(cell, 'text', 2)
|
||||
|
||||
self.defaultViewController = self.notebook.setDefault(None)
|
||||
|
||||
# Disble help support
|
||||
if haveGnomeSupport:
|
||||
self._gui.get_widget('menu_help').show()
|
||||
|
||||
self.defaultViewController.viewWidget.setRenderGL(self.__renderGL)
|
||||
|
||||
self.saveDialog = dialogs.SaveDialog(self)
|
||||
|
||||
# Watch for config changes
|
||||
|
@ -389,6 +277,20 @@ class GtkUI(glchess.ui.UI):
|
|||
'move_format', 'promotion_type', 'board_view',
|
||||
'enable_networking']:
|
||||
glchess.config.watch(key, self.__applyConfig)
|
||||
|
||||
def _initOpenGL(self):
|
||||
# Configure openGL
|
||||
try:
|
||||
gtk.gdkgl
|
||||
except AttributeError:
|
||||
self.glConfig = None
|
||||
else:
|
||||
display_mode = (gtk.gdkgl.MODE_RGB | gtk.gdkgl.MODE_DEPTH | gtk.gdkgl.MODE_DOUBLE)
|
||||
try:
|
||||
self.glConfig = gtk.gdkgl.Config(mode = display_mode)
|
||||
except gtk.gdkgl.NoMatches:
|
||||
display_mode &= ~gtk.gdkgl.MODE_DOUBLE
|
||||
self.glConfig = gtk.gdkgl.Config(mode = display_mode)
|
||||
|
||||
# Public methods
|
||||
|
||||
|
@ -452,17 +354,19 @@ class GtkUI(glchess.ui.UI):
|
|||
# Get the human to play against this AI
|
||||
if self.__defaultBlackAI is None:
|
||||
self.__defaultBlackAI = name
|
||||
|
||||
def setDefaultView(self, feedback):
|
||||
|
||||
def setView(self, title, feedback):
|
||||
"""Extends ui.UI"""
|
||||
self.defaultViewController.feedback = feedback
|
||||
return self.defaultViewController
|
||||
|
||||
def addView(self, title, feedback):
|
||||
"""Extends ui.UI"""
|
||||
view = self.notebook.addView(title, feedback)
|
||||
view.viewWidget.setRenderGL(self.__renderGL)
|
||||
return view
|
||||
moveFormat = glchess.config.get('move_format')
|
||||
showComments = glchess.config.get('show_comments')
|
||||
self.view = chessview.GtkView(self, title, feedback, moveFormat = moveFormat, showComments = showComments)
|
||||
self.view.viewWidget.setRenderGL(self.__renderGL)
|
||||
viewport = self.__getWidget('game_viewport')
|
||||
child = viewport.get_child()
|
||||
if child is not None:
|
||||
viewport.remove(child)
|
||||
viewport.add(self.view.widget)
|
||||
return self.view
|
||||
|
||||
def addLogWindow(self, title, executable, description):
|
||||
"""
|
||||
|
@ -590,14 +494,6 @@ class GtkUI(glchess.ui.UI):
|
|||
return error
|
||||
self.__saveGameDialogs.pop(view)
|
||||
|
||||
def _removeView(self, view):
|
||||
"""Remove a view from the UI.
|
||||
|
||||
'view' is the view to remove.
|
||||
"""
|
||||
self.notebook.removeView(view)
|
||||
self._updateViewButtons()
|
||||
|
||||
# Private methods
|
||||
|
||||
def __resize(self):
|
||||
|
@ -673,33 +569,26 @@ class GtkUI(glchess.ui.UI):
|
|||
self.__renderGL = value
|
||||
menuItem = self.__getWidget('menu_view_3d')
|
||||
menuItem.set_active(self.__renderGL)
|
||||
self.notebook.defaultView.viewWidget.setRenderGL(self.__renderGL)
|
||||
for view in self.notebook.views:
|
||||
view.viewWidget.setRenderGL(self.__renderGL)
|
||||
self.view.viewWidget.setRenderGL(self.__renderGL)
|
||||
|
||||
elif name == 'show_comments':
|
||||
menuItem = self.__getWidget('menu_view_comment')
|
||||
menuItem.set_active(value)
|
||||
for view in self.notebook.views:
|
||||
view.setShowComments(value)
|
||||
self.view.setShowComments(value)
|
||||
|
||||
elif name == 'show_move_hints':
|
||||
menuItem = self.__getWidget('menu_view_move_hints')
|
||||
menuItem.set_active(value)
|
||||
for view in self.notebook.views:
|
||||
view.feedback.showMoveHints(value)
|
||||
self.view.feedback.showMoveHints(value)
|
||||
|
||||
elif name == 'show_numbering':
|
||||
menuItem = self.__getWidget('menu_view_numbering')
|
||||
menuItem.set_active(value)
|
||||
for view in self.notebook.views:
|
||||
view.feedback.showBoardNumbering(value)
|
||||
self.notebook.defaultView.feedback.showBoardNumbering(value)
|
||||
self.view.feedback.showBoardNumbering(value)
|
||||
|
||||
elif name == 'move_format':
|
||||
self._gui.get_widget('menu_movef_%s' % value).set_active(True)
|
||||
for view in self.notebook.views:
|
||||
view.setMoveFormat(value)
|
||||
self.view.setMoveFormat(value)
|
||||
|
||||
elif name == 'promotion_type':
|
||||
try:
|
||||
|
@ -852,9 +741,7 @@ class GtkUI(glchess.ui.UI):
|
|||
string += ' (latest)'
|
||||
moveNumber = -1
|
||||
|
||||
view = self.notebook.getView()
|
||||
if view is not None:
|
||||
view._setMoveNumber(moveNumber)
|
||||
self.view._setMoveNumber(moveNumber)
|
||||
|
||||
def _on_menu_movef_human_activate(self, widget):
|
||||
"""Gtk+ callback"""
|
||||
|
@ -928,36 +815,32 @@ class GtkUI(glchess.ui.UI):
|
|||
def _on_view_changed(self, widget, page, pageNum):
|
||||
"""Gtk+ callback"""
|
||||
# Set the window title to the name of the game
|
||||
view = self.notebook.getView()
|
||||
title = _('Chess')
|
||||
if view is not None:
|
||||
title += " - %s" % view.title
|
||||
if self.view is not None:
|
||||
title += " - %s" % self.view.title
|
||||
self._gui.get_widget('glchess_app').set_title(title)
|
||||
|
||||
# Set toolbar/menu buttons to state for this game
|
||||
self._updateViewButtons()
|
||||
|
||||
# Update timers
|
||||
if view is not None:
|
||||
self.setTimers(view.whiteTime, view.blackTime)
|
||||
if self.view is not None:
|
||||
self.setTimers(self.view.whiteTime, self.view.blackTime)
|
||||
|
||||
def _updateViewButtons(self):
|
||||
"""
|
||||
"""
|
||||
view = self.notebook.getView()
|
||||
enableWidgets = (view is not None) and view.isActive
|
||||
self.__getWidget('end_game_button').set_sensitive(enableWidgets)
|
||||
enableWidgets = self.view is not None and self.view.isActive
|
||||
self.__getWidget('save_game_button').set_sensitive(enableWidgets)
|
||||
self.__getWidget('menu_save_item').set_sensitive(enableWidgets)
|
||||
self.__getWidget('menu_save_as_item').set_sensitive(enableWidgets)
|
||||
self.__getWidget('menu_end_game_item').set_sensitive(enableWidgets)
|
||||
|
||||
combo = self.__getWidget('history_combo')
|
||||
if view is None:
|
||||
if self.view is None:
|
||||
if combo.get_model() != None:
|
||||
combo.set_model(None)
|
||||
else:
|
||||
(model, selected) = view._getModel()
|
||||
(model, selected) = self.view._getModel()
|
||||
combo.set_model(model)
|
||||
if selected < 0:
|
||||
selected = len(model) + selected
|
||||
|
@ -978,25 +861,15 @@ class GtkUI(glchess.ui.UI):
|
|||
|
||||
def _on_save_game_button_clicked(self, widget):
|
||||
"""Gtk+ callback"""
|
||||
view = self.notebook.getView()
|
||||
|
||||
if view.feedback.getFileName() is not None:
|
||||
view.feedback.save()
|
||||
elif not self.__saveGameDialogs.has_key(view):
|
||||
self.__saveGameDialogs[view] = dialogs.GtkSaveGameDialog(self, view)
|
||||
if self.view.feedback.getFileName() is not None:
|
||||
self.view.feedback.save()
|
||||
elif not self.__saveGameDialogs.has_key(self.view):
|
||||
self.__saveGameDialogs[self.view] = dialogs.GtkSaveGameDialog(self, self.view)
|
||||
|
||||
def _on_save_as_game_button_clicked(self, widget):
|
||||
"""Gtk+ callback"""
|
||||
view = self.notebook.getView()
|
||||
if not self.__saveGameDialogs.has_key(view):
|
||||
self.__saveGameDialogs[view] = dialogs.GtkSaveGameDialog(self, view, view.feedback.getFileName())
|
||||
|
||||
def _on_end_game_button_clicked(self, widget):
|
||||
"""Gtk+ callback"""
|
||||
view = self.notebook.getView()
|
||||
assert(view is not None)
|
||||
if view.feedback is not None:
|
||||
view.feedback.close()
|
||||
if not self.__saveGameDialogs.has_key(self.view):
|
||||
self.__saveGameDialogs[self.view] = dialogs.GtkSaveGameDialog(self, self.view, self.view.feedback.getFileName())
|
||||
|
||||
def _on_help_clicked(self, widget):
|
||||
"""Gtk+ callback"""
|
||||
|
@ -1113,9 +986,8 @@ class GtkUI(glchess.ui.UI):
|
|||
def _quit(self):
|
||||
# Check if any views need saving
|
||||
viewsToSave = []
|
||||
for view in self.notebook.views:
|
||||
if view.feedback.needsSaving():
|
||||
viewsToSave.append(view)
|
||||
if self.view.feedback.needsSaving():
|
||||
viewsToSave.append(self.view)
|
||||
|
||||
if len(viewsToSave) == 0:
|
||||
self.feedback.onQuit()
|
||||
|
|
118
src/lib/history.py
Normal file
118
src/lib/history.py
Normal file
|
@ -0,0 +1,118 @@
|
|||
__author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
|
||||
__license__ = 'GNU General Public License Version 2'
|
||||
__copyright__ = 'Copyright 2005-2006 Robert Ancell'
|
||||
|
||||
import os
|
||||
import errno
|
||||
|
||||
import chess.pgn
|
||||
from defaults import *
|
||||
|
||||
class GameHistory:
|
||||
|
||||
def getUnfinishedGame(self):
|
||||
g = None
|
||||
try:
|
||||
f = file(UNFINISHED_FILE, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
lines.reverse()
|
||||
index = 0
|
||||
for line in lines:
|
||||
fname = line.strip()
|
||||
try:
|
||||
p = chess.pgn.PGN(fname, 1)
|
||||
except chess.pgn.Error, e:
|
||||
print e.description
|
||||
continue
|
||||
except IOError, e:
|
||||
print e.strerror
|
||||
continue
|
||||
else:
|
||||
g = p[0]
|
||||
break
|
||||
index += 1
|
||||
except IOError, e:
|
||||
if e.errno != errno.ENOENT:
|
||||
print 'Failed to read unfinished list'
|
||||
return None
|
||||
lines = []
|
||||
else:
|
||||
lines = lines[index:]
|
||||
|
||||
# Write the list back
|
||||
try:
|
||||
f = file(UNFINISHED_FILE, 'w')
|
||||
lines.reverse()
|
||||
f.writelines(lines)
|
||||
f.close()
|
||||
except IOError:
|
||||
print 'Failed to read unfinished list'
|
||||
|
||||
return g
|
||||
|
||||
def load(self, date):
|
||||
return
|
||||
|
||||
def _getFilename(self, game):
|
||||
date = game.getTag(chess.pgn.TAG_DATE)
|
||||
try:
|
||||
(year, month, day) = date.split('.')
|
||||
except ValueError:
|
||||
directory = HISTORY_DIR
|
||||
else:
|
||||
directory = os.path.join(HISTORY_DIR, year, month, day)
|
||||
|
||||
# Create the directory
|
||||
try:
|
||||
os.makedirs(directory)
|
||||
except OSError, e:
|
||||
if e.errno != errno.EEXIST:
|
||||
return None # FIXME
|
||||
|
||||
# Get a unique name for the file
|
||||
count = 0
|
||||
fname = os.path.join(directory, date)
|
||||
while os.path.exists(fname):
|
||||
count += 1
|
||||
fname = os.path.join(directory, '%s-%d' % (date, count))
|
||||
|
||||
return fname
|
||||
|
||||
def save(self, g):
|
||||
"""Save a game in the history.
|
||||
|
||||
'g' is the game to save
|
||||
"""
|
||||
fname = self._getFilename(g)
|
||||
|
||||
lines = g.getLines()
|
||||
try:
|
||||
f = file(fname, 'w')
|
||||
for line in lines:
|
||||
f.write(line + '\n')
|
||||
f.write('\n')
|
||||
f.close()
|
||||
except IOError, e:
|
||||
# FIXME: This should be in a dialog
|
||||
self.logger.addLine('Unable to autosave to %s: %s' % (fname, str(e)))
|
||||
|
||||
# Update unfinished list
|
||||
result = g.getTag(chess.pgn.TAG_RESULT)
|
||||
try:
|
||||
f = file(UNFINISHED_FILE, 'r')
|
||||
lines = f.readlines()
|
||||
f.close()
|
||||
|
||||
f = file(UNFINISHED_FILE, 'w')
|
||||
for line in lines:
|
||||
l = line.strip()
|
||||
if l == fname and result != chess.pgn.RESULT_INCOMPLETE:
|
||||
continue
|
||||
f.write(l + '\n')
|
||||
if result == chess.pgn.RESULT_INCOMPLETE:
|
||||
f.write(fname + '\n')
|
||||
f.close()
|
||||
except IOError:
|
||||
print 'Failed to update unfinished list'
|
742
src/lib/main.py
742
src/lib/main.py
|
@ -12,542 +12,23 @@ import os
|
|||
import errno
|
||||
from gettext import gettext as _
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import config
|
||||
import ui
|
||||
import gtkui
|
||||
import scene.cairo
|
||||
import scene.opengl
|
||||
import scene.human
|
||||
import game
|
||||
import player
|
||||
import chess.board
|
||||
import chess.lan
|
||||
import chess.pgn
|
||||
import ai
|
||||
import network
|
||||
import display
|
||||
import history
|
||||
|
||||
from defaults import *
|
||||
|
||||
import chess.pgn
|
||||
|
||||
class MovePlayer(game.ChessPlayer):
|
||||
"""This class provides a pseudo-player to watch for piece movements"""
|
||||
# The game to control
|
||||
__game = None
|
||||
|
||||
waitForMoves = False
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""Constructor for a move player.
|
||||
|
||||
'chessGame' is the game to make changes to (ChessGame).
|
||||
"""
|
||||
self.__game = chessGame
|
||||
game.ChessPlayer.__init__(self, '@move')
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onPieceMoved(self, piece, start, end, delete):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
if self.__game.view.moveNumber != -1:
|
||||
return
|
||||
p = self.__game.view.scene.movePiece(piece, end, delete, self.__game.isStarted())
|
||||
|
||||
# If a player move notify when animation completes
|
||||
if self.__game.isStarted() and self.__game.view.moveNumber == -1 and start is not None and start != end:
|
||||
self.__game.view.scene.waitingPiece = p
|
||||
|
||||
def onPlayerMoved(self, player, move):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
self.__game.needsSaving = True
|
||||
|
||||
# Update clocks
|
||||
if player is self.__game.getWhite():
|
||||
if self.__game.wT is not None:
|
||||
self.__game.wT.controller.pause()
|
||||
if self.__game.bT is not None:
|
||||
self.__game.bT.controller.run()
|
||||
else:
|
||||
if self.__game.bT is not None:
|
||||
self.__game.bT.controller.pause()
|
||||
if self.__game.wT is not None:
|
||||
self.__game.wT.controller.run()
|
||||
|
||||
# Complete move if not waiting for visual indication of move end
|
||||
if self.__game.view.moveNumber != -1:
|
||||
player.endMove()
|
||||
|
||||
self.__game.view.controller.addMove(move)
|
||||
|
||||
def onGameEnded(self, game):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
self.__game.view.controller.endGame(game)
|
||||
|
||||
class HumanPlayer(game.ChessPlayer):
|
||||
"""
|
||||
"""
|
||||
__game = None
|
||||
|
||||
def __init__(self, chessGame, name):
|
||||
"""Constructor.
|
||||
|
||||
'chessGame' is the game this player is in (game.ChessGame).
|
||||
'name' is the name of this player (string).
|
||||
"""
|
||||
game.ChessPlayer.__init__(self, name)
|
||||
self.__game = chessGame
|
||||
|
||||
def readyToMove(self):
|
||||
# FIXME: ???
|
||||
self.__game.view.controller.setAttention(True)
|
||||
|
||||
class AIPlayer(ai.Player):
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, application, name, profile, level, description):
|
||||
"""
|
||||
"""
|
||||
executable = profile.path
|
||||
for arg in profile.arguments[1:]:
|
||||
executable += ' ' + arg
|
||||
self.window = application.ui.controller.addLogWindow(profile.name, executable, description)
|
||||
ai.Player.__init__(self, name, profile, level)
|
||||
|
||||
def addText(self, text, style):
|
||||
"""Called by ai.Player"""
|
||||
self.window.addText(text, style)
|
||||
|
||||
class CairoPiece(scene.ChessPieceFeedback):
|
||||
"""
|
||||
"""
|
||||
|
||||
model = None
|
||||
|
||||
location = ''
|
||||
|
||||
def __init__(self, scene, piece):
|
||||
self.scene = scene
|
||||
self.piece = piece
|
||||
|
||||
def onDeleted(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
self.scene.pieces.pop(self.piece)
|
||||
|
||||
def onMoved(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
# If waiting for this piece then end players turn
|
||||
if self.scene.waitingPiece is self:
|
||||
self.scene.game.view.pieceMoved()
|
||||
|
||||
class SceneCairo(scene.SceneFeedback, scene.human.SceneHumanInput):
|
||||
"""
|
||||
"""
|
||||
controller = None
|
||||
|
||||
# The game this scene is rendering
|
||||
game = None
|
||||
|
||||
# TODO
|
||||
moveNumber = None
|
||||
pieces = None
|
||||
|
||||
# FIXME: Abort when scenes changed
|
||||
waitingPiece = None
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""
|
||||
"""
|
||||
self.controller = scene.cairo.Scene(self)
|
||||
self.game = chessGame
|
||||
self.pieces = {}
|
||||
scene.human.SceneHumanInput.__init__(self)
|
||||
|
||||
def getPieces(self):
|
||||
return self.pieces.values()
|
||||
|
||||
def movePiece(self, piece, location, delete, animate):
|
||||
"""
|
||||
"""
|
||||
# Get the model for this piece creating one if it doesn't exist
|
||||
try:
|
||||
p = self.pieces[piece]
|
||||
except KeyError:
|
||||
# Make the new model
|
||||
pieceName = {chess.board.PAWN: 'pawn', chess.board.ROOK: 'rook', chess.board.KNIGHT: 'knight',
|
||||
chess.board.BISHOP: 'bishop', chess.board.QUEEN: 'queen', chess.board.KING: 'king'}[piece.getType()]
|
||||
chessSet = {chess.board.WHITE: 'white', chess.board.BLACK: 'black'}[piece.getColour()]
|
||||
p = CairoPiece(self, piece)
|
||||
p.model = self.controller.addChessPiece(chessSet, pieceName, location, p)
|
||||
self.pieces[piece] = p
|
||||
|
||||
# Move the model
|
||||
p.location = location
|
||||
p.model.move(location, delete, animate)
|
||||
|
||||
return p
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.cairo.Scene"""
|
||||
if self.game.view.controller is not None:
|
||||
self.game.view.controller.render()
|
||||
|
||||
def startAnimation(self):
|
||||
"""Called by scene.cairo.Scene"""
|
||||
self.game.application.ui.controller.startAnimation()
|
||||
|
||||
def getSquare(self, x, y):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.controller.getSquare(x, y)
|
||||
|
||||
def setBoardHighlight(self, coords):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.controller.setBoardHighlight(coords)
|
||||
|
||||
def playerIsHuman(self):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.game.currentPlayerIsHuman()
|
||||
|
||||
def squareIsFriendly(self, coord):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.squareIsFriendly(coord)
|
||||
|
||||
def canMove(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.getCurrentPlayer().canMove(start, end) # FIXME: Promotion type
|
||||
|
||||
def moveHuman(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.game.moveHuman(start, end)
|
||||
|
||||
class OpenGLPiece(scene.ChessPieceFeedback):
|
||||
"""
|
||||
"""
|
||||
|
||||
model = None
|
||||
|
||||
location = ''
|
||||
|
||||
def __init__(self, scene, piece):
|
||||
self.scene = scene
|
||||
self.piece = piece
|
||||
|
||||
def onDeleted(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
self.scene.pieces.pop(self.piece)
|
||||
|
||||
def onMoved(self):
|
||||
"""Called by scene.ChessPieceFeedback"""
|
||||
# If waiting for this piece then end players turn
|
||||
if self.scene.waitingPiece is self:
|
||||
self.scene.waitingPiece = None
|
||||
self.scene.game.getCurrentPlayer().endMove()
|
||||
|
||||
class SceneOpenGL(scene.SceneFeedback, scene.human.SceneHumanInput):
|
||||
"""
|
||||
"""
|
||||
# The game this scene is rendering
|
||||
game = None
|
||||
|
||||
# TODO
|
||||
pieces = None
|
||||
|
||||
# FIXME: Abort when scenes changed
|
||||
waitingPiece = None
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""Constructor for a glChess scene.
|
||||
|
||||
'chessGame' is the game the scene is rendering (game.ChessGame).
|
||||
"""
|
||||
self.game = chessGame
|
||||
self.pieces = {}
|
||||
|
||||
# Call parent constructors
|
||||
scene.human.SceneHumanInput.__init__(self)
|
||||
self.controller = scene.opengl.Scene(self)
|
||||
|
||||
def getPieces(self):
|
||||
return self.pieces.values()
|
||||
|
||||
def movePiece(self, piece, location, delete, animate):
|
||||
"""
|
||||
"""
|
||||
# Get the model for this piece creating one if it doesn't exist
|
||||
try:
|
||||
p = self.pieces[piece]
|
||||
except KeyError:
|
||||
# Make the new model
|
||||
pieceName = {chess.board.PAWN: 'pawn', chess.board.ROOK: 'rook', chess.board.KNIGHT: 'knight',
|
||||
chess.board.BISHOP: 'bishop', chess.board.QUEEN: 'queen', chess.board.KING: 'king'}[piece.getType()]
|
||||
chessSet = {chess.board.WHITE: 'white', chess.board.BLACK: 'black'}[piece.getColour()]
|
||||
p = OpenGLPiece(self, piece)
|
||||
p.model = self.controller.addChessPiece(chessSet, pieceName, location, p)
|
||||
self.pieces[piece] = p
|
||||
|
||||
# Move the model
|
||||
p.location = location
|
||||
p.model.move(location, delete)
|
||||
|
||||
return p
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.opengl.Scene"""
|
||||
if self.game.view.controller is not None:
|
||||
self.game.view.controller.render()
|
||||
|
||||
def startAnimation(self):
|
||||
"""Called by scene.opengl.Scene"""
|
||||
self.game.application.ui.controller.startAnimation()
|
||||
|
||||
def getSquare(self, x, y):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.controller.getSquare(x, y)
|
||||
|
||||
def setBoardHighlight(self, coords):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.controller.setBoardHighlight(coords)
|
||||
|
||||
def playerIsHuman(self):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.game.currentPlayerIsHuman()
|
||||
|
||||
def squareIsFriendly(self, coord):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.squareIsFriendly(coord)
|
||||
|
||||
def canMove(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
return self.playerIsHuman() and self.game.getCurrentPlayer().canMove(start, end) # FIXME: Promotion type
|
||||
|
||||
def moveHuman(self, start, end):
|
||||
"""Called by scene.human.SceneHumanInput"""
|
||||
self.game.moveHuman(start, end)
|
||||
|
||||
class Splashscreen(ui.ViewFeedback):
|
||||
"""
|
||||
"""
|
||||
application = None
|
||||
scene = None
|
||||
|
||||
def __init__(self, application):
|
||||
"""Constructor.
|
||||
|
||||
'application' is ???
|
||||
"""
|
||||
self.application = application
|
||||
self.cairoScene = scene.cairo.Scene(self)
|
||||
self.scene = scene.opengl.Scene(self)
|
||||
|
||||
def updateRotation(self, animate = True):
|
||||
boardView = config.get('board_view')
|
||||
if boardView == 'black':
|
||||
rotation = 180.0
|
||||
else:
|
||||
rotation = 0.0
|
||||
self.cairoScene.controller.setBoardRotation(rotation, animate)
|
||||
self.scene.controller.setBoardRotation(rotation, animate)
|
||||
|
||||
def onRedraw(self):
|
||||
"""Called by scene.SceneFeedback"""
|
||||
self.controller.render()
|
||||
|
||||
def showBoardNumbering(self, showNumbering):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.showBoardNumbering(showNumbering)
|
||||
self.cairoScene.showBoardNumbering(showNumbering)
|
||||
|
||||
def renderGL(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.render()
|
||||
|
||||
def renderCairoStatic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.cairoScene.renderStatic(context)
|
||||
|
||||
def renderCairoDynamic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.cairoScene.renderDynamic(context)
|
||||
|
||||
def reshape(self, width, height):
|
||||
"""Called by ui.View"""
|
||||
self.scene.reshape(width, height)
|
||||
self.cairoScene.reshape(width, height)
|
||||
|
||||
class View(ui.ViewFeedback):
|
||||
"""
|
||||
"""
|
||||
# The game this view is rendering
|
||||
game = None
|
||||
|
||||
# The scene for this view
|
||||
scene = None
|
||||
|
||||
# The controller object for this view
|
||||
controller = None
|
||||
|
||||
moveNumber = None
|
||||
|
||||
def __init__(self, game):
|
||||
"""Constructor.
|
||||
|
||||
'game' is ???
|
||||
"""
|
||||
self.game = game
|
||||
|
||||
# Use a Cairo scene by default - it will be replaced by an OpenGL one if that is the requested view
|
||||
# I wanted to remove this but then scene is None and there are a number of issues...
|
||||
# This should be cleaned up
|
||||
self.scene = SceneCairo(game)
|
||||
config.watch('board_view', self.__onBoardViewChanged)
|
||||
|
||||
def __onBoardViewChanged(self, key, value):
|
||||
self.updateRotation()
|
||||
|
||||
def updateRotation(self, animate = True):
|
||||
"""
|
||||
"""
|
||||
# Get the angle to face
|
||||
player = self.game.getCurrentPlayer()
|
||||
if player is self.game.getWhite():
|
||||
rotation = 0.0
|
||||
elif player is self.game.getBlack():
|
||||
rotation = 180.0
|
||||
|
||||
# Decide if we should face this angle
|
||||
boardView = config.get('board_view')
|
||||
if boardView == 'white':
|
||||
rotation = 0.0
|
||||
elif boardView == 'black':
|
||||
rotation = 180.0
|
||||
elif boardView == 'human':
|
||||
if not isinstance(player, HumanPlayer):
|
||||
return
|
||||
|
||||
self.scene.controller.setBoardRotation(rotation, animate)
|
||||
|
||||
def pieceMoved(self):
|
||||
"""
|
||||
"""
|
||||
if self.scene.waitingPiece is None:
|
||||
return
|
||||
self.scene.waitingPiece = None
|
||||
self.game.getCurrentPlayer().endMove()
|
||||
|
||||
def showMoveHints(self, showHints):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.showMoveHints(showHints)
|
||||
|
||||
def showBoardNumbering(self, showNumbering):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.controller.showBoardNumbering(showNumbering)
|
||||
|
||||
def updateScene(self, sceneClass):
|
||||
"""
|
||||
"""
|
||||
if isinstance(self.scene, sceneClass):
|
||||
return
|
||||
self.pieceMoved()
|
||||
self.scene = sceneClass(self.game)
|
||||
self.reshape(self.width, self.height)
|
||||
self.setMoveNumber(self.moveNumber)
|
||||
self.updateRotation(animate = False)
|
||||
|
||||
def renderGL(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneOpenGL)
|
||||
self.scene.controller.render()
|
||||
|
||||
def renderCairoStatic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneCairo)
|
||||
return self.scene.controller.renderStatic(context)
|
||||
|
||||
def renderCairoDynamic(self, context):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.updateScene(SceneCairo)
|
||||
self.scene.controller.renderDynamic(context)
|
||||
|
||||
def reshape(self, width, height):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.scene.controller.reshape(width, height)
|
||||
|
||||
def select(self, x, y):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.select(x, y)
|
||||
|
||||
def deselect(self, x, y):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.scene.deselect(x, y)
|
||||
|
||||
def setMoveNumber(self, moveNumber):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
self.moveNumber = moveNumber
|
||||
|
||||
# Lock the scene if not tracking the game
|
||||
self.scene.enableHumanInput(moveNumber == -1)
|
||||
|
||||
# Get the state of this scene
|
||||
piecesByLocation = self.game.getAlivePieces(moveNumber)
|
||||
|
||||
# Remove any models not present
|
||||
requiredPieces = piecesByLocation.values()
|
||||
for piece in self.scene.getPieces():
|
||||
try:
|
||||
requiredPieces.index(piece.piece)
|
||||
except ValueError:
|
||||
piece.model.move(piece.location, True)
|
||||
|
||||
# Move the models in the scene
|
||||
for (location, piece) in piecesByLocation.iteritems():
|
||||
self.scene.movePiece(piece, location, False, True)
|
||||
|
||||
# Can't wait for animation if not looking at the latest move
|
||||
if moveNumber != -1:
|
||||
self.pieceMoved()
|
||||
|
||||
def save(self, fileName = None):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
if fileName is None:
|
||||
fileName = self.game.fileName
|
||||
assert(fileName is not None)
|
||||
|
||||
try:
|
||||
f = file(fileName, 'w')
|
||||
except IOError, e:
|
||||
return e.args[1]
|
||||
|
||||
self.game.application.logger.addLine('Saving game %s to %s' % (repr(self.game.name), fileName))
|
||||
|
||||
pgnGame = chess.pgn.PGNGame()
|
||||
self.game.toPGN(pgnGame)
|
||||
|
||||
lines = pgnGame.getLines()
|
||||
for line in lines:
|
||||
f.write(line + '\n')
|
||||
f.write('\n')
|
||||
f.close()
|
||||
|
||||
self.game.fileName = fileName
|
||||
self.game.needsSaving = False
|
||||
|
||||
def getFileName(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.game.fileName
|
||||
|
||||
def needsSaving(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
return self.game.needsSaving and (self.game.fileName is not None)
|
||||
|
||||
def close(self):
|
||||
"""Called by ui.ViewFeedback"""
|
||||
# The user requests the game to end, for now we just do it
|
||||
self.game.remove()
|
||||
|
||||
|
||||
class PlayerTimer(ui.TimerFeedback):
|
||||
"""
|
||||
"""
|
||||
|
@ -591,6 +72,9 @@ class ChessGame(game.ChessGame):
|
|||
# The file this is saved to
|
||||
fileName = None
|
||||
needsSaving = True
|
||||
|
||||
# Date this game started
|
||||
date = '????.??.??'
|
||||
|
||||
# Mapping between piece names and promotion types
|
||||
__promotionMapping = {'queen': chess.board.QUEEN, 'knight': chess.board.KNIGHT, 'bishop': chess.board.BISHOP, 'rook': chess.board.ROOK}
|
||||
|
@ -613,16 +97,18 @@ class ChessGame(game.ChessGame):
|
|||
# Call parent constructor
|
||||
game.ChessGame.__init__(self)
|
||||
|
||||
self.view = View(self)
|
||||
self.view.controller = application.ui.controller.addView(name, self.view)
|
||||
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)
|
||||
|
||||
# Watch for piece moves with a player
|
||||
self.__movePlayer = MovePlayer(self)
|
||||
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.
|
||||
|
@ -634,10 +120,10 @@ class ChessGame(game.ChessGame):
|
|||
Returns an AI player to use (game.ChessPlayer).
|
||||
"""
|
||||
description = _("'%(name)s' in '%(game)s'") % {'name': name, 'game': self.name}
|
||||
player = AIPlayer(self.application, name, profile, level, description)
|
||||
self.__aiPlayers.append(player)
|
||||
self.application.watchAIPlayer(player)
|
||||
return player
|
||||
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.
|
||||
|
@ -646,8 +132,7 @@ class ChessGame(game.ChessGame):
|
|||
|
||||
Returns a human player to use (game.ChessPlayer).
|
||||
"""
|
||||
player = HumanPlayer(self, name)
|
||||
return player
|
||||
return player.HumanPlayer(self, name)
|
||||
|
||||
def setTimer(self, duration, whiteTime, blackTime):
|
||||
self.duration = duration
|
||||
|
@ -668,8 +153,8 @@ class ChessGame(game.ChessGame):
|
|||
|
||||
Returns True if the current player is human and able to move.
|
||||
"""
|
||||
player = self.getCurrentPlayer()
|
||||
return isinstance(player, HumanPlayer) and player.isReadyToMove()
|
||||
p = self.getCurrentPlayer()
|
||||
return isinstance(p, player.HumanPlayer) and p.isReadyToMove()
|
||||
|
||||
def squareIsFriendly(self, coord):
|
||||
"""
|
||||
|
@ -683,8 +168,8 @@ class ChessGame(game.ChessGame):
|
|||
"""
|
||||
"""
|
||||
assert(self.currentPlayerIsHuman())
|
||||
player = self.getCurrentPlayer()
|
||||
if player is self.getWhite():
|
||||
p = self.getCurrentPlayer()
|
||||
if p is self.getWhite():
|
||||
colour = chess.board.WHITE
|
||||
else:
|
||||
colour = chess.board.BLACK
|
||||
|
@ -697,7 +182,7 @@ class ChessGame(game.ChessGame):
|
|||
|
||||
# Make the move
|
||||
move = chess.lan.encode(colour, start, end, promotionType = promotionType)
|
||||
player.move(move)
|
||||
p.move(move)
|
||||
|
||||
# Notify move
|
||||
self.view.controller.setAttention(False)
|
||||
|
@ -710,32 +195,33 @@ class ChessGame(game.ChessGame):
|
|||
white = self.getWhite()
|
||||
black = self.getBlack()
|
||||
|
||||
pgnGame.setTag(pgnGame.PGN_TAG_EVENT, self.name)
|
||||
pgnGame.setTag(pgnGame.PGN_TAG_WHITE, white.getName())
|
||||
pgnGame.setTag(pgnGame.PGN_TAG_BLACK, black.getName())
|
||||
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.PGNToken.GAME_TERMINATE_WHITE_WIN,
|
||||
game.RESULT_BLACK_WINS: chess.pgn.PGNToken.GAME_TERMINATE_BLACK_WIN,
|
||||
game.RESULT_DRAW: chess.pgn.PGNToken.GAME_TERMINATE_DRAW}
|
||||
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(pgnGame.PGN_TAG_RESULT, value)
|
||||
pgnGame.setTag(chess.pgn.TAG_RESULT, value)
|
||||
|
||||
rules = {game.RULE_RESIGN: pgnGame.PGN_TERMINATE_ABANDONED,
|
||||
game.RULE_TIMEOUT: pgnGame.PGN_TERMINATE_TIME_FORFEIT,
|
||||
game.RULE_DEATH: pgnGame.PGN_TERMINATE_DEATH}
|
||||
rules = {game.RULE_RESIGN: 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(pgnGame.PGN_TAG_TERMINATION, value)
|
||||
pgnGame.setTag(chess.pgn.TAG_TERMINATION, value)
|
||||
|
||||
if self.duration > 0:
|
||||
pgnGame.setTag(pgnGame.PGN_TAG_TIME_CONTROL, str(self.duration))
|
||||
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:
|
||||
|
@ -764,16 +250,16 @@ class ChessGame(game.ChessGame):
|
|||
"""
|
||||
return self.view.scene.controller.animate(timeStep)
|
||||
|
||||
def endMove(self, player):
|
||||
game.ChessGame.endMove(self, player)
|
||||
def endMove(self, p):
|
||||
game.ChessGame.endMove(self, p)
|
||||
self.view.updateRotation()
|
||||
|
||||
def remove(self):
|
||||
"""Remove this game"""
|
||||
# Remove AI player windows
|
||||
for player in self.__aiPlayers:
|
||||
player.window.close()
|
||||
self.application.unwatchAIPlayer(player)
|
||||
for p in self.__aiPlayers:
|
||||
p.window.close()
|
||||
self.application.unwatchAIPlayer(p)
|
||||
|
||||
# Stop the game
|
||||
self.abort()
|
||||
|
@ -797,8 +283,8 @@ class UI(ui.UIFeedback):
|
|||
self.controller = gtkui.GtkUI(self)
|
||||
self.application = application
|
||||
|
||||
self.splashscreen = Splashscreen(self)
|
||||
self.splashscreen.controller = self.controller.setDefaultView(self.splashscreen)
|
||||
self.splashscreen = display.Splashscreen(self)
|
||||
self.splashscreen.controller = self.controller.setView('SPLASHSCREEN', self.splashscreen) # FIXME
|
||||
|
||||
self.ggzConfig = network.GGZConfig()
|
||||
dialog = network.GGZNetworkDialog(self)
|
||||
|
@ -892,14 +378,13 @@ class Application:
|
|||
|
||||
# The network game detector
|
||||
__detector = None
|
||||
|
||||
# The games present
|
||||
__games = None
|
||||
|
||||
# The game in progress
|
||||
__game = None
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor for glChess application"""
|
||||
self.__aiProfiles = {}
|
||||
self.__games = []
|
||||
self.ioHandlers = {}
|
||||
self.networkConnections = {}
|
||||
|
||||
|
@ -907,6 +392,8 @@ class Application:
|
|||
|
||||
self.ui = UI(self)
|
||||
|
||||
self.history = history.GameHistory()
|
||||
|
||||
self.logger = self.ui.controller.addLogWindow('Application Log', '', '')
|
||||
|
||||
def addAIProfile(self, profile):
|
||||
|
@ -931,22 +418,27 @@ class Application:
|
|||
except KeyError:
|
||||
return None
|
||||
|
||||
def watchAIPlayer(self, player):
|
||||
def watchAIPlayer(self, p):
|
||||
"""
|
||||
"""
|
||||
self.ioHandlers[player.fileno()] = player
|
||||
self.ui.controller.watchFileDescriptor(player.fileno())
|
||||
self.ioHandlers[p.fileno()] = p
|
||||
self.ui.controller.watchFileDescriptor(p.fileno())
|
||||
|
||||
def unwatchAIPlayer(self, player):
|
||||
def unwatchAIPlayer(self, p):
|
||||
"""
|
||||
"""
|
||||
fd = player.fileno()
|
||||
fd = p.fileno()
|
||||
if fd is not None:
|
||||
self.ioHandlers.pop(fd)
|
||||
|
||||
def setGame(self, g):
|
||||
if self.__game is not None:
|
||||
self._save(self.__game)
|
||||
self.__game = g
|
||||
|
||||
def addNetworkGame(self, name):
|
||||
g = ChessGame(self, name)
|
||||
self.__games.append(g)
|
||||
self.setGame(g)
|
||||
return g
|
||||
|
||||
def addGame(self, name, whiteName, whiteType, blackName, blackType):
|
||||
|
@ -964,22 +456,22 @@ class Application:
|
|||
|
||||
# Create the game
|
||||
g = ChessGame(self, name)
|
||||
self.__games.append(g)
|
||||
self.setGame(g)
|
||||
|
||||
msg = ''
|
||||
if whiteType is None:
|
||||
player = g.addHumanPlayer(whiteName)
|
||||
p = g.addHumanPlayer(whiteName)
|
||||
else:
|
||||
(profile, level) = whiteType
|
||||
player = g.addAIPlayer(whiteName, self.__aiProfiles[profile], level)
|
||||
g.setWhite(player)
|
||||
p = g.addAIPlayer(whiteName, self.__aiProfiles[profile], level)
|
||||
g.setWhite(p)
|
||||
|
||||
if blackType is None:
|
||||
player = g.addHumanPlayer(blackName)
|
||||
p = g.addHumanPlayer(blackName)
|
||||
else:
|
||||
(profile, level) = blackType
|
||||
player = g.addAIPlayer(blackName, self.__aiProfiles[profile], level)
|
||||
g.setBlack(player)
|
||||
p = g.addAIPlayer(blackName, self.__aiProfiles[profile], level)
|
||||
g.setBlack(p)
|
||||
|
||||
return g
|
||||
|
||||
|
@ -994,9 +486,9 @@ class Application:
|
|||
gameProperties = ui.Game()
|
||||
|
||||
gameProperties.path = path
|
||||
gameProperties.name = pgnGame.getTag(pgnGame.PGN_TAG_EVENT)
|
||||
gameProperties.white.name = pgnGame.getTag(pgnGame.PGN_TAG_WHITE)
|
||||
gameProperties.black.name = pgnGame.getTag(pgnGame.PGN_TAG_BLACK)
|
||||
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)
|
||||
|
@ -1031,6 +523,7 @@ class Application:
|
|||
return
|
||||
|
||||
newGame = self.addGame(gameProperties.name, gameProperties.white.name, w, gameProperties.black.name, b)
|
||||
newGame.date = pgnGame.getTag(chess.pgn.TAG_DATE)
|
||||
newGame.fileName = path
|
||||
if gameProperties.moves:
|
||||
newGame.start(gameProperties.moves)
|
||||
|
@ -1046,19 +539,19 @@ class Application:
|
|||
moves[i].nag = pgnMoves[i].nag
|
||||
|
||||
# Get the last player to resign if the file specifies it
|
||||
result = pgnGame.getTag(pgnGame.PGN_TAG_RESULT, None)
|
||||
if result == chess.pgn.PGNToken.GAME_TERMINATE_DRAW:
|
||||
result = pgnGame.getTag(chess.pgn.TAG_RESULT, None)
|
||||
if result == chess.pgn.RESULT_DRAW:
|
||||
if newGame.result == game.RESULT_IN_PROGRESS:
|
||||
newGame.getCurrentPlayer().resign()
|
||||
elif newGame.result != game.RESULT_DRAW:
|
||||
print 'PGN file specifies draw, glChess expects a win'
|
||||
elif result == chess.pgn.PGNToken.GAME_TERMINATE_WHITE_WIN:
|
||||
elif result == chess.pgn.RESULT_WHITE_WIN:
|
||||
print 'FIXME: Handle white win in PGN'
|
||||
elif result == chess.pgn.PGNToken.GAME_TERMINATE_BLACK_WIN:
|
||||
elif result == chess.pgn.RESULT_BLACK_WIN:
|
||||
print 'FIXME: Handle black win in PGN'
|
||||
|
||||
duration = 0
|
||||
value = pgnGame.getTag(pgnGame.PGN_TAG_TIME_CONTROL)
|
||||
value = pgnGame.getTag(chess.pgn.TAG_TIME_CONTROL)
|
||||
if value is not None:
|
||||
timers = value.split(':')
|
||||
try:
|
||||
|
@ -1140,88 +633,37 @@ class Application:
|
|||
def animate(self, timeStep):
|
||||
"""
|
||||
"""
|
||||
animating = False
|
||||
for g in self.__games:
|
||||
if g.animate(timeStep):
|
||||
animating = True
|
||||
return animating
|
||||
return self.__game.animate(timeStep)
|
||||
|
||||
def quit(self):
|
||||
"""Quit glChess"""
|
||||
# Notify the UI
|
||||
self.ui.controller.close()
|
||||
|
||||
if self.__game is not None:
|
||||
# Save the current game to the history
|
||||
self._save(self.__game)
|
||||
|
||||
# Save any games not saved to a file
|
||||
self.__autosave()
|
||||
|
||||
# Abort current games (will delete AIs etc)
|
||||
for game in self.__games[:]:
|
||||
game.abort()
|
||||
# Abort current game (will delete AIs etc)
|
||||
self.__game.abort()
|
||||
|
||||
# Exit the application
|
||||
sys.exit()
|
||||
|
||||
# Private methods
|
||||
|
||||
def _removeGame(self, g):
|
||||
"""
|
||||
"""
|
||||
self.__games.remove(g)
|
||||
def _save(self, g):
|
||||
if len(g.getMoves()) < 2:
|
||||
return
|
||||
pgnGame = chess.pgn.PGNGame()
|
||||
g.toPGN(pgnGame)
|
||||
self.history.save(pgnGame)
|
||||
|
||||
def __autoload(self):
|
||||
"""Restore games from the autosave file"""
|
||||
path = AUTOSAVE_FILE
|
||||
|
||||
try:
|
||||
p = chess.pgn.PGN(path)
|
||||
games = p[:]
|
||||
except chess.pgn.Error, e:
|
||||
self.logger.addLine('Ignoring invalid autoload file %s: %s' % (path, str(e)))
|
||||
return
|
||||
except IOError, e:
|
||||
# The file doesn't have to exist...
|
||||
if e.errno != errno.ENOENT:
|
||||
self.logger.addLine('Unable to autoload from %s: %s' % (path, str(e)))
|
||||
return
|
||||
|
||||
self.logger.addLine('Auto-loading from %s...' % path)
|
||||
|
||||
# Delete the file once loaded
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
# Restore each game
|
||||
for pgnGame in games:
|
||||
pgnGame = self.history.getUnfinishedGame()
|
||||
if pgnGame is not None:
|
||||
self.addPGNGame(pgnGame, None)
|
||||
|
||||
def __autosave(self):
|
||||
"""Save any open games to the autosave file"""
|
||||
if len(self.__games) == 0:
|
||||
return
|
||||
|
||||
fname = AUTOSAVE_FILE
|
||||
self.logger.addLine('Auto-saving to %s...' % fname)
|
||||
|
||||
try:
|
||||
f = file(fname, 'a')
|
||||
for g in self.__games:
|
||||
# Ignore games that are saved to a file
|
||||
if g.fileName is not None:
|
||||
continue
|
||||
|
||||
pgnGame = chess.pgn.PGNGame()
|
||||
g.toPGN(pgnGame)
|
||||
|
||||
lines = pgnGame.getLines()
|
||||
for line in lines:
|
||||
f.write(line + '\n')
|
||||
f.write('\n')
|
||||
f.close()
|
||||
except IOError, e:
|
||||
# FIXME: This should be in a dialog
|
||||
self.logger.addLine('Unable to autosave to %s: %s' % (fname, str(e)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = Application()
|
||||
|
|
94
src/lib/player.py
Normal file
94
src/lib/player.py
Normal file
|
@ -0,0 +1,94 @@
|
|||
__author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
|
||||
__license__ = 'GNU General Public License Version 2'
|
||||
__copyright__ = 'Copyright 2005-2006 Robert Ancell'
|
||||
|
||||
import game
|
||||
import ai
|
||||
|
||||
class MovePlayer(game.ChessPlayer):
|
||||
"""This class provides a pseudo-player to watch for piece movements"""
|
||||
# The game to control
|
||||
__game = None
|
||||
|
||||
waitForMoves = False
|
||||
|
||||
def __init__(self, chessGame):
|
||||
"""Constructor for a move player.
|
||||
|
||||
'chessGame' is the game to make changes to (ChessGame).
|
||||
"""
|
||||
self.__game = chessGame
|
||||
game.ChessPlayer.__init__(self, '@move')
|
||||
|
||||
# Extended methods
|
||||
|
||||
def onPieceMoved(self, piece, start, end, delete):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
if self.__game.view.moveNumber != -1:
|
||||
return
|
||||
p = self.__game.view.scene.movePiece(piece, end, delete, self.__game.isStarted())
|
||||
|
||||
# If a player move notify when animation completes
|
||||
if self.__game.isStarted() and self.__game.view.moveNumber == -1 and start is not None and start != end:
|
||||
self.__game.view.scene.waitingPiece = p
|
||||
|
||||
def onPlayerMoved(self, p, move):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
self.__game.needsSaving = True
|
||||
|
||||
# Update clocks
|
||||
if p is self.__game.getWhite():
|
||||
if self.__game.wT is not None:
|
||||
self.__game.wT.controller.pause()
|
||||
if self.__game.bT is not None:
|
||||
self.__game.bT.controller.run()
|
||||
else:
|
||||
if self.__game.bT is not None:
|
||||
self.__game.bT.controller.pause()
|
||||
if self.__game.wT is not None:
|
||||
self.__game.wT.controller.run()
|
||||
|
||||
# Complete move if not waiting for visual indication of move end
|
||||
if self.__game.view.moveNumber != -1:
|
||||
p.endMove()
|
||||
|
||||
self.__game.view.controller.addMove(move)
|
||||
|
||||
def onGameEnded(self, game):
|
||||
"""Called by chess.board.ChessPlayer"""
|
||||
self.__game.view.controller.endGame(game)
|
||||
|
||||
class HumanPlayer(game.ChessPlayer):
|
||||
"""
|
||||
"""
|
||||
__game = None
|
||||
|
||||
def __init__(self, chessGame, name):
|
||||
"""Constructor.
|
||||
|
||||
'chessGame' is the game this player is in (game.ChessGame).
|
||||
'name' is the name of this player (string).
|
||||
"""
|
||||
game.ChessPlayer.__init__(self, name)
|
||||
self.__game = chessGame
|
||||
|
||||
def readyToMove(self):
|
||||
# FIXME: ???
|
||||
self.__game.view.controller.setAttention(True)
|
||||
|
||||
class AIPlayer(ai.Player):
|
||||
"""
|
||||
"""
|
||||
|
||||
def __init__(self, application, name, profile, level, description):
|
||||
"""
|
||||
"""
|
||||
executable = profile.path
|
||||
for arg in profile.arguments[1:]:
|
||||
executable += ' ' + arg
|
||||
self.window = application.ui.controller.addLogWindow(profile.name, executable, description)
|
||||
ai.Player.__init__(self, name, profile, level)
|
||||
|
||||
def addText(self, text, style):
|
||||
"""Called by ai.Player"""
|
||||
self.window.addText(text, style)
|
|
@ -116,9 +116,6 @@ class NetworkController:
|
|||
"""
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
class ViewFeedback:
|
||||
"""Template class for feedback from a view object"""
|
||||
|
||||
|
@ -211,10 +208,6 @@ class ViewFeedback:
|
|||
"""
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
"""This method is called when the user requests this view be closed"""
|
||||
pass
|
||||
|
||||
class ViewController:
|
||||
"""Template class for methods to control a view"""
|
||||
|
||||
|
@ -251,11 +244,7 @@ class ViewController:
|
|||
'requiresAttention' is a flag to show if this view requires attention.
|
||||
"""
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
"""Close this view"""
|
||||
pass
|
||||
|
||||
|
||||
class UIFeedback:
|
||||
"""Template class for feedback from a UI"""
|
||||
|
||||
|
@ -419,19 +408,8 @@ class UI:
|
|||
"""
|
||||
return None
|
||||
|
||||
def setDefaultView(self, feedback):
|
||||
"""Set the default view to render.
|
||||
|
||||
'feedback' is a object to report view events with (extends ViewFeedback).
|
||||
|
||||
This will override the previous default view.
|
||||
|
||||
Returns a view controller object (extends ViewController).
|
||||
"""
|
||||
return None
|
||||
|
||||
def addView(self, title, feedback):
|
||||
"""Add a view to the UI.
|
||||
def setView(self, title, feedback):
|
||||
"""Set the view to display.
|
||||
|
||||
'title' is the title for the view (string).
|
||||
'feedback' is a object to report view events with (extends ViewFeedback).
|
||||
|
|
Loading…
Reference in a new issue