Is there any possible way to create a hitbox using tkinter frame? - python-3.x

I have a tkinter frame and 2 sprites, I can easily make my vel = 0 when I'm at the exact pos of the wall. The issue is, that inside the sprite (I need the character to stop when he hits the edge of the wall).
import tkinter ##SETUP##
from tkinter import *
import threading
from threading import Timer
window = Tk() ##WINDOW SETUP##
window.title("Project Gladiator, Pre-Alpha 0.3")
window.config(bg = "white")
frame = Frame(window, width = 1000, height = 1000)
frame.grid(row = 0, column = 0)
frame.focus_set()
Px = 50 #char pos ##VARIABLES##
Py = 50 #char pos
X = 5 #char Xcel
Xchange = 5 #sets X
Direction = "Down" #different sprites based on movement
Level = 0 #used for collision (starts from 0)
PlayerF1 = PhotoImage(file = "Mainchar.png") #replace by each frame ##MAIN CHAR##
Playerd = Label(frame, image = PlayerF1)
Playerd.place(x = Px, y = Py)
w = False #movement button presses
a = False #movement button presses
s = False #movement button presses
d = False #movement button presses
##########################################################################---LEVEL---DRAWING---##########################################################################
#TEXTURES#
Wallimg = PhotoImage(file = "Lategame-tile.png")
#TESTLEVELONE#
Walltest = Label(window, image = Wallimg)
WalltestX = 30
WalltestY = 30
##########################################################################---MOVEMENT---##########################################################################
def keypress(event):
if event.char == "w":
global w
global a
global s
global d
global Xchange
w = True
elif event.char == "a":
a = True
elif event.char == "s":
s = True
elif event.char == "d":
d = True
elif event.char == "`":
Console = Tk()
Console.title("Console")
Console.config(bg = "green")
Output = Label(Console, text = "Output", bg = "green", fg = "yellow", font = "none 9")
#output goes here...
Input = Label(Console, text = "Input", bg = "green", fg = "yellow", font = "none 9")
Commandline = Entry(Console, bg = "white")
def SubmitCommand():
Command = Commandline.get()
print(f"Command Submitted \"{Command}\"")
Commandline.delete(0, END)
Commandsub = Button(Console, text = "Submit", bg = "green", fg = "yellow", command = SubmitCommand)
Output.grid(row = 0, column = 0)
Input.grid(row = 2, column = 0)
Commandline.grid(row = 3, column = 0)
Commandsub.grid(row = 3, column = 1)
def keyup(event):
if event.char == "w":
global w
global a
global s
global d
w = False
elif event.char == "a":
a = False
elif event.char == "s":
s = False
elif event.char == "d":
d = False
def wpress():
def wcheck():
if w == True:
global Direction
global Py
global Px
global X
global Xchange
X = Xchange
Direction = "Up"
Py = Py - X
if Level == 0 and Py == 30 and Px == 30:
Py = Py + X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if a == True:
X = Xchange
Direction = "Right"
Px = Px - X
if Level == 0 and Py == 30 and Px == 30:
Px = Px + X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if s == True:
X = Xchange
Direction = "Down"
Py = Py + X
if Level == 0 and Py == 30 and Px == 30:
Py = Py - X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if d == True:
X = Xchange
Direction = "Left"
Px = Px + X
if Level == 0 and Py == 30 and Px == 30:
Px = Px - X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
wpress()
c = Timer(0.001, wcheck)
c.start()
frame.bind("<Key>", keypress)
frame.bind("<KeyRelease>", keyup)
wpress()
##########################################################################---END---MOVEMENT---##########################################################################
##########################################################################---LEVELS---##########################################################################
def DrawTEST1(): #wall test
Walltest.place(x = WalltestX, y = WalltestY)
DrawTEST1()
window.mainloop()
The focus is on this part
def wpress():
def wcheck():
if w == True:
global Direction
global Py
global Px
global X
global Xchange
X = Xchange
Direction = "Up"
Py = Py - X
if Level == 0 and Py == 30 and Px == 30:
Py = Py + X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if a == True:
X = Xchange
Direction = "Right"
Px = Px - X
if Level == 0 and Py == 30 and Px == 30:
Px = Px + X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if s == True:
X = Xchange
Direction = "Down"
Py = Py + X
if Level == 0 and Py == 30 and Px == 30:
Py = Py - X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
if d == True:
X = Xchange
Direction = "Left"
Px = Px + X
if Level == 0 and Py == 30 and Px == 30:
Px = Px - X
Playerd.place(x = Px, y = Py)
elif w == False:
X = 0
wpress()
c = Timer(0.001, wcheck)
c.start()
frame.bind("<Key>", keypress)
I have been pulling my hair out on this one for quite some time. If anyone can help then that would be great! Thanks in advance!

