当前位置: 移动技术网 > IT编程>脚本编程>Python > [实验楼] Python 实现 2048 游戏

[实验楼] Python 实现 2048 游戏

2020年07月26日  | 移动技术网IT编程  | 我要评论

参考:https://www.shiyanlou.com/courses/368

本节实验中将学习和实践以下知识点:
Python 基本知识
curses 终端图形编程库
random 随机数模块
collections 容器数据类型库
状态机的概念

项目样式如下
在这里插入图片描述
(main函数中)state 存储当前状态, state_actions 这个词典变量作为状态转换的规则,它的 key 是状态,value 是返回下一个状态的函数:在这里插入图片描述
与原代码相比,添加了上下左右键处理(原来只有wasd键),胜利后按任意键(除了r,q)继续游戏,把一部分函数改的更亲民了(比如move_is_possible,not_game,原来的好变态…不,是高深)


import curses  # curses 用来在终端上显示图形界面
from random import randrange, choice # random 模块用来生成随机数

actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']

letter_codes = [ord(ch) for ch in 'WASDRQwasdrq'] + [259,260,258,261] 
# ord转换为ASCII码  后面四个是上左下右
#[87, 65, 83, 68, 82, 81, 119, 97, 115, 100, 114, 113, 259, 260, 258, 261]

win_continue = False #胜利后是否继续
actions_dict = dict(zip(letter_codes, actions * 2 + actions[:4]))
#{87: 'Up', 65: 'Left', 83: 'Down', 68: 'Right', 82: 'Restart', 81: 'Exit', 119: 'Up', 97: 'Left', 115: 'Down', 100: 'Right', 114: 'Restart', 113: 'Exit', 259: 'Up', 260: 'Left', 258: 'Down', 261: 'Right'}

#对角置换
def transpose(field):
    return [list(row) for row in zip(*field)]

#水平反置
def invert(field):
    return [row[::-1] for row in field]

# 用户输入
def get_user_action(keybord):
    char = '#'
    while char not in actions_dict:
        char = keybord.getch()  # 返回按下键位的 ASCII 码值
    return actions_dict[char]

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
        self.field = [[0 for i in range(self.width)] for j in range(self.height)]
        self.spawn()
        self.spawn()

    # 随机生成一个2或4
    def spawn(self):
        new_element = 4 if randrange(100) > 89 else 2
        (i, j) = choice([(i, j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0]) #选择一个空位置
        self.field[i][j] = new_element


    def move(self, direction):
        def move_row_left(row):
            def tighten(row): #向左紧凑
                new_row = [i for i in row if i != 0]
                new_row += [0 for i in range(len(row) - len(new_row))]
                return new_row

            def merge(row):   #相同合并
                pair = False
                new_row = []
                for i in range(len(row)):
                    if pair:
                        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):
                self.field = moves[direction](self.field)
                self.spawn()
                return True
            else:
                return False
                

    def is_win(self):
        global win_continue
        if win_continue:
             return False
        return any(any(i >= self.win_value for i in row) for row in self.field)

    def is_gameover(self):
        return not any(self.move_is_possible(move) for move in actions)

        
    def draw(self, screen):
        help_str1 = '(W)Up (S)Down (A)Left (D)Right'
        help_str2 = '       (R)Restart (Q)Exit        '
        gameover_str = '        Game Over     '
        win_str = '         You Win       '

        # 绘制函数
        def cast(string):
            # addstr()将传入的内容展示到屏幕上
            screen.addstr(string + '\n')

        # 水平线
        def draw_hor_separator():
            line =  '+------' * self.width + '+'
            cast(line)
     
       
        def draw_row(row):
            cast(''.join('|{:^6}'.format(num) if num > 0 else '!      ' for num in row) + '|')

        screen.clear()
        cast('SCORE: ' + str(self.score))
        if self.highscore != 0:
            cast('HIGHSCORE: ' + str(self.highscore))

        for row in self.field:
            draw_hor_separator()
            draw_row(row)
        draw_hor_separator()

        # 提示文字
        if self.is_win():
            cast(win_str)
        else:
            if self.is_gameover():
                cast(gameover_str)
            else:
                cast(help_str1)
        cast(help_str2)

    #判断某个方向能否移动
    #有0一定可以,否则需要这个方向上有相邻且相同的数
    def move_is_possible(self, direction):
        if any(any(num==0 for num in row) for row in self.field):
            return True
        if direction == 'Left' or direction == 'Right':
            for i in range(0, self.height):
                for j in range(1, self.width):
                    if self.field[i][j] == self.field[i][j-1]:
                        return True
        else:
            for i in range(1, self.height):
                for j in range(0, self.width):
                    if self.field[i][j] == self.field[i-1][j]:
                        return True
        return False

def main(stdscr):
    #标准屏幕 来自curses库
    def init():
        # 初始化棋盘
        global win_continue
        win_continue = False
        game_field.reset()
        return 'Game'

    def not_game(state):
        # 绘制界面
        game_field.draw(stdscr)
        if state == 'Win':
            stdscr.addstr('press other key to continue\n')
        # 获取用户键盘输入
        action = get_user_action(stdscr)            
        if action == 'Exit':
            return 'Exit'
        if action == 'Restart':
            return 'Init'
        if state == 'Win':
            global win_continue
            win_continue = True
            return 'Game'
        return state

    def game():
        game_field.draw(stdscr)
        action = get_user_action(stdscr)

        if action == 'Exit':
            return 'Exit'
        if action == 'Restart':
            return 'Init'
        if game_field.move(action):
            if game_field.is_win():
                return 'Win'
            if game_field.is_gameover():
                return 'GameOver'
        return 'Game'

    # 字典,每个状态对应一个状态处理函数
    state_actions = {
        'Init': init,
        'Win': lambda: not_game('Win'),
        'GameOver': lambda: not_game("GameOver"),
        'Game': game
    }

    curses.use_default_colors()
    game_field = GameField(win=2048)

    state = 'Init'

    while state != 'Exit':
        state = state_actions[state]()

curses.wrapper(main) 
"""
首先, curses.wrapper 函数会激活并初始化终端进入 'curses 模式'。
在这个模式下会禁止输入的字符显示在终端上、禁止终端程序的行缓冲(line buffering),即字符在输入时就可以使用,不需要遇到换行符或回车。
接着,curses.wrapper 函数需要传一个函数作为参数,这个传进去的函数必须满足第一个参数为主窗体(main window) stdscr。 在前面的代码里,可以看到我们给 curses.wrapper(main) 的 main 函数中传入了一个 stdscr。
最后,stdscr 作为 window.addstr(str)、window.clear() 方法的调用需要窗体对象(window object),在 game_field.draw(stdscr) 中传入 draw 方法中。
"""

本文地址:https://blog.csdn.net/qq_33831360/article/details/107550841

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网