Jake

Continuing to clone famous and fairly easy games, I now made my own version of Snake.
In this version you cannot leave the screen, there are never more than three apples on the screen and there are no blocks inside the field.
Your current score is displayed in the screen caption.
To get more information about Snake, check this Wikipedia entry.

Controls

  • Arrow keys move the snake
  • Escape pauses and unpauses the game
  • ‘r’ resets the game

The First Example The Second Example The Third Example


# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech 16th of June, 2015
#         edited 17th of June, 2015

# importing needed modules
import pygame, sys, time, math, os, random

""" CLASSES """
# dummy class for global variables
class dummy():
	pass

# snake class (controls the whole game)
class snake():
	# init
	def __init__(self, _pos, _dir = [1, 0]):
		self.pos = _pos
		self.dir = _dir
		self.color = [0, 150, 0]
		
		self.parts = []
		self.partcolor = [0, 255, 0]
		self.length = 3
		
		self.apples = []
		self.applecolor = [255, 0, 0]
		self.appleticks = 0
		self.maxapples = 3
		
		self.speed = 10
		self.canChangeDir = True
		
		self.setCaption()
	
	# update screen caption
	def setCaption(self):
		if main.GAMEOVER:
			pygame.display.set_caption(main.CAPTION + " (gameover, " + str(self.length) + " points)")
		else:
			pygame.display.set_caption(main.CAPTION + " (" + str(self.length) + " points)")
	
	# change direction
	def setdir(self, _dir):
		if self.canChangeDir:
			if self.dir[0] != 0:
				# cannot change in x
				if _dir[1] != 0:
					# can change
					self.dir = _dir
					self.canChangeDir = False
			
			elif self.dir[1] != 0:
				# cannot change in y
				if _dir[0] != 0:
					# can change
					self.dir = _dir
					self.canChangeDir = False
	
	# tick
	def tick(self):
		# interpret keys
		if pygame.K_UP in main.KEYSDOWN:
			self.setdir([0, -1])
		if pygame.K_DOWN in main.KEYSDOWN:
			self.setdir([0, 1])
		
		if pygame.K_LEFT in main.KEYSDOWN:
			self.setdir([-1, 0])
		if pygame.K_RIGHT in main.KEYSDOWN:
			self.setdir([1, 0])
		
		# apples
		if len(self.apples) < self.maxapples:
			self.appleticks += 1
			
			# spawn in a new apple
			if self.appleticks >= 60:
				self.appleticks = 0
				
				# random position for the new apple
				p = [random.randint(0, main.WIDTH - 1), random.randint(0, main.HEIGHT - 1)]
				
				# apples must not appear inside the snake
				add = True
				for _ in self.parts:
					if p[0] == _[0] and p[1] == _[1]:
						add = False
				if self.pos[0] == p[0] and self.pos[1] == p[1]:
					add = False
				
				if add:
					self.apples.append(p)
		
		# move
		if main.TICKS % self.speed == 0:
			# check for any apples
			for _ in self.apples:
				if _[0] == self.pos[0] and _[1] == self.pos[1]:
					# apple collected!
					self.apples.remove(_)
					self.length += 1
					self.setCaption()
			
			# check if snake can move
			move = True
			for _ in self.parts:
				if self.pos[0] == _[0]:
					if self.pos[1] + self.dir[1] == _[1]:
						move = False
				if self.pos[1] == _[1]:
					if self.pos[0] + self.dir[0] == _[0]:
						move = False
			
			# check screen borders
			if self.pos[0] + self.dir[0] < 0 or self.pos[0] + self.dir[0] >= main.WIDTH:
				move = False
			
			if self.pos[1] + self.dir[1] < 0 or self.pos[1] + self.dir[1] >= main.HEIGHT:
				move = False
			
			# move or set gameover to true
			if move:
				# create part
				self.parts.append(self.pos[:])
				
				self.pos[0] += self.dir[0]
				self.pos[1] += self.dir[1]
				self.canChangeDir = True
			
			else:
				main.GAMEOVER = True
				self.setCaption()
			
			# remove excess parts
			while len(self.parts) > self.length:
				self.parts.pop(0)
			
	# render
	def render(self, _surface):
		# render parts
		for _ in self.parts:
			_surface.set_at(_, self.partcolor)
		
		# render snake head
		_surface.set_at(self.pos, self.color)
		
		# render apples
		for _ in self.apples:
			_surface.set_at(_, self.applecolor)

""" FUNCTIONS """
# gets the mouse position
def getMousePos():
	p = pygame.mouse.get_pos()
	return [p[0], p[1]]

# validates color integer
# extra feature: _min and _max implementation
def colorValid(_color, _min = 0, _max = 255):
	newColor = math.fabs(_color)
	n = _max - _min
	if newColor > n:
		if int(newColor / n) % 2 == 0:
			newColor = newColor % n
		else:
			newColor = n - (newColor % n)
	
	return int(newColor) + _min

