~ubuntu-branches/ubuntu/precise/gnome-games/precise-proposed

« back to all changes in this revision

Viewing changes to glchess/src/lib/game.py

  • Committer: Package Import Robot
  • Author(s): Rodrigo Moya
  • Date: 2011-05-30 13:32:04 UTC
  • mfrom: (1.3.4)
  • mto: (163.1.3 precise)
  • mto: This revision was merged to the branch mainline in revision 143.
  • Revision ID: package-import@ubuntu.com-20110530133204-celaq1v1dsxc48q1
Tags: upstream-3.0.2
ImportĀ upstreamĀ versionĀ 3.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
"""
3
 
"""
4
 
 
5
 
__author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
6
 
__license__ = 'GNU General Public License Version 2'
7
 
__copyright__ = 'Copyright 2005-2006  Robert Ancell'
8
 
 
9
 
import chess.board
10
 
import chess.san
11
 
 
12
 
# Game results
13
 
RESULT_IN_PROGRESS         = '*'
14
 
RESULT_WHITE_WINS          = '1-0'
15
 
RESULT_BLACK_WINS          = '0-1'
16
 
RESULT_DRAW                = '1/2-1/2'
17
 
 
18
 
# Reasons for the result
19
 
RULE_CHECKMATE             = 'CHECKMATE'
20
 
RULE_STALEMATE             = 'STALEMATE'
21
 
RULE_TIMEOUT               = 'TIMEOUT'
22
 
RULE_FIFTY_MOVES           = 'FIFTY_MOVES'
23
 
RULE_THREE_FOLD_REPETITION = 'THREE_FOLD_REPETITION'
24
 
RULE_INSUFFICIENT_MATERIAL = 'INSUFFICIENT_MATERIAL'
25
 
RULE_RESIGN                = 'RESIGN'
26
 
RULE_DEATH                 = 'DEATH'
27
 
RULE_AGREEMENT             = 'AGREEMENT'
28
 
RULE_ABANDONMENT           = 'ABANDONMENT'
29
 
 
30
 
class ChessMove:
31
 
    """
32
 
    """
33
 
 
34
 
    # The move number (game starts at 0)
35
 
    number     = 0
36
 
    
37
 
    # The player and piece that moved
38
 
    player     = None
39
 
    piece      = None
40
 
    
41
 
    # The piece that was promoted to (or None)
42
 
    promotion  = None
43
 
    
44
 
    # The victim piece (or None)
45
 
    victim     = None
46
 
 
47
 
    # The start and end position of the move
48
 
    start      = None
49
 
    end        = None
50
 
    
51
 
    # The move in CAN and SAN format
52
 
    canMove    = ''
53
 
    sanMove    = ''
54
 
 
55
 
    # The game result after this move
56
 
    opponentInCheck = False
57
 
    opponentCanMove = False
58
 
    
59
 
    # If this move can be used as a resignation
60
 
    fiftyMoveRule = False
61
 
    threeFoldRepetition = False
62
 
    
63
 
    # A comment about this move
64
 
    comment = ''
65
 
 
66
 
    # Numeric annotation glyph for move
67
 
    nag     = ''
68
 
 
69
 
class ChessPlayer:
70
 
    """
71
 
    """
72
 
 
73
 
    def __init__(self, name):
74
 
        """Constructor for a chess player.
75
 
 
76
 
        'name' is the name of the player.
77
 
        """
78
 
        self.__name = str(name)
79
 
        self.__game = None
80
 
        self.__readyToMove = False
81
 
        self.isAlive = True
82
 
 
83
 
    # Methods to extend
84
 
 
85
 
    def onPieceMoved(self, piece, start, end, delete):
86
 
        """Called when a chess piece is moved.
87
 
        
88
 
        'piece' is the piece that has been moved (chess.board.ChessPiece).
89
 
        'start' is the location the piece in LAN format (string) or None if the piece has been created.
90
 
        'end' is the location the piece has moved to in LAN format (string).
91
 
        'delete' is a flag to show if the piece should be deleted when it arrives there (boolean).
92
 
        """
