Skip to content

Instantly share code, notes, and snippets.

@wilfreddv
Last active April 26, 2018 16:28
Show Gist options
  • Save wilfreddv/8d8f5f9d13e31d8b41af8acab3d9531a to your computer and use it in GitHub Desktop.
Save wilfreddv/8d8f5f9d13e31d8b41af8acab3d9531a to your computer and use it in GitHub Desktop.
A very simple clone of the game '2048' in Python using PyGame
import pygame
import random
S_WIDTH, S_HEIGHT = 400, 400
DIV = 5
T_WIDTH, T_HEIGHT = (S_WIDTH - DIV * 5) // 4, (S_HEIGHT - DIV * 5) // 4
BLACK= (0,0,0)
COLORS = {
2 : (40,20,255),
4 : (255,0,0),
8 : (0,255,0),
16 : (0,0,255),
32 : (255,255,0),
64 : (0,255,255),
128 : (120,120,120),
256 : (120,255,60),
512 : (60,255,120),
1024: (255,120,60),
2048: (120,60,255),
4096: (0,0,130),
8192: (44,77,11),
16384:(240,30,10),
32768:(50,240,10),
65536:(255,255,255)
}
pygame.init()
screen = pygame.display.set_mode((S_WIDTH, S_HEIGHT))
pygame.display.set_caption('2048')
pygame.font.init()
FONT = pygame.font.Font(None, 30)
class Tile:
def __init__(self, width, height):
self.width = width
self.height = height
self.value = int(random.choice([2]*20+[4]))
self.color = COLORS[self.value]
def merge(self, other=None):
if other == None:
return 2
if self.value == other.value:
self.value *= 2
self.color = COLORS[self.value]
return self.value
return 1
class Board:
def __init__(self):
self.tiles = {}
for i in range(16):
self.tiles[i] = None
self.posses = []
for row in range(4):
for column in range(4):
self.posses.append(( column * (T_WIDTH + DIV) + DIV , row * (T_HEIGHT + DIV) + DIV ))
def add_tile(self):
if self.is_full():
return False
t = random.randint(0,15)
while self.tiles[t] != None:
t = random.randint(0,15)
self.tiles[t] = Tile(T_WIDTH, T_HEIGHT)
def print_tiles(self):
for pos, tile in self.tiles.items():
if tile == None:
continue
text = FONT.render(str(tile.value), 0, BLACK)
t_relx, t_rely = center_text(text, tile.width, tile.height)
x, y = self.posses[pos]
pygame.draw.rect(screen, tile.color, (x, y, T_WIDTH, T_HEIGHT))
screen.blit(text, (x+t_relx,y+t_rely))
def is_deadlock(self):
#Check if no tiles can be merged by checking if
#neighbours' value is the same
if not self.is_full():
return False
else:
for i in [0,1,2,4,5,6,8,9,10]:
val = self.tiles[i].value
if val == self.tiles[i+1].value: return False
if val == self.tiles[i+4].value: return False
for i in [3,7,11]:
val = self.tiles[i].value
if val == self.tiles[i+4].value: return False
for i in [12,13,14]:
val = self.tiles[i].value
if val == self.tiles[i+1].value: return False
return True
def is_full(self):
for tile in self.tiles.values():
if tile == None:
return False
return True
def _merge(self, col, row, direction):
#Internal function to handle merging of tiles
pos = col+row*4
if direction == 'up': rangemod = (pos+4, 16, 4)
elif direction == 'down': rangemod = (pos-4, -1, -4)
elif direction == 'left': rangemod = (pos+1, pos + 4 - (pos % 4) )
elif direction == 'right': rangemod = (pos-1, pos - 1 - (pos % 4), -1)
if self.tiles[pos] == None: return
for posx in range(*rangemod):
if self.tiles[posx] == None: continue
ctrl = self.tiles[pos].merge(self.tiles[posx])
if ctrl == 2:
return 2
elif ctrl == 1:
break
else:
self.tiles[posx] = None
return 0
def up(self):
moved = False
for col in range(4):
for row in range(3):
if self._merge(col, row, 'up') == 0: moved = True
for _ in range(3):
for col in range(4):
for row in range(3):
pos = col+row*4
if self.tiles[pos] == None:
if self.tiles[pos+4] == None: continue
self.tiles[pos], self.tiles[pos+4] = self.tiles[pos+4], None
moved = True
return moved
def right(self):
moved = False
for row in range(4):
for col in range(3, 0, -1):
if self._merge(col, row, 'right') == 0: moved = True
for _ in range(3):
for row in range(4):
for col in range(3, 0, -1):
pos = row*4 + col
if self.tiles[pos] == None:
if self.tiles[pos-1] == None: continue
self.tiles[pos], self.tiles[pos-1] = self.tiles[pos-1], None
moved = True
return moved
def down(self):
moved = False
for col in range(4):
for row in range(3, -1, -1):
if self._merge(col, row, 'down') == 0: moved = True
for _ in range(3):
for col in range(4):
for row in range(3, 0, -1):
pos = col+row*4
if self.tiles[pos] == None:
if self.tiles[pos-4] == None: continue
self.tiles[pos], self.tiles[pos-4] = self.tiles[pos-4], None
moved = True
return moved
def left(self):
moved = False
for row in range(4):
for col in range(3):
if self._merge(col, row, 'left') == 0: moved = True
for _ in range(3):
for row in range(4):
for col in range(3):
pos = row*4 + col
if self.tiles[pos] == None:
if self.tiles[pos+1] == None: continue
self.tiles[pos], self.tiles[pos+1] = self.tiles[pos+1], None
moved = True
return moved
def center_text(text, width, height):
x, y = text.get_size()
return ( (width - x) // 2, (height - y) // 2)
def main():
board = Board()
board.add_tile()
moved = False
while not board.is_deadlock():
for event in pygame.event.get():
if event.type == pygame.QUIT: pygame.quit(); quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit(); quit()
if event.key == pygame.K_UP: moved = board.up()
elif event.key == pygame.K_RIGHT: moved = board.right()
elif event.key == pygame.K_LEFT: moved = board.left()
elif event.key == pygame.K_DOWN: moved = board.down()
if moved:
board.add_tile()
moved = False
screen.fill(BLACK)
board.print_tiles()
pygame.display.update()
score = sum([tile.value for tile in board.tiles.values()])
print("Final score: {}".format(score))
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: pygame.quit(); quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit(); quit()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment