Python 学习笔记,制作控制台窗口小游戏-2048

Python 学习笔记,制作控制台窗口小游戏-2048

博文地址: http://www.tonglei.win/post/python/python-%E7%BB%88%E7%AB%AF2048%E5%B0%8F%E6%B8%B8%E6%88%8F%E5%AE%8C%E6%95%B4%E5%AE%9E%E7%8E%B0/

代码来源: https://github.com/JLUNeverMore/easy_2048-in-200-lines

作为Python入门用实例

1. 声明文档为 utf-8 编码

#-*- coding: utf-8 -*-

2. 引入 curses,然后画棋盘第一行

curses 是控制台的图形界面

curses介绍: https://docs.python.org/3/howto/curses.html

#-*- coding: utf-8 -*-
import curses 

def main(stdscr): 
    curses.use_default_colors()

    stdscr.addstr('+------' * 4 + '+')
    stdscr.addstr('\n')

    stdscr.addstr('|      ' * 4 + '|')
    stdscr.addstr('\n')

    stdscr.addstr('|      ' * 4 + '|')
    stdscr.addstr('\n')

    stdscr.addstr('+------' * 4 + '+')
    stdscr.addstr('\n')

    stdscr.getch()

curses.wrapper(main)

3. 画出棋盘的另外三行

声明方法使代码看起来简洁一些

#-*- coding: utf-8 -*-
import curses, time 

def main(stdscr): 

    def cast(string): # 打印一行文本到控制台
        stdscr.addstr(string + '\n')

    def draw_hor_separator(): # 打印横向分割线
        cast('+------' * 4 + '+')

    def draw_row(): # 打印显示数字的格子
        cast('|      ' * 4 + '|')

    curses.use_default_colors() # 设置控制台窗口的颜色

    draw_hor_separator() # +------+------+------+------+
    draw_row()           # |      |      |      |      |
    draw_hor_separator() # +------+------+------+------+
    draw_row()           # |      |      |      |      |
    draw_hor_separator() # +------+------+------+------+
    draw_row()           # |      |      |      |      |
    draw_hor_separator() # +------+------+------+------+
    draw_row()           # |      |      |      |      |
    draw_hor_separator() # +------+------+------+------+

    stdscr.getch() # 等待用户输入后结束

curses.wrapper(main)

4. 在格子中显示数字,并保持格子形状不变形

这个需求主要改动 draw_row() 方法,我们将一行的数字值传给该方法,并在方法中输出出来

#-*- coding: utf-8 -*-
import curses, time 

def main(stdscr): 

    def cast(string): # 打印一行文本到控制台
        stdscr.addstr(string + '\n')

    def draw_hor_separator(): # 打印横向分割线
        cast('+------' * 4 + '+')

    def draw_row(row): # 打印显示数字的格子
        # 将每列数字
        cast(''.join([('|' + '{:^6}'.format(cell if cell > 0 else '')) for cell in row]) + '|')
        
    curses.use_default_colors() # 设置控制台窗口的颜色

    draw_hor_separator()              # +------+------+------+------+
    draw_row([0, 0, 0, 0])            # |      |      |      |      |
    draw_hor_separator()              # +------+------+------+------+
    draw_row([2, 4, 8, 16])           # |   2  |   4  |   8  |  16  |
    draw_hor_separator()              # +------+------+------+------+
    draw_row([32, 64, 128, 256])      # |  32  |  64  |  128 |  256 |
    draw_hor_separator()              # +------+------+------+------+
    draw_row([512, 1024, 2048, 4096]) # |  512 | 1024 | 2048 | 4096 |
    draw_hor_separator()              # +------+------+------+------+

    stdscr.getch() # 等待用户输入后结束

curses.wrapper(main)

5. 接收控制台指令

2048 游戏有6个按键操作:

  • w, s, a, d 控制数字平移方向
  • r 放弃当前游戏,重置棋盘
  • q 退出游戏

接收指令通过 stdscr.getch() 方法实现

# stdscr.getch() # 等待用户输入后结束
char = ''
while char != 'q': # q 按下后退出
    char = chr(stdscr.getch())
    # if char == 'w':
    # if char == 's':
    # if char == 'a':
    # if char == 'd':
    # if char == 'r':

6. 初始化棋盘

将棋盘数据用二维数组对象存储起来,并且增加初始化、向上下左右移动、判断是否可移动方法game 。第一步先实现向左的移动和判断

#-*- coding: utf-8 -*-
import curses
from random import randrange, choice

def main(stdscr): 
    cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

    def cast(string): # 打印一行文本到控制台
        stdscr.addstr(string + '\n')

    def draw_hor_separator(): # 打印横向分割线
        cast('+------' * 4 + '+')

    def draw_row(row): # 打印显示数字的格子
        # 将每列数字
        cast(''.join([('|' + '{:^6}'.format(cell if cell > 0 else '')) for cell in row]) + '|')

    def draw_game():
        global cells
        stdscr.clear()
        for row in cells:
            draw_hor_separator() # +------+------+------+------+
            draw_row(row)        # |      |      |      |      |
        draw_hor_separator()

    def init_game(): # 清空棋盘
        global cells
        cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
        
    def spawn(): # 随机生成一个数字,放到数值为0的位置
        global cells
        new_element = 4 if randrange(100) > 89 else 2 # 0.1 的概率出现4
        (i, j) = choice([(i, j) for i in range(4) for j in range(4)])
        (i, j) = choice([(i, j) for i in range(4) for j in range(4) if cells[i][j] == 0])
        cells[i][j] = new_element

    def move_is_possible(direction): # direction 'w', 's', 'a', 'd'
        def row_is_left_movable(row):
            def change(i):
                if row[i] == 0 and row[i + 1] != 0:
                    return True
                if row[i] != 0 and row[i + 1] == row[i]:
                    return True
                return False 
            return any(change(i) for i in range(len(row) - 1))

        global cells
        return any(row_is_left_movable(row) for row in cells)

    def move(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(4 - 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])
                        pair = False
                    else: # 未找到合并的单元格,查找下一个可合并的单元格
                        if i + 1 < 4 and row[i] == row[i + 1]: # 找到了下一个可合并的单元格对
                            pair = True
                            new_row.append(0)
                        else:
                            new_row.append(row[i])
                return new_row
            return tighten(merge(tighten(row)))

        if move_is_possible(direction):
            global cells
            cells = [move_row_left(row) for row in cells]
            spawn()
            draw_game()
            return True
        else:
            return False 

    curses.use_default_colors() # 设置控制台窗口的颜色

    # stdscr.getch() # 等待用户输入后结束
    char = ''
    while char != 'q': # q 按下后退出
        char = chr(stdscr.getch())
        # if char == 'w':
        # if char == 's':
        if char == 'a':
            move('a')
        # if char == 'd':
        if char == 'r':
            init_game()
            spawn()
            spawn()
            draw_game()

curses.wrapper(main)

7. 增加其他几个方向

  • 向右 = 反转->向左->反转
  • 向上 = 对折->向左->对折
  • 向下 = 对折->向右->对折

反转的实现

return [row[::-1] for row in field]

对折的实现

return [list(row) for row in zip(*field)]

修改 move 方法

    def move(direction): # 移动格子,贴边 > 合并 > 贴边
        ...
        if move_is_possible(direction):
            global cells
            if direction == 'a':
                cells = [move_row_left(row) for row in cells]
            elif direction == 'd':
                cells = invert([move_row_left(row) for row in invert(cells)])
            elif direction == 'w':
                cells = transpose([move_row_left(row) for row in transpose(cells)])
            elif direction == 's':
                cells = transpose(invert([move_row_left(row) for row in invert(transpose(cells))]))

            spawn()
            draw_game()
            return True
        else:
            return False 

修改 move_is_possible 方法

    def move_is_possible(direction): # direction 'w', 's', 'a', 'd'
        ...
        if direction == 'a':
            return any(row_is_left_movable(row) for row in cells)
        elif direction == 'd':
            return any(row_is_left_movable(row) for row in invert(cells))
        elif direction == 'w':
            return any(row_is_left_movable(row) for row in transpose(cells))
        elif direction == 's':
            return any(row_is_left_movable(row) for row in invert(transpose(cells)))
        return False

演示效果

完整代码

#-*- coding: utf-8 -*-
import curses
from random import randrange, choice

