# Python 2.7.7 Code
# Pygame 1.9.1 (for Python 2.7.7)
# Jonathan Frech

# Version 1.0
#    * 30th of May, 2015
#    * 31st of May, 2015
#    * first instance

# Version 1.1
#   * 31st of May, 2015
#   * better key control
#   * more variables used
#   * more rows deleted at once yield more points
#   * -> changed scoring system
#   * added new looks
#   * worked on COM
#   * tried to fix row deletion
#   * commenting

# Version 1.2
#   * 1st of June, 2015
#   * 2nd of June, 2015
#   * 3rd of June, 2015
#   * finally fixed row deletion bugs (hopefully all)
#   * optimized scoring
#   * fixed turning bug (the game freezing at certain conditions)
#   * optimized turning
#   * better tile look handling
#   * changed game over and pause visualization
#   * brick is now randomly turned at spawn
#   * tried to work on COM (failed)
#   * fixed (yet) another row deletion bug
#   * commenting
#   * banned COM idea

""" VERSION """
VERSION_NAME = "Jetris"
VERSION_NUMBER = 1.2
VERSION_CAPTION = VERSION_NAME + " v. " + str(VERSION_NUMBER)

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

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

# tile class (for 1x1 tiles)
# partially used in brick class
class tile():
	# init
	def __init__(self, _pos, _color = None):
		self.pos = _pos
		self.color = _color
		
		# if no color is specified, pick one at random
		if self.color == None:
			self.color = [random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]
		
		self.dead = False
		self.shouldFall = 0
	
	# render function
	def render(self, _surface):
		# variables
		x, y = self.pos[0] * main.TILESIZE[0], self.pos[1] * main.TILESIZE[1]
		_x, _y = main.TILESIZE[0] / 2, main.TILESIZE[1] / 2
		color = self.color
		
		if main.LOOK > len(main.LOOKFUNCTIONS) - 1:
			main.LOOK = 0
		
		main.LOOKFUNCTIONS[main.LOOK](_surface, x, y, _x, _y, color)
		
	
	# checks the row (if it needs to get deleted or not)
	def checkRow(self):
		# debug to see if row check works
		#self.color = [100, 150, 200]
		
		# make sure self. is not dead
		if not self.dead:
			# if self is the last tile in the checked row
			if self.pos[0] >= main.TILEQUANTITY[0] - 1:
				# row gets deleted
				for _ in main.TILES:
					if _.pos[1] == self.pos[1]:
						_.dead = True
				
				# tiles above get moved down
				for _ in main.TILES:
					if _.pos[1] < self.pos[1]:
						_.shouldFall += 1
				
				# track the deleted row
				main.ROWSDELETED       += 1
				main.ROWSDELETEDINTICK += 1
				
				# debug
				#print "[" + str(main.TICKS) + "] row got deleted."
			
			# if self is not the last in the row,
			# a neighbour must be found
			else:
				for _ in main.TILES:
					if not _.dead and _.pos[0] == self.pos[0] + 1 and _.pos[1] == self.pos[1]:
						_.checkRow()
						break

# brick class (for currently moved brick)
class brick():
	# init function
	def __init__(self, _shape, _pos, _color = None, _generateTiles = True):
		# init
		self.shape = _shape
		self.pos = _pos
		self.color = _color
		self.generateTiles = _generateTiles
		
		# if no color is specified, pick one at random
		if self.color == None:
			self.color = [random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)]
		
		# further init
		self.tiles = []
		self.solid = False
	
	# tick function
	def tick(self):
		# handle pressed times
		if main.KEY_BRICKLEFT in main.KEYSDOWN:
			main.KEY_BRICKLEFTPRESSED += 1
		if main.KEY_BRICKRIGHT in main.KEYSDOWN:
			main.KEY_BRICKRIGHTPRESSED += 1
		
		# move
		if main.KEY_BRICKLEFTPRESSED > main.KEYHOLDDELAY:
			if main.KEY_BRICKLEFTPRESSED % main.KEYREPEAT == 0:
				# move left
				if main.KEY_BRICKLEFT in main.KEYSDOWN:
					self.move("left")
		
		if main.KEY_BRICKRIGHTPRESSED > main.KEYHOLDDELAY:
			if main.KEY_BRICKRIGHTPRESSED % main.KEYREPEAT == 0:
				# move right
				if main.KEY_BRICKRIGHT in main.KEYSDOWN:
					self.move("right")
		
		movedDown = False
		if main.BRICKMOVETICKS % main.KEYREPEAT == 0:
			# move down
			if main.KEY_BRICKDOWN in main.KEYSDOWN:
				self.move("down")
				movedDown = True
		
		# if user did not move the brick
		# downward, do it
		if not movedDown:
			if main.TICKS % main.BRICKFALLTIME == 0:
				self.move("down")
	
	# handles events
	def handleEvent(self, _event):
		if _event.type == pygame.KEYDOWN:
			if _event.key == main.KEY_BRICKTURN:
				self.turn()
			
			elif _event.key == main.KEY_BRICKLEFT:
				self.move("left")
				main.KEY_BRICKLEFTPRESSED = 0
			
			elif _event.key == main.KEY_BRICKRIGHT:
				self.move("right")
				main.KEY_BRICKRIGHTPRESSED = 0
			
			elif _event.key == main.KEY_BRICKBOTTOM:
				self.moveToBottom()
		
	# render function
	def render(self, _surface):
		# calculate tiles:
		self.calculateTiles()
		
		for _ in self.tiles:
			_.render(_surface)
	
	# turn shape
	def turn(self):
		rawshape = []
		y = -1
		for _y in self.shape:
			x = -1; y += 1
			for _x in _y:
				x += 1
				X, Y = len(self.shape) - y - 1, x
				
				while len(rawshape) < Y+1:
					rawshape.append([])
				while len(rawshape[Y]) < X+1:
					rawshape[Y].append(" ")
				rawshape[Y][X] = _x
		
		# translate shape
		shape = []
		for y in rawshape:
			s = ""
			for x in y:
				s += x
			shape.append(s)
		
		# duplicate position
		pos = self.pos[:]
		
		# fix position based on wall
		y = -1
		for _y in shape:
			y += 1; x = -1
			for _x in _y:
				x += 1
				if _x == "#":
					# there is a tile at (x, y)
					
					# could implement: left and upper bounds
					# (currently not needed)
					
					# right bound
					while pos[0] + x > main.TILEQUANTITY[0] - 1:
						# that tile is outside the right bound
						pos[0] -= 1
					
					# bottom bound
					while pos[1] + y > main.TILEQUANTITY[1] - 1:
						# that tile is outside the bottom bound
						pos[1] -= 1
		
		# check if shape interferes with any tiles
		valid = True
		y = -1
		for _y in shape:
			y += 1; x = -1
			for _x in _y:
				x += 1
				if _x == "#":
					# there is a tile at (x, y)
					
					# check all tiles
					for _ in main.TILES:
						if _.pos[0] == pos[0] + x and _.pos[1] == pos[1] + y:
							valid = False
							break
		
		# if everything is correct, take on shape and position
		if valid:
			self.shape = shape
			self.pos = pos
		
	# get solid
	def getSolid(self):
		self.solid = True
		if self.generateTiles:
			for _ in self.tiles:
				main.TILES.append(_)
	
	# calculate the tiles needed to create self.shape
	def calculateTiles(self):
		self.tiles = []
		y = -1
		for _y in self.shape:
			y += 1; x = -1
			for _x in _y:
				x += 1
				if _x == "#":
					self.tiles.append(tile([self.pos[0] + x, self.pos[1] + y], self.color))
	
	# move as far as it can
	def moveToBottom(self):
		while not self.solid:
			self.move("down")
	
	# move brick
	def move(self, _dir):
		# calculate tiles:
		self.calculateTiles()
		
		""" UP """
		if _dir == "up":
			# tile bound
			for mTile in main.TILES:
				for sTile in self.tiles:
					if mTile.pos[0] == sTile.pos[0]:
						# x value the same
						if mTile.pos[1] == sTile.pos[1] - 1:
							# cannot move
							return False
			
			# board bound			
			for sTile in self.tiles:
				if sTile.pos[1] <= 0:
					# cannot move
					return False
					
			# move
			self.pos[1] -= 1
		
		""" DOWN """
		if _dir == "down":
			# tile bound
			for mTile in main.TILES:
				for sTile in self.tiles:
					if mTile.pos[0] == sTile.pos[0]:
						# x value the same
						if mTile.pos[1] == sTile.pos[1] + 1:
							# cannot move
							self.getSolid()
							return False
			
			# board bound			
			for sTile in self.tiles:
				if sTile.pos[1] >= main.TILEQUANTITY[1] - 1:
					# cannot move
					self.getSolid()
					return False
					
			# move
			self.pos[1] += 1
				
		""" LEFT """
		if _dir == "left":
			# tile bound
			for mTile in main.TILES:
				for sTile in self.tiles:
					if mTile.pos[1] == sTile.pos[1]:
						# y value the same
						if mTile.pos[0] == sTile.pos[0] - 1:
							# cannot move
							return False
			
			# board bound			
			for sTile in self.tiles:
				if sTile.pos[0] <= 0:
					# cannot move
					return False
					
			# move
			self.pos[0] -= 1
		
		
		""" RIGHT """
		if _dir == "right":
			# tile bound
			for mTile in main.TILES:
				for sTile in self.tiles:
					if mTile.pos[1] == sTile.pos[1]:
						# y value the same
						if mTile.pos[0] == sTile.pos[0] + 1:
							# cannot move
							return False
			
			# board bound			
			for sTile in self.tiles:
				if sTile.pos[0] >= main.TILEQUANTITY[0] - 1:
					# cannot move
					return False
					
			# move
			self.pos[0] += 1
		
		# succesfully moved
		return True