Not sure if you figured it out yet, but since I encountered something in the same vain as this, I'd like to share what I came up with.
Just keep in mind that Tkinter isn't exactly made for this sort of thing, and something like Pygame would be a better choice.
The simple way:
If your sprite is a simple one, like a square shape, just points in each direction slightly outside your sprite, and on each move check if that point is overlapping with the wall or not.
Example: if your sprite is 25x25, then you could have the center as x, y, and the point for collision detection to the right could then be x+26, y. The left could then be x-26, y.
The problem here is that you are using labels in a frame, so you'd either have to figure out the position with trail and error, or use the .bbox method.
The slightly more complicated way (but ultimately the easier way):
If your sprite has somewhat of an odd shape, and you want it to be more accurate, you could put it on a Canvas instead of a Frame. That gives you the option to draw out a polygon that you can trace around the sprite. Then have it move with the sprite, and simply check if that overlaps with a wall, enemy, bullet, trap or whatever else you can think of. This would give you far more flexibility.
Canvas is also a better choice in general if you want to do more complex things, as it allows you to move things around easier, draw out shapes, set tags ect. You can still put labels, buttons and all that good stuff on a canvas if you want those, no need to use a frame unless you are just packing stuff into it.
Lastly I would highly recommend using classes, it will make your life so much easier and is one of the major strengths of the language. They can be a bit confusing at first, but it is worth the time it takes to learn many times over. Once you get used to them, you'll wonder how you managed to do anything without them.

Related

how do i add play again for tkinter python?

