First Python program: Snake
$begingroup$
I've started learning Python yesterday with the intention of studying machine learning. Before this, my experience with programming was exclusive to my first semester where I had a C course.
I decided to work towards a final objective: have an AI learn how to win the snake game. For that goal, I had to make the actual game. I didn't want to copy an already made game as I'm still learning. So with that in mind, I spent an entire night building my little snake game.
I would like the game to be as good as possible before I started the machine learning part, which is why I'm posting it here. My biggest problem right now is controlling the speed of the game. I achieved this by limiting the FPS number based on the current score but it seems like a cheap way to do it.
I divided the game in two files: vars.py where I define most variables and functions and snake.py with the actual game and a few other things. This is the way I was taught to program so if it's wrong or not done please feel free to point that out.
vars.py
import random
import math
width = 800
height = 600
BG = 255, 255, 255
FOOD_C = 128, 0, 0
BODY_C = 0, 0, 0
sqr_size = 10
SPEED = sqr_size
def dist(a, b):
return math.sqrt((b.pos[0] - a.pos[0])**2 + (b.pos[1] - a.pos[1])**2)
def check_food(snake, food): #Check if food is eaten
if dist(snake, food) > sqr_size:
return False
else:
return True
def loser(snake, food): #Check if lost the game
if snake.pos[0]<sqr_size or snake.pos[0]>width-sqr_size:
return True
if snake.pos[1]<sqr_size or snake.pos[1]>height-sqr_size:
return True
for i in snake.body[1:]:
if i == snake.pos:
return True
def game_speed(snake):
if (10 + snake.score()//2) < 30:
return 10 + snake.score()//2
else:
return 30
class Snake(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
self.mov = "UP"
self.body = [self.pos[:]]
def change_mov(self, key): #Decide where to move
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
def score(self):
return len(self.body)
def move(self, eat): #Snake movement
if self.mov == "UP": self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN": self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT": self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT": self.pos[0] = self.pos[0] + SPEED
self.body.insert(0, self.pos[:])
if not eat:
self.body.pop()
class Food(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
snake.py
import pygame, sys
import vars
#Initialising pygame
pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Times New Roman', 30)
clock = pygame.time.Clock()
screen = pygame.display.set_mode((vars.width,vars.height))
pygame.display.update()
#Initialising variables
lost = False
eat = False
snake = vars.Snake()
food = vars.Food()
screen.fill(vars.BG)
key1 = "0"
def whatkey(event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
return "LEFT"
if event.key == pygame.K_RIGHT:
return "RIGHT"
if event.key == pygame.K_UP:
return "UP"
if event.key == pygame.K_DOWN:
return "DOWN"
while not lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
key1 = whatkey(event)
#How the game works
snake.change_mov(key1)
eat = vars.check_food(snake, food)
snake.move(eat)
if eat:
food = vars.Food()
lost = vars.loser(snake, food)
#Screen drawings
screen.fill(vars.BG)
for i in snake.body:
pygame.draw.circle(screen, vars.BODY_C, (i[0], i[1]), vars.sqr_size, 0)
pygame.draw.circle(screen, vars.FOOD_C, (food.pos[0], food.pos[1]), vars.sqr_size, 0)
pygame.display.set_caption("Snake. Your score is: {}".format(snake.score()))
pygame.display.update()
#Control of the game speed via fps
#Not related to the SPEED variable. That is for movement
msElapsed = clock.tick(vars.game_speed(snake))
#Lose screen
pygame.display.update()
screen.fill(vars.BG)
textsurface1 = myfont.render('You lost. Your score is:', False, (0, 0, 0))
textsurface2 = myfont.render("{}".format(snake.score()), False, (0, 0, 0))
screen.blit(textsurface1,(250, 200))
screen.blit(textsurface2,(380,280))
pygame.display.update()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
python beginner pygame snake-game
New contributor
$endgroup$
add a comment |
$begingroup$
I've started learning Python yesterday with the intention of studying machine learning. Before this, my experience with programming was exclusive to my first semester where I had a C course.
I decided to work towards a final objective: have an AI learn how to win the snake game. For that goal, I had to make the actual game. I didn't want to copy an already made game as I'm still learning. So with that in mind, I spent an entire night building my little snake game.
I would like the game to be as good as possible before I started the machine learning part, which is why I'm posting it here. My biggest problem right now is controlling the speed of the game. I achieved this by limiting the FPS number based on the current score but it seems like a cheap way to do it.
I divided the game in two files: vars.py where I define most variables and functions and snake.py with the actual game and a few other things. This is the way I was taught to program so if it's wrong or not done please feel free to point that out.
vars.py
import random
import math
width = 800
height = 600
BG = 255, 255, 255
FOOD_C = 128, 0, 0
BODY_C = 0, 0, 0
sqr_size = 10
SPEED = sqr_size
def dist(a, b):
return math.sqrt((b.pos[0] - a.pos[0])**2 + (b.pos[1] - a.pos[1])**2)
def check_food(snake, food): #Check if food is eaten
if dist(snake, food) > sqr_size:
return False
else:
return True
def loser(snake, food): #Check if lost the game
if snake.pos[0]<sqr_size or snake.pos[0]>width-sqr_size:
return True
if snake.pos[1]<sqr_size or snake.pos[1]>height-sqr_size:
return True
for i in snake.body[1:]:
if i == snake.pos:
return True
def game_speed(snake):
if (10 + snake.score()//2) < 30:
return 10 + snake.score()//2
else:
return 30
class Snake(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
self.mov = "UP"
self.body = [self.pos[:]]
def change_mov(self, key): #Decide where to move
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
def score(self):
return len(self.body)
def move(self, eat): #Snake movement
if self.mov == "UP": self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN": self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT": self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT": self.pos[0] = self.pos[0] + SPEED
self.body.insert(0, self.pos[:])
if not eat:
self.body.pop()
class Food(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
snake.py
import pygame, sys
import vars
#Initialising pygame
pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Times New Roman', 30)
clock = pygame.time.Clock()
screen = pygame.display.set_mode((vars.width,vars.height))
pygame.display.update()
#Initialising variables
lost = False
eat = False
snake = vars.Snake()
food = vars.Food()
screen.fill(vars.BG)
key1 = "0"
def whatkey(event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
return "LEFT"
if event.key == pygame.K_RIGHT:
return "RIGHT"
if event.key == pygame.K_UP:
return "UP"
if event.key == pygame.K_DOWN:
return "DOWN"
while not lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
key1 = whatkey(event)
#How the game works
snake.change_mov(key1)
eat = vars.check_food(snake, food)
snake.move(eat)
if eat:
food = vars.Food()
lost = vars.loser(snake, food)
#Screen drawings
screen.fill(vars.BG)
for i in snake.body:
pygame.draw.circle(screen, vars.BODY_C, (i[0], i[1]), vars.sqr_size, 0)
pygame.draw.circle(screen, vars.FOOD_C, (food.pos[0], food.pos[1]), vars.sqr_size, 0)
pygame.display.set_caption("Snake. Your score is: {}".format(snake.score()))
pygame.display.update()
#Control of the game speed via fps
#Not related to the SPEED variable. That is for movement
msElapsed = clock.tick(vars.game_speed(snake))
#Lose screen
pygame.display.update()
screen.fill(vars.BG)
textsurface1 = myfont.render('You lost. Your score is:', False, (0, 0, 0))
textsurface2 = myfont.render("{}".format(snake.score()), False, (0, 0, 0))
screen.blit(textsurface1,(250, 200))
screen.blit(textsurface2,(380,280))
pygame.display.update()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
python beginner pygame snake-game
New contributor
$endgroup$
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago
add a comment |
$begingroup$
I've started learning Python yesterday with the intention of studying machine learning. Before this, my experience with programming was exclusive to my first semester where I had a C course.
I decided to work towards a final objective: have an AI learn how to win the snake game. For that goal, I had to make the actual game. I didn't want to copy an already made game as I'm still learning. So with that in mind, I spent an entire night building my little snake game.
I would like the game to be as good as possible before I started the machine learning part, which is why I'm posting it here. My biggest problem right now is controlling the speed of the game. I achieved this by limiting the FPS number based on the current score but it seems like a cheap way to do it.
I divided the game in two files: vars.py where I define most variables and functions and snake.py with the actual game and a few other things. This is the way I was taught to program so if it's wrong or not done please feel free to point that out.
vars.py
import random
import math
width = 800
height = 600
BG = 255, 255, 255
FOOD_C = 128, 0, 0
BODY_C = 0, 0, 0
sqr_size = 10
SPEED = sqr_size
def dist(a, b):
return math.sqrt((b.pos[0] - a.pos[0])**2 + (b.pos[1] - a.pos[1])**2)
def check_food(snake, food): #Check if food is eaten
if dist(snake, food) > sqr_size:
return False
else:
return True
def loser(snake, food): #Check if lost the game
if snake.pos[0]<sqr_size or snake.pos[0]>width-sqr_size:
return True
if snake.pos[1]<sqr_size or snake.pos[1]>height-sqr_size:
return True
for i in snake.body[1:]:
if i == snake.pos:
return True
def game_speed(snake):
if (10 + snake.score()//2) < 30:
return 10 + snake.score()//2
else:
return 30
class Snake(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
self.mov = "UP"
self.body = [self.pos[:]]
def change_mov(self, key): #Decide where to move
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
def score(self):
return len(self.body)
def move(self, eat): #Snake movement
if self.mov == "UP": self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN": self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT": self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT": self.pos[0] = self.pos[0] + SPEED
self.body.insert(0, self.pos[:])
if not eat:
self.body.pop()
class Food(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
snake.py
import pygame, sys
import vars
#Initialising pygame
pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Times New Roman', 30)
clock = pygame.time.Clock()
screen = pygame.display.set_mode((vars.width,vars.height))
pygame.display.update()
#Initialising variables
lost = False
eat = False
snake = vars.Snake()
food = vars.Food()
screen.fill(vars.BG)
key1 = "0"
def whatkey(event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
return "LEFT"
if event.key == pygame.K_RIGHT:
return "RIGHT"
if event.key == pygame.K_UP:
return "UP"
if event.key == pygame.K_DOWN:
return "DOWN"
while not lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
key1 = whatkey(event)
#How the game works
snake.change_mov(key1)
eat = vars.check_food(snake, food)
snake.move(eat)
if eat:
food = vars.Food()
lost = vars.loser(snake, food)
#Screen drawings
screen.fill(vars.BG)
for i in snake.body:
pygame.draw.circle(screen, vars.BODY_C, (i[0], i[1]), vars.sqr_size, 0)
pygame.draw.circle(screen, vars.FOOD_C, (food.pos[0], food.pos[1]), vars.sqr_size, 0)
pygame.display.set_caption("Snake. Your score is: {}".format(snake.score()))
pygame.display.update()
#Control of the game speed via fps
#Not related to the SPEED variable. That is for movement
msElapsed = clock.tick(vars.game_speed(snake))
#Lose screen
pygame.display.update()
screen.fill(vars.BG)
textsurface1 = myfont.render('You lost. Your score is:', False, (0, 0, 0))
textsurface2 = myfont.render("{}".format(snake.score()), False, (0, 0, 0))
screen.blit(textsurface1,(250, 200))
screen.blit(textsurface2,(380,280))
pygame.display.update()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
python beginner pygame snake-game
New contributor
$endgroup$
I've started learning Python yesterday with the intention of studying machine learning. Before this, my experience with programming was exclusive to my first semester where I had a C course.
I decided to work towards a final objective: have an AI learn how to win the snake game. For that goal, I had to make the actual game. I didn't want to copy an already made game as I'm still learning. So with that in mind, I spent an entire night building my little snake game.
I would like the game to be as good as possible before I started the machine learning part, which is why I'm posting it here. My biggest problem right now is controlling the speed of the game. I achieved this by limiting the FPS number based on the current score but it seems like a cheap way to do it.
I divided the game in two files: vars.py where I define most variables and functions and snake.py with the actual game and a few other things. This is the way I was taught to program so if it's wrong or not done please feel free to point that out.
vars.py
import random
import math
width = 800
height = 600
BG = 255, 255, 255
FOOD_C = 128, 0, 0
BODY_C = 0, 0, 0
sqr_size = 10
SPEED = sqr_size
def dist(a, b):
return math.sqrt((b.pos[0] - a.pos[0])**2 + (b.pos[1] - a.pos[1])**2)
def check_food(snake, food): #Check if food is eaten
if dist(snake, food) > sqr_size:
return False
else:
return True
def loser(snake, food): #Check if lost the game
if snake.pos[0]<sqr_size or snake.pos[0]>width-sqr_size:
return True
if snake.pos[1]<sqr_size or snake.pos[1]>height-sqr_size:
return True
for i in snake.body[1:]:
if i == snake.pos:
return True
def game_speed(snake):
if (10 + snake.score()//2) < 30:
return 10 + snake.score()//2
else:
return 30
class Snake(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
self.mov = "UP"
self.body = [self.pos[:]]
def change_mov(self, key): #Decide where to move
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
def score(self):
return len(self.body)
def move(self, eat): #Snake movement
if self.mov == "UP": self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN": self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT": self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT": self.pos[0] = self.pos[0] + SPEED
self.body.insert(0, self.pos[:])
if not eat:
self.body.pop()
class Food(object):
def __init__(self):
self.pos = [random.randint(1, (width-sqr_size)/10)*10,
random.randint(1, (height-sqr_size)/10)*10]
snake.py
import pygame, sys
import vars
#Initialising pygame
pygame.init()
pygame.font.init()
myfont = pygame.font.SysFont('Times New Roman', 30)
clock = pygame.time.Clock()
screen = pygame.display.set_mode((vars.width,vars.height))
pygame.display.update()
#Initialising variables
lost = False
eat = False
snake = vars.Snake()
food = vars.Food()
screen.fill(vars.BG)
key1 = "0"
def whatkey(event):
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
return "LEFT"
if event.key == pygame.K_RIGHT:
return "RIGHT"
if event.key == pygame.K_UP:
return "UP"
if event.key == pygame.K_DOWN:
return "DOWN"
while not lost:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
key1 = whatkey(event)
#How the game works
snake.change_mov(key1)
eat = vars.check_food(snake, food)
snake.move(eat)
if eat:
food = vars.Food()
lost = vars.loser(snake, food)
#Screen drawings
screen.fill(vars.BG)
for i in snake.body:
pygame.draw.circle(screen, vars.BODY_C, (i[0], i[1]), vars.sqr_size, 0)
pygame.draw.circle(screen, vars.FOOD_C, (food.pos[0], food.pos[1]), vars.sqr_size, 0)
pygame.display.set_caption("Snake. Your score is: {}".format(snake.score()))
pygame.display.update()
#Control of the game speed via fps
#Not related to the SPEED variable. That is for movement
msElapsed = clock.tick(vars.game_speed(snake))
#Lose screen
pygame.display.update()
screen.fill(vars.BG)
textsurface1 = myfont.render('You lost. Your score is:', False, (0, 0, 0))
textsurface2 = myfont.render("{}".format(snake.score()), False, (0, 0, 0))
screen.blit(textsurface1,(250, 200))
screen.blit(textsurface2,(380,280))
pygame.display.update()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
python beginner pygame snake-game
python beginner pygame snake-game
New contributor
New contributor
edited 8 hours ago
Mast
7,49163788
7,49163788
New contributor
asked 9 hours ago
André RochaAndré Rocha
163
163
New contributor
New contributor
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago
add a comment |
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago
add a comment |
2 Answers
2
active
oldest
votes
$begingroup$
For efficiency reasons, you should always do x1**2 + y1**2 < r**2
rather than sqrt(x1**2 + y1**2) < r
, because sqrt
is much much slower than pow
. Because You don't need a square root to compare distances. This is the special case of x1**2 + y1**2 < x2**2 + y2**2
.
Sometimes sqrt
distances computing when you have a bunch of things on your screen becomes the slowest thing in your program. And there is absolutely no reason to do it.
My suggestion is to keep everything squared, until you really need to compute sqrt
(which you don't)
Also, storing a direction in a string works ("DOWN"
), but isn't very practical, it is a beginner pattern called 'stringly typed code'. You could instead make a constant called DOWN
which corresponds to the vector pointing down ((0, -1)
to make it simple or 0 - 1j
if you like complex numbers (best imo) or a custom object if you like OOP). You can then replace:
if self.mov == "UP":
self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN":
self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT":
self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT":
self.pos[0] = self.pos[0] + SPEED
# becomes
self.pos += self.mov
with constants, math becomes easier:
UP = 0 + 1j
DOWN = 0 - 1j
LEFT = -1 + 0j
RIGHT = 1 + 0j
2*DOWN + 5*LEFT # means move 2 cases down and 5 left how intuitive
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
# becomes
if key - self.mov:
self.mov = key
It takes time getting used to but it is worth it, you can always use tuples but they are the same except math doesn't work with them. The fact is Complex numbers are awesome it generalises nicely for arbitrary directions and distances. And if you assign to constants, you don't ever need to see them.
New contributor
$endgroup$
$begingroup$
I'll also suggest looking intonumpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.
$endgroup$
– scnerd
3 hours ago
add a comment |
$begingroup$
I'm on my phone so I'll just mention something minor I noticed.
check_food
's return is redundant. dist(snake, food) > sqr_size
already evaluates to a boolean value. You just need to negate that:
def check_food(snake, food): #Check if food is eaten
return not dist(snake, food) > sqr_size
Or simply:
def check_food(snake, food): #Check if food is eaten
return dist(snake, food) <= sqr_size
And there's a similar situation in loser
. The first two conditions are just returning True
. They could be "connected" via or
to be simplified.
$endgroup$
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just usedeat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use<=
instead, as that would be the opposite of>
.
$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
return StackExchange.using("mathjaxEditing", function () {
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
});
});
}, "mathjax-editing");
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
André Rocha is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213665%2ffirst-python-program-snake%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
For efficiency reasons, you should always do x1**2 + y1**2 < r**2
rather than sqrt(x1**2 + y1**2) < r
, because sqrt
is much much slower than pow
. Because You don't need a square root to compare distances. This is the special case of x1**2 + y1**2 < x2**2 + y2**2
.
Sometimes sqrt
distances computing when you have a bunch of things on your screen becomes the slowest thing in your program. And there is absolutely no reason to do it.
My suggestion is to keep everything squared, until you really need to compute sqrt
(which you don't)
Also, storing a direction in a string works ("DOWN"
), but isn't very practical, it is a beginner pattern called 'stringly typed code'. You could instead make a constant called DOWN
which corresponds to the vector pointing down ((0, -1)
to make it simple or 0 - 1j
if you like complex numbers (best imo) or a custom object if you like OOP). You can then replace:
if self.mov == "UP":
self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN":
self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT":
self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT":
self.pos[0] = self.pos[0] + SPEED
# becomes
self.pos += self.mov
with constants, math becomes easier:
UP = 0 + 1j
DOWN = 0 - 1j
LEFT = -1 + 0j
RIGHT = 1 + 0j
2*DOWN + 5*LEFT # means move 2 cases down and 5 left how intuitive
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
# becomes
if key - self.mov:
self.mov = key
It takes time getting used to but it is worth it, you can always use tuples but they are the same except math doesn't work with them. The fact is Complex numbers are awesome it generalises nicely for arbitrary directions and distances. And if you assign to constants, you don't ever need to see them.
New contributor
$endgroup$
$begingroup$
I'll also suggest looking intonumpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.
$endgroup$
– scnerd
3 hours ago
add a comment |
$begingroup$
For efficiency reasons, you should always do x1**2 + y1**2 < r**2
rather than sqrt(x1**2 + y1**2) < r
, because sqrt
is much much slower than pow
. Because You don't need a square root to compare distances. This is the special case of x1**2 + y1**2 < x2**2 + y2**2
.
Sometimes sqrt
distances computing when you have a bunch of things on your screen becomes the slowest thing in your program. And there is absolutely no reason to do it.
My suggestion is to keep everything squared, until you really need to compute sqrt
(which you don't)
Also, storing a direction in a string works ("DOWN"
), but isn't very practical, it is a beginner pattern called 'stringly typed code'. You could instead make a constant called DOWN
which corresponds to the vector pointing down ((0, -1)
to make it simple or 0 - 1j
if you like complex numbers (best imo) or a custom object if you like OOP). You can then replace:
if self.mov == "UP":
self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN":
self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT":
self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT":
self.pos[0] = self.pos[0] + SPEED
# becomes
self.pos += self.mov
with constants, math becomes easier:
UP = 0 + 1j
DOWN = 0 - 1j
LEFT = -1 + 0j
RIGHT = 1 + 0j
2*DOWN + 5*LEFT # means move 2 cases down and 5 left how intuitive
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
# becomes
if key - self.mov:
self.mov = key
It takes time getting used to but it is worth it, you can always use tuples but they are the same except math doesn't work with them. The fact is Complex numbers are awesome it generalises nicely for arbitrary directions and distances. And if you assign to constants, you don't ever need to see them.
New contributor
$endgroup$
$begingroup$
I'll also suggest looking intonumpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.
$endgroup$
– scnerd
3 hours ago
add a comment |
$begingroup$
For efficiency reasons, you should always do x1**2 + y1**2 < r**2
rather than sqrt(x1**2 + y1**2) < r
, because sqrt
is much much slower than pow
. Because You don't need a square root to compare distances. This is the special case of x1**2 + y1**2 < x2**2 + y2**2
.
Sometimes sqrt
distances computing when you have a bunch of things on your screen becomes the slowest thing in your program. And there is absolutely no reason to do it.
My suggestion is to keep everything squared, until you really need to compute sqrt
(which you don't)
Also, storing a direction in a string works ("DOWN"
), but isn't very practical, it is a beginner pattern called 'stringly typed code'. You could instead make a constant called DOWN
which corresponds to the vector pointing down ((0, -1)
to make it simple or 0 - 1j
if you like complex numbers (best imo) or a custom object if you like OOP). You can then replace:
if self.mov == "UP":
self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN":
self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT":
self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT":
self.pos[0] = self.pos[0] + SPEED
# becomes
self.pos += self.mov
with constants, math becomes easier:
UP = 0 + 1j
DOWN = 0 - 1j
LEFT = -1 + 0j
RIGHT = 1 + 0j
2*DOWN + 5*LEFT # means move 2 cases down and 5 left how intuitive
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
# becomes
if key - self.mov:
self.mov = key
It takes time getting used to but it is worth it, you can always use tuples but they are the same except math doesn't work with them. The fact is Complex numbers are awesome it generalises nicely for arbitrary directions and distances. And if you assign to constants, you don't ever need to see them.
New contributor
$endgroup$
For efficiency reasons, you should always do x1**2 + y1**2 < r**2
rather than sqrt(x1**2 + y1**2) < r
, because sqrt
is much much slower than pow
. Because You don't need a square root to compare distances. This is the special case of x1**2 + y1**2 < x2**2 + y2**2
.
Sometimes sqrt
distances computing when you have a bunch of things on your screen becomes the slowest thing in your program. And there is absolutely no reason to do it.
My suggestion is to keep everything squared, until you really need to compute sqrt
(which you don't)
Also, storing a direction in a string works ("DOWN"
), but isn't very practical, it is a beginner pattern called 'stringly typed code'. You could instead make a constant called DOWN
which corresponds to the vector pointing down ((0, -1)
to make it simple or 0 - 1j
if you like complex numbers (best imo) or a custom object if you like OOP). You can then replace:
if self.mov == "UP":
self.pos[1] = self.pos[1] - SPEED
if self.mov == "DOWN":
self.pos[1] = self.pos[1] + SPEED
if self.mov == "LEFT":
self.pos[0] = self.pos[0] - SPEED
if self.mov == "RIGHT":
self.pos[0] = self.pos[0] + SPEED
# becomes
self.pos += self.mov
with constants, math becomes easier:
UP = 0 + 1j
DOWN = 0 - 1j
LEFT = -1 + 0j
RIGHT = 1 + 0j
2*DOWN + 5*LEFT # means move 2 cases down and 5 left how intuitive
if key == "UP" and self.mov != "DOWN":
self.mov = key
if key == "DOWN" and self.mov != "UP":
self.mov = key
if key == "RIGHT" and self.mov != "LEFT":
self.mov = key
if key == "LEFT" and self.mov != "RIGHT":
self.mov = key
# becomes
if key - self.mov:
self.mov = key
It takes time getting used to but it is worth it, you can always use tuples but they are the same except math doesn't work with them. The fact is Complex numbers are awesome it generalises nicely for arbitrary directions and distances. And if you assign to constants, you don't ever need to see them.
New contributor
edited 3 hours ago
New contributor
answered 6 hours ago
Benoît PilatteBenoît Pilatte
1615
1615
New contributor
New contributor
$begingroup$
I'll also suggest looking intonumpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.
$endgroup$
– scnerd
3 hours ago
add a comment |
$begingroup$
I'll also suggest looking intonumpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.
$endgroup$
– scnerd
3 hours ago
$begingroup$
I'll also suggest looking into
numpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.$endgroup$
– scnerd
3 hours ago
$begingroup$
I'll also suggest looking into
numpy
for direction vectors. It's not strictly necessary for 2D vectors (I actually really like that idea of using complex numbers, I might borrow that), but it'll certainly show up once you start looking into AI code, so you might as well get used to it now.$endgroup$
– scnerd
3 hours ago
add a comment |
$begingroup$
I'm on my phone so I'll just mention something minor I noticed.
check_food
's return is redundant. dist(snake, food) > sqr_size
already evaluates to a boolean value. You just need to negate that:
def check_food(snake, food): #Check if food is eaten
return not dist(snake, food) > sqr_size
Or simply:
def check_food(snake, food): #Check if food is eaten
return dist(snake, food) <= sqr_size
And there's a similar situation in loser
. The first two conditions are just returning True
. They could be "connected" via or
to be simplified.
$endgroup$
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just usedeat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use<=
instead, as that would be the opposite of>
.
$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
add a comment |
$begingroup$
I'm on my phone so I'll just mention something minor I noticed.
check_food
's return is redundant. dist(snake, food) > sqr_size
already evaluates to a boolean value. You just need to negate that:
def check_food(snake, food): #Check if food is eaten
return not dist(snake, food) > sqr_size
Or simply:
def check_food(snake, food): #Check if food is eaten
return dist(snake, food) <= sqr_size
And there's a similar situation in loser
. The first two conditions are just returning True
. They could be "connected" via or
to be simplified.
$endgroup$
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just usedeat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use<=
instead, as that would be the opposite of>
.
$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
add a comment |
$begingroup$
I'm on my phone so I'll just mention something minor I noticed.
check_food
's return is redundant. dist(snake, food) > sqr_size
already evaluates to a boolean value. You just need to negate that:
def check_food(snake, food): #Check if food is eaten
return not dist(snake, food) > sqr_size
Or simply:
def check_food(snake, food): #Check if food is eaten
return dist(snake, food) <= sqr_size
And there's a similar situation in loser
. The first two conditions are just returning True
. They could be "connected" via or
to be simplified.
$endgroup$
I'm on my phone so I'll just mention something minor I noticed.
check_food
's return is redundant. dist(snake, food) > sqr_size
already evaluates to a boolean value. You just need to negate that:
def check_food(snake, food): #Check if food is eaten
return not dist(snake, food) > sqr_size
Or simply:
def check_food(snake, food): #Check if food is eaten
return dist(snake, food) <= sqr_size
And there's a similar situation in loser
. The first two conditions are just returning True
. They could be "connected" via or
to be simplified.
edited 9 hours ago
answered 9 hours ago
CarcigenicateCarcigenicate
3,40911531
3,40911531
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just usedeat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use<=
instead, as that would be the opposite of>
.
$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
add a comment |
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just usedeat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use<=
instead, as that would be the opposite of>
.
$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just used
eat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
You are absolutely right. I deleted the check_food function altogether and just used
eat = vars.dist(snake, food) < vars.sqr_size
$endgroup$
– André Rocha
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use
<=
instead, as that would be the opposite of >
.$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
@AndréRocha Unless you've double checked that logic, you may want to use
<=
instead, as that would be the opposite of >
.$endgroup$
– Carcigenicate
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
$begingroup$
I did, it was eating the food while it was walking on an adjacent space. This way that won't happen
$endgroup$
– André Rocha
9 hours ago
add a comment |
André Rocha is a new contributor. Be nice, and check out our Code of Conduct.
André Rocha is a new contributor. Be nice, and check out our Code of Conduct.
André Rocha is a new contributor. Be nice, and check out our Code of Conduct.
André Rocha is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f213665%2ffirst-python-program-snake%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
var $window = $(window),
onScroll = function(e) {
var $elem = $('.new-login-left'),
docViewTop = $window.scrollTop(),
docViewBottom = docViewTop + $window.height(),
elemTop = $elem.offset().top,
elemBottom = elemTop + $elem.height();
if ((docViewTop elemBottom)) {
StackExchange.using('gps', function() { StackExchange.gps.track('embedded_signup_form.view', { location: 'question_page' }); });
$window.unbind('scroll', onScroll);
}
};
$window.on('scroll', onScroll);
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
$begingroup$
Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers.
$endgroup$
– Mast
8 hours ago
$begingroup$
@Mast I'm sorry, I didn't know. However, the edits I made weren't to incorporate the answers. I changed circles to squares, for example and colours I believe but didn't know I shouldn't. I won't do it again.
$endgroup$
– André Rocha
7 hours ago
$begingroup$
Feel free to post a follow-up question with improved code if your code has changed significantly enough in the meantime. You can save such edits for then. No problem.
$endgroup$
– Mast
5 hours ago