93
 
        pass
94
 
    
95
 
    def onPlayerMoved(self, player, move):
96
 
        """Called when a player has moved.
97
 
        
98
 
        'player' is the player that has moved (ChessPlayer).
99
 
        'move' is the record for this move (ChessMove).
100
 
        """
101
 
        pass
102
 
    
103
 
    def onUndoMove(self):
104
 
        pass
105
 
    
106
 
    def onPlayerStartTurn(self, player):
107
 
        pass
108
 
 
109
 
    def onGameEnded(self, game):
110
 
        """Called when a chess game has ended.
111
 
        
112
 
        'game' is the game that has ended (Game).
113
 
        """
114
 
        pass
115
 
    
116
 
    def readyToMove(self):
117
 
        """FIXME
118
 
        """
119
 
        pass
120
 
 
121
 
    # Public methods
122
 
 
123
 
    def getName(self):
124
 
        """Get the name of this player.
125
 
        
126
 
        Returns the player name (string).
127
 
        """
128
 
        return self.__name
129
 
    
130
 
    def getGame(self):
131
 
        """Get the game this player is in.
132
 
        
133
 
        Returns the game (Game) or None if not in a game.
134
 
        """
135
 
        return self.__game
136
 
    
137
 
    def getRemainingTime(self):
138
 
        """Get the amount of time this player has remaining.
139
 
        
140
 
        Returns the amount of time in milliseconds.
141
 
        """
142
 
        if self is self.__game.getWhite():
143
 
            timer = self.__game.whiteTimer
144
 
        elif self is self.__game.getBlack():
145
 
            timer = self.__game.blackTimer
146
 
        else:
147
 
            return 0
148
 
        
149
 
        if timer is None:
150
 
            return 0
151
 
        else:
152
 
            return timer.controller.getRemaining()
153
 
 
154
 
    def isReadyToMove(self):
155
 
        """
156
 
        """
157
 
        return self.__readyToMove
158
 
    
159
 
    def canMove(self, start, end, promotionType = chess.board.QUEEN):
160
 
        """
161
 
        """
162
 
        return self.__game.canMove(self, start, end, promotionType)
163
 
 
164
 
    def move(self, move):
165
 
        """Move a piece.
166
 
        
167
 
        'move' is the move to make in Normal/Long/Standard Algebraic format (string).
168
 
        """
169
 
        self.__game.move(self, move)
170
 
        
171
 
    def undo(self):
172
 
        """Undo moves until it is this players turn"""
173
 
        self.__game.undo(self)
174
 
        
175
 
    def endMove(self):
176
 
        """Complete this players turn"""
177
 
        self.__game.endMove(self)
178
 
        
179
 
    def resign(self):
180
 
        """Resign from the game"""
181
 
        self.__game.resign(self)
182
 
        
183
 
    def claimDraw(self):
184
 
        """Claim a draw"""
185
 
        return self.__game.claimDraw()
186
 
 
187
 
    def outOfTime(self):
188
 
        """Report this players timer has expired"""
189
 
        self.__game.outOfTime(self)
190
 
        
191
 
    def die(self):
192
 
        """Report this player has died"""
193
 
        self.isAlive = False
194
 
        if self.__game is not None:
195
 
            self.__game.killPlayer(self)
196
 
 
197
 
    # Private methods
198
 
    
199
 
    def _setGame(self, game):
200
 
        """
201
 
        """
202
 
        self.__game = game
203
 
        
204
 
    def _setReadyToMove(self, readyToMove):
205
 
        if self.__readyToMove == readyToMove:
206
 
            return
207
 
        self.__readyToMove = readyToMove
208
 
        if readyToMove is True:
209
 
            self.readyToMove()
210
 
 
211
 
class ChessGameBoard(chess.board.ChessBoard):
212
 
    """
213
 
    """
