Exemple #1
0
 def __init__(self, *_):
     super().__init__()
     self.squareSize = QSize(50, 50)
     self.board = Board(14, 14)
     self.pieces = {}
     self.highlights = []
     self.playerHighlights = {
         'r': self.PlayerHighlight(12, 1, QColor('#bf3b43')),
         'b': self.PlayerHighlight(1, 1, QColor('#4185bf')),
         'y': self.PlayerHighlight(1, 12, QColor('#c09526')),
         'g': self.PlayerHighlight(12, 12, QColor('#4e9161'))
     }
     # Player labels
     self.redName = None
     self.redNameEdit = None
     self.redRating = '?'
     self.blueName = None
     self.blueNameEdit = None
     self.blueRating = '?'
     self.yellowName = None
     self.yellowNameEdit = None
     self.yellowRating = '?'
     self.greenName = None
     self.greenNameEdit = None
     self.greenRating = '?'
     self.createPlayerLabels()
     # Drag-drop
     self.setAcceptDrops(True)
     self.dragStart = None
     self.clickedSquare = None
     self.maskedSquare = None
     self.mouseButton = None
     self.currentPlayer = None
     # Board orientation
     self.orientation = deque(['r', 'b', 'y', 'g'])
     # Arrows and square highlight
     self.arrowStart = None
     self.keyModifier = None
     self.arrowColor = None
     self.squareColor = None
     # Coordinate help
     self.coordinate = None
     self.setMouseTracking(True)
Exemple #2
0
 def __init__(self):
     super().__init__()
     self.variant = '?'
     self.board = Board(14, 14)
     self.result = self.NoResult
     self.currentPlayer = self.NoPlayer
     self.moveNumber = 0
     self.currentMove = self.Node('root', [], None)
     self.currentMove.fen4 = self.startFen4
     self.redName = self.NoPlayer
     self.blueName = self.NoPlayer
     self.yellowName = self.NoPlayer
     self.greenName = self.NoPlayer
     self.redRating = '?'
     self.blueRating = '?'
     self.yellowRating = '?'
     self.greenRating = '?'
     self.chesscomMoveText = ''
     self.moveText = ''
     self.moveDict = dict()
     self.inverseMoveDict = dict()
     self.index = 0  # Used by getMoveText() method
     self.fenMoveNumber = 1
Exemple #3
0
def guiTest(chessBoard):
    app = QApplication(sys.argv)
    board = Board(chessBoard)
    sys.exit(app.exec_())