""" LOOK FUNCTIONS """
# simple rectangles
def look0(_surface, x, y, _x, _y, color):
	pygame.draw.rect(_surface, color, [x, y, main.TILESIZE[0], main.TILESIZE[1]])

# smaller rectangles
def look1(_surface, x, y, _x, _y, color):
	n = 2
	pygame.draw.rect(_surface, color, [x + n, y + n, main.TILESIZE[0] - n*2, main.TILESIZE[1] - n*2])

# two differntly colored rectangles
def look2(_surface, x, y, _x, _y, color):
	p1 = [ x, y, main.TILESIZE[0], main.TILESIZE[1] ]
	n = 2
	p2 = [x + n, y + n, main.TILESIZE[0] - n*2, main.TILESIZE[1] - n*2]
	n = 50
	c = [colorValid(color[0] - n), colorValid(color[1] - n), colorValid(color[2] - n)]
	
	pygame.draw.rect(_surface, color, p1)
	pygame.draw.rect(_surface, c, p2)

# two colored rectangles with white rectangle
def look3(_surface, x, y, _x, _y, color):
	p1 = [x, y, main.TILESIZE[0], main.TILESIZE[1]]
	n = 2
	p2 = [x + n, y + n, main.TILESIZE[0] - n*2, main.TILESIZE[1] - n*2]
	p3 = [x + 2*n, y + 2*n, 4*n, 4*n]
	n = 50
	c = [colorValid(color[0] - n), colorValid(color[1] - n), colorValid(color[2] - n)]
	
	pygame.draw.rect(_surface, color, p1)
	pygame.draw.rect(_surface, c, p2)
	pygame.draw.rect(_surface, [255, 255, 255], p3)

# two small rectangles
# on at the top left, on at the bottom right
def look4(_surface, x, y, _x, _y, color):
	n = 2
	sX, sY = (main.TILESIZE[0] - n*3) / 2, (main.TILESIZE[1] - n*3) / 2
	p1 = [x + n, y + n, sX, sY]
	p2 = [x + n + sX, y + n + sY, sX, sY]
	
	pygame.draw.rect(_surface, color, p1)
	pygame.draw.rect(_surface, color, p2)

# four smaller rectangles
def look5(_surface, x, y, _x, _y, color):
	n = 2
	sX, sY = (main.TILESIZE[0] / 2) - 3*n, (main.TILESIZE[1] / 2) - 3*n
	p1 = [x + n, y + n, sX, sY]
	p2 = [x + n, y + n + sY + n, sX, sY]
	p3 = [x + n + sX + n, y + n, sX, sY]
	p4 = [x + n + sX + n, y + n + sY + n, sX, sY]
	
	pygame.draw.rect(_surface, color, p1)
	pygame.draw.rect(_surface, color, p2)
	pygame.draw.rect(_surface, color, p3)
	pygame.draw.rect(_surface, color, p4)

