2014年开始了python的学习,小巧轻便却不乏功能强大使其深深的吸引着我。
偶然在网上接触到了pygame可以编写小巧的游戏,于是就通过网络搜索pygame教程,简单了解Pygame教程中的游戏开发,由此切入,模仿网络中的代码,开始了2048之旅。
首先我们需要创建Map
类。
Map
类的属性:
Map
类的方法:
此地图类是这些属性和方法的 logical group,这是python的OOP思想体现。对于代码中类方法的实现有很多细节需要介绍。
self.fc_map = [[0 for i in range(size)] for j in range(size)]
用此种方法创建二维数组,而非暴力直接[[0]*size]*size
,原因在于暴力方法是一种shadow copy,当赋值list[0][0] = 1
时候,list(二维)每行的首元素都会赋值成1.
在4*4的二维列表中,(0, 0)
到(3, 3)
编号0-15
,问:12是几行几列?
答案是12/4
(这里是整除
)行12%4
列.
以前在简书上写过关于Python条件运算符的介绍。
self.fc_map = map(list, zip(*self.fc_map)[::-1]
上行代码实现了逆时针转90°。
顺时针旋转:
rotated = zip(*original[::-1])
zip()
的作用简单说就是矩阵转置,不过会将二维list内维转化成tuple,需要用map(list, [tuple, tuple, tuple])
转化回来。
关于zip()
,还有一个打印杨辉三角的程序可以参考下。
def pascals_triangles(n):
x = [[1]]
for i in range(n-1):
x.append(map(sum, zip([0]+x[-1], x[-1]+[0])))
return x
关于切片操作,参考廖雪峰的python教程.
判断gameover可以先判断是否有0在map里,或者是否有两个相邻的(上下/左右)值是否一样,true的话返回false,否则返回true。
只写了左移的方法adjust
,上移是通过逆向旋转90°再调整,再旋转3*90°。
下移右移同之。
pygame.init()
screen = pygame.display.set_mode((PIXEL * SIZE, PIXEL * SIZE + SCORE_PIXEL))
pygame.display.set_caption("2048")
pygame.init()
初始化pygame.display.set_mode((x, y))
创建一个大小为(x, y)
的窗口pygame.display.set_caption('title')
设置窗口的标题。pygame object for representing images
pygame.Surface.blit()
将一个image画到另一个image上pygame.Surface.convert()
或pygame.Surface.convert_alpha()
转化image像素格式pygame.Surface.fill()
填充给Surface颜色pygame.font.Font(filename, size)
创建字体pygame.font.Font.render()
书写内容pygame.event.get()
从队列中获得事件pygame.event.wait()
从队列中等待一个single事件pygame.key.get_pressed()
按下的键pygame.time.Clock()
创造一个追踪时间的对象pygame.time.Clock().tick(12)
窗口每秒更新至少12次pygame.time.delay(3000)
延时
# -*- coding: utf-8 -*-
#!/usr/bin/env python
import random, sys, pygame
from pygame import *
PIXEL = 150
SCORE_PIXEL = 100
SIZE = 4
FC_ZERO = -1
class Map:
def __init__(self, size):
self.size = size
self.score = 0
self.fc_map = [[0 for i in range(size)] for j in range(size)]
self.add()
self.add()
def add(self):
while True:
p = random.randint(0, self.size*self.size - 1)
if self.fc_map[p/self.size][p%self.size] == 0:
x = 2 if random.randint(0, 3) > 0 else 4
self.fc_map[p/self.size][p%self.size] = x
self.score += x
break
def adjust(self):
changed = False
for a in self.fc_map:
b = []
b = a[:]
while 0 in b:
b.remove(0)
if len(b)>1:
for i in range(1, len(b)):
if b[i] == b[i-1]:
b[i-1] += b[i]
b[i] = FC_ZERO
while FC_ZERO in b:
b.remove(FC_ZERO)
b += [0] * (self.size-len(b))
for i in range(self.size):
if a[i]!=b[i]:
changed = True
a[:] = b
return changed
def rotate90(self):
# self.fc_map = [[self.fc_map[c][r] for c in range(self.size)] for r in reversed(range(self.size))] # bad way
self.fc_map = map(list, zip(*self.fc_map)[::-1])
def over(self):
for r in range(self.size):
for c in range(self.size):
if self.fc_map[r][c] == 0:
return False
for r in range(self.size):
for c in range(self.size-1):
if self.fc_map[r][c] == self.fc_map[r][c+1]:
return False
for r in range(self.size-1):
for c in range(self.size):
if self.fc_map[r][c] == self.fc_map[r+1][c]:
return False
return True
def moveUp(self):
self.rotate90()
if self.adjust():
self.add()
self.rotate90
self.rotate90
self.rotate90
def moveDown(self):
self.rotate90()
self.rotate90()
self.rotate90()
if self.adjust():
self.add()
self.rotate90()
def moveRight(self):
self.rotate90()
self.rotate90()
if self.adjust():
self.add()
self.rotate90()
self.rotate90()
def moveLeft(self):
if self.adjust():
self.add()
def show(M):
for i in range(SIZE):
for j in range(SIZE):
screen.blit(block[(i+j)%2] if M.fc_map[i][j]==0 else block[2 + (i+j)%2], (j*PIXEL, i*PIXEL))
if M.fc_map[i][j] != 0:
map_text = map_font.render(str(M.fc_map[i][j]), True, (106, 90, 205))
text_rect = map_text.get_rect()
text_rect.center = (j*PIXEL + PIXEL/2, i*PIXEL + PIXEL/2)
screen.blit(map_text, text_rect)
screen.blit(score_block, (0, PIXEL*SIZE))
score_text = score_font.render(("Game Over with score: " if M.over() else "Score: ") + str(M.score), True, (106, 90, 205))
score_rect = score_text.get_rect()
score_rect.center = (PIXEL*SIZE/2, PIXEL*SIZE+SCORE_PIXEL/2)
screen.blit(score_text, score_rect)
pygame.display.update()
M = Map(SIZE)
pygame.init()
screen = pygame.display.set_mode((PIXEL*SIZE, PIXEL*SIZE+SCORE_PIXEL))
pygame.display.set_caption("2048")
block = [pygame.Surface((PIXEL, PIXEL)) for i in range(4)]
block[0].fill((151, 251, 152))
block[1].fill((240, 255, 255))
block[2].fill((0, 255, 127))
block[3].fill((255, 255, 255))
score_block = pygame.Surface((PIXEL*SIZE, SCORE_PIXEL))
score_block.fill((245, 245, 245))
map_font = pygame.font.Font(None, PIXEL*2/3)
score_font = pygame.font.Font(None, SCORE_PIXEL*2/3)
clock = pygame.time.Clock()
show(M)
while not M.over():
clock.tick(12)
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
pressed_keys = pygame.key.get_pressed()
if pressed_keys[K_w] or pressed_keys[K_UP]:
M.moveUp()
elif pressed_keys[K_s] or pressed_keys[K_DOWN]:
M.moveDown()
elif pressed_keys[K_a] or pressed_keys[K_LEFT]:
M.moveLeft()
elif pressed_keys[K_d] or pressed_keys[K_RIGHT]:
M.moveRight()
show(M)
pygame.time.delay(2000)