def main(stdscr): 
    cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]

    def cast(string): # 打印一行文本到控制台
        stdscr.addstr(string + '\n')

    def draw_hor_separator(): # 打印横向分割线
        cast('+------' * 4 + '+')

    def draw_row(row): # 打印显示数字的格子
        cast(''.join([('|' + '{:^6}'.format(cell if cell > 0 else '')) for cell in row]) + '|')

    def draw_game():
        global cells
        stdscr.clear()
        for row in cells:
            draw_hor_separator() # +------+------+------+------+
            draw_row(row)        # |      |      |      |      |
        draw_hor_separator()

    def init_game(): # 清空棋盘
        global cells
        cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
        
    def spawn(): # 随机生成一个数字,放到数值为0的位置
        global cells
        new_element = 4 if randrange(100) > 89 else 2 # 0.1 的概率出现4
        (i, j) = choice([(i, j) for i in range(4) for j in range(4)])
        (i, j) = choice([(i, j) for i in range(4) for j in range(4) if cells[i][j] == 0])
        cells[i][j] = new_element

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

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

    def move_is_possible(direction): # direction 'w', 's', 'a', 'd'
        def row_is_left_movable(row):
            def change(i):
                if row[i] == 0 and row[i + 1] != 0:
                    return True
                if row[i] != 0 and row[i + 1] == row[i]:
                    return True
                return False 
            return any(change(i) for i in range(len(row) - 1))

        global cells
        if direction == 'a':
            return any(row_is_left_movable(row) for row in cells)
        elif direction == 'd':
            return any(row_is_left_movable(row) for row in invert(cells))
        elif direction == 'w':
            return any(row_is_left_movable(row) for row in transpose(cells))
        elif direction == 's':
            return any(row_is_left_movable(row) for row in invert(transpose(cells)))
        return False

    def move(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(4 - 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])
                        pair = False
                    else: # 未找到合并的单元格,查找下一个可合并的单元格
                        if i + 1 < 4 and row[i] == row[i + 1]: # 找到了下一个可合并的单元格对
                            pair = True
                            new_row.append(0)
                        else:
                            new_row.append(row[i])
                return new_row
            return tighten(merge(tighten(row)))

        if move_is_possible(direction):
            global cells
            if direction == 'a':
                cells = [move_row_left(row) for row in cells]
            elif direction == 'd':
                cells = invert([move_row_left(row) for row in invert(cells)])
            elif direction == 'w':
                cells = transpose([move_row_left(row) for row in transpose(cells)])
            elif direction == 's':
                cells = transpose(invert([move_row_left(row) for row in invert(transpose(cells))]))

            spawn()
            draw_game()
            return True
        else:
            return False 

    curses.use_default_colors() # 设置控制台窗口的颜色

    # stdscr.getch() # 等待用户输入后结束
    char = ''
    while char != 'q': # q 按下后退出
        char = chr(stdscr.getch())
        if char == 'w':
            move('w')
        if char == 's':
            move('s')
        if char == 'a':
            move('a')
        if char == 'd':
            move('d')
        if char == 'r':
            init_game()
            spawn()
            spawn()
            draw_game()

curses.wrapper(main)

至此,游戏的主要内容都已经完成了。

8. 增加按键提示、分数、游戏胜利与失败判定

这一步主要是增加细节处理,最终完成 2048 小游戏。

效果演示

最终代码如下:

#-*- coding: utf-8 -*-
import curses
from random import randrange, choice

cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
score = 0

