Sliding Puzzle

This is my version of a sliding puzzle.
A sliding puzzle is based on a number of tiles (15 in this case) which are scrambled.
The objective of the game then is to slide the tiles around and get back to the original image.
As an image I took a photo of an apple in front of a black background.
For more information on sliding puzzles, check this Wikipedia entry.

Controls

  • F1 takes a screenshot
  • F2 starts and stops scrambling the image
  • F3 solves the puzzle
  • Mouse clicks slide tiles

The solved image The scrambled image The image currently being solved


# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech 15th of April, 2016
#         edited 16th of April, 2016

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

# load an image
IMAGE_PATH = os.getcwd() + "/apple.png"

""" CLASSES """
# piece class
class Piece():
	# initialize
	def __init__(self, gamepos, n):
		self.n = n
		self.size = [200, 200]
		self.setgamepos(gamepos)
		self.pos = [random.randint(0, G.width - self.size[0]), random.randint(0, G.height - self.size[1])]
		
		
		self.surface = pygame.Surface(self.size)
		self.surface.blit(G.image, [-self.gamepos[0] * self.size[0], -self.gamepos[1] * self.size[1]])

	# gamepos in range [0:3] and [0:3]
	def setgamepos(self, gamepos):
		self.gamepos = gamepos
		self.dest = [self.gamepos[0] * self.size[0], self.gamepos[1] * self.size[1]]

	# check if piece was clicked
	def clicked(self, gamepos):
		if gamepos[0] == self.gamepos[0] and gamepos[1] == self.gamepos[1]:
			return True

		return False

	# tick
	def tick(self):
		for _ in range(0, 2):
			dif = (self.dest[_] - self.pos[_]) / 10.
			if abs(dif) < .1:
				self.pos[_] = self.dest[_]
			else:
				self.pos[_] += dif

	# render
	def render(self, surface):
		surface.blit(self.surface, intpos(self.pos))

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

# load an image from string
def loadimg(string = None):
	# standard string
	if not string:
		string = "(1, 1)<<<Here the stringified image would go... The only problem is that that would men 5,000,000 characters...>>>"

	# figure out size
	size = re.findall("\(\d*, \d*\)", string)[0]
	string = string[len(size):len(string)]
	size = size[1:len(size)-1]
	size = re.split(", ", size)
	size = [int(size[0]), int(size[1])]
	
	# draw image
	img = pygame.Surface(size)
	for _ in range(0, len(string), 3):
		img.set_at([(_/3)%size[0], (_/3)/size[1]], [ord(string[_]), ord(string[_+1]), ord(string[_+2])])

	# return
	return img

# save an image to string
def getimgstr(path):
	# load image
	image = pygame.image.load(os.getcwd() + "/" + path)
	rect = image.get_rect()
	
	# generate string
	string = "(" + str(rect.right) + ", " + str(rect.bottom) + ")"
	for y in range(0, rect.bottom):
		for x in range(0, rect.right):
			color = image.get_at([x, y])
			string += chr(color[0]) + chr(color[1]) + chr(color[2])

	# escape and return
	return string#.encode("string-escape")


""" GAME """
# game class
class GAME():
	# initialize program
	def __init__(self):
		self.width, self.height = 800, 800
		self.size = [self.width, self.height]
		self.surface = pygame.Surface(self.size)
		self.screen = pygame.display.set_mode(self.size)

		self.ticks = 0
		self.running = True

		self.image = pygame.image.load(IMAGE_PATH)
		self.image = pygame.transform.scale(self.image, self.size)

		# functions
		pygame.display.set_caption("Sliding Puzzle")
	
	# initialize game
	def init(self):
		self.pieces = []
		n = 0
		for x in range(0, 4):
			for y in range(0, 4):
				self.pieces.append(Piece([x, y], n))
				n += 1

		self.pieces.pop(-1)

		self.scrambling = False

	# do one random move
	def scramble(self):
		moved = False
		while not moved:
			moved = self.move([random.randint(0, self.width), random.randint(0, self.height)])

	def move(self, mousepos):
		for piece in self.pieces:
			if piece.clicked([mousepos[0] / piece.size[0], mousepos[1] / piece.size[1]]):
				for _ in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
					pos = [mousepos[0] / piece.size[0] + _[0], mousepos[1] / piece.size[1] + _[1]]
					occupied = False
					for p in self.pieces:

						if p.clicked(pos) or pos[0] < 0 or pos[0] >= 4 or pos[1] < 0 or pos[1] >= 4:
							occupied = True

					if not occupied:
						piece.setgamepos(pos)
						return True

		return False

	# tick function
	def tick(self):
		# handle events
		for event in pygame.event.get():
			if event.type == pygame.QUIT:
				self.quit()
			if event.type == pygame.KEYDOWN:
				# screnshot
				if event.key == pygame.K_F1:
					self.screenshot()

				# scramble
				if event.key == pygame.K_F2:
					self.scrambling = not self.scrambling

				# solve (that is cheating!)
				if event.key == pygame.K_F3:
					self.init()

			# handle mouse button presses
			if event.type == pygame.MOUSEBUTTONDOWN:
				if event.button == 1:
					mousepos = list(pygame.mouse.get_pos())
					self.move(mousepos)
		
		if self.scrambling and self.ticks % 2 == 0:
			self.scramble()

		for piece in self.pieces:
			piece.tick()

	# render function
	def render(self):
		# fill
		self.surface.fill([0, 0, 0])

		for piece in self.pieces:
			piece.render(self.surface)

		# blit and flip
		self.screen.blit(self.surface, [0, 0])
		pygame.display.flip()
	
	# quits
	def quit(self):
		self.running = False

	# takes a screenshot
	def screenshot(self):
		path = os.getcwd() + "/out/"
		
		try:
			if not os.path.isdir(path):
				os.mkdir(path)
			
			name = "img" + str(len(os.listdir(path))) + ".png"
			pygame.image.save(self.surface, path + name)
		except:
			pass

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

# start game
G = GAME()
G.init()
G.run()
Advertisements

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