Exemple #4
0
class Algorithm(QObject):
    """The Algorithm is the underlying logic responsible for changing the current state of the board."""
    boardChanged = pyqtSignal(Board)
    gameOver = pyqtSignal(str)
    currentPlayerChanged = pyqtSignal(str)
    fen4Generated = pyqtSignal(str)
    pgn4Generated = pyqtSignal(str)
    moveTextChanged = pyqtSignal(str)
    selectMove = pyqtSignal(tuple)
    removeMoveSelection = pyqtSignal()
    removeHighlight = pyqtSignal(QColor)
    addHighlight = pyqtSignal(int, int, int, int, QColor)
    playerNamesChanged = pyqtSignal(str, str, str, str)
    playerRatingChanged = pyqtSignal(str, str, str, str)
    cannotReadPgn4 = pyqtSignal()

    NoResult, Team1Wins, Team2Wins, Draw = ['*', '1-0', '0-1',
                                            '1/2-1/2']  # Results
    NoPlayer, Red, Blue, Yellow, Green = ['?', 'r', 'b', 'y', 'g']  # Players
    playerQueue = deque([Red, Blue, Yellow, Green])

    startFen4 = '3yRyNyByKyQyByNyR3/3yPyPyPyPyPyPyPyP3/14/bRbP10gPgR/bNbP10gPgN/bBbP10gPgB/bKbP10gPgQ/' \
                'bQbP10gPgK/bBbP10gPgB/bNbP10gPgN/bRbP10gPgR/14/3rPrPrPrPrPrPrPrP3/3rRrNrBrQrKrBrNrR3 ' \
                'r rKrQbKbQyKyQgKgQ - 0 1'

    # chess.com: [player to move] - [dead 1/0] - [kingside castle 1/0] - [queenside castle 1/0] - [points] - [ply] -
    chesscomStartFen4 = 'R-0,0,0,0-1,1,1,1-1,1,1,1-0,0,0,0-0-3,yR,yN,yB,yK,yQ,yB,yN,yR,3/3,yP,yP,yP,yP,yP,yP,yP,yP,3/' \
                        '14/bR,bP,10,gP,gR/bN,bP,10,gP,gN/bB,bP,10,gP,gB/bK,bP,10,gP,gQ/bQ,bP,10,gP,gK/bB,bP,10,gP,gB/'\
                        'bN,bP,10,gP,gN/bR,bP,10,gP,gR/14/3,rP,rP,rP,rP,rP,rP,rP,rP,3/3,rR,rN,rB,rQ,rK,rB,rN,rR,3'

    def __init__(self):
        super().__init__()
        self.variant = '?'
        self.board = Board(14, 14)
        self.result = self.NoResult
        self.currentPlayer = self.NoPlayer
        self.moveNumber = 0
        self.currentMove = self.Node('root', [], None)
        self.currentMove.fen4 = self.startFen4
        self.redName = self.NoPlayer
        self.blueName = self.NoPlayer
        self.yellowName = self.NoPlayer
        self.greenName = self.NoPlayer
        self.redRating = '?'
        self.blueRating = '?'
        self.yellowRating = '?'
        self.greenRating = '?'
        self.chesscomMoveText = ''
        self.moveText = ''
        self.moveDict = dict()
        self.inverseMoveDict = dict()
        self.index = 0  # Used by getMoveText() method
        self.fenMoveNumber = 1

    class Node:
        """Generic node class. Basic element of a tree."""
        def __init__(self, name, children, parent):
            self.name = name
            self.children = children
            self.parent = parent
            self.fen4 = None
            self.comment = None

        def add(self, node):
            """Adds node to children."""
            self.children.append(node)

        def pop(self):
            """Removes last child from node."""
            self.children.pop()

        def getRoot(self):
            """Backtracks tree and returns root node."""
            if self.parent is None:
                return self
            return self.parent.getRoot()

        def pathFromRoot(self, actions=None):
            """Returns a list of nextMove() actions to reach the current node from the root."""
            if not actions:
                actions = []
            if self.parent is None:
                return actions
            else:
                var = self.parent.children.index(self)
                actions.insert(0, 'nextMove(' + str(var) + ')')
            return self.parent.pathFromRoot(actions)

        def getMoveNumber(self):
            """Returns the move number in the format (ply, variation, move). NOTE: does NOT support subvariations."""
            varNum = [int(a.strip('nextMove()')) for a in self.pathFromRoot()]
            ply, var, move = (0, 0, 0)
            plyCount = True
            i = 0
            while i < len(varNum):
                if varNum[i] != 0:
                    var = varNum[i]
                    plyCount = False
                else:
                    if plyCount:
                        ply += 1
                    else:
                        move += 1
                i += 1
            return str(ply + 1) + '-' + str(var) + '-' + str(
                move + 1) if var != 0 else str(ply)

    def updatePlayerNames(self, red, blue, yellow, green):
        """Sets player names to names entered in the player name labels."""
        self.redName = red if not (red == 'Player Name' or red == '') else '?'
        self.blueName = blue if not (blue == 'Player Name'
                                     or blue == '') else '?'
        self.yellowName = yellow if not (yellow == 'Player Name'
                                         or yellow == '') else '?'
        self.greenName = green if not (green == 'Player Name'
                                       or green == '') else '?'
        self.getPgn4()  # Update PGN4

    def updatePlayerRating(self, red, blue, yellow, green):
        """Sets player rating to rating entered in the player name labels."""
        self.redRating = red
        self.blueRating = blue
        self.yellowRating = yellow
        self.greenRating = green

    def setResult(self, value):
        """Updates game result, if changed."""
        if self.result == value:
            return
        if self.result == self.NoResult:
            self.result = value
            self.gameOver.emit(self.result)
        else:
            self.result = value
        self.getPgn4()  # Update PGN4

    def setCurrentPlayer(self, value):
        """Updates current player, if changed."""
        if self.currentPlayer == value:
            return
        self.currentPlayer = value
        self.setPlayerQueue(self.currentPlayer)
        self.currentPlayerChanged.emit(self.currentPlayer)

    def setPlayerQueue(self, currentPlayer):
        """Rotates player queue such that the current player is the first in the queue."""
        while self.playerQueue[0] != currentPlayer:
            self.playerQueue.rotate(-1)

    def setBoard(self, board):
        """Updates board, if changed."""
        if self.board == board:
            return
        self.board = board
        self.boardChanged.emit(self.board)

    def setupBoard(self):
        """Initializes board."""
        self.setBoard(Board(14, 14))

    def newGame(self):
        """Initializes board and sets starting position."""
        if SETTINGS.value('chesscom'):
            fen4 = self.chesscomStartFen4
        else:
            fen4 = self.startFen4
        self.setBoardState(fen4)
        self.fen4Generated.emit(fen4)

    def getFen4(self, emitSignal=True):
        """Gets FEN4 from current board state."""
        fen4 = self.board.getFen4()
        # Append character for current player
        fen4 += self.currentPlayer + ' '
        fen4 += self.board.castlingAvailability() + ' '
        fen4 += '- '  # En passant target square, n/a
        fen4 += str(self.moveNumber) + ' '  # Number of quarter-moves
        fen4 += str(self.moveNumber // 4 +
                    1)  # Number of full moves, starting from 1
        if SETTINGS.value('chesscom'):
            chesscomPrefix = self.currentPlayer.upper() + '-0,0,0,0' + \
                             self.toChesscomCastling(self.board.castlingAvailability()) + '-0,0,0,0-' + \
                             str(self.moveNumber) + '-'
            fen4 = chesscomPrefix + self.board.getChesscomFen4()
        if emitSignal:
            self.fen4Generated.emit(fen4)
        return fen4

    def toChesscomCastling(self, castling):
        """Converts castling availability string to chess.com compatible format."""
        s = '-'
        s += '1,' if 'rK' in castling else '0,'
        s += '1,' if 'bK' in castling else '0,'
        s += '1,' if 'yK' in castling else '0,'
        s += '1' if 'gK' in castling else '0'
        s += '-'
        s += '1,' if 'rQ' in castling else '0,'
        s += '1,' if 'bQ' in castling else '0,'
        s += '1,' if 'yQ' in castling else '0,'
        s += '1' if 'gQ' in castling else '0'
        return s

    def setCastlingAvailability(self, fen4):
        """Sets castling availability according to FEN4."""
        castling = fen4.split(' ')[2]
        RED, BLUE, YELLOW, GREEN = range(4)
        QUEENSIDE, KINGSIDE = (0, 1)
        self.board.castle[RED][KINGSIDE] = (
            1 << self.square(10, 0)) if 'rK' in castling else 0
        self.board.castle[RED][QUEENSIDE] = (
            1 << self.square(3, 0)) if 'rQ' in castling else 0
        self.board.castle[BLUE][KINGSIDE] = (
            1 << self.square(0, 10)) if 'bK' in castling else 0
        self.board.castle[BLUE][QUEENSIDE] = (
            1 << self.square(0, 3)) if 'bQ' in castling else 0
        self.board.castle[YELLOW][KINGSIDE] = (
            1 << self.square(3, 13)) if 'yK' in castling else 0
        self.board.castle[YELLOW][QUEENSIDE] = (
            1 << self.square(10, 13)) if 'yQ' in castling else 0
        self.board.castle[GREEN][KINGSIDE] = (
            1 << self.square(13, 3)) if 'gK' in castling else 0
        self.board.castle[GREEN][QUEENSIDE] = (
            1 << self.square(13, 10)) if 'gQ' in castling else 0

    def setBoardState(self, fen4):
        """Sets board according to FEN4."""
        if not fen4:
            return
        if self.getFen4(False) == fen4:  # Do not emit fen4Generated signal
            return
        self.setupBoard()
        self.board.parseFen4(fen4)
        self.setResult(self.NoResult)
        if SETTINGS.value('chesscom'):
            self.setCurrentPlayer(fen4[0].lower())
            self.moveNumber = 0
            self.fenMoveNumber = 1
        else:
            self.setCurrentPlayer(fen4.split(' ')[1])
            self.moveNumber = int(fen4.split(' ')[-2])
            self.fenMoveNumber = int(fen4.split(' ')[-2]) + 1
        self.currentMove = self.Node('root', [], None)
        self.currentMove.fen4 = fen4
        self.chesscomMoveText = ''
        self.moveText = ''
        self.moveDict.clear()
        self.getPgn4()  # Update PGN4

    def toChesscomMove(self, moveString):
        """Converts move string to chess.com move notation."""
        moveString = moveString.split()
        if moveString[0][1] == 'P':
            moveString.pop(0)
            if len(moveString) == 3:
                piece = moveString[1][1]
                if piece != 'P':
                    moveString[1] = 'x' + piece
                else:
                    moveString[1] = 'x'
            else:
                moveString.insert(1, '-')
        elif len(moveString) == 4:
            # Castling move
            shortCastle = [
                'rK h1 rR k1', 'bK a8 bR a11', 'yK g14 yR d14', 'gK n7 gR n4'
            ]
            longCastle = [
                'rK h1 rR d1', 'bK a8 bR a4', 'yK g14 yR k14', 'gK n7 gR n11'
            ]
            if ' '.join(moveString) in shortCastle:
                moveString = 'O-O'
            elif ' '.join(moveString) in longCastle:
                moveString = 'O-O-O'
            else:
                moveString[0] = moveString[0][1]
                piece = moveString[2][1]
                if piece != 'P':
                    moveString[2] = 'x' + piece
                else:
                    moveString[2] = 'x'
        else:
            if moveString != 'O-O' and moveString != 'O-O-O':
                moveString[0] = moveString[0][1]
                moveString.insert(2, '-')
        moveString = ''.join(moveString)
        return moveString

    def fromChesscomMove(self, move, player):
        """Returns fromFile, fromRank, toFile, toRank from chess.com move."""
        if move == 'O-O':
            if player == self.Red:
                fromFile, fromRank, toFile, toRank = (7, 0, 10, 0)
            elif player == self.Blue:
                fromFile, fromRank, toFile, toRank = (0, 7, 0, 10)
            elif player == self.Yellow:
                fromFile, fromRank, toFile, toRank = (6, 13, 3, 13)
            elif player == self.Green:
                fromFile, fromRank, toFile, toRank = (13, 6, 13, 3)
            else:
                fromFile, fromRank, toFile, toRank = [None] * 4
        elif move == 'O-O-O':
            if player == self.Red:
                fromFile, fromRank, toFile, toRank = (7, 0, 3, 0)
            elif player == self.Blue:
                fromFile, fromRank, toFile, toRank = (0, 7, 0, 3)
            elif player == self.Yellow:
                fromFile, fromRank, toFile, toRank = (6, 13, 10, 13)
            elif player == self.Green:
                fromFile, fromRank, toFile, toRank = (13, 6, 13, 10)
            else:
                fromFile, fromRank, toFile, toRank = [None] * 4
        else:
            for c in reversed(move):
                if c.isupper():
                    move = move.replace(c, '')
            move = move.replace('x', '')
            move = move.replace('-', '')
            move = move.replace('+', '')
            move = move.replace('#', '')
            prev = ''
            i = 0
            for char in move:
                if (not char.isdigit()) and prev.isdigit():
                    move = [move[:i], move[i:]]
                    break
                prev = char
                i += 1
            fromFile = ord(move[0][0]) - 97
            fromRank = int(move[0][1:]) - 1
            toFile = ord(move[1][0]) - 97
            toRank = int(move[1][1:]) - 1
        return fromFile, fromRank, toFile, toRank

    def toAlgebraic(self, moveString):
        """Converts move string to algebraic notation."""
        moveString = moveString.split()
        if moveString[0][1] == 'P':
            moveString.pop(0)
            if len(moveString) == 3:
                moveString[1] = 'x'
        elif len(moveString) == 4:
            # Castling move
            shortCastle = [
                'rK h1 rR k1', 'bK a8 bR a11', 'yK g14 yR d14', 'gK n7 gR n4'
            ]
            longCastle = [
                'rK h1 rR d1', 'bK a8 bR a4', 'yK g14 yR k14', 'gK n7 gR n11'
            ]
            if ' '.join(moveString) in shortCastle:
                moveString = 'O-O'
            elif ' '.join(moveString) in longCastle:
                moveString = 'O-O-O'
            else:
                moveString[0] = moveString[0][1]
                moveString[2] = 'x'
        else:
            if moveString != 'O-O' and moveString != 'O-O-O':
                moveString[0] = moveString[0][1]
        moveString = ''.join(moveString)
        return moveString

    def fromAlgebraic(self, move, player):
        """Returns fromFile, fromRank, toFile, toRank from algebraic move."""
        if move == 'O-O':
            if player == self.Red:
                fromFile, fromRank, toFile, toRank = (7, 0, 10, 0)
            elif player == self.Blue:
                fromFile, fromRank, toFile, toRank = (0, 7, 0, 10)
            elif player == self.Yellow:
                fromFile, fromRank, toFile, toRank = (6, 13, 3, 13)
            elif player == self.Green:
                fromFile, fromRank, toFile, toRank = (13, 6, 13, 3)
            else:
                fromFile, fromRank, toFile, toRank = [None] * 4
        elif move == 'O-O-O':
            if player == self.Red:
                fromFile, fromRank, toFile, toRank = (7, 0, 3, 0)
            elif player == self.Blue:
                fromFile, fromRank, toFile, toRank = (0, 7, 0, 3)
            elif player == self.Yellow:
                fromFile, fromRank, toFile, toRank = (6, 13, 10, 13)
            elif player == self.Green:
                fromFile, fromRank, toFile, toRank = (13, 6, 13, 10)
            else:
                fromFile, fromRank, toFile, toRank = [None] * 4
        else:
            if move[0].isupper():
                move = move[1:]
            move = move.replace('x', '')
            prev = ''
            i = 0
            for char in move:
                if (not char.isdigit()) and prev.isdigit():
                    move = [move[:i], move[i:]]
                    break
                prev = char
                i += 1
            fromFile = ord(move[0][0]) - 97
            fromRank = int(move[0][1:]) - 1
            toFile = ord(move[1][0]) - 97
            toRank = int(move[1][1:]) - 1
        return fromFile, fromRank, toFile, toRank

    def strMove(self, fromFile, fromRank, toFile, toRank):
        """Returns move in string form, separated by spaces, i.e. '<piece> <from> <captured piece> <to>'."""
        piece: str = self.board.getData(fromFile, fromRank)
        captured: str = self.board.getData(toFile, toRank)
        char = (piece + ' ' + chr(97 + fromFile) + str(fromRank + 1) + ' ' +
                captured * (captured != ' ') + ' ' + chr(97 + toFile) +
                str(toRank + 1))  # chr(97) = 'a'
        return char

    def prevMove(self):
        """Sets board state to previous move."""
        if self.currentMove.name == 'root':
            return
        moveString = self.currentMove.name
        moveString = moveString.split()
        piece = moveString[0]
        fromFile = ord(moveString[1][0]) - 97  # chr(97) = 'a'
        fromRank = int(moveString[1][1:]) - 1
        if len(moveString) == 4:
            captured = moveString[2]
            toFile = ord(moveString[3][0]) - 97
            toRank = int(moveString[3][1:]) - 1
        else:
            captured = ' '
            toFile = ord(moveString[2][0]) - 97
            toRank = int(moveString[2][1:]) - 1
        self.board.undoMove(fromFile, fromRank, toFile, toRank, piece,
                            captured)
        self.currentMove = self.currentMove.parent
        self.moveNumber -= 1
        self.playerQueue.rotate(1)
        self.setCurrentPlayer(self.playerQueue[0])
        # Signal View to remove last move highlight
        if self.currentPlayer == self.Red:
            color = QColor('#33bf3b43')
        elif self.currentPlayer == self.Blue:
            color = QColor('#334185bf')
        elif self.currentPlayer == self.Yellow:
            color = QColor('#33c09526')
        elif self.currentPlayer == self.Green:
            color = QColor('#334e9161')
        else:
            color = QColor('#00000000')
        self.removeHighlight.emit(color)
        if not self.currentMove.name == 'root':
            key = self.inverseMoveDict[self.currentMove]
            self.selectMove.emit(key)
        else:
            self.removeMoveSelection.emit()
        self.getFen4()  # Update FEN4
        self.getPgn4()  # Update PGN4

    def nextMove(self, var=0):
        """Sets board state to next move. Follows main variation by default (var=0)."""
        if not self.currentMove.children:
            return
        moveString = self.currentMove.children[var].name
        moveString = moveString.split()
        fromFile = ord(moveString[1][0]) - 97  # chr(97) = 'a'
        fromRank = int(moveString[1][1:]) - 1
        if len(moveString) == 4:
            toFile = ord(moveString[3][0]) - 97
            toRank = int(moveString[3][1:]) - 1
        else:
            toFile = ord(moveString[2][0]) - 97
            toRank = int(moveString[2][1:]) - 1
        self.board.makeMove(fromFile, fromRank, toFile, toRank)
        self.currentMove = self.currentMove.children[var]
        self.moveNumber += 1
        # Signal View to add move highlight and remove highlights of next player
        if self.currentPlayer == self.Red:
            color = QColor('#33bf3b43')
        elif self.currentPlayer == self.Blue:
            color = QColor('#334185bf')
        elif self.currentPlayer == self.Yellow:
            color = QColor('#33c09526')
        elif self.currentPlayer == self.Green:
            color = QColor('#334e9161')
        else:
            color = QColor('#00000000')
        self.addHighlight.emit(fromFile, fromRank, toFile, toRank, color)
        self.playerQueue.rotate(-1)
        self.setCurrentPlayer(self.playerQueue[0])
        if self.currentPlayer == self.Red:
            color = QColor('#33bf3b43')
        elif self.currentPlayer == self.Blue:
            color = QColor('#334185bf')
        elif self.currentPlayer == self.Yellow:
            color = QColor('#33c09526')
        elif self.currentPlayer == self.Green:
            color = QColor('#334e9161')
        else:
            color = QColor('#00000000')
        self.removeHighlight.emit(color)
        key = self.inverseMoveDict[self.currentMove]
        self.selectMove.emit(key)
        self.getFen4()  # Update FEN4
        self.getPgn4()  # Update PGN4

    def firstMove(self):
        """Sets board state to first move."""
        while self.currentMove.name != 'root':
            self.prevMove()

    def lastMove(self):
        """Sets board state to last move."""
        self.firstMove()
        while self.currentMove.children:
            self.nextMove()

    def makeMove(self, fromFile, fromRank, toFile, toRank):
        """This method must be implemented to define the proper logic corresponding to the game type (Teams or FFA)."""
        return False

    def getPgn4(self):
        """Generates PGN4 from current game."""
        pgn4 = ''

        # Tags: "?" if data unknown, "-" if not applicable
        pgn4 += '[Variant "' + self.variant + '"]\n'
        pgn4 += '[Site "www.chess.com/4-player-chess"]\n'
        pgn4 += '[Date "' + datetime.utcnow().strftime(
            '%a %b %d %Y %H:%M:%S (UTC)') + '"]\n'
        # pgn4 += '[Event "-"]\n'
        # pgn4 += '[Round "-"]\n'
        pgn4 += '[Red "' + self.redName + '"]\n' if not self.redName == '?' else ''
        pgn4 += '[RedElo "' + self.redRating + '"]\n' if not self.redRating == '?' else ''
        pgn4 += '[Blue "' + self.blueName + '"]\n' if not self.blueName == '?' else ''
        pgn4 += '[BlueElo "' + self.blueRating + '"]\n' if not self.blueRating == '?' else ''
        pgn4 += '[Yellow "' + self.yellowName + '"]\n' if not self.yellowName == '?' else ''
        pgn4 += '[YellowElo "' + self.yellowRating + '"]\n' if not self.yellowRating == '?' else ''
        pgn4 += '[Green "' + self.greenName + '"]\n' if not self.greenName == '?' else ''
        pgn4 += '[GreenElo "' + self.greenRating + '"]\n' if not self.greenRating == '?' else ''
        # pgn4 += '[Result "' + self.result + '"]\n'  # 1-0 (r & y win), 0-1 (b & g win), 1/2-1/2 (draw), * (no result)
        # pgn4 += '[Mode "ICS"]\n'  # ICS = Internet Chess Server, OTB = Over-The-Board
        pgn4 += '[TimeControl "G/1 d15"]\n'  # 1-minute game with 15 seconds delay per move
        pgn4 += '[PlyCount "' + str(
            self.moveNumber) + '"]\n'  # Total number of quarter-moves
        startFen4 = self.currentMove.getRoot().fen4
        if SETTINGS.value('chesscom'):
            if startFen4 != self.chesscomStartFen4:
                pgn4 += '[SetUp "1"]\n'
                pgn4 += '[StartFen4 "' + startFen4 + '"]\n'
        else:
            if startFen4 != self.startFen4:
                pgn4 += '[SetUp "1"]\n'
                pgn4 += '[StartFen4 "' + startFen4 + '"]\n'
        pgn4 += '[CurrentMove "' + self.currentMove.getMoveNumber() + '"]\n'
        pgn4 += '[CurrentPosition "' + self.getFen4() + '"]\n\n'

        # Movetext
        if SETTINGS.value('chesscom'):
            pgn4 = pgn4[:-1]  # remove newline
            pgn4 += self.chesscomMoveText
        else:
            pgn4 += self.moveText

            # Append result
            pgn4 += self.result

        self.pgn4Generated.emit(pgn4)

    def updateMoveText(self):
        """Updates movetext and dictionary."""
        self.chesscomMoveText = ''
        self.moveText = ''
        self.moveDict.clear()
        self.index = 0
        self.getMoveText(self.currentMove.getRoot(), self.fenMoveNumber)
        self.inverseMoveDict = {
            value: key
            for key, value in self.moveDict.items()
        }
        self.moveTextChanged.emit(self.moveText)
        if self.currentMove.name != 'root':
            key = self.inverseMoveDict[self.currentMove]
            self.selectMove.emit(key)

    def getMoveText(self, node, move=1, var=0):
        """Traverses move tree to generate movetext and updates move dictionary to keep track of the nodes associated
        with the movetext."""
        if node.children:
            main = node.children[0]
            if len(node.children) > 1:
                variations = node.children[1:]
            else:
                variations = None
        else:
            main = None
            variations = None
        # If different FEN4 starting position used, insert move number if needed
        if node.name == 'root' and move != 1 and (move - 1) % 4:
            token = str((move - 1) // 4 + 1) + '.'
            self.chesscomMoveText += token
            self.moveText += token + ' '
            self.moveDict[(self.index, token)] = None
            self.index += 1
            token = '.' * ((move - 1) % 4)
            if token:
                self.moveText += token
                self.moveDict[(self.index, token)] = None
                self.index += 1
            if (move - 1) % 4:
                self.chesscomMoveText += ' '
                self.moveText += ' '
        # Main move has variations
        if main and variations:
            if not (move - 1) % 4:
                token = str(move // 4 + 1) + '.'
                self.chesscomMoveText += '\n' + token + ' '
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = None
                self.index += 1
            else:
                self.chesscomMoveText += '.. '
            # Add main move to movetext before expanding variations, but do not expand main move yet
            chesscomToken = self.toChesscomMove(main.name)
            self.chesscomMoveText += chesscomToken + ' '
            token = self.toAlgebraic(main.name)
            self.moveText += token + ' '
            self.moveDict[(self.index, token)] = main
            self.index += 1
            if main.comment:
                self.chesscomMoveText += '{ ' + main.comment + ' } '
                self.moveText += '{ ' + main.comment + ' } '
            # Expand variations
            for variation in variations:
                if self.moveText[-2] == ')':
                    self.index += 1
                token = '('
                self.chesscomMoveText += token + ' '
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = None
                self.index += 1
                token = str(move // 4 + 1)
                self.chesscomMoveText += token
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = None
                self.index += 1
                token = '.' * ((move - 1) % 4)
                if token:
                    self.moveText += token
                    self.moveDict[(self.index, token)] = None
                    self.index += 1
                if (move - 1) % 4:
                    self.chesscomMoveText += '.. '
                    self.moveText += ' '
                else:
                    self.chesscomMoveText += '. '
                chesscomToken = self.toChesscomMove(variation.name)
                self.chesscomMoveText += chesscomToken + ' '
                token = self.toAlgebraic(variation.name)
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = variation
                self.index += 1
                if variation.comment:
                    self.chesscomMoveText += '{ ' + variation.comment + ' } '
                    self.moveText += '{ ' + variation.comment + ' } '
                self.getMoveText(variation, move + 1, var + 1)
            # Expand main move
            self.index += 1
            self.getMoveText(main, move + 1, var)
        # Main move has no variations
        elif main and not variations:
            if not (move - 1) % 4:
                token = str(move // 4 + 1) + '.'
                self.chesscomMoveText += '\n' + token + ' '
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = None
                self.index += 1
            else:
                self.chesscomMoveText += '.. '
            chesscomToken = self.toChesscomMove(main.name)
            self.chesscomMoveText += chesscomToken + ' '
            token = self.toAlgebraic(main.name)
            self.moveText += token + ' '
            self.moveDict[(self.index, token)] = main
            self.index += 1
            if main.comment:
                self.chesscomMoveText += '{ ' + main.comment + ' } '
                self.moveText += '{ ' + main.comment + ' } '
            self.getMoveText(main, move + 1, var)
        # Node is leaf node (i.e. end of variation or main line)
        else:
            if var != 0:
                token = ')'
                self.chesscomMoveText += token + ' '
                self.moveText += token + ' '
                self.moveDict[(self.index, token)] = None

    def split_(self, movetext):
        """Splits movetext into tokens."""
        x = split('\s+(?={)|(?<=})\s+', movetext
                  )  # regex: one or more spaces followed by { or preceded by }
        movetext = []
        for y in x:
            if y:
                if y[0] != '{':
                    for z in y.split():
                        movetext.append(z)
                else:
                    movetext.append(y)
        return movetext

    def parseChesscomPgn4(self, pgn4):
        """Parses chess.com PGN4 and sets game state accordingly."""
        startPosition = None
        currentMove = None
        lines = pgn4.split('\n')
        movetext = ''
        for line in lines:
            if line == '':
                continue
            elif line[0] == '[' and line[-1] == ']':
                tag = line.strip('[]').split('"')[:-1]
                tag[0] = tag[0].strip()
                if tag[0] == 'Variant' and tag[1] == 'FFA':
                    self.cannotReadPgn4.emit()
                    return False
                elif tag[0] == 'Red':
                    self.redName = tag[1]
                elif tag[0] == 'RedElo':
                    self.redRating = tag[1]
                elif tag[0] == 'Blue':
                    self.blueName = tag[1]
                elif tag[0] == 'BlueElo':
                    self.blueRating = tag[1]
                elif tag[0] == 'Yellow':
                    self.yellowName = tag[1]
                elif tag[0] == 'YellowElo':
                    self.yellowRating = tag[1]
                elif tag[0] == 'Green':
                    self.greenName = tag[1]
                elif tag[0] == 'GreenElo':
                    self.greenRating = tag[1]
                elif tag[0] == 'Result':
                    self.result = tag[1]
                elif tag[0] == 'StartFen4':
                    startPosition = tag[1]
                elif tag[0] == 'CurrentMove':
                    currentMove = tag[1]
                else:
                    # Irrelevant tags
                    pass
            else:
                if not currentMove:
                    self.cannotReadPgn4.emit()
                    return False
                movetext += line + ' '
            # Generate game from movetext
            self.newGame()
            tokens = self.split_(movetext)
            for token in tokens:
                if token[0] == '(' and len(token) > 1:
                    index = tokens.index(token)
                    tokens.insert(index + 1, token[1:])
                    tokens[index] = token[0]
            roots = []
            prev = None
            i = 0
            for token in tokens:
                try:
                    next_ = tokens[i + 1]
                except IndexError:
                    next_ = None
                if (token[0].isdigit()
                        and token[-1] == '.') or token in '..RT#':
                    pass
                elif token[0] == '{':
                    # Comment
                    self.currentMove.comment = token[1:-1].strip()
                elif token == '(':
                    # Next move is variation
                    if not prev == ')':
                        self.prevMove()
                        roots.append(self.currentMove)
                    else:
                        roots.append(self.currentMove)
                elif token == ')':
                    # End of variation
                    root = roots.pop()
                    while self.currentMove.name != root.name:
                        self.prevMove()
                    if next_ != '(':
                        # Continue with previous line
                        self.nextMove()
                else:
                    fromFile, fromRank, toFile, toRank = self.fromChesscomMove(
                        token, self.currentPlayer)
                    self.makeMove(fromFile, fromRank, toFile, toRank)
                prev = token
                i += 1
        # Set game position to CurrentMove ("ply-variation-move")
        self.firstMove()
        currentMove = [int(c) for c in currentMove.split('-')]
        if len(currentMove) == 1:
            ply = currentMove[0]
            for _ in range(ply):
                self.nextMove()
        else:
            ply, variation, move = currentMove
            for _ in range(ply - 1):
                self.nextMove()
            self.nextMove(variation)
            for _ in range(move - 1):
                self.nextMove()
        # Emit signal to update player names and rating
        self.playerNamesChanged.emit(self.redName, self.blueName,
                                     self.yellowName, self.greenName)
        self.playerRatingChanged.emit(self.redRating, self.blueRating,
                                      self.yellowRating, self.greenRating)
        return True

    def parsePgn4(self, pgn4):
        """Parses PGN4 and sets game state accordingly."""
        currentPosition = None
        lines = pgn4.split('\n')
        for line in lines:
            if line == '':
                continue
            elif line[0] == '[' and line[-1] == ']':
                tag = line.strip('[]').split('"')[:-1]
                tag[0] = tag[0].strip()
                if tag[0] == 'Variant' and tag[1] == 'FFA':
                    self.cannotReadPgn4.emit()
                    return False
                elif tag[0] == 'Red':
                    self.redName = tag[1]
                elif tag[0] == 'Blue':
                    self.blueName = tag[1]
                elif tag[0] == 'Yellow':
                    self.yellowName = tag[1]
                elif tag[0] == 'Green':
                    self.greenName = tag[1]
                elif tag[0] == 'Result':
                    self.result = tag[1]
                elif tag[0] == 'CurrentPosition':
                    currentPosition = tag[1]
                else:
                    # Irrelevant tags
                    pass
            else:
                if not currentPosition:
                    self.cannotReadPgn4.emit()
                    return False
                # Generate game from movetext
                self.newGame()
                line = line.replace(' *', '')
                line = line.replace(' 1-0', '')
                line = line.replace(' 0-1', '')
                line = line.replace(' 1/2-1/2', '')
                if line == '*':
                    # No movetext to process
                    break
                roots = []
                tokens = self.split_(line)
                prev = None
                i = 0
                for token in tokens:
                    try:
                        next_ = tokens[i + 1]
                    except IndexError:
                        next_ = None
                    if token[0].isdigit() or token[0] == '.':
                        pass
                    elif token[0] == '{':
                        # Comment
                        self.currentMove.comment = token[1:-1].strip()
                    elif token == '(':
                        # Next move is variation
                        if not prev == ')':
                            self.prevMove()
                            roots.append(self.currentMove)
                        else:
                            roots.append(self.currentMove)
                    elif token == ')':
                        # End of variation
                        root = roots.pop()
                        while self.currentMove.name != root.name:
                            self.prevMove()
                        if next_ != '(':
                            # Continue with previous line
                            self.nextMove()
                    else:
                        fromFile, fromRank, toFile, toRank = self.fromAlgebraic(
                            token, self.currentPlayer)
                        self.makeMove(fromFile, fromRank, toFile, toRank)
                    prev = token
                    i += 1
        # Set game position to FEN4
        self.firstMove()
        node = None
        for node in self.traverse(self.currentMove, self.currentMove.children):
            if node.fen4 == currentPosition:
                break
        if node:
            actions = node.pathFromRoot()
            for action in actions:
                exec('self.' + action)
        # Emit signal to update player names
        self.playerNamesChanged.emit(self.redName, self.blueName,
                                     self.yellowName, self.greenName)
        return True

    def traverse(self, tree, children):
        """Traverses nodes of tree in breadth-first order."""
        yield tree
        last = tree
        for node in self.traverse(tree, children):
            for child in node.children:
                yield child
                last = child
            if last == node:
                return
Exemple #5
0
 def setupBoard(self):
     """Initializes board."""
     self.setBoard(Board(14, 14))
Exemple #6
0
import sys

from PyQt5.QtWidgets import QApplication
from gui.board import Board

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Board()
    sys.exit(app.exec_())
Exemple #7
0
def main():
    """Main function of the game.
    
    This function initializes the game and enters the PyGame main loop.
    """

    # Inits PyGame module
    pygame.init()

    # Loads Sugar standard cursor
    a, b, c, d = pygame.cursors.load_xbm("gui/standardcursor.xbm",
                                         "gui/standardcursor_mask.xbm")
    pygame.mouse.set_cursor(a, b, c, d)

    internal_size = (
        1200, 825
    )  # The game is designed to work in this size (xo display size)
    target_size = (
        900, 619
    )  # The game will be sown in this size, useful for testing in regular PCs with less resolution than xo

    flags = 0
    if olpcgames.ACTIVITY:
        # Running as Activity
        target_size = olpcgames.ACTIVITY.game_size
        #logic.Mesh.init_mesh(log)   # Mesh isn't ready in this version
    else:
        pass
        # Uncomment this if want to execute fullscreen on regular PCs
        # flags = pygame.FULLSCREEN

    real_screen = pygame.display.set_mode(target_size, flags)

    # The scale factor beetween internal and target
    if internal_size == target_size:
        scale = None
        internal_screen = real_screen  # The game works directly on the real screen
    else:
        # Running on regular PC, the screen its scaled to te target_size
        internal_screen = pygame.Surface(internal_size)
        scale = (internal_size[0] / float(target_size[0]),
                 internal_size[1] / float(target_size[1]))

    # Creates a new logic game, player names aren't used without mesh
    game = GameState("Jugador1", "Jugador2")
    board = Board(internal_screen, game)
    board.paint_board_elements()

    pygame.display.update()

    # This clock is used to keep the game at the desired FPS.
    clock = pygame.time.Clock()

    # Main loop
    update = True  # The first time the screen need to be updated
    running = True
    while running:

        # Waits for events, if none the game pauses:
        # http://wiki.laptop.org/go/Game_development_HOWTO#Reducing_CPU_Load
        milliseconds = clock.tick(
            MAX_FPS)  # waits if the game is running faster than MAX_FPS
        events = olpcgames.pausescreen.get_events(
            SLEEP_TIMEOUT
        )  # Event-management loop with support for pausing after X seconds (20 here)

        if events:
            for event in events:
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                    running = False

                if event.type == pygame.MOUSEBUTTONDOWN:
                    if scale:
                        x = event.pos[0] * scale[
                            0]  # Multiplies the real coordinates by the scale factor
                        y = event.pos[1] * scale[
                            1]  # to get the internal coordinates
                    else:
                        (x, y) = event.pos

                    update = board.processXY(x, y)

                if event.type == pygame.USEREVENT:
                    if event.code == olpcgames.FILE_READ_REQUEST:
                        game = _read_file(event.filename)
                        log.debug("Loaded:" + game.serialization())
                        board = Board(internal_screen, game)
                        update = True
                    elif event.code == olpcgames.FILE_WRITE_REQUEST:
                        _save_file(event.filename, game)

                if event.type > pygame.USEREVENT and event.type <= pygame.USEREVENT + 10:
                    log.debug("New user event")
                    board.user_event(event)
                    update = True

            if update == True:
                board.paint_board_elements()
                if scale:
                    pygame.transform.scale(internal_screen, target_size,
                                           real_screen)
                update = False

        pygame.display.flip()

    # Una vez que sale del loop manda la senal de quit para que cierre la ventana
    pygame.quit()
Exemple #8
0
 def __init__(self, win):
     self.win = win
     self.board = Board()
     self.is_moving_anim = False
     self.board.prep_anim()
     self.top_player = True
Exemple #9
0
class Engine:
    def __init__(self, win):
        self.win = win
        self.board = Board()
        self.is_moving_anim = False
        self.board.prep_anim()
        self.top_player = True

    def update(self):
        self.board.move_marbles()
        self.board.draw_board(self.win)
        self.board.draw_text(self.win, self.top_player)
        pygame.display.update()

        if self.board.animation_finished():
            self.is_moving_anim = False

    def move(self, pos=(-1, -1), pit_number=-1):
        if self.is_moving_anim == True:
            return False
        self.is_moving_anim = True

        # Find pit clicked by mouse
        if pit_number == -1:
            pit = self.board.get_pit(pos, self.top_player)
        else:
            pit = self.board.get_pit_by_number(pit_number, self.top_player)

        if pit == None:
            return False

        # Move the marbles
        last_pit = self.move_marbles(pit)

        # Check take (zbicie)
        self.check_take(last_pit)

        # Change move if the last pit was not a player's basket
        if not (last_pit.is_basket and last_pit.top_player == self.top_player):
            self.top_player = not self.top_player

    # Moves marbles and returns last pit
    def move_marbles(self, pit):
        marbles = pit.get_marbles()
        pit.remove_marbles()
        for i in range(len(marbles)):
            pit = pit.next_pit()
            if pit.is_basket and pit.top_player != self.top_player:
                pit = pit.next_pit()
            pit.add_marble(marbles[i])
        return pit

    def check_take(self, pit):
        cond = (pit.top_player == self.top_player
                and pit.get_marbles_count() == 1 and not pit.is_basket
                and pit.opposite_pit().get_marbles_count() != 0)
        if cond:
            self.board.get_basket(self.top_player).add_marble(
                pit.get_marbles()[0])
            pit.remove_marbles()
            opposite_pit = pit.opposite_pit()
            opposite_marbles = opposite_pit.get_marbles()
            opposite_pit.remove_marbles()
            for m in opposite_marbles:
                self.board.get_basket(self.top_player).add_marble(m)

    def game_over(self):
        return self.board.check_game_over(self.top_player)

    def update_baskets_gameover(self):
        self.board.update_baskets_gameover(self.top_player)

    def print_game_over(self):
        self.board.move_marbles()
        self.board.draw_board(self.win)
        self.board.draw_game_over(self.win)
        pygame.display.update()

    def gamestate(self):
        return self.board.generate_gamestate(self.top_player)
Exemple #10
0
 def _setup_board(self):
     return Board(self.root, self.players)
Exemple #11
0
class View(QWidget):
    """The View is responsible for rendering the current state of the board and signalling user interaction to the
    underlying logic."""
    clicked = pyqtSignal(QPoint)
    squareSizeChanged = pyqtSignal(QSize)
    playerNameEdited = pyqtSignal(str, str, str, str)
    playerRatingEdited = pyqtSignal(str, str, str, str)
    pieceMoved = pyqtSignal(QPoint, QPoint)
    dragStarted = pyqtSignal(QPoint)

    def __init__(self, *_):
        super().__init__()
        self.squareSize = QSize(50, 50)
        self.board = Board(14, 14)
        self.pieces = {}
        self.highlights = []
        self.playerHighlights = {
            'r': self.PlayerHighlight(12, 1, QColor('#bf3b43')),
            'b': self.PlayerHighlight(1, 1, QColor('#4185bf')),
            'y': self.PlayerHighlight(1, 12, QColor('#c09526')),
            'g': self.PlayerHighlight(12, 12, QColor('#4e9161'))
        }
        # Player labels
        self.redName = None
        self.redNameEdit = None
        self.redRating = '?'
        self.blueName = None
        self.blueNameEdit = None
        self.blueRating = '?'
        self.yellowName = None
        self.yellowNameEdit = None
        self.yellowRating = '?'
        self.greenName = None
        self.greenNameEdit = None
        self.greenRating = '?'
        self.createPlayerLabels()
        # Drag-drop
        self.setAcceptDrops(True)
        self.dragStart = None
        self.clickedSquare = None
        self.maskedSquare = None
        self.mouseButton = None
        self.currentPlayer = None
        # Board orientation
        self.orientation = deque(['r', 'b', 'y', 'g'])
        # Arrows and square highlight
        self.arrowStart = None
        self.keyModifier = None
        self.arrowColor = None
        self.squareColor = None
        # Coordinate help
        self.coordinate = None
        self.setMouseTracking(True)

    class SquareHighlight:
        """A square highlight."""
        Type = 1

        def __init__(self, file, rank, color):
            self.file = file
            self.rank = rank
            self.color = color

    class PlayerHighlight(SquareHighlight):
        """A player highlight. Same as square highlight, just renamed for convenience (unaltered subclass)."""
        pass

    class Arrow:
        """An arrow highlight, to show possible moves on the board."""
        Type = 2

        def __init__(self, origin, target, color):
            self.origin = origin
            self.target = target
            self.color = color

    class LegalMoveIndicator:
        """A legal move indicator."""
        Type = 3

        def __init__(self, square, capture=False):
            self.square = square
            self.capture = capture

    def rotateBoard(self, rotation):
        """Rotates board view (clockwise +, counterclockwise -)."""
        self.orientation.rotate(rotation)
        self.movePlayerLabels(self.orientation[0])
        self.removeArrows()
        self.update()

    def autoRotate(self, rotation):
        """Automatically rotates board after move is made or undone."""
        if SETTINGS.value('autorotate'):
            self.rotateBoard(rotation)

    def setCurrentPlayer(self, player):
        """Updates current player, if changed."""
        if self.currentPlayer == player:
            return
        self.currentPlayer = player

    def setBoard(self, board):
        """Updates board, if changed. Disconnects signals from old board and connects them to new board."""
        if self.board == board:
            return
        if self.board:
            try:
                self.board.disconnect()
            # If there are no signal-slot connections, TypeError is raised
            except TypeError:
                pass
        self.board = board
        if board:
            board.dataChanged.connect(self.update)
            board.boardReset.connect(self.update)
            board.boardReset.connect(self.resetHighlights)
            board.autoRotate.connect(self.autoRotate)
        self.updateGeometry()

    def setSquareSize(self, size):
        """Sets size of board squares and updates geometry accordingly."""
        if self.squareSize == size:
            return
        self.squareSize = size
        self.squareSizeChanged.emit(size)
        self.updateGeometry()

    def sizeHint(self):
        """Implements sizeHint() method. Computes and returns size based on size of board squares."""
        return QSize(self.squareSize.width() * self.board.files,
                     self.squareSize.height() * self.board.ranks)

    def squareRect(self, file, rank, orientation=None):
        """Returns square of type QRect at position (file, rank)."""
        sqSize = self.squareSize
        if orientation == 'b':
            return QRect(
                QPoint((self.board.ranks - (rank + 1)) * sqSize.width(),
                       (self.board.files - (file + 1)) * sqSize.height()),
                sqSize)
        elif orientation == 'y':
            return QRect(
                QPoint((self.board.files - (file + 1)) * sqSize.width(),
                       rank * sqSize.height()), sqSize)
        elif orientation == 'g':
            return QRect(QPoint(rank * sqSize.width(), file * sqSize.height()),
                         sqSize)
        else:  # red by default
            return QRect(
                QPoint(file * sqSize.width(),
                       (self.board.ranks - (rank + 1)) * sqSize.height()),
                sqSize)

    def squareCenter(self, square, orientation=None):
        """Returns center of square as QPoint."""
        sqSize = self.squareSize
        file = square.x()
        rank = square.y()
        if orientation == 'b':
            return QPoint((self.board.ranks -
                           (rank + 1)) * sqSize.width() + sqSize.width() / 2,
                          (self.board.files -
                           (file + 1)) * sqSize.height() + sqSize.height() / 2)
        elif orientation == 'y':
            return QPoint((self.board.files - (file + 1)) * sqSize.width() +
                          sqSize.width() / 2,
                          rank * sqSize.height() + sqSize.height() / 2)
        elif orientation == 'g':
            return QPoint(rank * sqSize.width() + sqSize.width() / 2,
                          file * sqSize.height() + sqSize.height() / 2)
        else:  # red by default
            return QPoint(file * sqSize.width() + sqSize.width() / 2,
                          (self.board.ranks -
                           (rank + 1)) * sqSize.height() + sqSize.height() / 2)

    def paintEvent(self, event):
        """Implements paintEvent() method. Draws squares and pieces on the board."""
        painter = QPainter()
        painter.begin(self)
        # Draw squares
        for rank in range(self.board.ranks):
            for file in range(self.board.files):
                # Do not paint 3x3 sub-grids at the corners
                if not ((file < 3 and rank < 3) or (file < 3 and rank > 10) or
                        (file > 10 and rank < 3) or (file > 10 and rank > 10)):
                    self.drawSquare(painter, file, rank)
        # Draw square highlights
        self.drawSquareHighlights(painter)
        painter.fillRect(self.squareRect(12, 1, self.orientation[0]),
                         QColor('#40bf3b43'))
        painter.fillRect(self.squareRect(1, 1, self.orientation[0]),
                         QColor('#404185bf'))
        painter.fillRect(self.squareRect(1, 12, self.orientation[0]),
                         QColor('#40c09526'))
        painter.fillRect(self.squareRect(12, 12, self.orientation[0]),
                         QColor('#404e9161'))
        # Show or hide player names
        if SETTINGS.value('shownames'):
            self.showNames()
        else:
            self.hideNames()
        # Draw pieces
        for rank in range(self.board.ranks):
            for file in range(self.board.files):
                if not self.maskedSquare == QPoint(
                        file, rank):  # When dragging a piece, don't paint it
                    self.drawPiece(painter, file, rank)
        # Draw coordinates
        if SETTINGS.value('showcoordinates'):
            for y in range(14):
                x = 0 if 2 < y < 11 else 3
                square = self.squareRect(x, y)
                square.moveTopLeft(QPoint(square.x() + 1, square.y() + 1))
                square = QRectF(square)  # Only works with QRectF, so convert
                if self.orientation[0] == 'b':
                    file = self.board.files - (y + 1)
                    rank = self.board.ranks - (x + 1)
                elif self.orientation[0] == 'y':
                    file = self.board.files - (x + 1)
                    rank = y
                elif self.orientation[0] == 'g':
                    file = y
                    rank = x
                else:  # red by default
                    file = x
                    rank = self.board.ranks - (y + 1)
                color = self.palette().color(QPalette.Light) if (file + rank) % 2 \
                    else self.palette().color(QPalette.Dark)
                font = QFont('Trebuchet MS', 10, QFont.Bold)
                painter.setPen(color)
                painter.setFont(font)
                if self.orientation[0] in 'ry':
                    painter.drawText(square, str(self.board.ranks - rank))
                else:
                    painter.drawText(square,
                                     chr(self.board.files - (file + 1) + 97))
            for x in range(14):
                y = 0 if 2 < x < 11 else 3
                square = self.squareRect(x, y)
                square.moveTopLeft(QPoint(square.x() - 1, square.y() - 1))
                square = QRectF(square)  # Only works with QRectF, so convert
                if self.orientation[0] == 'b':
                    file = self.board.files - (y + 1)
                    rank = self.board.ranks - (x + 1)
                elif self.orientation[0] == 'y':
                    file = self.board.files - (x + 1)
                    rank = y
                elif self.orientation[0] == 'g':
                    file = y
                    rank = x
                else:  # red by default
                    file = x
                    rank = self.board.ranks - (y + 1)
                color = self.palette().color(QPalette.Light) if (file + rank) % 2 \
                    else self.palette().color(QPalette.Dark)
                font = QFont('Trebuchet MS', 10, QFont.Bold)
                painter.setPen(color)
                painter.setFont(font)
                if self.orientation[0] in 'ry':
                    painter.drawText(square, Qt.AlignBottom | Qt.AlignRight,
                                     chr(file + 97))
                else:
                    painter.drawText(square, Qt.AlignBottom | Qt.AlignRight,
                                     str(rank + 1))
        # Draw arrows
        self.drawArrows(painter)
        # Draw legal moves
        if SETTINGS.value('showlegalmoves'):
            self.drawLegalMoves(painter)
        # Draw coordinate help
        if SETTINGS.value('coordinatehelp'):
            if self.coordinate:
                file = ord(self.coordinate[1][0]) - 97
                rank = int(self.coordinate[1][1:]) - 1
                if not ((file < 3 and rank < 3) or (file < 3 and rank > 10) or
                        (file > 10 and rank < 3) or (file > 10 and rank > 10)):
                    square = self.coordinate[0]
                    square = self.squareRect(square.x(), square.y())
                    square.moveTopLeft(QPoint(square.x(), square.y()))
                    square = QRectF(
                        square)  # Only works with QRectF, so convert
                    # Draw twice (grey and white with offset) to get shade effect for contrast
                    painter.setPen(QColor('#80404040'))
                    painter.setFont(QFont('Trebuchet MS', 20, QFont.Bold))
                    painter.drawText(square, Qt.AlignCenter | Qt.AlignVCenter,
                                     self.coordinate[1])
                    square.moveTopLeft(QPoint(square.x() - 1, square.y() - 1))
                    painter.setPen(QColor('white'))
                    painter.setFont(QFont('Trebuchet MS', 20, QFont.Bold))
                    painter.drawText(square, Qt.AlignCenter | Qt.AlignVCenter,
                                     self.coordinate[1])
        painter.end()

    def drawSquare(self, painter, file, rank):
        """Draws dark or light square at position (file, rank) using painter."""
        rect = self.squareRect(file, rank, self.orientation[0])
        fillColor = self.palette().color(
            QPalette.Midlight) if (file + rank) % 2 else self.palette().color(
                QPalette.Mid)
        painter.fillRect(rect, fillColor)

    def setPiece(self, char, icon):
        """Sets piece icon corresponding to algebraic piece name."""
        self.pieces[char] = icon
        self.update()

    def piece(self, char):
        """Returns piece icon corresponding to algebraic piece name."""
        return self.pieces[char]

    def drawPiece(self, painter, file, rank):
        """Draws piece at square (file, rank) using painter."""
        rect = self.squareRect(file, rank, self.orientation[0])
        char = self.board.getData(file, rank)
        if char != ' ':
            icon = self.piece(char)
            if not icon.isNull():
                icon.paint(painter, rect, Qt.AlignCenter)

    def squareAt(self, point, orientation=None):
        """Returns square (file, rank) of type QPoint that contains point."""
        sqSize = self.squareSize
        x = point.x() // sqSize.width()
        y = point.y() // sqSize.height()
        if (x < 0) or (x > 13) or (y < 0) or (y > 13):
            return QPoint()
        elif orientation == 'b':
            return QPoint(self.board.files - (y + 1),
                          self.board.ranks - (x + 1))
        elif orientation == 'y':
            return QPoint(self.board.files - (x + 1), y)
        elif orientation == 'g':
            return QPoint(y, x)
        else:  # red by default
            return QPoint(x, self.board.ranks - (y + 1))

    def mouseReleaseEvent(self, event):
        """Implements mouseReleaseEvent() method. Emits signal with clicked square (QPoint) in case of a left-click.
        Adds arrows and square highlights in case of a right-click (drag)."""
        point = self.squareAt(event.pos(), self.orientation[0])
        arrowEnd = self.squareAt(event.pos())
        if self.mouseButton == Qt.RightButton:
            if self.keyModifier == Qt.Key_1:
                self.arrowColor = QColor('#ab272f')
                self.squareColor = QColor('#80ab272f')
            elif self.keyModifier == Qt.Key_2:
                self.arrowColor = QColor('#2d71ab')
                self.squareColor = QColor('#802d71ab')
            elif self.keyModifier == Qt.Key_3:
                self.arrowColor = QColor('#ac8112')
                self.squareColor = QColor('#80ac8112')
            elif self.keyModifier == Qt.Key_4:
                self.arrowColor = QColor('#3a7d4d')
                self.squareColor = QColor('#803a7d4d')
            elif self.keyModifier == Qt.Key_0:
                self.arrowColor = QColor('#ff8c00')
                self.squareColor = QColor('#80ff8c00')
            else:
                if SETTINGS.value('autocolor'):
                    if self.orientation[0] == 'r':
                        self.arrowColor = QColor('#ab272f')
                        self.squareColor = QColor('#80ab272f')
                    elif self.orientation[0] == 'b':
                        self.arrowColor = QColor('#2d71ab')
                        self.squareColor = QColor('#802d71ab')
                    elif self.orientation[0] == 'y':
                        self.arrowColor = QColor('#ac8112')
                        self.squareColor = QColor('#80ac8112')
                    elif self.orientation[0] == 'g':
                        self.arrowColor = QColor('#3a7d4d')
                        self.squareColor = QColor('#803a7d4d')
                else:
                    self.arrowColor = QColor('#ff8c00')
                    self.squareColor = QColor('#80ff8c00')
            origin = self.squareCenter(self.arrowStart)
            target = self.squareCenter(arrowEnd)
            if origin == target:
                sq = self.squareAt(origin)
                file = sq.x()
                rank = sq.y()
                color = self.squareColor
                square = self.SquareHighlight(file, rank, color)
                # If already exists, remove existing
                removed = 0
                for highlight in reversed(self.highlights):
                    if highlight.Type == self.SquareHighlight.Type and highlight.file == file and \
                            highlight.rank == rank and highlight.color == color:
                        self.removeHighlight(highlight)
                        removed += 1
                if not removed:
                    # Do not allow drawing outside board
                    if not ((file < 3 and rank < 3) or
                            (file < 3 and rank > 10) or
                            (file > 10 and rank < 3) or
                            (file > 10 and rank > 10)):
                        self.addHighlight(square)
            else:
                color = self.arrowColor
                arrow = self.Arrow(origin, target, color)
                # If already exists, remove existing
                removed = 0
                for highlight in reversed(self.highlights):
                    if highlight.Type == self.Arrow.Type and highlight.origin == origin and \
                            highlight.target == target and highlight.color == color:
                        self.removeHighlight(highlight)
                        removed += 1
                if not removed:
                    # Do not allow drawing outside board
                    fromSquare = self.squareAt(origin)
                    toSquare = self.squareAt(target)
                    fromFile = fromSquare.x()
                    fromRank = fromSquare.y()
                    toFile = toSquare.x()
                    toRank = toSquare.y()
                    if not ((fromFile < 3 and fromRank < 3) or (fromFile < 3 and fromRank > 10) or
                            (fromFile > 10 and fromRank < 3) or (fromFile > 10 and fromRank > 10)) and not \
                            ((toFile < 3 and toRank < 3) or (toFile < 3 and toRank > 10) or
                             (toFile > 10 and toRank < 3) or (toFile > 10 and toRank > 10)):
                        self.addHighlight(arrow)
            return
        elif self.mouseButton == Qt.LeftButton:
            if point.isNull():
                return
            self.clicked.emit(point)
        else:
            return

    def mousePressEvent(self, event):
        """Implements mousePressEvent() method. Records drag start position, clicked square and mouse button. Also
        records arrow start and removes arrows if left-click on empty square."""
        self.dragStart = event.pos()
        self.clickedSquare = self.squareAt(event.pos(), self.orientation[0])
        self.mouseButton = event.buttons()
        self.arrowStart = self.squareAt(event.pos())
        # If empty square clicked with left mouse button, remove arrows
        if event.buttons() == Qt.LeftButton and \
                self.board.getData(self.clickedSquare.x(), self.clickedSquare.y()) == ' ':
            if self.keyModifier == Qt.Key_1:
                self.removeArrows([QColor('#80ab272f'), QColor('#ab272f')])
            elif self.keyModifier == Qt.Key_2:
                self.removeArrows([QColor('#802d71ab'), QColor('#2d71ab')])
            elif self.keyModifier == Qt.Key_3:
                self.removeArrows([QColor('#80ac8112'), QColor('#ac8112')])
            elif self.keyModifier == Qt.Key_4:
                self.removeArrows([QColor('#803a7d4d'), QColor('#3a7d4d')])
            elif self.keyModifier == Qt.Key_0:
                self.removeArrows([QColor('#80ff8c00'), QColor('#ff8c00')])
            else:
                self.removeArrows()

    def mouseMoveEvent(self, event):
        """Implements mouseMoveEvent() method. Executes drag action and gets coordinate of square under mouse."""
        # Coordinate help
        square = self.squareAt(event.pos(), self.orientation[0])
        text = chr(square.x() + 97) + str(square.y() + 1)
        square = self.squareAt(event.pos())
        self.coordinate = (square, text)
        self.update()
        # Drag action
        if not event.buttons() == Qt.LeftButton:
            return
        if not self.clickedSquare or not self.dragStart:
            return
        if (event.pos() - self.dragStart).manhattanLength() < 5:
            return
        char = self.board.getData(self.clickedSquare.x(),
                                  self.clickedSquare.y())
        if char[0] != self.currentPlayer:
            return
        if char != ' ':
            icon = self.piece(char)
            if icon.isNull():
                return
            self.showLegalMoves()
            iconPosition = self.squareRect(self.clickedSquare.x(),
                                           self.clickedSquare.y(),
                                           self.orientation[0]).topLeft()
            offset = QPoint(event.pos() - iconPosition)
            # Pixmap shown under cursor while dragging
            dpr = 2  # device pixel ratio
            pixmap = icon.pixmap(
                QSize(self.squareSize.width() * dpr,
                      self.squareSize.height() * dpr))
            pixmap.setDevicePixelRatio(dpr)
            # Serialize drag-drop data into QByteArray
            data = QByteArray()
            dataStream = QDataStream(data, QIODevice.WriteOnly)
            dataStream << icon << offset
            # Custom MIME data
            mimeData = QMimeData()
            mimeData.setData('dragdrop', data)
            # Drag action
            drag = QDrag(self)
            drag.setMimeData(mimeData)
            drag.setPixmap(pixmap)
            drag.setHotSpot(
                QPoint(self.squareSize.width() / 2,
                       self.squareSize.height() / 2))
            self.maskedSquare = self.clickedSquare
            self.dragStarted.emit(self.clickedSquare)
            drag.exec_()
            self.maskedSquare = None

    def dragEnterEvent(self, event):
        """Implements dragEnterEvent() method."""
        if event.mimeData().hasFormat('dragdrop'):
            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.ignore()
        else:
            event.ignore()

    def dragMoveEvent(self, event):
        """Implements dragMoveEvent() method."""
        if event.mimeData().hasFormat('dragdrop'):
            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.ignore()
        else:
            event.ignore()

    def dragLeaveEvent(self, event):
        """Implements dragLeaveEvent() method."""
        # Keep cursor on the board while dragging
        cursor = QCursor()
        position = cursor.pos()
        topLeft = self.mapToGlobal(
            self.geometry().topLeft()) - self.geometry().topLeft()
        bottomRight = self.mapToGlobal(
            self.geometry().bottomRight()) - self.geometry().topLeft()
        bound = lambda x, l, u: l if x < l else u if x > u else x
        x = bound(position.x(), topLeft.x() + 1, bottomRight.x() - 1)
        y = bound(position.y(), topLeft.y() + 1, bottomRight.y() - 1)
        if x != position.x() or y != position.y():
            cursor.setPos(x, y)

    def dropEvent(self, event):
        """Implements dropEvent() method. Handles drop action."""
        self.removeLegalMoveIndicators()
        if event.mimeData().hasFormat('dragdrop'):
            # Read data serialized from the QByteArray
            data = event.mimeData().data('dragdrop')
            dataStream = QDataStream(data, QIODevice.ReadOnly)
            icon = QIcon()
            offset = QPoint()
            dataStream >> icon >> offset
            # Send signal to make the move
            square = self.squareAt(event.pos(), self.orientation[0])
            self.pieceMoved.emit(self.clickedSquare, square)
            if event.source() == self:
                event.setDropAction(Qt.MoveAction)
                event.accept()
            else:
                event.ignore()
        else:
            event.ignore()

    def addHighlight(self, highlight):
        """Adds highlight to the list and redraws view."""
        self.highlights.append(highlight)
        self.update()

    def removeHighlight(self, highlight):
        """Removes highlight from the list and redraws view."""
        try:
            self.highlights.remove(highlight)
            self.update()
        except ValueError:
            pass

    def removeHighlightsOfColor(self, color):
        """Removes all highlights of color."""
        for highlight in reversed(
                self.highlights
        ):  # reversed list, because modifying while looping
            if not highlight.Type == self.LegalMoveIndicator.Type and highlight.color == color:
                self.removeHighlight(highlight)

    def removeArrows(self, colors=None):
        """Removes all arrows and highlighted squares drawn on the board."""
        if colors:
            for color in colors:
                self.removeHighlightsOfColor(color)
        else:
            colors = [
                QColor('#80ff8c00'),
                QColor('#80ab272f'),
                QColor('#802d71ab'),
                QColor('#80ac8112'),
                QColor('#803a7d4d')
            ]
            for highlight in reversed(
                    self.highlights
            ):  # reversed list, because modifying while looping
                if highlight.Type == self.Arrow.Type:
                    self.removeHighlight(highlight)
                elif highlight.Type == self.SquareHighlight.Type and highlight.color in colors:
                    self.removeHighlight(highlight)

    def drawSquareHighlights(self, painter):
        """Draws all recognized highlights stored in the list."""
        for highlight in self.highlights:
            if highlight.Type == self.SquareHighlight.Type:
                colors = [
                    QColor('#80ff8c00'),
                    QColor('#80ab272f'),
                    QColor('#802d71ab'),
                    QColor('#80ac8112'),
                    QColor('#803a7d4d')
                ]
                if highlight.color in colors:
                    rect = self.squareRect(highlight.file, highlight.rank)
                else:
                    rect = self.squareRect(highlight.file, highlight.rank,
                                           self.orientation[0])
                painter.fillRect(rect, highlight.color)

    def drawArrows(self, painter):
        """Draws arrows on the board."""
        for highlight in self.highlights:
            if highlight.Type == self.Arrow.Type:
                lineWidth = 10
                sqSize = self.squareSize
                painter.setPen(
                    QPen(highlight.color, lineWidth, Qt.SolidLine,
                         Qt.RoundCap))
                origin = highlight.origin
                target = highlight.target
                dx = target.x() - origin.x()
                dy = target.y() - origin.y()
                # Knight jumps
                if dx == sqSize.width() and dy == -2 * sqSize.height():
                    corner = QPoint(origin.x(),
                                    origin.y() - 2 * sqSize.height())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == 2 * sqSize.width() and dy == -sqSize.height():
                    corner = QPoint(origin.x() + 2 * sqSize.width(),
                                    origin.y())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == 2 * sqSize.width() and dy == sqSize.height():
                    corner = QPoint(origin.x() + 2 * sqSize.width(),
                                    origin.y())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == sqSize.width() and dy == 2 * sqSize.height():
                    corner = QPoint(origin.x(),
                                    origin.y() + 2 * sqSize.height())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == -sqSize.width() and dy == 2 * sqSize.height():
                    corner = QPoint(origin.x(),
                                    origin.y() + 2 * sqSize.height())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == -2 * sqSize.width() and dy == sqSize.height():
                    corner = QPoint(origin.x() - 2 * sqSize.width(),
                                    origin.y())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == -2 * sqSize.width() and dy == -sqSize.height():
                    corner = QPoint(origin.x() - 2 * sqSize.width(),
                                    origin.y())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                elif dx == -sqSize.width() and dy == -2 * sqSize.height():
                    corner = QPoint(origin.x(),
                                    origin.y() - 2 * sqSize.height())
                    line = QLineF(origin, corner)
                    painter.drawLine(line)
                    origin = corner
                # Other moves (and second part of knight jump)
                line = QLineF(origin, target)
                angle = line.angle()
                tip = line.p2()
                line.setLength(line.length() - 2 * lineWidth)
                painter.drawLine(line)
                tipOffset = line.p2()
                leftBase = QLineF()
                leftBase.setP1(tipOffset)
                leftBase.setLength(lineWidth)
                leftBase.setAngle(angle - 90)
                left = leftBase.p2()
                rightBase = QLineF()
                rightBase.setP1(tipOffset)
                rightBase.setLength(lineWidth)
                rightBase.setAngle(angle + 90)
                right = rightBase.p2()
                arrowHead = QPolygonF([left, right, tip])
                path = QPainterPath()
                path.addPolygon(arrowHead)
                painter.fillPath(path, QBrush(highlight.color))

    def showLegalMoves(self):
        """Shows legal moves."""
        if SETTINGS.value('showlegalmoves'):
            fromFile = self.clickedSquare.x()
            fromRank = self.clickedSquare.y()
            identifier = self.board.getData(fromFile, fromRank)
            if identifier != ' ' and identifier[0] == self.currentPlayer:
                color = ['r', 'b', 'y', 'g'].index(identifier[0])
                piece = ['P', 'N', 'B', 'R', 'Q', 'K'].index(identifier[1]) + 4
                self.addLegalMoveIndicators(piece, fromFile, fromRank, color)

    def addLegalMoveIndicators(self, piece, fromFile, fromRank, color):
        """Adds legal move indicators."""
        origin = self.board.square(fromFile, fromRank)
        moves = self.board.getSquares(
            self.board.legalMoves(piece, origin, color) & self.board.emptyBB)
        captures = self.board.getSquares(
            self.board.legalMoves(piece, origin, color)
            & self.board.occupiedBB)
        for move in moves:
            legalMoveIndicator = self.LegalMoveIndicator(
                QPoint(move[0], move[1]))
            self.addHighlight(legalMoveIndicator)
        for capture in captures:
            legalMoveIndicator = self.LegalMoveIndicator(
                QPoint(capture[0], capture[1]), True)
            self.addHighlight(legalMoveIndicator)

    def removeLegalMoveIndicators(self):
        """Removes all legal move indicators."""
        for highlight in reversed(self.highlights):
            if highlight.Type == self.LegalMoveIndicator.Type:
                self.removeHighlight(highlight)

    def drawLegalMoves(self, painter):
        """Draws legal move indicators."""
        for highlight in reversed(self.highlights):
            if highlight.Type == self.LegalMoveIndicator.Type:
                center = self.squareCenter(highlight.square,
                                           self.orientation[0])
                if highlight.capture:
                    lineWidth = 4
                    rx = self.squareSize.width() / 2 - lineWidth / 2
                    ry = rx
                    painter.setPen(
                        QPen(QColor('#40000000'), lineWidth, Qt.SolidLine))
                    painter.drawEllipse(center, rx, ry)
                else:
                    rx = self.squareSize.width() / 10
                    ry = rx
                    painter.setBrush(QColor('#40000000'))
                    painter.setPen(Qt.NoPen)
                    painter.drawEllipse(center, rx, ry)

    def highlightPlayer(self, player):
        """Adds highlight for player to indicate turn. Removes highlights for other players if they exist."""
        self.addHighlight(self.playerHighlights[player])
        for otherPlayer in self.playerHighlights:
            if otherPlayer != player:
                try:
                    self.removeHighlight(self.playerHighlights[otherPlayer])
                except ValueError:
                    pass

    def highlightChecks(self):
        """Adds red square highlight for kings in check."""
        checkColor = QColor('#ccff0000')
        for highlight in reversed(
                self.highlights
        ):  # reversed list, because modifying while looping
            if highlight.Type == self.SquareHighlight.Type and highlight.color == checkColor:
                self.removeHighlight(highlight)
        for color in range(4):
            inCheck, (file, rank) = self.board.kingInCheck(color)
            if inCheck:
                highlight = self.SquareHighlight(file, rank, checkColor)
                self.addHighlight(highlight)

    def resetHighlights(self):
        """Clears list of highlights and redraws view."""
        self.highlights = []
        self.update()

    class PlayerName(QPushButton):
        """Editable player name label."""
        def __init__(self):
            super().__init__()
            self.setFixedSize(150, 50)
            self.setText('Player Name')
            self.setStyleSheet("""
                QPushButton {border: none; font-weight: bold;}
                QPushButton[player='red'] {color: #bf3b43;}
                QPushButton[player='blue'] {color: #4185bf;}
                QPushButton[player='yellow'] {color: #c09526;}
                QPushButton[player='green'] {color: #4e9161;}
                """)

    class PlayerNameEdit(QLineEdit):
        """Player name edit field."""
        focusOut = pyqtSignal()

        def __init__(self):
            super().__init__()
            self.setFixedSize(150, 50)
            self.setAlignment(Qt.AlignCenter)
            self.setFrame(False)
            self.setAttribute(Qt.WA_MacShowFocusRect, 0)
            self.installEventFilter(self)
            self.setStyleSheet("""
                QLineEdit {font-weight: bold;}
                QLineEdit[player='red'] {color: #bf3b43;}
                QLineEdit[player='blue'] {color: #4185bf;}
                QLineEdit[player='yellow'] {color: #c09526;}
                QLineEdit[player='green'] {color: #4e9161;}
                """)

        def eventFilter(self, object_, event):
            """Handles focusOut event."""
            if event.type() == QEvent.FocusOut:
                self.focusOut.emit()
            return False

    def createPlayerLabels(self):
        """Adds editable player name labels to board view."""
        # Red player
        self.redName = self.PlayerName()
        self.redName.setProperty('player', 'red')
        self.redName.move(550, 650)
        self.redName.setParent(self)
        self.redName.show()
        self.redName.clicked.connect(
            lambda: self.editPlayerName(self.redNameEdit))
        self.redNameEdit = self.PlayerNameEdit()
        self.redNameEdit.setProperty('player', 'red')
        self.redNameEdit.move(550, 650)
        self.redNameEdit.setParent(self)
        self.redNameEdit.show()
        self.redNameEdit.setHidden(True)
        self.redNameEdit.returnPressed.connect(
            lambda: self.setPlayerName(self.redName))
        self.redNameEdit.focusOut.connect(
            lambda: self.setPlayerName(self.redName))
        # Blue player
        self.blueName = self.PlayerName()
        self.blueName.setProperty('player', 'blue')
        self.blueName.move(0, 650)
        self.blueName.setParent(self)
        self.blueName.show()
        self.blueName.clicked.connect(
            lambda: self.editPlayerName(self.blueNameEdit))
        self.blueNameEdit = self.PlayerNameEdit()
        self.blueNameEdit.setProperty('player', 'blue')
        self.blueNameEdit.move(0, 650)
        self.blueNameEdit.setParent(self)
        self.blueNameEdit.show()
        self.blueNameEdit.setHidden(True)
        self.blueNameEdit.returnPressed.connect(
            lambda: self.setPlayerName(self.blueName))
        self.blueNameEdit.focusOut.connect(
            lambda: self.setPlayerName(self.blueName))
        # Yellow player
        self.yellowName = self.PlayerName()
        self.yellowName.setProperty('player', 'yellow')
        self.yellowName.move(0, 0)
        self.yellowName.setParent(self)
        self.yellowName.show()
        self.yellowName.clicked.connect(
            lambda: self.editPlayerName(self.yellowNameEdit))
        self.yellowNameEdit = self.PlayerNameEdit()
        self.yellowNameEdit.setProperty('player', 'yellow')
        self.yellowNameEdit.move(0, 0)
        self.yellowNameEdit.setParent(self)
        self.yellowNameEdit.show()
        self.yellowNameEdit.setHidden(True)
        self.yellowNameEdit.returnPressed.connect(
            lambda: self.setPlayerName(self.yellowName))
        self.yellowNameEdit.focusOut.connect(
            lambda: self.setPlayerName(self.yellowName))
        # Green player
        self.greenName = self.PlayerName()
        self.greenName.setProperty('player', 'green')
        self.greenName.move(550, 0)
        self.greenName.setParent(self)
        self.greenName.show()
        self.greenName.clicked.connect(
            lambda: self.editPlayerName(self.greenNameEdit))
        self.greenNameEdit = self.PlayerNameEdit()
        self.greenNameEdit.setProperty('player', 'green')
        self.greenNameEdit.move(550, 0)
        self.greenNameEdit.setParent(self)
        self.greenNameEdit.show()
        self.greenNameEdit.setHidden(True)
        self.greenNameEdit.returnPressed.connect(
            lambda: self.setPlayerName(self.greenName))
        self.greenNameEdit.focusOut.connect(
            lambda: self.setPlayerName(self.greenName))

    def movePlayerLabels(self, orientation):
        """Moves player labels when board orientation is changed."""
        if orientation == 'b':
            self.redName.move(550, 0)
            self.redNameEdit.move(550, 0)
            self.blueName.move(550, 650)
            self.blueNameEdit.move(550, 650)
            self.yellowName.move(0, 650)
            self.yellowNameEdit.move(0, 650)
            self.greenName.move(0, 0)
            self.greenNameEdit.move(0, 0)
        elif orientation == 'y':
            self.redName.move(0, 0)
            self.redNameEdit.move(0, 0)
            self.blueName.move(550, 0)
            self.blueNameEdit.move(550, 0)
            self.yellowName.move(550, 650)
            self.yellowNameEdit.move(550, 650)
            self.greenName.move(0, 650)
            self.greenNameEdit.move(0, 650)
        elif orientation == 'g':
            self.redName.move(0, 650)
            self.redNameEdit.move(0, 650)
            self.blueName.move(0, 0)
            self.blueNameEdit.move(0, 0)
            self.yellowName.move(550, 0)
            self.yellowNameEdit.move(550, 0)
            self.greenName.move(550, 650)
            self.greenNameEdit.move(550, 650)
        else:  # red by default
            self.redName.move(550, 650)
            self.redNameEdit.move(550, 650)
            self.blueName.move(0, 650)
            self.blueNameEdit.move(0, 650)
            self.yellowName.move(0, 0)
            self.yellowNameEdit.move(0, 0)
            self.greenName.move(550, 0)
            self.greenNameEdit.move(550, 0)

    def hideNames(self):
        """Hides player name labels."""
        self.redName.setHidden(True)
        self.blueName.setHidden(True)
        self.yellowName.setHidden(True)
        self.greenName.setHidden(True)

    def showNames(self):
        """Shows player name labels."""
        self.redName.setHidden(False)
        self.blueName.setHidden(False)
        self.yellowName.setHidden(False)
        self.greenName.setHidden(False)

    def editPlayerName(self, nameEdit):
        """Activates player name edit field."""
        name = self.sender()
        name.setHidden(True)
        nameEdit.setHidden(False)
        nameEdit.setFocus(True)

    def setPlayerName(self, name):
        """Updates player name label and deactivates player name edit field."""
        nameEdit = self.sender()
        s = name.text().split('\n')
        if len(s) == 2:
            rating = s[-1]
            n = nameEdit.text().split(' ')
            if len(n) > 1:
                e = n[-1].strip('()')
                if e.isdigit():
                    rating = '(' + e + ')'
                    name.setText(' '.join(n[:-1]) + '\n' + rating)
                else:
                    name.setText(' '.join(n) + '\n' + rating)
            else:
                name.setText(' '.join(n) + '\n' + rating)
        else:
            rating = None
            n = nameEdit.text().split(' ')
            if len(n) > 1:
                e = n[-1].strip('()')
                if e.isdigit():
                    rating = '(' + e + ')'
            if rating:
                name.setText(' '.join(n[:-1]) + '\n' + rating)
            else:
                name.setText(' '.join(n))
        nameEdit.setHidden(True)
        name.setHidden(False)
        red = self.redName.text().split('\n')
        blue = self.blueName.text().split('\n')
        yellow = self.yellowName.text().split('\n')
        green = self.greenName.text().split('\n')
        self.playerNameEdited.emit(red[0], blue[0], yellow[0], green[0])
        redRating = red[-1].strip('()') if len(red) > 1 else self.redRating
        blueRating = blue[-1].strip('()') if len(blue) > 1 else self.blueRating
        yellowRating = yellow[-1].strip(
            '()') if len(yellow) > 1 else self.yellowRating
        greenRating = green[-1].strip(
            '()') if len(green) > 1 else self.greenRating
        self.playerRatingEdited.emit(redRating, blueRating, yellowRating,
                                     greenRating)

    def setPlayerNames(self, red, blue, yellow, green):
        """Sets player names to names obtained from PGN4 file."""
        if red == '?':
            red = 'Player Name'
        if blue == '?':
            blue = 'Player Name'
        if yellow == '?':
            yellow = 'Player Name'
        if green == '?':
            green = 'Player Name'
        self.redName.setText(red)
        self.blueName.setText(blue)
        self.yellowName.setText(yellow)
        self.greenName.setText(green)
        self.setPlayerRating(self.redRating, self.blueRating,
                             self.yellowRating, self.greenRating)

    def setPlayerRating(self, redRating, blueRating, yellowRating,
                        greenRating):
        """Sets the player rating according to the PGN4 file."""
        if redRating != '?':
            self.redName.setText(self.redName.text() + '\n(' + redRating + ')')
        if blueRating != '?':
            self.blueName.setText(self.blueName.text() + '\n(' + blueRating +
                                  ')')
        if yellowRating != '?':
            self.yellowName.setText(self.yellowName.text() + '\n(' +
                                    yellowRating + ')')
        if greenRating != '?':
            self.greenName.setText(self.greenName.text() + '\n(' +
                                   greenRating + ')')
Exemple #12
0
def main():
    """Main function of the game.
    
    This function initializes the game and enters the PyGame main loop.
    """
    
    # Inits PyGame module
    pygame.init()
    
    # Loads Sugar standard cursor
    a, b, c, d = pygame.cursors.load_xbm("gui/standardcursor.xbm", "gui/standardcursor_mask.xbm")
    pygame.mouse.set_cursor(a, b, c, d)

    internal_size = (1200, 825)     # The game is designed to work in this size (xo display size)
    target_size = (900, 619)        # The game will be sown in this size, useful for testing in regular PCs with less resolution than xo
    
    flags = 0
    if olpcgames.ACTIVITY:
        # Running as Activity
        target_size = olpcgames.ACTIVITY.game_size
        #logic.Mesh.init_mesh(log)   # Mesh isn't ready in this version
    else:
        pass
        # Uncomment this if want to execute fullscreen on regular PCs
        # flags = pygame.FULLSCREEN
    
    real_screen = pygame.display.set_mode(target_size, flags)
    
    # The scale factor beetween internal and target
    if internal_size == target_size:
        scale = None
        internal_screen = real_screen   # The game works directly on the real screen
    else:
        # Running on regular PC, the screen its scaled to te target_size
        internal_screen = pygame.Surface(internal_size)
        scale = (internal_size[0] / float(target_size[0]), internal_size[1] / float(target_size[1]) )
    
    # Creates a new logic game, player names aren't used without mesh
    game = GameState("Jugador1", "Jugador2") 
    board = Board(internal_screen, game)
    board.paint_board_elements()
    
    pygame.display.update()
    
    # This clock is used to keep the game at the desired FPS.
    clock = pygame.time.Clock()
    
    # Main loop
    update = True       # The first time the screen need to be updated
    running = True
    while running:
        
        # Waits for events, if none the game pauses:
        # http://wiki.laptop.org/go/Game_development_HOWTO#Reducing_CPU_Load
        milliseconds = clock.tick(MAX_FPS)                              # waits if the game is running faster than MAX_FPS
        events = olpcgames.pausescreen.get_events(SLEEP_TIMEOUT)        # Event-management loop with support for pausing after X seconds (20 here)
        
        if events:
            for event in events:
                if event.type == pygame.QUIT:
                    running = False
                elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                    running = False
            
                if event.type == pygame.MOUSEBUTTONDOWN:
                    if scale:
                        x = event.pos[0] * scale[0]     # Multiplies the real coordinates by the scale factor
                        y = event.pos[1] * scale[1]     # to get the internal coordinates
                    else:
                        (x, y) = event.pos
                    
                    update = board.processXY(x, y)
                
                if event.type == pygame.USEREVENT:
                    if event.code == olpcgames.FILE_READ_REQUEST:
                        game = _read_file(event.filename)
                        log.debug("Loaded:" + game.serialization())
                        board = Board(internal_screen, game)
                        update = True
                    elif event.code == olpcgames.FILE_WRITE_REQUEST:
                        _save_file(event.filename, game)
                
                if event.type > pygame.USEREVENT and event.type <= pygame.USEREVENT + 10:
                    log.debug("New user event")
                    board.user_event(event)
                    update = True
                
            if update == True:
                board.paint_board_elements()
                if scale:
                    pygame.transform.scale(internal_screen, target_size, real_screen)
                update = False
            
        pygame.display.flip()
        
    # Una vez que sale del loop manda la senal de quit para que cierre la ventana
    pygame.quit()
board_image = pygame.transform.scale(board_image, (window_width, window_height))
screen.blit(board_image, [0, 0])
pygame.display.set_caption("Chessmachine")
clock = pygame.time.Clock()
gui_board = None

def main(screen, draggable_pieces):
    for event in pygame.event.get():
        for i in range(32):
            draggable_piece = draggable_pieces[i]
            if draggable_piece != None:
                gui_board.treat_draggable_piece(screen, draggable_piece, event)

    for draggable_piece in draggable_pieces:
        if draggable_piece != None:
            draggable_piece.update(screen, draggable_pieces, board_image)

if __name__ == "__main__":
    os.environ['SDL_VIDEO_CENTERED'] = '1'
    pygame.init()
    clock = pygame.time.Clock()
    
    gui_board = Board(screen, board_image, "Human", "Artificial")
    #gui_board.game.state.printBoard()
    state_board = gui_board.game.state.board
    
    while 1:
        main(screen, gui_board.pieces)
        pygame.display.update()
        clock.tick(60)