# circles
def look6(_surface, x, y, _x, _y, color):
	pygame.draw.circle(_surface, color, [x + _x, y + _y], _y)

# circles II
def look7(_surface, x, y, _x, _y, color):
	for _ in range(0, _y):
		c = [
			colorValid(color[0] - _ * 3),
			colorValid(color[1] - _ * 4),
			colorValid(color[2] - _ * 5)
		]
		pygame.draw.circle(_surface, c, [x + _x, y + _y], _y - _)

# crosses
def look8(_surface, x, y, _x, _y, color):
	pygame.draw.line(_surface, color, [x + _x, y], [x + _x, y + main.TILESIZE[1]], 10)
	pygame.draw.line(_surface, color, [x, y + _y], [x + main.TILESIZE[0], y + _y], 10)

# initialize the look functions
def initLook():
	main.LOOKFUNCTIONS = [
		look0,
		look1,
		look2,
		look3#,
		#look4,
		#look5,
		#look6,
		#look7,
		#look8
	]

""" FUNCTIONS """
# 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

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

# generate new brick
def newBrick():
	# brick types
	bricktypes = [
		[ "####",     main.COLOR_ROD    ], #rod
		[ "##; #; #", main.COLOR_HOOK1  ], #hook1
		[ "##;# ;#",  main.COLOR_HOOK2  ], #hook2
		[ " # ;###",  main.COLOR_T      ], #T
		[ " #;##;#",  main.COLOR_STAIR1 ], #stair1
		[ "# ;##; #", main.COLOR_STAIR2 ], #stair2
		[ "##;##",    main.COLOR_BLOCK  ] #block
	]
	
	# choose one at random
	bricktype = bricktypes[random.randint(0, len(bricktypes) - 1)]
	
	# create brick
	main.BRICK = brick(strToShape( bricktype[0] ), [main.TILEQUANTITY[0] / 2, 0], bricktype[1])
	
	# randomly turn brick
	for _ in range(0, random.randint(0, 2)):
		main.BRICK.turn()
	
	# count brick
	main.BRICKCOUNT += 1

# calculates score when a line is broken
# (multiply lines at once now make a difference!)
def score(_lines):
	# calculate score
	score = main.BRICKCOUNT * _lines * 13
	
	# update score and caption
	main.SCORE += score
	pygame.display.set_caption(main.CAPTION + " (score " + str(main.SCORE) + ")")

# turns a string into a shape
def strToShape(_str):
	shape = []
	for _ in re.split(";", _str):
		shape.append(_)
	return shape

