Jump to content


Check out our Community Blogs

DanielTan

Member Since 22 Jul 2013
Offline Last Active Apr 16 2014 04:09 PM
-----

#662415 Tkinter ball snake game

Posted by DanielTan on 01 August 2013 - 05:34 AM

Following my previous tkinter tutorial, I'll make another tutorial to illustrate the use of tkinter canvas methods to handle sprite collisions(However, I'll only be using simple graphics in this tutorial), like in java.

 

This game is simple. There is a ball in the game. When I click somewhere on the canvas, the ball will try to run into that direction. On the way, it will meet food ( grows bigger), traps(grow smalle), powerups(increase size or mega points). The game will end when i) the ball grows too big, too small, or when the time runs out in 1 minute.

 

Here's the prototype code : Attached File  gamefile.py   7.88KB   2197 downloads

 

Also, I'll be using the tkinter root.after() method to substitute the use of a thread (e.g. java applet Runnable) so that my game can proceed on a separate time line. 

 

Let's start by importing the tkinter module and the random module (a game is much fun with some random elements) and then writing the <__init__>, <run> functions

from tkinter import *
from random import*

'''New game to illustrate the use of tkinter to make a game that can handle collisions.
'''


class game:
    def __init__(self):
        self.root=Tk()
        self.RUN=False
        
        self.frame=Frame(bg="black")
        self.frame.pack();
        
        self.canvas=Canvas(self.frame, bg="black",width=300,height=300)
        self.canvas.pack()
        
        self.clock=Label(self.frame, bg="black", fg="white")
        self.clock.pack()
        self.points=Label(self.frame, bg="black", fg="white")
        self.points.pack()
        self.button=Button(self.frame, bg="black", fg="white", text="Click to start")
        self.button.pack()
        
        self.root.mainloop()


    def run(self):
        if self.RUN is True:
            self.root.after(10, self.run)

app=game()

game1.gif

This is what the current code looks like.

Notice that I have used self.RUN as a controller for the run function so that I can stop it simply by making it False. The <root.after> function means that after 10 milliseconds, it will invoke self.run again, making it a recursive function.

 

Then, we need to design the <start> function which will be the command invoked by the button when we click it to start the game.

def start(self):
        self.time=0
        self.RUN=True
        
        self.foodX=[]
        self.foodY=[]

        self.trapX=[]
        self.trapY=[]

        self.powerupX=[[],[]]
        self.powerupY=[[],[]]

        self.TEXT="Welcome to tkinter"
        self.point=0
        
        self.x=100
        self.y=100
        self.tempx=100
        self.tempy=100
        self.UP=False
        self.DOWN=False
        self.LEFT=False
        self.RIGHT=False

        self.size=3
        self.canvas.bind("<ButtonPress-1>", self.onMClick)
        self.run()

Right, once we got the things on the outside out, let's start designing the interior, the <paint> function, it will then be put into the <run> function, along with the  <end> function to stop the game.. Note also that I have bound the self.onMClick to the canvas on the left button click, so we need to do something about that too.

from tkinter import *
from random import *

'''New game to illustrate the use of tkinter to make a game that can handle collisions.
'''


