AGGIORNAMENTO INTERFACCIA GRAFICA:
Per fare l'interfaccia grafica ho trovato più comodo l'utilizzo della libreria "Turtle", ci permette quindi di creare forme geometriche, disegni e moldo altro utilizzando un cursore grafico (chiamato "turtle") che risponde ai comandi dati tramite il codice Python.

Codice:
La struttura del codice è la seguente:
- main.py
- board.py
- patch_turtle_image.py
- pieces.py
- images (cartella con le immagini dei pezzi e scacchiera)
main.py crea una configurazione iniziale su una scacchiera 8x8 rappresentata da oggetti della classe Board e oggetti di diverse classi per rappresentare i pezzi King, Queen, Bishop, Knight, Rook, Pawn.
from board import Board
from pieces import King, Queen, Bishop, Knight, Rook, Pawn
board = Board(8, 8)
pw = Pawn(0, 1, "white", board)
pw1 = Pawn(1, 1, "white", board)
pw2 = Pawn(2, 1, "white", board)
pw3 = Pawn(3, 1, "white", board)
pw4 = Pawn(4, 1, "white", board)
pw5 = Pawn(5, 1, "white", board)
pw6 = Pawn(6, 1, "white", board)
pw7 = Pawn(7, 1, "white", board)
rw = Rook(0, 0, "white", board)
rw1 = Rook(7, 0, "white", board)
knw = Knight(1, 0, "white", board)
knw1 = Knight(6, 0, "white", board)
bw = Bishop(2, 0, "white", board)
bw1 = Bishop(5, 0, "white", board)
wk = King(4, 0, "white", board)
wq = Queen(3, 0, "white", board)
pb = Pawn(0, 6, "black", board)
pb1 = Pawn(1, 6, "black", board)
pb2 = Pawn(2, 6, "black", board)
pb3 = Pawn(3, 6, "black", board)
pb4 = Pawn(4, 6, "black", board)
pb5 = Pawn(5, 6, "black", board)
pb6 = Pawn(6, 6, "black", board)
pb7 = Pawn(7, 6, "black", board)
rb = Rook(0, 7, "black", board)
rb1 = Rook(7, 7, "black", board)
kbw = Knight(1, 7, "black", board)
kbw1 = Knight(6, 7, "black", board)
bw = Bishop(2, 7, "black", board)
bw1 = Bishop(5, 7, "black", board)
wk = King(4, 7, "black", board)
wq = Queen(3, 7, "black", board)
board.mainloop()
board.py invece usa Turtle per la scacchiera e la classe Board ha una matrice come attributo per rappresentare la posizione dei pezzi in precise posizioni.
La classe Board ha vari metodi, come place_piece, is_on_board, contains_black_piece, logic2graphic, graphic2logic, is_spot_occupied, process_click, e mainloop. Servono per visualizzare correttamente la scacchiera e permettere all'utente di spostare i pezzi.
from turtle import Screen
import patch_turtle_image
import math
class Board:
def __init__(self, size_x, size_y):
'''
posizionamento
'''
self.size_x = size_x
self.size_y = size_y
self.grid = []
for x in range(self.size_x):
self.grid.append([])
for y in range(self.size_y):
self.grid[x].append(None)
hmm = self.grid[1][1]
print(hmm)
self.screen = Screen()
self.screen.setup(width=1.0, height=1.0, startx=None, starty=None)
self.screen.screensize(400, 400)
self.screen.bgpic("images/board.png")
self.screen.register_shape("images/w_pawn.png")
self.screen.register_shape("images/b_pawn.png")
self.screen.register_shape("images/w_rook.png")
self.screen.register_shape("images/b_rook.png")
self.screen.register_shape("images/w_knight.png")
self.screen.register_shape("images/b_knight.png")
self.screen.register_shape("images/w_bishop.png")
self.screen.register_shape("images/b_bishop.png")
self.screen.register_shape("images/w_queen.png")
self.screen.register_shape("images/b_queen.png")
self.screen.register_shape("images/w_king.png")
self.screen.register_shape("images/b_king.png")
self.screen.onscreenclick(self.process_click)
self.selected_piece = None
def place_piece(self, piece):
self.grid[piece.x][piece.y] = piece
print(piece)
def is_on_board(self, x, y):
'''
Da 0 a -1
0 1 2 3 4 5 6 7
x o y?
'''
if x < 0 or y < 0:
return False
if x >= self.size_x or y >= self.size_y:
return False
return True
def contains_black_piece(self, x, y):
if not self.is_on_board(x, y):
return False
if self.grid[x][y] is None:
return False
return self.grid[x][y].color == "black"
def logic2graphic(self, x, y):
return x * 50 - 175, y * 50 - 175
def graphic2logic(self, x, y):
return math.floor((x + 200) / 50), math.floor((y + 200) / 50)
def is_spot_occupied(self, x, y):
pass
def process_click(self, x, y):
x, y = self.graphic2logic(x, y)
p = self.grid[x][y]
if p is self.selected_piece:
print("unselect")
self.selected_piece = None
return
if self.selected_piece is None:
self.selected_piece = p
if self.selected_piece is None:
print("Nessun pezzo")
else:
print("Hai selezionato ", (x, y))
else:
if (x, y) in self.selected_piece.get_valid_moves()and self.grid[x][y] is None:
self.grid[self.selected_piece.x][self.selected_piece.y] = None
self.selected_piece.move_to(x, y)
self.grid[x][y] = self.selected_piece
self.selected_piece = None
print("Mossa ", (x, y))
elif (x, y) in self.selected_piece.get_valid_moves() and self.grid[x][y].color != self.selected_piece.color:
self.grid[x][y].my_turtle.ht()
self.grid[self.selected_piece.x][self.selected_piece.y] = None
self.selected_piece.move_to(x, y)
self.grid[x][y] = self.selected_piece
self.selected_piece = None
print("Mossa ", (x, y))
else:
print("Mossa invalida")
def mainloop(self):
self.screen.mainloop()
patch_turtle_image.py modifica la classe TurtleScreenBase per aggiungere _image e quindi estende la classe TurtleScreen con il metodo register_shape.
In termini più semplici aggiunge due metodi al modulo turtle, uno estende il comportamento della classe TurtleScreenBase per consentire il caricamento di immagini da file utilizzando la libreria PIL. L'altro invece estende il comportamento della classe TurtleScreen per consentire l'utilizzo di forme personalizzate come immagini.
from turtle import TurtleScreenBase
from PIL import ImageTk
@staticmethod
def _image(filename):
return ImageTk.PhotoImage(file=filename)
TurtleScreenBase._image = _image
from turtle import Shape, TurtleScreen
def register_shape(self, name, shape=None):
if shape is None:
shape = Shape("image", self._image(name))
elif isinstance(shape, tuple):
shape = Shape("polygon", shape)
self._shapes[name] = shape
TurtleScreen.register_shape = register_shape
pieces.py (vedi la prima discussione)
from turtle import Turtle
speed = 5
class Knight:
def __init__(self, x, y, color, board):
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_knight.png"
else:
img = "images/b_knight.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
def get_valid_moves(self):
moves = []
moves.append((self.x - 2, self.y - 1))
moves.append((self.x - 1, self.y - 2))
moves.append((self.x - 1, self.y + 2))
moves.append((self.x - 2, self.y + 1))
moves.append((self.x + 1, self.y + 2))
moves.append((self.x + 2, self.y - 1))
moves.append((self.x + 2, self.y + 1))
moves.append((self.x + 1, self.y - 2))
moves = [(x, y) for (x, y) in moves if x >= 0 and x < 8 and y >= 0 and y < 8]
return moves
class King:
def __init__(self, x, y, color, board):
'''
posizionamento
'''
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_king.png"
else:
img = "images/b_king.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def get_valid_moves(self):
'''
Calcolo King
'''
moves = []
moves.append((self.x + 1, self.y + 1))
moves.append((self.x + 1, self.y - 1))
moves.append((self.x - 1, self.y + 1))
moves.append((self.x - 1, self.y - 1))
moves.append((self.x + 1, self.y))
moves.append((self.x - 1, self.y))
moves.append((self.x, self.y + 1))
moves.append((self.x, self.y - 1))
moves = [(x, y) for (x, y) in moves if self.board.is_on_board(x, y)]
return moves
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
class Pawn:
def __init__(self, x, y, color, board):
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_pawn.png"
else:
img = "images/b_pawn.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
def get_valid_moves(self):
if self.color == "white":
if self.y == 1:
moves = [(self.x, 2), (self.x, 3)]
else:
moves = [(self.x, self.y + 1)]
if self.board.contains_black_piece(self.x + 1, self.y + 1):
pass
else:
if self.y == 6:
moves = [(self.x, 5), (self.x, 4)]
else:
moves = [(self.x, self.y - 1)]
moves = [(x, y) for (x, y) in moves if self.board.is_on_board(x, y)]
return moves
class Rook:
def __init__(self, x, y, color, board):
'''
posizionamento
'''
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_rook.png"
else:
img = "images/b_rook.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
def get_valid_moves(self):
return []
class Bishop:
def __init__(self, x, y, color, board):
'''
posizionamento
'''
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_bishop.png"
else:
img = "images/b_bishop.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
def get_valid_moves(self):
return []
class Queen:
def __init__(self, x, y, color, board):
'''
constructor
'''
self.x = x
self.y = y
self.color = color
self.board = board
if self.color == "white":
img = "images/w_queen.png"
else:
img = "images/b_queen.png"
self.my_turtle = Turtle(img)
self.my_turtle.speed(speed)
self.my_turtle.penup()
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
self.board.place_piece(self)
def move_to(self, x, y):
self.x = x
self.y = y
self.my_turtle.goto(self.board.logic2graphic(self.x, self.y))
def get_valid_moves(self):
return []
Risultato finale:
Per i numerosi bug purtroppo un po' di funzioni sono state rimosse, si cercherà di migliorare!

(Perdonate la qualità ma da una gif non possiamo pretendere molto)😅