I can't find a source on how to add play again for my python code. I am currently using a snake game that i found on YouTube but I honestly am having trouble figuring this out.
github.com/Amfuhr/Snake-game.git
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER", fill="red", tag="gameover")
The full script:
#The tkinter package (“Tk interface”) is the standard Python interface to the Tcl/Tk GUI toolkit.
#Tkinter are available on most Unix platforms, including macOS, as well as on Windows systems.
from tkinter import *
import random
#box size, game speed, and color schemes
#classes for the game settings
GAME_WIDTH = 700
GAME_HEIGHT = 700
SPEED = 400
#Snake and food size
SPACE_SIZE = 50
BODY_PARTS = 3
#class for snake and food
#RGB for the color of the backgrund, snake, and food
SNAKE_COLOR = "white"
FOOD_COLOR = "blue"
BACKGROUND_COLOR = "#000000"
class Snake:
#set the body size of the snake, a list of square graphics
def __init__(self):
self.body_size = BODY_PARTS
self.coordinates = []
self.squares = []
#list of coordinates
for i in range(0, BODY_PARTS):
self.coordinates.append([0, 0])
for x, y in self.coordinates:
square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR, tag="snake")
self.squares.append(square)
class Food:
def __init__(self):
#food is set into random location using the random module
#based on orientation. We use fill to set the food color
x = random.randint(0, (GAME_WIDTH / SPACE_SIZE)-1) * SPACE_SIZE
y = random.randint(0, (GAME_HEIGHT / SPACE_SIZE) - 1) * SPACE_SIZE
self.coordinates = [x, y]
canvas.create_oval(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=FOOD_COLOR, tag="food")
def next_turn(snake, food):
#head of the snake and the direction of the snake movements
x, y = snake.coordinates[0]
if direction == "up":
y -= SPACE_SIZE
elif direction == "down":
y += SPACE_SIZE
elif direction == "left":
x -= SPACE_SIZE
elif direction == "right":
x += SPACE_SIZE
snake.coordinates.insert(0, (x, y))
square = canvas.create_rectangle(x, y, x + SPACE_SIZE, y + SPACE_SIZE, fill=SNAKE_COLOR)
snake.squares.insert(0, square)
if x == food.coordinates[0] and y == food.coordinates[1]:
global score
score += 1
label.config(text="Score:{}".format(score))
canvas.delete("food")
food = Food()
else:
del snake.coordinates[-1]
canvas.delete(snake.squares[-1])
del snake.squares[-1]
if check_collisions(snake):
game_over()
else:
window.after(SPEED, next_turn, snake, food)
def change_direction(new_direction):
global direction
if new_direction == 'left':
if direction != 'right':
direction = new_direction
elif new_direction == 'right':
if direction != 'left':
direction = new_direction
elif new_direction == 'up':
if direction != 'down':
direction = new_direction
elif new_direction == 'down':
if direction != 'up':
direction = new_direction
def check_collisions(snake):
x, y = snake.coordinates[0]
if x < 0 or x >= GAME_WIDTH:
return True
elif y < 0 or y >= GAME_HEIGHT:
return True
for body_part in snake.coordinates[1:]:
if x == body_part[0] and y == body_part[1]:
return True
return False
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER", fill="red", tag="gameover")
#Window sizing so it does not game
window = Tk()
window.title("Snake game")
window.resizable(False, False)
score = 0
direction = 'down'
#this is to show the score in a specific font size
label = Label(window, text="Score:{}".format(score), font=('consolas', 40))
label.pack()
#canvas sets the background and opens the window
canvas = Canvas(window, bg=BACKGROUND_COLOR, height=GAME_HEIGHT, width=GAME_WIDTH)
canvas.pack()
window.update()
window_width = window.winfo_width()
window_height = window.winfo_height()
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
x = int((screen_width/2) - (window_width/2))
y = int((screen_height/2) - (window_height/2))
# Geometry method is used to set the dimensions of the
# Tkinter window and is used to set the position of the main
# window on the user’s desktop.
window.geometry(f"{window_width}x{window_height}+{x}+{y}")
window.bind('<Left>', lambda event: change_direction('left'))
window.bind('<Right>', lambda event: change_direction('right'))
window.bind('<Up>', lambda event: change_direction('up'))
window.bind('<Down>', lambda event: change_direction('down'))
snake = Snake()
food = Food()
next_turn(snake, food)
window.mainloop()
Think about it this way. Your app works upon running the first time around, and you just want to repeat, that behavior, (aka run that portion of the code again), once the initial game is over without having to restart the app.
This is precisely what functions are for.
The portion of your code that executes the game is just the last three lines of your script.
snake = Snake()
food = Food()
next_turn(snake, food)
So what you can do is stick those 3 lines into a function, and put the function call just before the mainloop call where the lines used to be.
def start_game(*arg):
canvas.delete(ALL)
snake = Snake()
food = Food()
next_turn()
start_game()
window.mainloop()
So now in order to restart the game, all you need to do is call the start_game function. Adding the canvas.delete(ALL) ensures that the "GAME OVER" message is removed before the new game starts.
Now all you need is a trigger. For this you can add a double-click signal on the canvas so that when it says "GAME OVER" all you need to do is double-click on the window and the new game starts. You can even add an instruction for the user.
To do this just add a window.bind call for the double-click action gesture:
window.bind('<Double-Button-1>', start_game)
Then to add the note to your "Game Over" message in your game_over function:
def game_over():
canvas.delete(ALL)
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/2,
font=('consolas',70), text="GAME OVER",
fill="red", tag="gameover")
canvas.create_text(canvas.winfo_width()/2, canvas.winfo_height()/1.8,
font=('consolas',20), text="double click for new game",
fill='green', tag='newgame')
And that should do it. This solution does create a new issue though, since there are no conditions on the new event binder, double clicking the screen will start a new game even if the current game is still ongoing.
I will leave this for you to try to solve. Or if you prefer you can use a different trigger.

Trying to get ball to return to middle after ball has left screen

