Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

board-drawing code to move an oval

I am working on a python checkers game for college. I have the board drawn, using tk, but I can't seem to implement a movement function for the pieces. If anyone see any errors in my code, or can offer help, I would appreciate. Here is the complete source. Thanks in advance.

I know that this draws the checker pieces. I don't know how to redraw the pieces, without deleting the other pieces. I have looked online at the move function, and tried simple test that worked, yet I have not been able to use it in my code.

I do know of recursion, however, I need the basic function to work, i.e. actually moving a piece on the screen, before I implement any more functions.

lst2 = []

#counter variable
i=0

#board variable is what stores the X/O/- values.
# It's a 2D list. We iterate over it, looking to see
# if there is a value that is X or O. If so, we draw
# text to the screen in the appropriate spot (based on
# i and j.
while i < len(board):
  j=0
  while j < len(board[i]):

    if board[i][j] == 2:
      lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
        (j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
        (j+1)*width + width/2 - 15, fill="Red",outline='Black'))
    elif board[i][j] == 4:
      lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
        (j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
        (j+1)*width + width/2 - 15, fill="Red",outline='Black'))
    elif board[i][j] == 1:
      lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
        (j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
        (j+1)*width + width/2 - 15, fill="Black",outline='Black'))
    elif board[i][j] == 3:
      lst2.append(canvas.create_oval((i+1)*width + width/2 + 15,
        (j+1)*height + height/2 +15,(i+1)*width + width/2 - 15,
        (j+1)*width + width/2 - 15, fill="Black",outline='Black'))

    j+=1

  i+=1
like image 643
William Armstrong Avatar asked Jul 19 '11 00:07

William Armstrong


2 Answers

You can move an item on a canvas using the coords and/or move methods to change the coordinates from what they are to what you want them to be.

Here's a simple example showing how to create and move an item on a canvas:

import tkinter as tk     # python 3
# import Tkinter as tk   # python 2

class Example(tk.Frame):
    """Illustrate how to drag items on a Tkinter canvas"""

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        # create a canvas
        self.canvas = tk.Canvas(width=400, height=400, background="bisque")
        self.canvas.pack(fill="both", expand=True)

        # this data is used to keep track of an
        # item being dragged
        self._drag_data = {"x": 0, "y": 0, "item": None}

        # create a couple of movable objects
        self.create_token(100, 100, "white")
        self.create_token(200, 100, "black")

        # add bindings for clicking, dragging and releasing over
        # any object with the "token" tag
        self.canvas.tag_bind("token", "<ButtonPress-1>", self.drag_start)
        self.canvas.tag_bind("token", "<ButtonRelease-1>", self.drag_stop)
        self.canvas.tag_bind("token", "<B1-Motion>", self.drag)

    def create_token(self, x, y, color):
        """Create a token at the given coordinate in the given color"""
        self.canvas.create_oval(
            x - 25,
            y - 25,
            x + 25,
            y + 25,
            outline=color,
            fill=color,
            tags=("token",),
        )

    def drag_start(self, event):
        """Begining drag of an object"""
        # record the item and its location
        self._drag_data["item"] = self.canvas.find_closest(event.x, event.y)[0]
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

    def drag_stop(self, event):
        """End drag of an object"""
        # reset the drag information
        self._drag_data["item"] = None
        self._drag_data["x"] = 0
        self._drag_data["y"] = 0

    def drag(self, event):
        """Handle dragging of an object"""
        # compute how much the mouse has moved
        delta_x = event.x - self._drag_data["x"]
        delta_y = event.y - self._drag_data["y"]
        # move the object the appropriate amount
        self.canvas.move(self._drag_data["item"], delta_x, delta_y)
        # record the new position
        self._drag_data["x"] = event.x
        self._drag_data["y"] = event.y

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
like image 200
Bryan Oakley Avatar answered Nov 15 '22 09:11

Bryan Oakley


6TH EDIT: Here are two solutions for you:

  1. (as Bryan suggests) either remember the old location of the moved piece, undraw it there (=> draw it in background color), redraw it at new location
  2. the simpler: clear and redraw entire board

5TH EDIT: Ok thanks for stripping the code down.

Explain exactly what is the problem with your board-drawing code? 'Moved piece is not deleted from old location'? 'All pieces are drawn at wrong coordinates or colors'? ...? It's not acceptable to just keep dumping code and saying "This code doesn't work".

"I don't know how to redraw the pieces, without deleting the other pieces." I think that's your problem right there. If you declare and call redrawBoard(), it should redraw ALL the pieces(!), not just the moved piece. Agree? i.e. you must iterate over all of board[][] and call drawPiece() on every piece. But your code appears to do that already?

Let me suggest you how to clean up your existing board-drawing code, and in that process you will almost surely find your bug. Obviously you need to clear and redraw the screen every time there is a move (or promotion), do you actually do that? Declare a fn redrawBoard() for that. If you do not do a clear, then after a move the piece will be displayed in its old AND new locations, which would be wrong obviously? (The comment about Frame rate is how often canvas will be updated each second. makes me wonder when you redraw, you do not need to redraw 10 times a second, unless you also have a clock or other changing data. But, hey, that also works.)

First, strongly suggest you use an enum to self-document the values used in board[][]

class Checkers():
    EMPTY=0
    RED_PIECE=1
    RED_KING=2
    BLACK_PIECE=3
    BLACK_KING=4

Next, you can greatly clean up the board-drawing code. Since all 4 piece-drawing cases call a common-case, make it a fn, and make that fn uncluttered:

def drawPiece(i,j,fillColor,outlineColor):
    """Draw single piece on screen."""
    x = (i+1)*width + width/2
    y = (j+1)*height + height/2
    lst2.append(canvas.create_oval(x+15,y+15,x-15,y-15,fill=fillColor,outline=outlineColor))

Now, the board-drawing code that calls these strictly really only has two cases: (2,4) or (1,3) assuming you got the enum right:

and by the way, never use a while-loop where a more legible for-loop would do:

for i in range(len(board)):
    for j in range(len(board[i])):
        if board[i][j] in (RED_PIECE,RED_KING):
            drawPiece(i,j,'Red','Black')
        elif board[i][j] in (BLACK_PIECE,BLACK_KING):
            drawPiece(i,j,'Black','Black')

Is that decomposition not infinitely easier to read and debug? It's self-documenting. Now your bug should practically leap out at you.

(By the way, you're currently drawing kings the exact same as pieces, but I guess you'll fix that later.)


4TH EDIT: You had us looking at the wrong fns, grr... you say your bug is actually in the board-drawing code. Would you please correct the title which still says "Implement a movement function"?


ORIGINAL REPLY: What machine yearning said, this is not a question - not yet: Tell us what you currently try, and why it doesn't work. Also, remove all unrelated code.

Looks like you're having difficulties with function moveTo(i,j) - but what exactly? (The globals secondPass, secondPosition signal you might be having trouble... do you know recursion? If not, no worries.)

Also, as a stylistic thing, and to make your life easy, this implementation is not OO, the globals scream bad decomposition. Try rewriting as a class Checkers, make board etc. a member, write an init() method. I would rename the function grid(x,y) to initialize(nrows,ncols).

(And cough, cough! Signs you adapted this from someone else...)

#Frame rate is how often canvas will be updated
# each second. For Tic Tac Toe, 10 should be plenty.
FRAME_RATE = 10
like image 45
smci Avatar answered Nov 15 '22 07:11

smci