214
 
    
215
 
    def __init__(self, game):
216
 
        """
217
 
        """
218
 
        self.__game = game
219
 
        chess.board.ChessBoard.__init__(self)
220
 
 
221
 
    def onPieceMoved(self, piece, start, end, delete):
222
 
        """Called by chess.board.ChessBoard"""
223
 
        self.__game._onPieceMoved(piece, start, end, delete)
224
 
 
225
 
class ChessGameSANConverter(chess.san.SANConverter):
226
 
    """
227
 
    """
228
 
        
229
 
    __colourToSAN = {chess.board.WHITE: chess.san.SANConverter.WHITE,
230
 
                     chess.board.BLACK: chess.san.SANConverter.BLACK}
231
 
    __sanToColour = {}
232
 
    for (a, b) in __colourToSAN.iteritems():
233
 
        __sanToColour[b] = a
234
 
        
235
 
    __typeToSAN = {chess.board.PAWN:   chess.san.SANConverter.PAWN,
236
 
                   chess.board.KNIGHT: chess.san.SANConverter.KNIGHT,
237
 
                   chess.board.BISHOP: chess.san.SANConverter.BISHOP,
238
 
                   chess.board.ROOK:   chess.san.SANConverter.ROOK,
239
 
                   chess.board.QUEEN:  chess.san.SANConverter.QUEEN,
240
 
                   chess.board.KING:   chess.san.SANConverter.KING}
241
 
    __sanToType = {}
242
 
    for (a, b) in __typeToSAN.iteritems():
243
 
        __sanToType[b] = a
244
 
        
245
 
    def __init__(self, board, moveNumber):
246
 
        self.board = board
247
 
        self.moveNumber = moveNumber
248
 
        chess.san.SANConverter.__init__(self)
249
 
    
250
 
    def decode(self, colour, move):
251
 
        (start, end, result, promotionType) = chess.san.SANConverter.decode(self, self.__colourToSAN[colour], move)
252
 
        return (start, end, self.__sanToType[promotionType])
253
 
    
254
 
    def encode(self, start, end, isTake, promotionType):
255
 
        if promotionType is None:
256
 
            promotion = self.QUEEN
257
 
        else:
258
 
            promotion = self.__typeToSAN[promotionType]
259
 
        return chess.san.SANConverter.encode(self, start, end, isTake, promotion)
260
 
 
261
 
    def getPiece(self, location):
262
 
        """Called by chess.san.SANConverter"""
263
 
        piece = self.board.getPiece(location, self.moveNumber)
264
 
        if piece is None:
265
 
            return None
266
 
        return (self.__colourToSAN[piece.getColour()], self.__typeToSAN[piece.getType()])
267
 
    
268
 
    def testMove(self, colour, start, end, promotionType, allowSuicide = False):
269
 
        """Called by chess.san.SANConverter"""
270
 
        move = self.board.testMove(self.__sanToColour[colour], start, end,
271
 
                                     self.__sanToType[promotionType], allowSuicide, self.moveNumber)
272
 
        if move is None:
273
 
            return False
274
 
 
275
 
        if move.opponentInCheck:
276
 
            if not move.opponentCanMove:
277
 
                return chess.san.SANConverter.CHECKMATE
278
 
            return chess.san.SANConverter.CHECK
279
 
        return True
280
 
 
281
 
class ChessGame:
282
 
    """
283
 
    """    
284
 
 
285
 
    def __init__(self):
286
 
        """Game constructor"""
287
 
        self.__players = []
288
 
        self.__spectators = []
289
 
        self.__whitePlayer = None
290
 
        self.__blackPlayer = None
291
 
        self.__currentPlayer = None
292
 
        self.__moves = []
293
 
        self.__inCallback = False
294
 
        self.__queuedCalls = []
295
 
        self.board = ChessGameBoard(self)
296
 
 
297
 
        self.__started = False
298
 
        self.result  = RESULT_IN_PROGRESS
