Hello, I would like to create a chess game GUI but I have a problem with my code. When I run my programm, at the beginning, my code is supposed to read a ".lvl" file to place pieces on the chessboard (same method as in Mario Sokoban). However, it doesn't work because pieces aren't display. Therefore, I would like to know : "What's wrong with my code ?"

Thanks in advance for your help :).

from tkinter import *
root=Tk()

#Variables for draw_board()
rows = 8
columns = 8
color1 = "#b35821" #Flery Orange
color2 = "#efcb9d" #New Tan
dim_square = 64
tile_center = dim_square/2
tiles=8
FEN_file="FEN_board.lvl"

#Variables for get_position()
x1=0
y1=0
x2=0
y2=0
clic=0
tour=0
tb=1800
tw=1800
clic1=0

#Variables for draw_pieces()
#k == King
#q == queen
#r == Rook
#b == Bishop
#n == Knight
#p == Pawn
#Importation Images Pièces NOIRES
blackb=PhotoImage(file="./pieces_image/blackb.gif")
blackk=PhotoImage(file="./pieces_image/blackk.gif")
blackn=PhotoImage(file="./pieces_image/blackn.gif")
blackp=PhotoImage(file="./pieces_image/blackp.gif")
blackq=PhotoImage(file="./pieces_image/blackq.gif")
blackr=PhotoImage(file="./pieces_image/blackr.gif")
#Importation Images Pièces BLANCHES
whiteb=PhotoImage(file="./pieces_image/whiteb.gif")
whitek=PhotoImage(file="./pieces_image/whitek.gif")
whiten=PhotoImage(file="./pieces_image/whiten.gif")
whitep=PhotoImage(file="./pieces_image/whitep.gif")
whiteq=PhotoImage(file="./pieces_image/whiteq.gif")
whiter=PhotoImage(file="./pieces_image/whiter.gif")

pieces=[blackb,blackk,blackn,blackp,blackq,blackr,whiteb,whitek,whiten,whitep,whiteq,whiter]


canvas=Canvas(root, width=512, height=512)
canvas.pack(expand=1, fill=BOTH)

board_representation=[]
for i in range(tiles):
      board_representation.append([""]*tiles)

def FEN_file_check(fname):
   try:
      f = open(fname,'r')
      f.close()
      return True
   except:
      return False

def interpretor(line):
   i,j=0,0

   for letter in line:
      if letter=="r" or letter=="n" or letter=="b" or letter=="q" or letter=="k" or letter=="p" or letter=="R" or letter=="N" or letter=="B" or letter=="Q" or letter=="K" or letter=="P":  
         board_representation[i][j]=letter
         for p in pieces:
            piecesx=i
            piecesy=j

      if i<tiles-1:
         i=i+1
      elif j<tiles-1:
         j=j+1
         i=0

def display_game(turn):
   canvas.delete(ALL)
   draw_chessboard()
   i,j=0,0
   for i in range(tiles):
      for j in range(tiles):
         board_representation[i][j]=" "
   if FEN_file_check(FEN_file):
      fich=open(FEN_file,"r")
      i= 1
      while i<=turn:
         line=fich.readline()
         i=i+1
      if line =="":
            messagebox.showinfo("ERROR, EMPTY FILE !")
      else :
            interpretor(line)
            fich.close
            canvas.bind('<Button-1>', move_image)
            canvas.bind('<Button-1>', get_position, add="+")
   else:
      messagebox.showerror("ERROR","FILE NOT FOUND !")

