2018-10-11 19:17:44 +00:00
|
|
|
import PySimpleGUI as sg
|
|
|
|
import os
|
|
|
|
import chess
|
|
|
|
import chess.pgn
|
|
|
|
import copy
|
2018-10-12 19:07:03 +00:00
|
|
|
import time
|
2018-10-11 19:17:44 +00:00
|
|
|
|
|
|
|
button_names = ('close', 'cookbook', 'cpu', 'github', 'pysimplegui', 'run', 'storage', 'timer')
|
2018-10-12 19:07:03 +00:00
|
|
|
|
|
|
|
CHESS_PATH = '.' # path to the chess pieces
|
2018-10-11 19:17:44 +00:00
|
|
|
|
|
|
|
BLANK = 0 # piece names
|
|
|
|
PAWNB = 1
|
|
|
|
KNIGHTB = 2
|
|
|
|
BISHOPB = 3
|
|
|
|
ROOKB = 4
|
|
|
|
KINGB = 5
|
|
|
|
QUEENB = 6
|
|
|
|
PAWNW = 7
|
|
|
|
KNIGHTW = 8
|
|
|
|
BISHOPW = 9
|
|
|
|
ROOKW = 10
|
|
|
|
KINGW = 11
|
|
|
|
QUEENW = 12
|
|
|
|
|
|
|
|
initial_board = [[ROOKB, KNIGHTB, BISHOPB, KINGB, QUEENB, BISHOPB, KNIGHTB, ROOKB ],
|
|
|
|
[PAWNB,]*8,
|
|
|
|
[BLANK,]*8,
|
|
|
|
[BLANK,]*8,
|
|
|
|
[BLANK,]*8,
|
|
|
|
[BLANK,]*8,
|
|
|
|
[PAWNW,]*8,
|
|
|
|
[ROOKW, KNIGHTW, BISHOPW, KINGW, QUEENW, BISHOPW, KNIGHTW, ROOKW]]
|
|
|
|
|
|
|
|
blank = os.path.join(CHESS_PATH, 'blank.png')
|
2018-10-12 20:09:11 +00:00
|
|
|
bishopB = os.path.join(CHESS_PATH, 'nbishopb.png')
|
|
|
|
bishopW = os.path.join(CHESS_PATH, 'nbishopw.png')
|
|
|
|
pawnB = os.path.join(CHESS_PATH, 'npawnb.png')
|
|
|
|
pawnW = os.path.join(CHESS_PATH, 'npawnw.png')
|
|
|
|
knightB = os.path.join(CHESS_PATH, 'nknightb.png')
|
|
|
|
knightW = os.path.join(CHESS_PATH, 'nknightw.png')
|
|
|
|
rookB = os.path.join(CHESS_PATH, 'nrookb.png')
|
|
|
|
rookW = os.path.join(CHESS_PATH, 'nrookw.png')
|
|
|
|
queenB = os.path.join(CHESS_PATH, 'nqueenB.png')
|
|
|
|
queenW = os.path.join(CHESS_PATH, 'nqueenW.png')
|
|
|
|
kingB = os.path.join(CHESS_PATH, 'nkingb.png')
|
|
|
|
kingW = os.path.join(CHESS_PATH, 'nkingw.png')
|
2018-10-11 19:17:44 +00:00
|
|
|
|
|
|
|
images = {BISHOPB: bishopB, BISHOPW: bishopW, PAWNB: pawnB, PAWNW: pawnW, KNIGHTB: knightB, KNIGHTW: knightW,
|
|
|
|
ROOKB: rookB, ROOKW: rookW, KINGB: kingB, KINGW: kingW, QUEENB: queenB, QUEENW: queenW, BLANK: blank}
|
|
|
|
|
|
|
|
def open_pgn_file(filename):
|
|
|
|
pgn = open(filename)
|
|
|
|
first_game = chess.pgn.read_game(pgn)
|
|
|
|
moves = [move for move in first_game.main_line()]
|
|
|
|
return moves
|
|
|
|
|
|
|
|
def render_square(image, key, location):
|
|
|
|
if (location[0] + location[1]) % 2:
|
|
|
|
color = '#B58863'
|
|
|
|
else:
|
|
|
|
color = '#F0D9B5'
|
|
|
|
return sg.RButton('', image_filename=image, size=(1, 1), button_color=('white', color), pad=(0, 0), key=key)
|
|
|
|
|
|
|
|
def redraw_board(window, board):
|
|
|
|
for i in range(8):
|
|
|
|
for j in range(8):
|
2018-10-12 19:07:03 +00:00
|
|
|
color = '#B58863' if (i+j) % 2 else '#F0D9B5'
|
2018-10-11 19:17:44 +00:00
|
|
|
piece_image = images[board[i][j]]
|
|
|
|
elem = window.FindElement(key=(i,j))
|
|
|
|
elem.Update(button_color = ('white', color),
|
|
|
|
image_filename=piece_image,)
|
|
|
|
|
|
|
|
def PlayGame():
|
|
|
|
|
|
|
|
menu_def = [['&File', ['&Open PGN File', 'E&xit' ]],
|
|
|
|
['&Help', '&About...'],]
|
|
|
|
|
2018-10-12 19:07:03 +00:00
|
|
|
# sg.SetOptions(margins=(0,0))
|
|
|
|
sg.ChangeLookAndFeel('GreenTan')
|
2018-10-11 19:17:44 +00:00
|
|
|
# create initial board setup
|
|
|
|
board = copy.deepcopy(initial_board)
|
|
|
|
# the main board display layout
|
2018-10-12 19:07:03 +00:00
|
|
|
board_layout = [[sg.T(' ')] + [sg.T('{}'.format(a), pad=((23,27),0), font='Any 13') for a in 'abcdefgh']]
|
2018-10-11 19:17:44 +00:00
|
|
|
# loop though board and create buttons with images
|
|
|
|
for i in range(8):
|
|
|
|
row = [sg.T(str(8-i)+' ', font='Any 13')]
|
|
|
|
for j in range(8):
|
|
|
|
piece_image = images[board[i][j]]
|
|
|
|
row.append(render_square(piece_image, key=(i,j), location=(i,j)))
|
|
|
|
row.append(sg.T(str(8-i)+' ', font='Any 13'))
|
|
|
|
board_layout.append(row)
|
|
|
|
# add the labels across bottom of board
|
2018-10-12 19:07:03 +00:00
|
|
|
board_layout.append([sg.T(' ')] + [sg.T('{}'.format(a), pad=((23,27),0), font='Any 13') for a in 'abcdefgh'])
|
2018-10-11 19:17:44 +00:00
|
|
|
|
|
|
|
# setup the controls on the right side of screen
|
|
|
|
openings = ('Any', 'Defense', 'Attack', 'Trap', 'Gambit','Counter', 'Sicillian', 'English','French', 'Queen\'s openings', 'King\'s Openings','Indian Openings')
|
|
|
|
|
|
|
|
board_controls = [[sg.RButton('New Game', key='Open PGN File'), sg.RButton('Draw')],
|
|
|
|
[sg.RButton('Resign Game'), sg.RButton('Set FEN')],
|
|
|
|
[sg.RButton('Player Odds'),sg.RButton('Training') ],
|
|
|
|
[sg.Drop(openings),sg.Text('Opening/Style')],
|
|
|
|
[sg.CBox('Play a White', key='_white_')],
|
|
|
|
[sg.Text('Move List')],
|
|
|
|
[sg.Multiline([], do_not_clear=True, autoscroll=True, size=(15,10),key='_movelist_')],]
|
|
|
|
|
|
|
|
# layouts for the tabs
|
|
|
|
controls_layout = [[sg.Text('Performance Parameters', font='_ 20')],
|
|
|
|
[sg.T('Put stuff like AI engine tuning parms on this tab')]]
|
|
|
|
|
|
|
|
statistics_layout = [[sg.Text('Statistics', font=('_ 20'))],
|
|
|
|
[sg.T('Game statistics go here?')]]
|
|
|
|
|
|
|
|
board_tab = [[sg.Column(board_layout)]]
|
|
|
|
|
|
|
|
# the main window layout
|
|
|
|
layout = [[sg.Menu(menu_def, tearoff=False)],
|
|
|
|
[sg.TabGroup([[sg.Tab('Board',board_tab),
|
|
|
|
sg.Tab('Controls', controls_layout),
|
2018-10-12 19:07:03 +00:00
|
|
|
sg.Tab('Statistics', statistics_layout)]], title_color='red'),
|
2018-10-11 19:17:44 +00:00
|
|
|
sg.Column(board_controls)],
|
|
|
|
[sg.Text('Click anywhere on board for next move', font='_ 14')]]
|
|
|
|
|
2018-10-12 20:09:11 +00:00
|
|
|
window = sg.Window('Chess', default_button_element_size=(12,1), auto_size_buttons=False, icon='kingb.ico').Layout(layout)
|
2018-10-11 19:17:44 +00:00
|
|
|
|
|
|
|
# ---===--- Loop taking in user input --- #
|
|
|
|
i = 0
|
|
|
|
moves = None
|
|
|
|
while True:
|
|
|
|
button, value = window.Read()
|
|
|
|
if button in (None, 'Exit'):
|
|
|
|
break
|
|
|
|
if button == 'Open PGN File':
|
|
|
|
filename = sg.PopupGetFile('', no_window=True)
|
|
|
|
if filename is not None:
|
|
|
|
moves = open_pgn_file(filename)
|
|
|
|
i = 0
|
|
|
|
board = copy.deepcopy(initial_board)
|
|
|
|
window.FindElement('_movelist_').Update(value='')
|
|
|
|
if button == 'About...':
|
|
|
|
sg.Popup('Powerd by Engine Kibitz Chess Engine')
|
|
|
|
if type(button) is tuple and moves is not None and i < len(moves):
|
|
|
|
move = moves[i] # get the current move
|
|
|
|
window.FindElement('_movelist_').Update(value='{} {}\n'.format(i+1, str(move)), append=True)
|
|
|
|
move_from = move.from_square # parse the move-from and move-to squares
|
|
|
|
move_to = move.to_square
|
2018-10-12 20:09:11 +00:00
|
|
|
row, col = move_from // 8, move_from % 8
|
2018-10-11 19:17:44 +00:00
|
|
|
piece = board[row][col] # get the move-from piece
|
2018-10-12 19:07:03 +00:00
|
|
|
button = window.FindElement(key=(row,col))
|
2018-10-12 20:09:11 +00:00
|
|
|
for x in range(3):
|
|
|
|
button.Update(button_color = ('white' , 'red' if x % 2 else 'white'))
|
|
|
|
window.Refresh()
|
|
|
|
time.sleep(.05)
|
2018-10-11 19:17:44 +00:00
|
|
|
board[row][col] = BLANK # place blank where piece was
|
2018-10-12 20:09:11 +00:00
|
|
|
row, col = move_to // 8, move_to % 8 # compute move-to square
|
2018-10-11 19:17:44 +00:00
|
|
|
board[row][col] = piece # place piece in the move-to square
|
|
|
|
redraw_board(window, board)
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
PlayGame()
|