299
 
        self.rule    = None    
300
 
        self.whiteTimer = None
301
 
        self.blackTimer = None
302
 
        
303
 
    def getAlivePieces(self, moveNumber = -1):
304
 
        """Get the alive pieces on the board.
305
 
        
306
 
        'moveNumber' is the move to get the pieces from (integer).
307
 
        
308
 
        Returns a dictionary of the alive pieces (board.ChessPiece) keyed by location.
309
 
        Raises an IndexError exception if moveNumber is invalid.
310
 
        """
311
 
        return self.board.getAlivePieces(moveNumber)
312
 
    
313
 
    def getDeadPieces(self, moveNumber = -1):
314
 
        """Get the dead pieces from the game.
315
 
        
316
 
        'moveNumber' is the move to get the pieces from (integer).
317
 
        
318
 
        Returns a list of the pieces (board.ChessPiece) in the order they were killed.
319
 
        Raises an IndexError exception if moveNumber is invalid.
320
 
        """
321
 
        return self.board.getDeadPieces(moveNumber)
322
 
    
323
 
    def setTimers(self, whiteTimer, blackTimer):
324
 
        """
325
 
        """
326
 
        self.whiteTimer = whiteTimer
327
 
        self.blackTimer = blackTimer
328
 
 
329
 
    def setWhite(self, player):
330
 
        """Set the white player in the game.
331
 
        
332
 
        'player' is the player to use as white.
333
 
        
334
 
        If the game has started or there is a white player an exception is thrown.
335
 
        """
336
 
        assert(self.__started is False)
337
 
        assert(self.__whitePlayer is None)
338
 
        self.__whitePlayer = player
339
 
        self.__connectPlayer(player)
340
 
 
341
 
    def getWhite(self):
342
 
        """Returns the current white player (player.Player)"""
343
 
        return self.__whitePlayer
344
 
    
345
 
    def setBlack(self, player):
346
 
        """Set the black player in the game.
347
 
        
348
 
        'player' is the player to use as black.
349
 
        
350
 
        If the game has started or there is a black player an exception is thrown.
351
 
        """
352
 
        assert(self.__started is False)
353
 
        assert(self.__blackPlayer is None)
354
 
        self.__blackPlayer = player
355
 
        self.__connectPlayer(player)
356
 
        
357
 
    def getBlack(self):
358
 
        """Returns the current white player (player.Player)"""
359
 
        return self.__blackPlayer
360
 
    
361
 
    def getCurrentPlayer(self):
362
 
        """Get the player to move"""
363
 
        return self.__currentPlayer
364
 
    
365
 
    def addSpectator(self, player):
366
 
        """Add a spectator to the game.
367
 
        
368
 
        'player' is the player spectating.
369
 
        
370
 
        This can be called after the game has started.
371
 
        """
372
 
        self.__spectators.append(player)
373
 
        self.__connectPlayer(player)
374
 
 
375
 
    def isStarted(self):
376
 
        """Returns True if the game has been started"""
377
 
        return self.__started
378
 
        
379
 
    def start(self, moves = []):
380
 
        """Start the game.
381
 
        
382
 
        'moves' is a list of moves to start with.
383
 
        
384
 
        If there is no white or black player then an exception is raised.
385
 
        """
386
 
        assert(self.__whitePlayer is not None and self.__blackPlayer is not None)
387
 
        
388
 
        # Disabled for now
389
 
        #import network
390
 
        #self.x = network.GameReporter('Test Game', 12345)
391
 
        #print 'Reporting'
392
 
 
393
 
        # Load starting moves
394
 
        self.__currentPlayer = self.__whitePlayer
395
 
        for move in moves:
396
 
            self.move(self.__currentPlayer, move)
397
 
            if self.__currentPlayer is self.__whitePlayer:
398
 
                self.__currentPlayer = self.__blackPlayer
399
 
            else:
400
 
                self.__currentPlayer = self.__whitePlayer