I'm trying to recreate a game of pong just for fun.
Here is my full code as of right now.
import pygame, random, math
pygame.init()
#colours:-------------------------------------------------------------
R = random.randrange(1,255)
B = random.randrange(1,255)
G = random.randrange(1,255)
WHITE = (255, 255, 255)
GREEN = (39, 133, 20)
YELLOW = (252, 252, 25)
BLACK = (0, 0, 0)
BLUE = (30, 100, 225)
RED = (255,0,0)
RANDOM_COLOR = (R, B, G)
#Surface:-------------------------------------------------------------
width = 700
height = 600
size = (width, height)
screen = pygame.display.set_mode(size)
screen_rect = screen.get_rect()
pygame.display.set_caption("Pong Remake")
background = pygame.image.load("background.png").convert()
background = pygame.transform.scale(background, (width, height))
logo = pygame.image.load("logo.png").convert()
logo.set_colorkey((BLACK))
credits = pygame.image.load("credits.png")
credits.set_colorkey((BLACK))
#variables:-----------------------------------------------------------
clock = pygame.time.Clock()
done = False
text = pygame.font.Font(None,25)
display_instructions = True
instruction_page = 1
start_font = pygame.font.Font("C:\Windows\Fonts\BAUHS93.TTF", 35)
instruction_font = pygame.font.Font(None, 17)
win_lose_font = pygame.font.Font("C:\Windows\Fonts\BAUHS93.TTF",50)
score = pygame.font.Font(None, 100)
bounce = pygame.mixer.Sound("bounce.wav")
playerOne_score = 0
playerTwo_score = 0
playerOne = ""
playerTwo = ""
x = 350
y = 300
ball_rect = pygame.Rect(x,y,10,10)
paddleOne_rect = pygame.Rect(10, 250, 20, 60)
paddleTwo_rect = pygame.Rect(670, 250, 20, 60)
x_speed = random.randrange(5, 10)
y_speed = random.randrange(5,10)
def draw_background(screen, pic, x,y):
screen.blit(pic, (x,y))
#main loop
#INPUT v ---------------------------------------------------------
#Start Page
while not done and display_instructions:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
if event.type == pygame.MOUSEBUTTONDOWN:
instruction_page += 1
if instruction_page == 2:
display_instructions = False
#Shows the start of the page
if instruction_page == 1:
draw_background(screen, logo, 100,-150)
draw_background(screen, credits, 100,50)
instruction_text = instruction_font.render("How to Play. The objective to this game is to score the ball on the other side before the opponent can.", False, WHITE)
instruction_text_three = instruction_font.render("First Player to get 10 points wins, Have Fun and Good Luck!", False, WHITE)
instruction_text_two = instruction_font.render("For Player One, use the a and the z keys to move up and down, For Player Two, use the k and m keys.", False, WHITE)
continue_text= start_font.render("Click to Play...",True, WHITE)
screen.blit(continue_text, [200, 400])
screen.blit(instruction_text, [0,500])
screen.blit(instruction_text_three, [0,532])
screen.blit(instruction_text_two,[0,516])
if instruction_page == 2:
display_instructions = False
clock.tick(60)
pygame.display.flip()
while not done:
click = False
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.MOUSEBUTTONDOWN:
click = True
#INPUT ^ =========================================================
#PROCESS v -------------------------------------------------------
str(playerOne_score)
str(playerTwo_score)
scoreOne = text.render("Player One:" + str(playerOne_score), False, WHITE)
scoreTwo = text.render("Player Two:" + str(playerTwo_score), False, WHITE)
#moves paddles with keys on keyboar
key = pygame.key.get_pressed()
if key[pygame.K_a]: paddleOne_rect.move_ip(0, -10)
if key[pygame.K_z]: paddleOne_rect.move_ip(0, 10)
if key[pygame.K_k]: paddleTwo_rect.move_ip(0, -10)
if key[pygame.K_m]: paddleTwo_rect.move_ip(0, 10)
#makes sure paddles stay on screen
paddleOne_rect.clamp_ip(screen_rect)
paddleTwo_rect.clamp_ip(screen_rect)
ball_rect.move_ip(x_speed, y_speed)
if ball_rect.y + ball_rect.height> screen_rect.height or ball_rect.y < 0:
y_speed = y_speed * -1
bounce.play()
if ball_rect.collidelist([paddleOne_rect, paddleTwo_rect]) > -1:
x_speed = -x_speed
R = random.randrange(1,255)
B = random.randrange(1,255)
G = random.randrange(1,255)
bounce.play()
if ball_rect.x >= 700:
x_speed * -1
playerOne_score += 1
pygame.display.flip
if ball_rect.x <= 0:
x_speed * -1
playerTwo_score += 1
#PROCESS ^ =======================================================
#DRAWING GOES BELOW HERE v ------------------------------------
draw_background(screen, background, 0,0)
screen.blit(scoreOne, (0,0))
screen.blit(scoreTwo, (500,0))
pygame.draw.ellipse(screen, WHITE,ball_rect )
pygame.draw.rect(screen,RANDOM_COLOR, paddleOne_rect)
pygame.draw.rect(screen,RANDOM_COLOR, paddleTwo_rect)
pygame.draw.line(screen, WHITE, (350,0),(350,700), 1)
#DRAWING GOES ABOVE HERE ^ ------------------------------------
pygame.display.flip()
clock.tick(60)
pygame.quit()
What I am currently having problems with at the moment is when the ball goes off the screen, I want it to go back to the middle again as someone has scored a point. But I'm a bit stuck on what to do.
If you guys can help me out, that would be amazing!!
There is a lot of code here, so this does not follow specific variables you used, but I hope this helps.
1) Find the width of you screen
2) Take the x and y coordinates that you use to know where to draw the ball
3) Make an if statement that essentially says
(pseudocode)
if x > 1000
score1 += 1
x = 500
if x < 0
score2 += 1
x = 500
``
I hope this can set you on the right track, and I suggest checking out the pygame docs.
Cheers!