def refresh():
   canvas.delete(ALL)
   i,j=0,0
   for i in range(tiles):
      for j in range(tiles):
         if board_representation[i][j]=="r" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackr)
            canvas.pack()
         elif board_representation[i][j]=="n" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackn)
            canvas.pack()
         elif board_representation[i][j]=="b" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackb)
            canvas.pack()
         elif board_representation[i][j]=="q" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackq)
            canvas.pack()
         elif board_representation[i][j]=="k" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackk)
            canvas.pack()
         elif board_representation[i][j]=="p" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=blackp)
            canvas.pack()
         elif board_representation[i][j]=="R" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whiter)
            canvas.pack()
         elif board_representation[i][j]=="N" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whiten)
            canvas.pack()
         elif board_representation[i][j]=="B" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whiteb)
            canvas.pack()
         elif board_representation[i][j]=="Q" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whiteq)
            canvas.pack()
         elif board_representation[i][j]=="K" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whitek)
            canvas.pack()
         elif board_representation[i][j]=="P" :
            canvas.create_image(tile_center+i*dim_square,tile_center+j*dim_square,image=whitep)
            canvas.pack()

def move_validation(xdirection,ydirection):
   global turn
   move=False
   if board_representation[piecesx+xdirection][piecesy+ydirection]==" ":
      move=True
   if move==True:
       piecesx=piecesx+xdirection
       piecesy=piecesy+ydirection
       refresh()

def draw_chessboard():
    color = color2
    for r in range(rows):
        color = color1 if color == color2 else color2
        for c in range(columns):
            x1 = (c * dim_square)
            y1 = ((7-r) * dim_square)
            x2 = x1 + dim_square
            y2 = y1 + dim_square
            canvas.create_rectangle(x1, y1, x2, y2, fill=color, tags="area")
            color = color1 if color == color2 else color2

def draw_pieces():
    canvas.create_image(32,32, image=blackk, anchor=CENTER, state=NORMAL, tags="blackk")
    canvas.create_image(96,32, image=blackp, anchor=CENTER, state=NORMAL, tags="blackp")

def get_position(event):
    global clic,x1,y1,x2,y2,tour,tb,tw,clic1
    if clic==0:
        x1=event.x
        y1=event.y
        coordinates.delete(0.0,END)
        coordinates.insert(END,"clic detecte en x1="+str(x1)+" , y1 = " +str(y1))
        clic=1
    elif clic==1:
        x2=event.x
        y2=event.y
        coordinates.delete(0.0,END)
        coordinates.insert(END,"clic detecte en x2="+str(x2)+" , y2 = " +str(y2))
        clic=0
        tour=(tour+1)
        if (tour%2)==1:
            clic1=1

        if (tour%2)==0:
            clic1=2

def move_image(event):
    global x1,y1,x2,y2,img, init_image
    init_image=[]
    if clic==0: #First Clic
        x1,y1=canvas.coords("blackk")
        closest=canvas.find_closest(x1,x1,halo=32)
        x1=int(x1)
        y1=int(y1)
        init_image.append(x1)
        init_image.append(y1)
        print(init_image)
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        canvas.delete("blackk")
        canvas.create_image(x2, y2, image=blackk,anchor=CENTER,tags="blackk")

draw_chessboard()
"""draw_pieces()"""
turn = 1
display_game(1)

canvas.bind('<Button-1>', move_image)
canvas.bind('<Button-1>', get_position, add="+")
coordinates=Text(root,height=2)
coordinates.pack()

root.mainloop()

I added a print statements in my code at line 66 and it confirmed that my function interpretor() read my ".lvl" file.

This is what I got in my shell. As you can see, the first line is my ".lvl" file. However, I still have the problem that it doesn't display pieces on the board :(.

=========== RESTART: C:\Users\Thierry\Desktop\Chess\Chessboard.py ===========
rnbqkbnrpppppppp0000000000000000000000000000000000000000PPPPPPPPRNBQKBNR
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Thierry\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1549, in __call__
    return self.func(*args)
  File "C:\Users\Thierry\Desktop\Chess\Chessboard.py", line 201, in move_image
    x1,y1=canvas.coords("blackk")
ValueError: not enough values to unpack (expected 2, got 0)

It just returned me float coordinates of my image as expected. The error happen only on the first left-click because there is none "blackk" piece on the board. It's why, I want to solve the problem of piece display.

You can simply do nothing if the value is None

t = canvas.coords("blackk")
if not t:
    return
assert(len(t) == 2)
x1, y1 = t
...

Edited 7 Months Ago by Gribouillis