class game:
    def __init__(self):
        self.root=Tk()
        self.RUN=False
        
        self.frame=Frame(bg="black")
        self.frame.pack();
        
        self.canvas=Canvas(self.frame, bg="black",width=300,height=300)
        self.canvas.pack()
        
        self.clock=Label(self.frame, bg="black", fg="white")
        self.clock.pack()
        self.points=Label(self.frame, bg="black", fg="white")
        self.points.pack()
        self.button=Button(self.frame, bg="black", fg="white", text="Click to start",command=self.start)
        self.button.pack()
        
        self.root.mainloop()

    def start(self):
        self.time=0
        self.RUN=True
        
        self.foodX=[]
        self.foodY=[]

        self.trapX=[]
        self.trapY=[]

        self.powerupX=[[],[]]
        self.powerupY=[[],[]]

        self.TEXT="Welcome to tkinter"
        self.point=0
        
        self.x=100
        self.y=100
        self.tempx=100
        self.tempy=100
        self.UP=False
        self.DOWN=False
        self.LEFT=False
        self.RIGHT=False

        self.size=3
        self.canvas.bind("<ButtonPress-1>", self.onMClick)
        self.run()

    def paint(self):
        self.canvas.delete(ALL)
        self.canvas.create_text(100,100, text=self.TEXT, fill="green")

        if self.time//100<=60:
            if 10*self.size >0:
                self.TEXT="Welcome to tkinter"
                ball=self.canvas.create_oval(self.x-10*self.size,self.y-10*self.size,self.x+10*self.size,self.y+10*self.size, fill="white")
            elif 10*self.size>150:
                self.clock['text']="You lost"
                self.end()
            else:
                self.clock['text']="You lost"
                self.end()
        else:
            self.clock['text']="Time's up"
            self.end()

    def onMClick(self,event):
        self.tempx=event.x
        self.tempy=event.y
        if event.x> self.x and self.x is not self.tempx :
            self.RIGHT=True
            self.LEFT=False
        elif event.x< self.x and self.x is not self.tempx :
            self.LEFT=True
            self.RIGHT=False
        else:
            self.x=self.tempx    
            self.RIGHT=False
            self.LEFT=False
        if event.y> self.y and self.y is not self.tempy :
            self.DOWN=True
            self.UP=False
        elif event.y< self.y and self.y is not self.tempy :
            self.UP=True
            self.DOWN=False
        else:
            self.y=self.tempy
            self.DOWN=False
            self.UP=False
    
    def run(self):
        if self.RUN is True:
            self.time+=1
            self.clock['text']="TIME:" + str(self.time//100)
            self.points['text']="Points gathered: " + str(self.point)
            self.paint()
            self.root.after(10, self.run)
    
    def end(self):
        self.RUN=False
        self.canvas.unbind("<ButtonPress-1>")

 

app=game()

the code is rewritten with modifications in the <run> function to play the paint.

 

Here's how it looks like.

game2.gif

 

Note that the <onMClick> function controls the direction of the circle on click. However, we don't have a code to control how it moves yet. I didn't put the <move> code into the <onMClick> function because the <__init__> can't handle events.

 

So, I allowed the event code to handle the directions, and another <move> function to handle the movement of the circle. Note we'd also have to write code to allow the override of directions. i.e. when it moves left, it cannot move right.

def move(self, b,speed):
        if self.UP==True and self.y-b>0:
            self.y-=speed
        elif self.UP==True and self.y-b<=0:
            self.UP=False
            self.DOWN=True
        if self.DOWN==True and self.y+b<300:
            self.y+=speed
        elif self.DOWN==True and self.y+b>=300:
            self.DOWN=False
            self.UP=True
        if self.LEFT==True and self.x-b>0:
            self.x-=speed
        elif self.LEFT==True and self.x-b<=0:
            self.LEFT=False
            self.RIGHT=True
        if self.RIGHT==True and self.x+b<300:
            self.x+=speed
        elif self.RIGHT==True and self.x+b>=300:
            self.RIGHT=False
            self.LEFT=True
    
    def run(self):
        if self.RUN is True:
            self.time+=1
            self.clock['text']="TIME:" + str(self.time//100)
            self.points['text']="Points gathered: " + str(self.point)
            self.move(10*self.size,2)
            self.paint()
            self.root.after(10, self.run)

Now then, add it to the main code and play with it for a while. The circle could only move in 8 directions, though i have also include a collision handler. Note that the ball bounces off walls! 

 

game3.gif

 

I will continue on creating food and collision handlers for them in the next post. That's all for now, happy coding!

 

 

 


  • 2


#661841 Tkinter Tic Tac Toe tutorial

Posted by DanielTan on 22 July 2013 - 06:17 AM

Having seen the java tutorial on tic tac toe, i felt that the python tutorials page needed more push.
Since we already have a shell IO game, I'll do one using tkinter (not the best, but I like it)
 
I'm taking that you have a basis in basic tkinter(if not, another tutorial is underway, i guess), and let's build on that. 

I like tkinter in that by manually writing the code, it gives me more control over where, what, how of the program.

http://effbot.org/tkinterbook/

This site is where I learnt tkinter, though. 
 
Note that you can choose whether if you want to do single player mode, or double player mode.
But we'll start with double player as our basis, shall we?
First, we start by importing what we need and starting the __init__ function main class, along with calling the root. 

from tkinter import Frame, Canvas, Label, Button, LEFT,RIGHT, ALL, Tk
from random import randint

class main:
   
    def __init__(self,master):
        self.frame = Frame(master)
        self.frame.pack(fill="both", expand=True)
        

root=Tk()
app=main(root)
root.mainloop()

This is a basic model of a GUI. I  could have done <from tkinter import * >for simplicity but i think this would improve performance.
then, we add the canvas, and a few buttons to control the flow of the game.

from tkinter import Frame, Canvas, Label, Button, LEFT,RIGHT, ALL, Tk
from random import randint

class main:
   
    def __init__(self,master):
        self.frame = Frame(master)
        self.frame.pack(fill="both", expand=True)
        self.canvas = Canvas(self.frame, width=300, height=300)
        self.canvas.pack(fill="both", expand=True)
        self.label=Label(self.frame, text='Tic Tac Toe Game', height=6, bg='black', fg='blue')
        self.label.pack(fill="both", expand=True)
        self.frameb=Frame(self.frame)
        self.frameb.pack(fill="both", expand=True)
        self.Start1=Button(self.frameb, text='Click here to start\ndouble player', height=4, command=self.start1,bg='white', fg='purple')
        self.Start1.pack(fill="both", expand=True, side=RIGHT)
        self.Start2=Button(self.frameb, text='Click here to start\nsingle player', height=4, command=self.start2,bg='purple', fg='white')
        self.Start2.pack(fill="both", expand=True, side=LEFT)  

root=Tk()
app=main(root)
root.mainloop()

self refers to the main class.
the <frame.pack()> means that I'm using the pack geometry manager which basically helps me to add the widgets into the frames.
Frame are used to organize the widgets.
The canvas is where the lights and actions meet.
Labels are used to display text.
Now then notice that each button, Start1 and Start2, unlike java, has their own method to call functions.
We'll write the single player function, start1 first.
 

...
def start1(self):
        self.canvas.delete(ALL)
        self.label['text']=('Tic Tac Toe Game')
        self.canvas.bind("<ButtonPress-1>", self.sgplayer)  
        self._board()
        self.TTT=[[0,0,0],[0,0,0],[0,0,0]]
        self.i=0
        self.j=False

First, we refresh the canvas by deleting everything, then redrawing them again.
I bound a function self.sgplayer, which will do the gaming to the canvas when the button is clicked
The self._board() is the function we will use to draw the board.
self.TTT is the matrix I use to record what is drawn at where.
Note that in other languages, they might not choose to use to use a matrix to store such data (the java tutorials don't, anyway), but using the matrix will simplify our calculations, as you'll see later on, especially in the AI code (We are never going to go through each winning case manually,never! )
self.i is a counter(for turn taking) and self.j is simply there to be used in end() where we end the game.
 
You might notice that the python code is pretty self explanatory. So I don't do much commenting. There isn't much of a need
 
then we draw the board.

...
def _board(self):
        self.canvas.create_rectangle(0,0,300,300, outline="black")
        self.canvas.create_rectangle(100,300,200,0, outline="black")
        self.canvas.create_rectangle(0,100,300,200, outline="black")

now that's done, on to the gaming!
 

def sgplayer(self,event):
        for k in range(0,300,100):
            for j in range(0,300,100):
                if event.x in range(k,k+100) and event.y in range(j,j+100):
                    if self.canvas.find_enclosed(k,j,k+100,j+100)==():
                        if self.i%2==0:
                            X=(2*k+100)/2
                            Y=(2*j+100)/2
                            X1=int(k/100)
                            Y1=int(j/100)
                            self.canvas.create_oval( X+25, Y+25, X-25, Y-25, width=4, outline="black")
                            self.TTT[Y1][X1]+=1
                            self.i+=1
                        else:                         
                            X=(2*k+100)/2
                            Y=(2*j+100)/2
                            X1=int(k/100)
                            Y1=int(j/100)
                            self.canvas. create_line( X+20, Y+20, X-20, Y-20, width=4, fill="black")
                            self.canvas. create_line( X-20, Y+20, X+20, Y-20, width=4, fill="black")
                            self.TTT[Y1][X1]+=9
                            self.i+=1
        self.check()

Ah. Now this gets a little tricky. When the user input is registered, the computer:
1)check where the user clicked.
2)set a bounding box
3)check if there is nothing in that box (so that it can draw
4)check whose turn is it.
5) draw the circle or cross. and add the corresponding value to self.TTT, the matrix
6) check if anybody wins.(that's what the self.check() is for.
 
Onto to the self.check()

def check(self):
        #horizontal check
        for i in range(0,3):
            if sum(self.TTT[i])==27:
                self.label['text']=('2nd player wins!')
                self.end()
            if sum(self.TTT[i])==3:
                self.label['text']=('1st player wins!')
                self.end()
        #vertical check
        #the matrix below transposes self.TTT so that it could use the sum fucntion again
        self.ttt=[[row[i] for row in self.TTT] for i in range(3)]
        for i in range(0,3):            
            if sum(self.ttt[i])==27:
                self.label['text']=('2nd player wins!')
                self.end()
            if sum(self.ttt[i])==3:
                self.label['text']=('1st player wins!')
                self.end()
        #check for diagonal wins
        if self.TTT[1][1]==9:
            if self.TTT[0][0]==self.TTT[1][1] and self.TTT[2][2]==self.TTT[1][1] :
                self.label['text']=('2nd player wins!')
                self.end()
            if self.TTT[0][2]==self.TTT[1][1] and self.TTT[2][0]==self.TTT[1][1] :
                self.label['text']=('2nd player wins!')
                self.end()
        if self.TTT[1][1]==1:
            if self.TTT[0][0]==self.TTT[1][1] and self.TTT[2][2]==self.TTT[1][1] :
                self.label['text']=('1st player wins!')
                self.end()
            if self.TTT[0][2]==self.TTT[1][1] and self.TTT[2][0]==self.TTT[1][1] :
                self.label['text']=('1st player wins!')
                self.end()
        #check for draws
        if self.j==False:
            a=0
            for i in range(0,3):
                a+= sum(self.TTT[i])
            if a==41:
                self.label['text']=("It's a pass!")
                self.end()

here I used the sum functions for lists just to add the elements together. Also, i checked if there are passes, i.e. 5(1)+4(9)=41. five circles and four crosses are the maximum, but if nobody wins then it's a pass.

So you don't have to set boolean checkers for each row== 

note: it also checks if self.j has been triggered so that it won't check if someone has won.

If it is a win, then we should end the game and stop anybody from clicking.

def end(self):
        self.canvas.unbind("<ButtonPress-1>")
        self.j=True

And we're done! copy all that into the main class and you got yourself a game. (NOTE: the functions for the second button has not been written so you'll have to delete all those single player controls if you were to play double player.)

 

I'll continue onto the juicier single player controls in my next post. Au revoir for now.

Attached Files

  • Attached File  ticTT.py   9.25KB   2014 downloads

  • 2


Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download