How to compile code into a Class with functions

I don't know how to turn this code into a class statement and then run it!
I want to have the same result as this but using class statements.
import pygame
import random
pygame.init()
winh = 500
winl = 500
win = pygame.display.set_mode((winh, winl))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot():
def __init__(self):
self.x = x
self.y = y
self.width = width
self.direction = 'right'
def direction(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.direction = "up"
if keys[pygame.K_a]:
self.direction = "left"
if keys[pygame.K_s]:
self.direction = "down"
if keys[pygame.K_d]:
self.direction = "right"
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
y -= width
if direction == "down":
y += width
if direction == "left":
x -= width
if direction == "right":
x += width
if y > winh - 20:
y = 20
if y < 20:
y = winh - 20
if x > winl - 20:
x = 20
if x < 0:
x = winl - 20
win.fill((0,0,0))
dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
So you're class was getting there already. A class is an encapsulation of data and functions that are common to that "object". Anything for or about that object should go into this class. But when code for keyboard handling was put into a Dot class, this was going in the wrong direction. A Dot is a thing draw to a screen, it has a size and colour, and position. It should not be responsible for handling user-input. That's outside the scope of what a Dot is.
When updating the class, I chose to base it on the PyGame sprite class. This is a little bit more work at first, and it needs to be written in a particular way - have an image, and a rect, and (hopefully) an update() function. But being a sprite it gets lots of functionality that's already written, including the PyGame collision routines.
Near the main loop, there's a couple of additions. The first is creating the sprite dotty, and a sprite-group sprites to hold it. It's a bit weird to create a sprite group for a single sprite, but I assumed in the future there would be more than one.
Then in the actual loop, the code calls sprites.update(). This is a handy routine that calls the update() function for every sprite in the group. Then later on, there's a call to sprites.draw( win ) which paints every sprite in the group to the screen. This uses the sprite's image and the rect to know where, and what to paint.
import pygame
import random
pygame.init()
WIN_H = 500
WIN_L = 500
win = pygame.display.set_mode((WIN_H, WIN_L))
width = 20
vel = 5
y = 250
x = 250
score = 0
direction = "up"
class Dot( pygame.sprite.Sprite ):
def __init__( self, x,y, size=20, direction='right', colour=(0,255,0) ):
pygame.sprite.Sprite.__init__( self )
self.size = size
self.colour = colour
self.direction = direction
self.image = pygame.Surface( ( size, size ), pygame.SRCALPHA )
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.image.fill( colour )
def move( self, x_dir, y_dir ):
global WIN_H, WIN_L
# Adjust our co-oridinates
self.rect.x += x_dir
self.rect.y += y_dir
# Stay on the screen, and wrap around
if (self.rect.left >= WIN_L ):
self.rect.right = 0
elif (self.rect.right <= 0 ):
self.rect.left = WIN_L
if (self.rect.top >= WIN_H ):
self.rect.bottom = 0
elif (self.rect.bottom <= 0):
self.rect.top = WIN_H
def update( self ):
# TODO - handle animation, collisions, whatever
pass
# make some sprites
dotty = Dot( x,y )
sprites = pygame.sprite.Group() # a group, for a single sprite (or more)
sprites.add( dotty )
p = True
run = True
while run:
pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
direction = "up"
if keys[pygame.K_a]:
direction = "left"
if keys[pygame.K_s]:
direction = "down"
if keys[pygame.K_d]:
direction = "right"
if direction == "up":
dotty.move( 0, -width )
if direction == "down":
dotty.move( 0, width )
if direction == "left":
dotty.move( -width, 0 )
if direction == "right":
dotty.move( width, 0 )
# update all the sprites
sprites.update()
# repaint the screen
win.fill((0,0,0))
sprites.draw( win )
#dot = pygame.draw.rect(win, (0, 255, 0), (x, y, width, width))
pygame.display.update()
pygame.quit()
The movement code was moved into the Dot class. This allows the Dot to adjust its position, but also take care of screen-wrapping issues. If this sprite was say, some kind of projectile, maybe when it crossed the screen boundary it would call the sprite.kill() function to remove itself from the sprite group. But since a Dot is clearly not a projectile, it can warp to the other side.

How to generate the food for a snake game

I can not figure out how to generate the food for the snake to eat. I know the position of the snake at line 97 and 98, I have created a class to generate a pixel where I want to draw a peace of food at line 22 (EDIT: should probably be a function, commented #def (?) in the code). All I have to do is add 15 pixels at the x and y coordinates from the position that is randomly allocated and print it to get a block.
The problem is to check if I eat it or not. It should be something like:
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
...add a point and make snake longer...
The problem is putting it all together for some reason.. Can some one give me a hint or solve how I should write this class so I can continue with other problems? Thank you!
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
i took your code and i think i work something out, pls note that this only monitors when the snake goes over the block, then it prints: yummy, so you will have to add the detail, also note that i dont use your class to generate the food:
import pygame
import random
#Global variables
#Color
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
#Start length of snake
snake_length = 3
#Set the width of the segments of the snake
segment_width = 15
segment_height = 15
# Margin within each segment
segment_margin = 3
#Set initial speed
x_change = segment_width + segment_margin
y_change = 0
#def (?)
class Food():
#Class to print food
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
class Segment(pygame.sprite.Sprite):
""" Class to represent the segment of the snake. """
# Methods
# Constructer function
def __init__(self, x, y):
#Call the parents constructor
super().__init__()
#Set height, width
self.image = pygame.Surface([segment_width, segment_height])
self.image.fill(WHITE)
#Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
#Call this function so the Pygame library can initialize itself
pygame.init()
#Create an 800x600 size screen
screen = pygame.display.set_mode([800, 600])
#Set the title of the window
pygame.display.set_caption("Snake")
allspriteslist = pygame.sprite.Group()
#Create an initial snake
snake_segments = []
for i in range(snake_length):
x = 250 - (segment_width + segment_margin) * i
y = 30
segment = Segment(x, y)
snake_segments.append(segment)
allspriteslist.add(segment)
clock = pygame.time.Clock()
done = False
x_food = random.randint(0, 785)
y_food = random.randint(0, 585)
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
#Set the speed based on the key pressed
#We want the speed to be enough that we move a full
#Segment, plus the margin
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = (segment_width + segment_margin) * -1
y_change = 0
if event.key == pygame.K_RIGHT:
x_change = (segment_width + segment_margin)
y_change = 0
if event.key == pygame.K_UP:
x_change = 0
y_change = (segment_height + segment_margin) * -1
if event.key == pygame.K_DOWN:
x_change = 0
y_change = (segment_width + segment_margin)
if y < y_food+30:
if x > x_food and x < x_food+30 or x+20 > x_food and x+20<x_food+30:
print('yummy')
#Get rid of last segment of the snake
#.pop() command removes last item in list
old_segment = snake_segments.pop()
allspriteslist.remove(old_segment)
#Figure out where new segment will be
x = snake_segments[0].rect.x + x_change
y = snake_segments[0].rect.y + y_change
segment = Segment(x, y)
#Insert new segment to the list
snake_segments.insert(0, segment)
allspriteslist.add(segment)
#Draw
#Clear screen
screen.fill(BLACK)
pygame.draw.rect(screen, WHITE, [x_food, y_food, 30, 30])
allspriteslist.draw(screen)
#Flip screen
pygame.display.flip()
#Pause
clock.tick(5)
pygame.quit()
hope this helped, thanks!
if x >= x_food && x <= x_food + 15 || y >= y_food && y <= y_food + 15:
Why do you OR these pairs of conditions? Don't all 4 tests have to be true at the same time?
if x >= x_food && x <= x_food + 15 && y >= y_food && y <= y_food + 15:
how about using:
if snake_headx == foodx and snake_heady == foody:
food_eated()
snake_grow()
just a suggestion though
didnt read all ur code, just thought u might find it usefull.
actuely ive came to a solution, so basicly what you want is a square, and when the snake comes near that square something should happen?, well ive got a racegame that makes you crash when you hit a square car, so il just copy the code here:
if y < thing_starty+thing_height:
if x > thing_startx and x < thing_startx+thing_width or x+car_width > thing_startx and x+car_width<thing_startx+thing_width:
snake_eated()
snake_grow()
this monitors the x and y of your car (or snake) and checks when the thing (or food) 's y is smaller than your car, then it checks the x's and alot of other things, and basicly it creates a big line all around your square that you cannot cross in your case you'd just need to add the rest, would this work?
Make your food a sprite with a simple filled rectangle as the image, and then use sprite collision pygame.sprite.spritecollide() to check if your snake collides with your food. Pygame will take care of the actual logic whether two rectangles overlap for you.
Also, since you are already using sprite groups, I suggest you write an update function for your snake segments which moves them instead of creating a new segment every turn. Then you can simply call allspriteslist.update() in your main game loop, which will call the update function for every snake segment.
Finally, you might want to have a look at the numerous snake examples on the pygame website.

Python Tic Tac Toe using minimax does not make best move

I have been trying to get this to work for over a week now and cant seem to get it to choose the best possible move. Can someone take a look and help me out? I feel like I have missed something stupid.
Thanks!
from tkinter import *
import tkinter.messagebox
tk = Tk()
tk.title("Tic Tac Toe")
click = True
X = "X"
O = "O"
EMPTY = " "
TIE = "TIE"
NUM_SQUARES = 9
def new_board():
"""Create new game board."""
board = []
for square in range(NUM_SQUARES):
board.append(EMPTY)
return board
def Player1Win(board, i):
if(board[0]==1 and board[1]==1 and board[2]==1)or(board[0]==1 and board[3]==1 and board[6]==1)or(board[2]==1 and board[5]==1 and board[8]==1)or(board[3]==1 and board[4]==1 and board[5]==1)or(board[6]==1 and board[7]==1 and board[8]==1)or(board[0]==1 and board[4]==1 and board[8]==1)or(board[2]==1 and board[4]==1 and board[6]==1)or(board[1]==1 and board[4]==1 and board[7]==1)or(board[0]==1 and board[3]==1 and board[6]==1):
if(i==0):
tkinter.messagebox.showinfo("Winner X won")
return True
else:
return False
def Player2Win(board, i):
if(board[0]==0 and board[1]==0 and board[2]==0)or(board[0]==0 and board[3]==0 and board[6]==0)or(board[2]==0 and board[5]==0 and board[8]==0)or(board[3]==0 and board[4]==0 and board[5]==0)or(board[6]==0 and board[7]==0 and board[8]==0)or(board[0]==0 and board[4]==0 and board[8]==0)or(board[2]==0 and board[4]==0 and board[6]==0)or(board[1]==0 and board[4]==0 and board[7]==0):
if(i==0):
tkinter.messagebox.showinfo("Winner O won")
return True
else:
return False
board2=new_board()
board=new_board()
def legal_moves(board):
"""Create list of legal moves."""
moves = []
count = 0
for square in range(NUM_SQUARES):
if board[square] == EMPTY:
count += 1
moves.append(square)
print(count)
return moves
def minimax(board2):
global click
#Get all possible steps
moves=legal_moves(board2)
#Check copy board to win
if(Player1Win(board2,2) == True):
return 10
if(Player2Win(board2,2) == True):
return -10
if(len(moves)==0):
return 0
j = 1000
for i in moves:
if click:
click=True #change to Player X
board2[i]=0 #This is a copy of the board is not the main
else:
click=False #to change Player O
board2[i]=1 #This is a copy of the board is not the main
v = minimax(board2) # Recursive call functions
# # Select the minimum number for O and maximum for X
if j == 1000 or (click == False and j < v) or (click == True and v < j):
j = v
if click:
return j-1 # If player O
else:
return j+1 # If player X
def bestMove():
global click
# Get all possible steps
moves=legal_moves(board)
# Move all the data from the main board
for k in range(NUM_SQUARES):
board2.append(board[k])
j = 1000
best = -1
for i in moves:
if click:
click = False #change to Player X
board2[i]=0
else:
click = True #change to Player O
board2[i]=1
v = minimax(board2)
# Select the minimum number for O and maximum for X
if j == 1000 or (click == False and j < v) or (click == True and v < j):
j = v
best = i
return best
def Update(board):
global click
if button1["text"]=="X":
board[0]=1
if button2["text"]=="X":
board[1]=1
if button3["text"]=="X":
board[2]=1
if button4["text"]=="X":
board[3]=1
if button5["text"]=="X":
board[4]=1
if button6["text"]=="X":
board[5]=1
if button7["text"]=="X":
board[6]=1
if button8["text"]=="X":
board[7]=1
if button9["text"]=="X":
board[8]=1
if button1["text"]=="O":
board[0]=0
if button2["text"]=="O":
board[1]=0
if button3["text"]=="O":
board[2]=0
if button4["text"]=="O":
board[3]=0
if button5["text"]=="O":
board[4]=0
if button6["text"]=="O":
board[5]=0
if button7["text"]=="O":
board[6]=0
if button8["text"]=="O":
board[7]=0
if button9["text"]=="O":
board[8]=0
def move(i):
global click
click = False
if i == 0:
button1["text"] = "O"
board[0]=0
elif i == 1:
button2["text"] = "O"
board[1]=0
elif i == 2:
button3["text"] = "O"
board[2]=0
elif i == 3:
button4["text"] = "O"
board[3]=0
elif i == 4:
button5["text"] = "O"
board[4]=0
elif i == 5:
button6["text"] = "O"
board[5]=0
elif i == 6:
button7["text"] = "O"
board[6]=0
elif i == 7:
button8["text"] = "O"
board[7]=0
elif i == 8:
button9["text"] = "O"
board[8]=0
def checker(buttons):
global click
if buttons["text"] == " ":
buttons["text"] = "X"
click = True
Update(board)
best = bestMove()
move(best)
Player1Win(board, 0)
Player2Win(board, 0)
buttons = StringVar()
button1 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button1))
button1.grid(row=1,column=0,sticky = S+N+E+W)
button2 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button2))
button2.grid(row=1,column=1,sticky = S+N+E+W)
button3 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button3))
button3.grid(row=1,column=2,sticky = S+N+E+W)
button4 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button4))
button4.grid(row=2,column=0,sticky = S+N+E+W)
button5 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button5))
button5.grid(row=2,column=1,sticky = S+N+E+W)
button6 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button6))
button6.grid(row=2,column=2,sticky = S+N+E+W)
button7 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button7))
button7.grid(row=3,column=0,sticky = S+N+E+W)
button8 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button8))
button8.grid(row=3,column=1,sticky = S+N+E+W)
button9 = Button(tk,text=" ",font=('Times 26 bold'), height = 4, width = 8, command =lambda:checker(button9))
button9.grid(row=3,column=2,sticky = S+N+E+W)
tk.mainloop()
print ("Please Begin Play")
I would suggest you add print statements to see what is happening. Start with printing "click" for each pass through the for() loop and also print what the return (j +- 1) and the value for "i" right before the return statements (and please don't use "i", "l", or "O" as single digit variable names as they can look like numbers, i.e, o=1, o=l). You can handle the really, really long and really, really, confusing line a lot better with a list of win combinations.
## a simple example that will require some modification by you
def check_for_winner(player, board):
## fill in the rest yourself
win_combos=[[0, 1, 2], [3, 4, 5], [6, 7, 8], [0, 4, 8], [0, 3, 6]]
for x, y, z in win_combos:
print "checking", x, y, z
if board[x] == board[y] == board[z]:
print "**** We have a winner", player, x, y, z
return True
return False

Resources