""" 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)
			
			# quit
			if event.key == pygame.K_q:
				if pygame.key.get_mods() == 2**10:
					quit()
			
			# reset game
			if event.key == main.KEY_RESET:
				resetGame()
			
			# pause game
			if event.key == main.KEY_PAUSE:
				if main.PAUSED:
					main.PAUSED = False
				else:
					main.PAUSED = True
			
			# change look
			if event.key == main.KEY_LOOK:
				main.LOOK += 1
			
			# toggle if land brick is shown
			if event.key == main.KEY_LANDING:
				if main.LANDBRICK:
					main.LANDBRICK = False
				else:
					main.LANDBRICK = True
		
		# if game is not paused
		if not main.PAUSED:
			# handle brick
			if not main.GAMEOVER:
				main.BRICK.handleEvent(event)
	
	# ticking the game
	# if game is not paused
	if not main.PAUSED:
		
		# tick individual tick counts
		main.BRICKFALLTIMETICKS += 1
		main.BRICKMOVETICKS += 1
		
		# speed up bricks
		if main.BRICKFALLTIMETICKS >= 30*60: # every half a minute
			main.BRICKFALLTIMETICKS = 0
			if main.BRICKFALLTIME - 5 > 15:
				main.BRICKFALLTIME -= 5
			else:
				main.BRICKFALLTIME = 15
		
		# tick and create the brick
		if not main.GAMEOVER:
			if main.BRICK.solid:
				newBrick()
			else:
				main.BRICK.tick()
		
		# check tiles
		for _ in main.TILES:
			# unused gameover visualization
			"""
			if main.GAMEOVER:
				if random.randint(0, 10) == 0:
					if random.randint(0, 1) == 0:
						_.color = main.COLOR_GAMEOVER1
					else:
						_.color = main.COLOR_GAMEOVER2
			"""
			
			# check every tile to the far left
			if not _.dead and _.pos[0] <= 0:
				_.checkRow()
			
		# remove dead tiles
		for _ in main.TILES:
			# remove
			if _.dead:
				main.TILES.remove(_)
			
			# get the tiles to fall
			elif _.shouldFall > 0:
				# if tile is already at bottom it cannot fall
				if _.pos[1] >= main.TILEQUANTITY[1] - 1:
					_.shouldFall = 0
				
				# tile is not at bottom
				else:
					# canFall defines if the tile can fall
					canFall = True
					
					# check oher tiles
					for __ in main.TILES:
						if __.pos[0] == _.pos[0] and __.pos[1] == _.pos[1] + 1:
							# tile __ is directly under tile _
							if __.shouldFall > 0:
								# tile __ is still falling, tile _'s way is obstructed
								canFall = False
					
					# if tile can fall, move it
					if canFall:
						_.pos[1] += 1
						_.shouldFall -= 1
		
		# update score
		if main.ROWSDELETEDINTICK > 0:
			# debug
			#print "<<<" + str(main.ROWSDELETEDINTICK) + ">>> row(s) deleted!"
			
			# score
			score(main.ROWSDELETEDINTICK)
			
			# score variables reset
			main.ROWSDELETEDINTICK = 0
		
		# check game over
		for _ in main.TILES:
			if _.pos[1] <= 1:
				main.GAMEOVER = True
				pygame.display.set_caption(main.CAPTION + ", game over (" + str(main.SCORE) + ")")
		
		# check, if game is still focused
		if not pygame.key.get_focused():
			main.PAUSED = True

# render function
def render():
	# fill
	main.SURF.fill(main.COLOR)
	
	# render land brick
	# (where the brick will land)
	if not main.GAMEOVER and main.LANDBRICK:
		landbrick = brick(main.BRICK.shape[:], main.BRICK.pos[:], main.COLOR_LANDING, False)
		landbrick.moveToBottom()
		landbrick.render(main.SURF)
	
	# render tiles
	for _ in main.TILES:
		if not _.dead:
			_.render(main.SURF)
	
	# render brick
	if not main.GAMEOVER:
		main.BRICK.render(main.SURF)
	
	# render that the game is over
	if main.GAMEOVER:
		if main.VISUALIZEGAMEOVER:
			for _ in range(0, main.HEIGHT, 5):
				pygame.draw.line(main.SURF, main.COLOR_GAMEOVER1, [0, _], [main.WIDTH, _], 3)
	
	# render that game is paused (if game is not over)
	elif main.PAUSED:
		if main.VISUALIZEPAUSE:
			if main.HEIGHT > main.WIDTH:
				n = main.HEIGHT
			else:
				n = main.WIDTH
			for _ in range(0, n*2, 30):
				pygame.draw.line(main.SURF, main.COLOR_PAUSE, [_, 0], [0, _])
	
	# blit and flip
	main.SCREEN.blit(main.SURF, [0, 0])
	pygame.display.flip()
	
	# update icon
	# using the current game surface as icon and have a border around it
	if main.ICONTYPE == "surfaceWidthBorder":
		c = colorValid(main.TICKS, 50)
		color = [c, c, c]
		pygame.draw.line(main.SURF, color, [0, 0], [main.WIDTH, 0], 30)
		pygame.draw.line(main.SURF, color, [0, main.HEIGHT], [main.WIDTH, main.HEIGHT], 30)
		pygame.draw.line(main.SURF, color, [0, 0], [0, main.HEIGHT], 30)
		pygame.draw.line(main.SURF, color, [main.WIDTH, 0], [main.WIDTH, main.HEIGHT], 30)
		
	# using the current game surface as icon
	elif main.ICONTYPE == "surface":
		pygame.display.set_icon(main.SURF)
	
	# displaying pixels with random color (only colors used in the game)
	elif main.ICONTYPE == "pixels":
		if main.TICKS % 60 == 0 or main.TICKS <= 1:
			for x in range(0, main.ICONSIZE[0]):
				for y in range(0, main.ICONSIZE[1]):
					main.ICON.set_at([x, y], main.BRICKCOLORS[random.randint(0, len(main.BRICKCOLORS) - 1)])
			pygame.display.set_icon(pygame.transform.scale(main.ICON, main.ICONSACELSIZE))

""" INIT; NEWGAME """
# reset game
def resetGame():
	# tile variables
	main.TILES = []
	main.GAMEOVER = False
	main.SCORE = 0
	
	# brick variables
	main.BRICKCOUNT = 0
	main.BRICKFALLTIME = 60
	main.BRICKFALLTIMETICKS = 0
	main.BRICK = None
	newBrick()
	
	# caption init
	main.CAPTION = VERSION_CAPTION
	pygame.display.set_caption(main.CAPTION)
	
	# miscellaneous variables
	main.PAUSED = False

# initialize program
def init():
	# key bindings
	main.KEY_BRICKDOWN   = pygame.K_DOWN
	main.KEY_BRICKBOTTOM = pygame.K_SPACE
	main.KEY_BRICKTURN   = pygame.K_UP
	
	main.KEY_BRICKLEFT   = pygame.K_LEFT
	main.KEY_BRICKRIGHT  = pygame.K_RIGHT
	main.KEY_BRICKLEFTPRESSED  = 0
	main.KEY_BRICKRIGHTPRESSED = 0
	
	main.KEY_RESET       = pygame.K_BACKSPACE
	main.KEY_PAUSE       = pygame.K_ESCAPE
	main.KEY_LANDING     = pygame.K_TAB
	main.KEY_LOOK        = pygame.K_RSHIFT
	
	main.KEYREPEAT = 7
	main.KEYHOLDDELAY = 25
	
	# colors
	main.COLOR_LANDING   = [ 50,  50,  50]
	main.COLOR_GAMEOVER1 = [  0,   0,   0]
	main.COLOR_GAMEOVER2 = [255, 255, 255]
	main.COLOR_PAUSE     = [255, 255, 255]
	
	BRI                  =  255 # brightness
	main.COLOR_ROD       = [  0,   0, BRI]
	main.COLOR_HOOK1     = [BRI,   0,   0]
	main.COLOR_HOOK2     = [  0, BRI,   0]
	main.COLOR_T         = [BRI, BRI,   0]
	main.COLOR_STAIR1    = [BRI,   0, BRI]
	main.COLOR_STAIR2    = [  0, BRI, BRI]
	main.COLOR_BLOCK     = [BRI, BRI, BRI]
	main.BRICKCOLORS = [main.COLOR_ROD, main.COLOR_HOOK1, main.COLOR_HOOK2, main.COLOR_T, main.COLOR_STAIR1, main.COLOR_STAIR2, main.COLOR_BLOCK]
	
	# game variables
	main.TILEQUANTITY = [15, 20]
	main.TILESIZE = [30, 30]
	resetGame()
	main.LOOK = 0
	main.LANDBRICK = True
	main.BRICKMOVETICKS = 0
	main.ROWSDELETED = 0
	main.ROWSDELETEDINTICK = 0
	
	main.VISUALIZEPAUSE = True
	main.VISUALIZEGAMEOVER = False
	
	# screen variables
	main.WIDTH  = main.TILEQUANTITY[0] * main.TILESIZE[0]
	main.HEIGHT = main.TILEQUANTITY[1] * main.TILESIZE[1]
	main.SIZE = [main.WIDTH, main.HEIGHT]
	main.CENTER = [main.WIDTH / 2., main.HEIGHT / 2.]
	main.SCREEN = pygame.display.set_mode(main.SIZE)
	main.SURF = pygame.Surface(main.SIZE)
	main.COLOR = [30, 30, 30]
	
	# icon variables
	main.ICONTYPE = "pixels"
	main.ICONSIZE = [2, 2]
	main.ICONSACELSIZE = [100, 100]
	main.ICON = pygame.Surface(main.ICONSIZE)
	
	# miscellaneous variables
	main.TICKS = 0
	main.KEYSDOWN = []
	
	# functions
	initLook()
	pygame.display.set_caption(main.CAPTION)

""" 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()