def main(stdscr):
    def cast(string): # 打印一行文本到控制台
        stdscr.addstr(string + '\n')

    def draw_hor_separator(): # 打印横向分割线
        cast('+------' * 4 + '+')

    def draw_row(row): # 打印显示数字的格子
        cast(''.join([('|' + '{:^6}'.format(cell if cell > 0 else '')) for cell in row]) + '|')

    def new_game():
        init_game()
        spawn()
        spawn()
        draw_game()

    def draw_game():
        global cells
        stdscr.clear()
        for row in cells:
            draw_hor_separator() # +------+------+------+------+
            draw_row(row)        # |      |      |      |      |
        draw_hor_separator()

        cast('Score: ' + str(score))
        if is_win():
            cast("You win!")
        elif is_gameover():
            cast("Game over!")
        else:
            cast('(W)Up (S)Down (A)Left (D)Right')
        cast('(R)Restart (Q)Exit')

    def init_game(): # 清空棋盘
        global cells
        global score
        cells = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
        score = 0
        
    def spawn(): # 随机生成一个数字,放到数值为0的位置
        global cells
        new_element = 4 if randrange(100) > 89 else 2 # 0.1 的概率出现4
        (i, j) = choice([(i, j) for i in range(4) for j in range(4)])
        (i, j) = choice([(i, j) for i in range(4) for j in range(4) if cells[i][j] == 0])
        cells[i][j] = new_element

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

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

    def move_is_possible(direction): # direction 'w', 's', 'a', 'd'
        def row_is_left_movable(row):
            def change(i):
                if row[i] == 0 and row[i + 1] != 0:
                    return True
                if row[i] != 0 and row[i + 1] == row[i]:
                    return True
                return False 
            return any(change(i) for i in range(len(row) - 1))

        global cells
        if direction == 'a':
            return any(row_is_left_movable(row) for row in cells)
        elif direction == 'd':
            return any(row_is_left_movable(row) for row in invert(cells))
        elif direction == 'w':
            return any(row_is_left_movable(row) for row in transpose(cells))
        elif direction == 's':
            return any(row_is_left_movable(row) for row in invert(transpose(cells)))
        return False

    def move(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(4 - len(new_row))]
                return new_row
            def merge(row): # 合并
                pair = False # 是否可合并
                new_row = [] # 合并后的结果
                for i in range(len(row)):
                    if pair: # 已找到可合并的单元格,进行合并
                        global score
                        new_row.append(2 * row[i])
                        score += 2 * row[i]
                        pair = False
                    else: # 未找到合并的单元格,查找下一个可合并的单元格
                        if i + 1 < 4 and row[i] == row[i + 1]: # 找到了下一个可合并的单元格对
                            pair = True
                            new_row.append(0)
                        else:
                            new_row.append(row[i])
                return new_row
            return tighten(merge(tighten(row)))

        if move_is_possible(direction):
            global cells
            if direction == 'a':
                cells = [move_row_left(row) for row in cells]
            elif direction == 'd':
                cells = invert([move_row_left(row) for row in invert(cells)])
            elif direction == 'w':
                cells = transpose([move_row_left(row) for row in transpose(cells)])
            elif direction == 's':
                cells = transpose(invert([move_row_left(row) for row in invert(transpose(cells))]))

            spawn()
            draw_game()
            return True
        else:
            return False 
    
    def is_win():
        global cells;
        return any(any(i >= 2048 for i in row) for row in cells)

    def is_gameover():
        return not any(move_is_possible(move) for move in ['w','s','a','d'])

    curses.use_default_colors() # 设置控制台窗口的颜色

    new_game()
    char = ''
    while char != 'q':
        char = chr(stdscr.getch())
        if char in ['w','s','a','d']:
            move(char)
        if char == 'r':
            new_game()

curses.wrapper(main)

本页所有代码可从以下位置下载:https://gitee.com/tonglei0429/python-learn/tree/master/game-2048

转载于:https://my.oschina.net/tonglei0429/blog/3067138

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python学习笔记》是由皮大庆编写的一本关于Python语言学习的教材。在这本书中,作者详细介绍了Python语言的基础知识、语法规则以及常用的编程技巧。 首先,作者简要介绍了Python语言的特点和优势。他提到,Python是一种易于学习和使用的编程语言,受到了广大程序员的喜爱。Python具有简洁、清晰的语法结构,使得代码可读性极高,同时也提供了丰富的库和模块,能够快速实现各种功能。 接着,作者详细讲解了Python的基本语法。他从变量、数据类型、运算符等基础知识开始,逐步介绍了条件语句、循环控制、函数、模块等高级概念。同时,作者通过大量的示例代码和实践案例,帮助读者加深对Python编程的理解和应用。 在书中,作者还特别强调了编写规范和良好的编程习惯。他从命名规范、注释风格、代码缩进等方面指导读者如何写出清晰、可读性强的Python代码。作者认为,良好的编程习惯对于提高代码质量和提高工作效率非常重要。 此外,作者还介绍了Python的常用库和模块。他提到了一些常用的库,如Numpy、Pandas、Matplotlib等。这些库在数据处理、科学计算、可视化等领域有广泛的应用,帮助读者更好地解决实际问题。 总的来说,《Python学习笔记》是一本非常实用和全面的Python学习教材。通过学习这本书,读者可以系统地学习和掌握Python编程的基础知识和高级应用技巧,为以后的编程学习和工作打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值