401
 
 
402
 
        self.__started = True
403
 
 
404
 
        # Stop if both players aren't alive
405
 
        if not self.__whitePlayer.isAlive:
406
 
            self.killPlayer(self.__whitePlayer)
407
 
            return
408
 
        if not self.__blackPlayer.isAlive:
409
 
            self.killPlayer(self.__blackPlayer)
410
 
            return
411
 
 
412
 
        # Stop if game ended on loaded moves
413
 
        if self.result != RESULT_IN_PROGRESS:
414
 
            self._notifyEndGame()
415
 
            return
416
 
 
417
 
        self.startLock()
418
 
        
419
 
        # Inform other players of the result
420
 
        for player in self.__players:
421
 
            player.onPlayerStartTurn(self.__currentPlayer)
422
 
 
423
 
        # Get the next player to move
424
 
        self.__currentPlayer._setReadyToMove(True)
425
 
 
426
 
        self.endLock()
427
 
 
428
 
    def getSquareOwner(self, coord):
429
 
        """TODO
430
 
        """
431
 
        piece = self.board.getPiece(coord)
432
 
        if piece is None:
433
 
            return None
434
 
        
435
 
        colour = piece.getColour()
436
 
        if colour is chess.board.WHITE:
437
 
            return self.__whitePlayer
438
 
        elif colour is chess.board.BLACK:
439
 
            return self.__blackPlayer
440
 
        else:
441
 
            return None
442
 
        
443
 
    def canMove(self, player, start, end, promotionType):
444
 
        """Test if a player can move.
445
 
        
446
 
        'player' is the player making the move.
447
 
        'start' is the location to move from in LAN format (string).
448
 
        'end' is the location to move from in LAN format (string).
449
 
        'promotionType' is the piece type to promote pawns to. FIXME: Make this a property of the player
450
 
        
451
 
        Return True if can move, otherwise False.
452
 
        """
453
 
        if player is not self.__currentPlayer:
454
 
            return False
455
 
        
456
 
        if player is self.__whitePlayer:
457
 
            colour = chess.board.WHITE
458
 
        elif player is self.__blackPlayer:
459
 
            colour = chess.board.BLACK
460
 
        else:
461
 
            assert(False)
462
 
 
463
 
        move = self.board.testMove(colour, start, end, promotionType = promotionType)
464
 
 
465
 
        return move is not None
466
 
    
467
 
    def move(self, player, move):
468
 
        """Get a player to make a move.
469
 
        
470
 
        'player' is the player making the move.
471
 
        'move' is the move to make in SAN or LAN format (string).
472
 
        """
473
 
        if self.__inCallback:
474
 
            self.__queuedCalls.append((self.move, (player, move)))
475
 
            return
476
 
        
477
 
        self.startLock()
478
 
        
479
 
        if player is not self.__currentPlayer:
480
 
            print 'Player attempted to move out of turn'
481
 
        else:
482
 
            self._move(player, move)
483
 
 
484
 
        self.endLock()
485
 
 
486
 
    def undo(self, player):
487
 
        if self.__inCallback:
488
 
            self.__queuedCalls.append((self.undo, (player,)))
489
 
            return
490
 
        
491
 
        self.startLock()
492
 
        
493
 
        self.__whitePlayer._setReadyToMove(False)
494
 
        self.__blackPlayer._setReadyToMove(False)
495
 
        
496
 
        # Pretend the current player is the oponent so when endMove() is called
497
 
        # this player will become the active player.
498
 
        # Yes, this IS a big hack...
499
 
        if player is self.__whitePlayer:
500
 
            self.__currentPlayer = self.__blackPlayer
501
 
        else:
502
 
            self.__currentPlayer = self.__whitePlayer
503
 
        
504
 
        # If this player hasn't moved then undo oponents move before their one
505
 
        if len(self.__moves) > 0 and self.__moves[-1].player is not player:
506
 
            count = 2
507
 
        else:
