- 刚刚在实验楼学习了2048小游戏,用下午和晚上的时间看懂代码加注释,现在把自己的理解和遇到的问题做一下总结,希望对大家有帮助。
实验链接:
https://www.shiyanlou.com/courses/368
首先2048游戏的玩法大家肯定都很了解:初始界面是一个二维矩阵,然后有两个数字(2或4),经过自己的上下左右移动,不相邻的数字紧挨到一起,相邻数字如果相同就合并为一个,然后继续随机产生2或4,经过这样的合并,数字不断变大,最终得到2048获得游戏胜利。
详细的原理可以去实验楼看实验步骤,最有用的就是这张图。这个游戏总共就是有有限的几个状态:初始化,游戏, 游戏胜利,游戏结束,退出游戏。这非常适合用有限状态机来解决。
- 简单说一下整体逻辑(
main
函数):初始化界面,等待用户动作,用户产生动作后,进入对应函数,产生相应的效果。举个例子:游戏初始运行init,
进入game
,用户按下w键,进入move
函数,产生向上的移动,同时合并数字和随机产生数字;当数字到达胜利条件,执win
函数,界面显示胜利。 - 主要运行流程(能力有限,尽量看吧):
下面将详细注释的完整代码贴下:
#-*- coding:utf-8 -*-
import curses #一种很丰富的库,看到啥学啥
from random import randrange, choice # generate and place new tile #choice 返回一个随机值,randrange 返回一个规定的数
from collections import defaultdict #默认字典的设置
#from itertools import chain
#ascll值列表
letter_codes = [ord(ch) for ch in 'WASDRQwasdrq'] #不区分大小写情况,全部包容,ord()返回字符的十进制整数
actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']#几个按键动作
actions_dict = dict(zip(letter_codes, actions * 2)) #将动作和ascll值打包成元组对
def get_user_action(keyboard):
"""得到用户的响应动作"""
char = "N"
#没有动作时一直等待
while char not in actions_dict:
char = keyboard.getch()
#返回响应的按键
return actions_dict[char]
def transpose(field):
"""矩阵转置"""
return [list(row) for row in zip(*field)]
def invert(field):
"""矩阵逆转"""
return [row[::-1] for row in field]
class GameField(object):
"""游戏场景"""
def __init__(self, height=4, width=4, win=2048):
"""初始化游戏窗口"""
self.height = height
self.width = width
self.win_value = win
self.score = 0
self.highscore = 0
self.reset()
def reset(self):
"""重置游戏"""
#记录最高分
if self.score > self.highscore:
self.highscore = self.score
#分数清零
self.score = 0
#遍历游戏区域,给整个区域全部赋值0
self.field = [[0 for i in range(self.width)] for j in range(self.height)]
#生成2或者4
self.spawn()
self.spawn()
def move(self, direction):
"""移动时候的变化"""
def move_row_left(row):
def tighten(row): # squeese non-zero elements together
"""将行中的非零部分补足为0"""
#不是0的数
new_row = [i for i in row if i != 0]
#准换列表 比如 4 0 0 2 转换成4 2 0 0
new_row += [0 for i in range(len(row) - len(new_row))]
return new_row
#row是一个列表
def merge(row):
"""数字合并"""
pair = False
new_row = []
for i in range(len(row)):
if pair:
#这个位置数字变为原来2倍
new_row.append(2 * row[i])
#记录得分值
self.score += 2 * row[i]
pair = False
else:
#判断临近的值是否相同
if i + 1 < len(row) and row[i] == row[i + 1]:
pair = True
new_row.append(0)
else:
new_row.append(row[i])
assert len(new_row) == len(row)
return new_row
return tighten(merge(tighten(row)))
moves = {
}
#四种移动情况全部包含
moves['Left'] = lambda field: \
[move_row_left(row) for row in field]
moves['Right'] = lambda field: \
invert(moves['Left'](invert(field)))
moves['Up'] = lambda field: \
transpose(moves['Left'](transpose(field))) #将向上转成向左来看,哦同时对应的区域的数字也要转到左边来
moves['Down'] = lambda field: \
transpose(moves['Right'](transpose(field)))
if direction in moves:
if self.move_is_possible(direction):
sel