# gets the position on a circle
# circle center                           : '_pos'
# circle radius                           : '_radius'
# angle from center to point on the circle: '_angle'
def getCirclePos(_pos, _radius, _angle):
	return [
				_pos[0] + _radius * math.cos(math.radians(_angle)),
				_pos[1] + _radius * math.sin(math.radians(_angle))
			]

# returns an integer version of given positon
def intpos(_pos):
	return [int(_pos[0]), int(_pos[1])]

# basic vector functions
def vecConvert(p1, p2):
	return [p2[0] - p1[0], p2[1] - p1[1]]
def vecLen(vec):
	return math.sqrt( (vec[0]**2) + (vec[1]**2) )
def vecMultiply(vec, n):
	return [vec[0] * n, vec[1] * n]
def vecGetPoint(vec, point):
	return [point[0] + vec[0], point[1] + vec[1]]
def vecAdd(vec1, vec2):
	return [vec1[0] + vec2[0], vec1[1] + vec2[1]]

# calculates distance between given positions
def posDistance(p1, p2):
	return math.sqrt( (p2[0] - p1[0])**2 + (p2[1] - p1[1])**2 )

# quits the program
def quit():
	sys.exit()

""" TICK; RENDER """
# tick function
def tick():
	# handle events
	for event in pygame.event.get():
		# quit
		if event.type == pygame.QUIT:
			quit()
		
		# keyup
		if event.type == pygame.KEYUP:
			# handle 'main.KEYSDOWN'
			if event.key in main.KEYSDOWN:
				main.KEYSDOWN.remove(event.key)
		
		# keydown
		if event.type == pygame.KEYDOWN:
			# handle 'main.KEYSDOWN'
			if event.key not in main.KEYSDOWN:
				main.KEYSDOWN.append(event.key)
			
			# reset
			if event.key == pygame.K_r:
				reset()
			
			# pause
			if event.key == pygame.K_ESCAPE:
				if main.PAUSED:
					main.PAUSED = False
				else:
					main.PAUSED = True
	
	# tick snake
	if not main.PAUSED and not main.GAMEOVER:
		main.SNAKE.tick()

# render function
def render():
	# render gameover screen
	if main.GAMEOVER:
		if main.GAMEOVERVISTICKS < main.WIDTH * 2:
			pygame.draw.line(main.SURF, [0, 0, 0], [0, main.GAMEOVERVISTICKS], [main.GAMEOVERVISTICKS, 0])
			main.GAMEOVERVISTICKS += 1
	else:
		# fill
		main.SURF.fill(main.COLOR)
		
		# snake
		main.SNAKE.render(main.SURF)
		
		# pause
		if main.PAUSED:
			for _ in range(0, main.WIDTH * 2, 4):
				pygame.draw.line(main.SURF, [255, 255, 255], [0, _], [_, 0])
	
	# blit and flip
	main.SCREEN.blit(pygame.transform.scale(main.SURF, main.SCALEDSIZE), [0, 0])
	pygame.display.flip()

""" RESET; INIT """
# resets game
def reset():
	main.PAUSED = False
	main.GAMEOVER = False
	main.GAMEOVERVISTICKS = 0
	main.SNAKE = snake([0, 0])

# initialize program
def init():
	main.PIXELSIZE = [20, 20]
	
	main.WIDTH, main.HEIGHT = 1080 / main.PIXELSIZE[0], 720 / main.PIXELSIZE[1]
	main.SIZE = [main.WIDTH, main.HEIGHT]
	main.SURF = pygame.Surface(main.SIZE)
	
	main.SCALEDSIZE = [main.WIDTH * main.PIXELSIZE[0], main.HEIGHT * main.PIXELSIZE[1]]
	main.SCREEN = pygame.display.set_mode(main.SCALEDSIZE)
	
	main.CAPTION = "Jake"
	main.COLOR = [50, 50, 50]
	main.TICKS = 0
	main.KEYSDOWN = []
	
	# functions
	reset()

""" RUN """
# run function (uses tick() and render())
def run():
	ticksPerSecond = 60
	lastTime = time.time() * 1000000000
	nsPerTick =  1000000000.0 / float(ticksPerSecond)
	
	ticks = 0
	frames = 0
	
	lastTimer = time.time() * 1000
	delta = 0.0
	
	while True:
		now = time.time() * 1000000000
		delta += float(now - lastTime) / float(nsPerTick)
		lastTime = now
		shouldRender = False
				
		while delta >= 1:
			ticks += 1
			main.TICKS += 1
			tick()
			delta -= 1
			shouldRender = True
		
		if shouldRender:
			frames += 1
			render()
		
		if time.time() * 1000 - lastTimer >= 1000:
			lastTimer += 1000
			
			# debug
			# print("Frames: " + str(frames) + ", ticks: " + str(ticks))
			
			frames = 0
			ticks = 0

# main variable
main = dummy()
init()

# start program
run()
Advertisements

2 thoughts on “Jake

  1. This is pretty nice. I modified a version of snake a while back for my Computer Science class. I implemented a high score feature that stored the score locally. Due to that, my CS teacher placed the game on our school server and everyone kept trying to beat each other’s score.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s