508
 
            count = 1
509
 
            
510
 
        for i in xrange(count):
511
 
            if len(self.__moves) != 0:
512
 
                self.board.undo()
513
 
                self.__moves = self.__moves[:-1]
514
 
                for p in self.__players:
515
 
                    p.onUndoMove()
516
 
        
517
 
        self.endLock()
518
 
        
519
 
    def startLock(self):
520
 
        assert(self.__inCallback is False)
521
 
        self.__inCallback = True
522
 
        
523
 
    def endLock(self):
524
 
        self.__inCallback = False
525
 
        while len(self.__queuedCalls) > 0:
526
 
            (call, args) = self.__queuedCalls[0]
527
 
            self.__queuedCalls = self.__queuedCalls[1:]
528
 
            call(*args)
529
 
 
530
 
    def _move(self, player, move):
531
 
        """
532
 
        """
533
 
        if self.result != RESULT_IN_PROGRESS:
534
 
            print 'Game completed'
535
 
            return
536
 
        
537
 
        if self.__currentPlayer is self.__whitePlayer:
538
 
            colour = chess.board.WHITE
539
 
        else:
540
 
            colour = chess.board.BLACK
541
 
 
542
 
        # If move is SAN process it as such
543
 
        try:
544
 
            (start, end, _, _, promotionType, _) = chess.lan.decode(colour, move)
545
 
        except chess.lan.DecodeError, e:
546
 
            converter = ChessGameSANConverter(self.board, len(self.__moves))
547
 
            try:
548
 
                (start, end, promotionType) = converter.decode(colour, move)
549
 
            except chess.san.Error, e:
550
 
                print 'Invalid move: ' + move
551
 
                return
552
 
 
553
 
        # Only use promotion type if a pawn move to far file
554
 
        piece = self.board.getPiece(start)
555
 
        promotion = None
556
 
        if piece is not None and piece.getType() is chess.board.PAWN:
557
 
            if colour is chess.board.WHITE:
558
 
                if end[1] == '8':
559
 
                    promotion = promotionType
560
 
            else:
561
 
                if end[1] == '1':
562
 
                    promotion = promotionType
563
 
 
564
 
        moveResult = self.board.movePiece(colour, start, end, promotionType)
565
 
        if moveResult is None:
566
 
            print 'Illegal move: ' + str(move)
567
 
            return
568
 
        
569
 
        # Re-encode for storing and reporting
570
 
        canMove = chess.lan.encode(colour, start, end, promotionType = promotion)
571
 
        converter = ChessGameSANConverter(self.board, len(self.__moves))
572
 
        try:
573
 
            sanMove = converter.encode(start, end, moveResult.victim != None, promotionType)
574
 
        except chess.san.Error:
575
 
            # If for some reason we couldn't make the SAN move the use the CAN move instead
576
 
            sanMove = canMove
577
 
 
578
 
        m = ChessMove()
579
 
        if len(self.__moves) == 0:
580
 
            m.number = 1
581
 
        else:
582
 
            m.number = self.__moves[-1].number + 1
583
 
        m.player              = self.__currentPlayer
584
 
        m.piece               = piece
585
 
        m.victim              = moveResult.victim
586
 
        m.start               = start
587
 
        m.end                 = end
588
 
        m.canMove             = canMove
589
 
        m.sanMove             = sanMove
590
 
        m.opponentInCheck     = moveResult.opponentInCheck
591
 
        m.opponentCanMove     = moveResult.opponentCanMove
592
 
        m.fiftyMoveRule       = moveResult.fiftyMoveRule
593
 
        m.threeFoldRepetition = moveResult.threeFoldRepetition
594
 
        #FIXME: m.comment             = move.comment
595
 
        #FIXME: m.nag                 = move.nag
596
 
 
597
 
        self.__moves.append(m)
598
 
 
599
 
        # This player has now moved
600
 
        self.__currentPlayer._setReadyToMove(False)