If you read the above mentioned documentation for method coords(), you see that it can be used to move the object, instead of deleting and recreating the image. You should be able to do something like

def move_image(event):
    global x1,y1,x2,y2,img, init_image
    init_image=[]
    if clic==0: #First Clic
        t = canvas.coords("blackk")
        if not t:
            return
        x1, y1 = t
        closest=canvas.find_closest(x1,x1,halo=32)
        x1=int(x1)
        y1=int(y1)
        init_image.append(x1)
        init_image.append(y1)
        print(init_image)
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        canvas.coords("blackk", x2, y2)

Also, good python code never uses the global statement. A good thing to do is to remove all the global statements in your program.

Edited 7 Months Ago by Gribouillis

Thanks for your help, it's a better way than mine for moving a piece. However, it doesn't solve the main problem that is : "Why after read the ".lvl" file, it doesn't put pieces on the board ?"

I cannot run your code because I don't have the images of the pieces and the lvl file. Can you zip the folder pieces_image and attach the zip to a post ?

I had a better result by adding a call to refresh() at the end of display_game(). Also adding a print(board_representation) at the end of interpretor() shows an error

rnbqkbnrpppppppp0000000000000000000000000000000000000000PPPPPPPPRNBQKBNR at line 70 in Chessboard.py
[['r', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['n', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['b', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['q', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['k', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['b', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['n', 'p', ' ', ' ', ' ', ' ', ' ', 'P'], ['r', 'p', ' ', ' ', ' ', ' ', ' ', 'R']] at line 84 in Chessboard.py

Using the printat module mentioned above is very useful here. Write

from tkinter import *
from printat import printat
print = printat

at the top of Chessboard.py, and save module printat on the python path.

Thanks to your help, it works :). I just modified my move_image function like that the closest piece from the first click will be the piece to move.

def move_image(event):
    global x1,y1,x2,y2,img, init_image,closest
    init_image=[]
    if clic==0: #First Clic
        x1=event.x
        y1=event.y
        closest=canvas.find_closest(x1,y1)
        t = canvas.coords(closest)
        if not t:
            return
        x1, y1 = t
        x1=int(x1)
        y1=int(y1)
        init_image.append(x1)
        init_image.append(y1)
        print(init_image)
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        canvas.coords(closest, x2, y2)

However, pieces can move everywhere because the move is defined by x and y coordinates and not by a reference for a tile like [0;0] , [0,7]... I have printed these reference in my "get_position()" function :

print(int(x2/dim_square),int(y2/dim_square))

Therefore, I would like to have a way to center the piece which move to its destination tile.

You can define a function to return the coordinates to move to

def destination(x, y):
    i, j = int(x/ux), int(y/uy)
    return i * vx + wx, j * vy + wy

All you have to do is to figure out the correct values for the constants ux, uy, vx, vy, wx, wy.

For example ux, uy, vx, vy may be dim_square and wx, wy may be -0.5 * dim_square. Also think about what happens if the user resizes the chessboard to a rectangle with the mouse. The constants may depend on the actual dimensions of the canvas.

Edited 7 Months Ago by Gribouillis

I just re-wrote the "move_image()" function and it works.

def move_image(event):
    global x1,y1,x2,y2,img, init_image,closest
    init_image=[]
    if clic==0: #First Clic
        x1=event.x
        y1=event.y
        i,j=(int(x1/dim_square),int(y1/dim_square))
        print(i,j)
        closest=canvas.find_closest(x1,y1)
        t = canvas.coords(closest)
        if not t:
            return
        x1, y1 = t
        x1=int(x1)
        y1=int(y1)
        init_image.append(x1)
        init_image.append(y1)
        print(init_image)
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        i,j=(int(x2/dim_square),int(y2/dim_square))
        print(i,j)
        x_center,y_center=tile_center+i*dim_square,tile_center+j*dim_square
        print(x_center,y_center)
        canvas.coords(closest, x_center, y_center)

I just have an error when I click on an empty tile but I don't know what the error means. Any Idea ?

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\Thierry\AppData\Local\Programs\Python\Python35\lib\tkinter\__init__.py", line 1549, in __call__
    return self.func(*args)
  File "C:\Users\Thierry\Desktop\Chess\Chessboard.py", line 219, in move_image
    x1, y1 = t
ValueError: too many values to unpack (expected 2)

Sorry, I tried but I didn't succeed to print t:(. Can you do it ?

Sorry, it's the human on the chair who failed because I didn't know how to break the mainloop of the programm.
Anyway... I got this:

>>> print(repr(t))
[384.0, 320.0, 448.0, 384.0] at line 1 in <pyshell#0>

Edited 7 Months Ago by titi157

It's exactly that because 384-320=64 and 64 is the width of a square.
I thought about something to test if thas only 2 arguments and reload the click to 0 if it's different but I don't know what to put after if t !=

def move_image(event):
    global x1,y1,x2,y2,img, init_image,closest,t,clic
    init_image=[]
    if clic==0: #First Clic
        x1=event.x
        y1=event.y
        i,j=(int(x1/dim_square),int(y1/dim_square))
        print(i,j)
        closest=canvas.find_closest(x1,y1)
        t = canvas.coords(closest)
        if t != ???:
              clic=0
        else:
            x1=int(x1)
            y1=int(y1)
            init_image.append(x1)
            init_image.append(y1)
            print(init_image)
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        i,j=(int(x2/dim_square),int(y2/dim_square))
        print(i,j)
        x_center,y_center=tile_center+i*dim_square,tile_center+j*dim_square
        print(x_center,y_center)
        canvas.coords(closest, x_center, y_center)

I don't understand the clic = 0 part. You can use

if len(t) == 4:
    ...
elif len(t) == 2:
    ...
else:
    ...

Edited 7 Months Ago by Gribouillis

clic=0 is the first click and clic=1 is the second click. It's why, I wanted to reload the click to 0 when the first click match an empty case. Your code works and it is so far easier than what I tried. I tried to check if it is an empty tile with my list board_representation[i][j]=="". Now, I need to use my use my game states' variables to only allow players to move their own pieces. Unfortunatedly, I never did that before so I think to read some documentation and a little bit of help could be helpful ^^.

#Game States
black_select=0
black_move=1
white_select=2
white_move=3

selected=None
state=black_select

The easiest way to maintain state variables is to work wih classes. You could have a class

class Game:
    def __init__(self):
        self.state = self.black_select
        ....

game = Game()
game.run()

I just worked on the game's states system but I don't know why I still can move black pieces at the first turn. I asked yet to reload the click to 0 when we click on a black piece represented by lowercases board_representation[i][j].islower(). Can you help me to fix this please ?

def move_image(event):
    global x1,y1,x2,y2,img, init_image,closest,t,clic,i,j,state
    init_image=[]
    if clic==0: #First Clic
        x1=event.x
        y1=event.y
        i,j=(int(x1/dim_square),int(y1/dim_square))
        print(i,j)
        closest=canvas.find_closest(x1,y1)
        t = canvas.coords(closest)
        if len(t)==4:
              clic=0
        elif len(t)==2:
              if state==0 and board_representation[i][j].isupper(): #Uppercases are for white and Lowercases for black
                  x1=int(x1)
                  y1=int(y1)
                  init_image.append(x1)
                  init_image.append(y1)
                  print(board_representation[i][j])
                  state+=1
                  print(state)
              elif state==0 and board_representation[i][j].islower():
                    clic=0
              elif state==2 and board_representation[i][j].islower():
                  x1=int(x1)
                  y1=int(y1)
                  init_image.append(x1)
                  init_image.append(y1)
                  print(board_representation[i][j])
                  state+=1
                  print(state)
              elif state==2 and board_representation[i][j].isupper():
                    clic=0
    elif clic==1: #Second Clic
        x2=event.x
        y2=event.y
        i,j=(int(x2/dim_square),int(y2/dim_square))
        x_center,y_center=tile_center+i*dim_square,tile_center+j*dim_square
        canvas.coords(closest, x_center, y_center)
        if state==1:
              state+=1
              print(state)
        elif state==3:
              state=0
              print(state)

You could perhaps make things easier by using enumeration types to represent the values of state variables, something like

from enum import Enum

class SqContent(Enum):
    white = 0
    black = 1
    empty = 2

class Click(Enum):
    first = 0
    second = 1

class Turn(Enum):
    white = 0
    black = 1

def on_click(event):
    global turn, clic
    i, j, content, closest = decode_click(event)
    if turn == Turn.white:
        if content == SqContent.white:
            if clic == Click.first:
                ...
            else:
                ...
        elif content == SqContent.black:
            if clic == Click.first:
                ...
            else:
                ...
        elif content == SqContent.empty:
            if clic == Click.first:
                ...
            else:
                ...            
    else:
        assert(turn == Turn.black)
        if content == SqContent.white:
            if clic == Click.first:
                ...
            else:
                ...
        elif content == SqContent.black:
            if clic == Click.first:
                ...
            else:
                ...
        elif content == SqContent.empty:
            if clic == Click.first:
                ...
            else:
                ...            

def decode_click(event):
    x1=event.x
    y1=event.y
    i,j=(int(x1/dim_square),int(y1/dim_square))
    if board_representation[i][j]:
        content = SqContent.black if board_representation[i][j].islower else SqContent.white

    else:
        content = SqContent.empty
    closest = canvas.find_closest(x1,y1)
    return i, j, content, closest

Edited 7 Months Ago by Gribouillis

Thanks for your help but I haven't any skill in everything related to class because I'm a beginner in programming so I would like to fix my problem without the need to use class ^^.

If you don't want to use classes, you can use symbolic constants, that's what C programmers do

SqContent_white = 3452
SqContent_black = 3453
SqContent_empty = 3454

Click_first = 654333
Click_second = 654334

Turn_white = 87644
Turn_black = 87645

def on_click(event):
    global turn, clic
    i, j, content, closest = decode_click(event)
    if turn == Turn_white:
        if content == SqContent_white:
            if clic == Click_first:
                ...
            else:
                ...
        elif content == SqContent_black:
            if clic == Click_first:
                ...
            else:
                ...
        elif content == SqContent_empty:
            if clic == Click_first:
                ...
            else:
                ...            
    else:
        assert(turn == Turn_black)
        if content == SqContent_white:
            if clic == Click_first:
                ...
            else:
                ...
        elif content == SqContent_black:
            if clic == Click_first:
                ...
            else:
                ...
        elif content == SqContent_empty:
            if clic == Click_first:
                ...
            else:
                ...            

def decode_click(event):
    x1 = event.x
    y1 = event.y
    i,j = (int(x1/dim_square), int(y1/dim_square))
    if board_representation[i][j]:
        content = SqContent_black if board_representation[i][j].islower() else SqContent_white

    else:
        content = SqContent_empty
    closest = canvas.find_closest(x1,y1)
    return i, j, content, closest

However, you'll have to learn classes very soon, because it is the tool you need to go further.

Edited 7 Months Ago by Gribouillis

I thought that symbolic constants don't exist in Python (Click Here) or is it the same thing as refactoring ? If this is not, can you explain me: How does it work ?

Edited 7 Months Ago by titi157

No, this article only means that symbolic constants don't exist in the core of the python language, but programmers can use class instances or other ersatz as symbolic constants. The enum.Enum type of python 3 is one way to implement this, but there are many other implementations.

This has nothing to do with refactoring, which means essentially take existing code and rewrite some parts to make the code better and more robust.

Hello,
I'm still working on my chess game and I've just finished the engine but I have some trouble to convert my coordinates [i][j] to find the correct pieces on my board [0 to 63] from the top-left to the bottom-right tile.
Here is an example : my coordinates [5][1]
What number I want the programm to convert to : 13 because it's the 13th tile from the top left.

Thanks in advance for your help :).

This question has already been answered. Start a new discussion instead.