601
 
 
602
 
        # Inform other players of the result
603
 
        for player in self.__players:
604
 
            player.onPlayerMoved(self.__currentPlayer, m)
605
 
 
606
 
        # Check if the game has ended
607
 
        result = RESULT_IN_PROGRESS
608
 
        if not m.opponentCanMove:
609
 
            if self.__currentPlayer is self.__whitePlayer:
610
 
                result = RESULT_WHITE_WINS
611
 
            else:
612
 
                result = RESULT_BLACK_WINS
613
 
            if m.opponentInCheck:
614
 
                rule = RULE_CHECKMATE
615
 
            else:
616
 
                result = RESULT_DRAW
617
 
                rule = RULE_STALEMATE
618
 
 
619
 
        # Check able to complete
620
 
        if not self.board.sufficientMaterial():
621
 
            result = RESULT_DRAW
622
 
            rule = RULE_INSUFFICIENT_MATERIAL
623
 
 
624
 
        if result is not RESULT_IN_PROGRESS:
625
 
            self.endGame(result, rule)
626
 
 
627
 
    def endMove(self, player):
628
 
        """
629
 
        """
630
 
        if self.__inCallback:
631
 
            self.__queuedCalls.append((self.endMove, (player,)))
632
 
            return
633
 
        
634
 
        if player is not self.__currentPlayer:
635
 
            print 'Player attempted to move out of turn'
636
 
            return
637
 
        if player.move is None:
638
 
            print "Ending move when haven't made one"
639
 
            return
640
 
 
641
 
        if self.__currentPlayer is self.__whitePlayer:
642
 
            self.__currentPlayer = self.__blackPlayer
643
 
        else:
644
 
            self.__currentPlayer = self.__whitePlayer
645
 
        
646
 
        self.startLock()
647
 
        
648
 
        # Inform other players of the result
649
 
        for player in self.__players:
650
 
            player.onPlayerStartTurn(self.__currentPlayer)
651
 
 
652
 
        # Notify the next player they can move
653
 
        if self.__started is True and self.result == RESULT_IN_PROGRESS:
654
 
            self.__currentPlayer._setReadyToMove(True)
655
 
 
656
 
        self.endLock()
657
 
 
658
 
    def resign(self, player):
659
 
        """Get a player to resign.
660
 
        
661
 
        'player' is the player resigning.
662
 
        """
663
 
        rule = RULE_RESIGN
664
 
        if player is self.__whitePlayer:
665
 
            self.endGame(RESULT_BLACK_WINS, rule)
666
 
        else:
667
 
            self.endGame(RESULT_WHITE_WINS, rule)
668
 
            
669
 
    def claimDraw(self):
670
 
        """
671
 
        """
672
 
        # TODO: Penalise if make an incorrect attempt
673
 
        try:
674
 
            move = self.__moves[-1]
675
 
        except IndexError:
676
 
            return False
677
 
        else:
678
 
            if move.fiftyMoveRule:
679
 
                rule = RULE_FIFTY_MOVES
680
 
            elif move.threeFoldRepetition:
681
 
                rule = RULE_THREE_FOLD_REPETITION
682
 
            else:
683
 
                return False
684
 
 
685
 
        self.endGame(RESULT_DRAW, rule)
686
 
        return True
687
 
 
688
 
    def killPlayer(self, player):
689
 
        """Report a player has died
690
 
        
691
 
        'player' is the player that has died.
692
 
        """
693
 
        if player is self.__whitePlayer:
694
 
            result = RESULT_BLACK_WINS
695
 
        elif player is self.__blackPlayer:
696
 
            result = RESULT_WHITE_WINS       
697
 
        self.endGame(result, RULE_DEATH)
698
 
 
699
 
    def outOfTime(self, player):
700
 
        """Report a player's timer has expired"""
701
 
        if player is self.__whitePlayer:
702
 
            result = RESULT_BLACK_WINS
703
 
        elif player is self.__blackPlayer:
704
 
            result = RESULT_WHITE_WINS
705
 
        else:
706
 
            assert(False)
707
 
        self.endGame(result, RULE_TIMEOUT)
708
 
        
709
 
    def abandon(self):
710
 
        self.endGame(RESULT_DRAW, RULE_ABANDONMENT)
711
 
 
712
 
    def endGame(self, result, rule):
713
 
        if self.result != RESULT_IN_PROGRESS:
714
 
            return
715
 
        self.result = result
716
 
        self.rule = rule
717
 
        if self.isStarted():
718
 
            self._notifyEndGame()
719
 
 
720
 
    def _notifyEndGame(self):
721
 
        self.__currentPlayer._setReadyToMove(False)
722
 
        for player in self.__players:
723
 
            player.onGameEnded(self)
724
 
 
725
 
    def getMoves(self):
726
 
        """
727
 
        """
728
 
        return self.__moves
729
 
 
730
 
    def abort(self):
731
 
        """End the game"""
732
 
        # Inform players
733
 
        for player in self.__players:
734
 
            player.onGameEnded(self)
735
 
 
736
 
    # Private methods:
737
 
 
738
 
    def __connectPlayer(self, player):
739
 
        """Add a player into the game.
740
 
        
741
 
        'player' is the player to add.
742
 
        
743
 
        The player will be notified of the current state of the board.
744
 
        """
745
 
        self.__players.append(player)
746
 
        player._setGame(self)
747
 
        
748
 
        # Notify the player of the current state
749
 
        # FIXME: Make the board iteratable...
750
 
        for file in '12345678':
751
 
            for rank in 'abcdefgh':
752
 
                coord = rank + file
753
 
                piece = self.board.getPiece(coord)
754
 
                if piece is None:
755
 
                    continue
756
 
 
757
 
                # These are moves from nowhere to their current location
758
 
                player.onPieceMoved(piece, None, coord, False)
759
 
 
760
 
    def _onPieceMoved(self, piece, start, end, delete):
761
 
        """Called by the chess board"""
762
 
        
763
 
        # Notify all players of creations and deletions
764
 
        # NOTE: Normal moves are done above since the SAN moves are calculated before the move...
765
 
        # FIXME: Change this so the SAN moves are done afterwards...
766
 
        for player in self.__players:
767
 
            player.onPieceMoved(piece, start, end, delete)
768
 
 
769
 
class NetworkChessGame(ChessGame):
770
 
    """
771
 
    """
772
 
    
773
 
    def move(self, player, move):
774
 
        """Get a player to make a move.
775
 
        
776
 
        'player' is the player making the move.
777
 
        'move' is the move to make. It can be of the form:
778
 
               A coordinate move in the form ((file0, rank0), (file1, rank1), promotionType) ((int, int), (int, int), chess.board.PIECE_TYPE) or
779
 
               A SAN move (string).
780
 
        """
781
 
        # Send to the server
782
 
        
783
 
            
784
 
if __name__ == '__main__':
785
 
    game = ChessGame()
786
 
    
787
 
    import pgn
788
 
    
789
 
    p = pgn.PGN('black.pgn')
790
 
    g = p.getGame(0)
791
 
 
792
 
    class PGNPlayer(ChessPlayer):
793
 
 
794
 
        def __init__(self, isWhite):
795
 
            self.__isWhite = isWhite
796
 
            self.__moveNumber = 1
797
 
        
798
 
        def readyToMove(self):
799
 
            if self.__isWhite:
800
 
                move = g.getWhiteMove(self.__moveNumber)
801
 
            else:
802
 
                move = g.getBlackMove(self.__moveNumber)
803
 
            self.__moveNumber += 1
804
 
            self.move(move)
805
 
            
806
 
    white = PGNPlayer(True)
807
 
    black = PGNPlayer(False)
808
 
    
809
 
    game.setWhite(white)
810
 
    game.setBlack(black)
811
 
    
812
 
    game.start()