用Python编写 2048 命令行小游戏

本篇博文围绕使用Python开发热门游戏2048 GAME(命令行版本)

代码未做任何优化(原生且随意)、全程以面向过程MVC的设计思想为主、开发环境是Ubuntu系统下的Pycharm

2048是我很久以前学习Python过程中的一个作业,接下来直入正题——

一、了解游戏

1. 介绍

2048》是一款单人在线和移动端游戏,由19岁的意大利人Gabriele Cirulli于2014年3月开发。游戏任务是在一个网格上滑动小方块来进行组合,直到形成一个带有有数字2048的方块(来源:维基百科

2. 玩法规则

  1. 通过方向键让方块整体上下左右移动
  2. 如果两个带有相同数字的方块在移动中碰撞,则它们会相加合并为一个新方块
  3. 每次出现方块移动时,都会有一个值为2或者4的新方块出现
  4. 初始开局时,4*4的方块,随机2个方块赋值2或者4
  5. 其中所出现的数字都是2的幂,2,4,8,16......

二、MVC设计

Model:无
View:终端界面(有时间再研究一下pyQt),打印二维列表,输入输出控制
Controller:二维列表-矩阵、数据控制、上下左右操作、计分机制、方块合并处理等等

三、核心函数

截图_20230928194512

通过观察游戏界面,可知数据由二维数组(线性代数--方阵)存储,将上图映射到如下代码

source = [
    [0, 0, 0, 0],
    [0, 0, 2, 2],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

通过玩法规则第1条可知,方向键上下左右移动,四种移动必然存在相似的操作,祁天暄讲师通过分析向左移动来书写后续代码,我这里也会以向左移动来分析如何写后续的代码

截图_20230928194603

[0, 0, 2, 2]

取上述一行,按左方向键移动后,可以看到两个方块2持续左移(如果左边还有非0的方块,那么就会顶住该非0的方块),然后相撞变成方块4,因为有方块移动,所以随机挑选一个方块0进行填充成2(不限于当前行,也可能发生在其他行):

截图_20230928194619

本行规律:

[0, 0, 2, 2] >发生滑动> [2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0] >随机填充> [4, 0, 0, 2]

经过多局游戏结合上方的规律,可以得知以下规律:

[0, 0, 2, 2] >发生滑动> [2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0] >随机填充> [4, 0, 0, 2]
[4, 0, 2, 2] >发生滑动> [4, 2, 2, 0] >相等相撞求和> [4, 4, 0, 0] >随机填充> [4, 4, 0, 4]
[4, 4, 2, 2] >发生滑动,不动> [4, 4, 2, 2] >相等相撞求和> [8, 4, 0, 0] >随机填充> [4, 0, 0, 2]

1. 滑动处理

发生滑动的环节,可以得出一个规律,有0在非0元素的前方则必滑动,否则不动

[0, 0, 2, 2] >发生滑动> [2, 2, 0, 0]
[4, 0, 2, 2] >发生滑动> [4, 2, 2, 0]
[4, 4, 2, 2] >发生滑动,不动> [4, 4, 2, 2]

所以此处构造一个zero_to_end函数功能就是将0移至末尾处,并保持非0元素应有的顺序,先看一下中规中矩的方式(冒泡式的移动,时间复杂度较高):

def zero_to_end(list_data):
    for i in range(3, -1, -1):
        for j in range(i):
            if list_data[j] == 0:
                list_data[j], list_data[j + 1] = list_data[j + 1], list_data[j]

再看一下另一种写法(采用该函数,时间复杂度为O(n)):

def zero_to_end(list_data):
    """
    重排序函数(核心算法)
    非0元素移至最前(保持顺序),0元素移至最后,充当中间人处理列表的角色
    :param list_data: list 一维列表
    :return: None
    """
    for i in range(3, -1, -1):
        if not list_data[i]:
            del list_data[i]
            list_data.append(0)

2. 相等相撞求和

经上一函数,每一行列表都被处理成:若干非0元素有序在前,若干0元素在后

[2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0]
[4, 2, 2, 0] >相等相撞求和> [4, 4, 0, 0]
[4, 4, 2, 2] >相等相撞求和> [8, 4, 0, 0]

相等相撞求和这个过程肯定要统一函数处理,增加复用性,因此需要详细拆分该流程的细节:

[4, 2, 2, 0]
如果第1个元素等==第2个元素:
    则第1个元素 + 第2个元素,并赋给第1个元素的位置
    删除第2个元素
    末尾追加一个0
[4, 2, 2, 0]
如果第2个元素==第3个元素(符合条件)
    则第2个元素 + 第3个元素,并赋给第2个元素的位置[4, 4, 2, 0]
    删除第3个元素[4, 2, 0]
    末尾追加一个0[4, 2, 0, 0]
[4, 2, 0, 0]
如果第3个元素==第4个元素
    则第3个元素 + 第4个元素,并赋给第3个元素的位置
    删除第4个元素
    末尾追加一个0

也就是说,相邻且相等的两个元素相加,应赋值给前方位置的元素,然后删除后方位置的元素,删除一个,肯定还要凑回去的,根据游戏规则,补0即可

其实上方的逻辑还可以进行优化,当检测到当前位置的元素为0时,直接打断循环即可(因为已经被zero_to_end函数处理过了),封装成merge_single函数如下:

def merge_single(list_data):
    """
    合并元素函数(核心算法)
    重排序后,左边两个相邻相同的非0元素相加,后方补0,并加分(可diy)
    如果两个相邻的元素不同或者为0,则不做其他操作
    :param list_data: list 一维列表
    :return: None
    """
    zero_to_end(list_data)  # 处理一维列表
    for i in range(3):
        if list_data[i] == 0: break  # 检测到当前位置为0,后方就不管了,直接打断
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2  # 等价于 += list_data[i + 1]
            del list_data[i + 1]  # 删除 [i + 1]位置的元素
            list_data.append(0)  # 补0

有点不太放心,放一条数据进行测试:

[4, 4, 2, 2] >i = 0,相加> [8, 4, 2, 2] >删除i + 1位置> [8, 2, 2] >补0> [8, 2, 2, 0] 
>循环结束第1次,i = 1,又相加> [8, 4, 2, 0] >又删除i + 1位置> [8, 4, 0] >又补0> [8, 4, 0, 0]
> 循环结束第2次,i = 2,发现是0,直接跳出循环 

3. 随机填充

玩法规则第3条,当游戏中,发生方块滑动时,在0元素区域随机抽取一个位置,随机赋值2或者4。

因此,先定义全局变量一个存2和4的元组,通过random模块实现随机索引获取2或者4。

random_tuple = (2, 4)  # 初始添加的值、移动时添加的值

通过while循环不断寻找随机方格,直到发现该方格存储0,那么该方格将被赋予新值。

def random_site():
    """
    随机填充0元素函数(非核心)
    随机挑选0元素的位置,进行随机填充random_list中的任意一个元素
    可通过增删改变random_list中的元素,从而影响到随机填充的数字
    :return: None
    """
    random_list_len = len(random_tuple)
    while True:
        x = random.randint(0, 3)
        y = random.randint(0, 3)
        if after_source[x][y] == 0:
            after_source[x][y] = random_tuple[random.randint(0, random_list_len - 1)]
            break

四、附加功能函数

通过以上三步,成功的完成了2048的核心功能,接下来逐一部署2048的初始化、游戏操作、用户操作、打印等等函数

1. 初始数据和矩阵比较

构造游戏初始数据,以全局变量表示:

score = 0  # 初始分数,后续累加即可
source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

单纯的一个source表示数据可能还不够,我构造了两个4*4的矩阵,分别命名为before_source和after_source:

before_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作前的矩阵
after_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作后的矩阵(当前打印的矩阵)

这两个矩阵,用于用户操作前的一个比较(后台比较),假设用户进行左向移动,那么移动前的数据传给before_source,移动后(即每一行调用merge_single函数后)的数据传给after_source,after_source才是用户需要看的,两者在后台进行比较后,倘若不同,则说明发生了“方块移动”,那么就要调用随机填充的函数,如果相同,说明没有方块滑动,则不可随机填充。假如对以下的数据发起左移操作后,是不存在元素移动的,即不会调用随机填充:

[
    [4, 0, 0, 0],
    [0, 0, 0, 0],
    [2, 4, 2, 0],
    [8, 2, 0, 0]
]

根据上述分析,构造比较函数compare_matrix:

def compare_matrix():
    """
    二维数组比较
    操作前后的二维数组(矩阵)进行比较
    如果不相等,说明有元素可移动,当移动时调用random_site()函数
    """
    if not (before_source == after_source):
        random_site()

2. 矩阵数据打印

每次执行完移动操作后(无论是上下还是左右),肯定都要反馈给用户数据界面,因此需要构造打印矩阵的函数

def print_list():
    """打印游戏过程中必看的矩阵信息"""
    for single_list in after_source:
        print(single_list)

3. main入口(框架搭建)

调用程序总该需要一个入口,构造main函数

循环开始阶段,通过global关键字操作全局变量before_source,此处需要注意:应使用深拷贝(需要导入copy模块),将当前的数据拷贝给before_source,如果采取浅拷贝,操作after_source后,before_source也会跟随变化,这样就导致before_source恒等于after_source。

上下左右以input输入(w 、s、a、d)来进行移动,n表示主动认输,q表示退出游戏。

每次执行完移动操作后,都需要进行反馈数据界面,所以循环末尾需要调用print_list函数和打印当前分数:

def main():
    """程序入口:初始化 + 输入 + 输出"""
    while True:
        global before_source
        before_source = copy.deepcopy(after_source)
        key = input("键入:")
        if key == "a": pass
        if key == "d": pass
        if key == "w": pass
        if key == "s": pass
        if key == "n": pass
        if key == "q": break  
        print_list()
            
            
main()  # 调用main函数,即正常游戏的入口

当准备输入时,手动中止程序,会发现很烦人的红色报错

因此加上try和except简单的处理一下:

def main():
    """程序入口:初始化 + 输入 + 输出"""
    while True:
        try:
            key = input("键入:")
            global before_source
            before_source = copy.deepcopy(after_source)
            if key == "a": pass
            if key == "d": pass
            if key == "w": pass
            if key == "s": pass
            if key == "n": pass
            if key == "q": break
            print(f"当前分数:{score}")
            print_list()
        except KeyboardInterrupt:
            break
        
            
            
main()  # 调用main函数,即正常游戏的入口

4. 实现认输功能

构建forfeit函数,打印最终分数后,人为抛出KeyboardInterrupt异常,直接调到except执行break打断循环(为了不在打印最终得分后,执行后续两条语句):

def forfeit():
    """认输"""
    print(f"玩家已认输,最终得分:{score}")
    raise KeyboardInterrupt

5. 加分机制

每发生方块碰撞合并后,相加数字即为当局加分,比如初始为0,两个相邻的方块2碰撞合并后变成方块4,当前分数+4,即分数为4。

在merge_single函数中(参考2.2的函数),list_data.append(0)语句后添加下述代码

global score
score += list_data[i]

即:

def merge_single(list_data):
    """
    合并元素函数(核心算法)
    重排序后,左边两个相邻相同的非0元素相加,后方补0,并加分(可diy)
    如果两个相邻的元素不同或者为0,则不做其他操作
    :param list_data: list 一维列表
    :return: None
    """
    zero_to_end(list_data)
    for i in range(3):
        if list_data[i] == 0: break
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2
            del list_data[i + 1]
            list_data.append(0)
            global score
            score += list_data[i]

6. 游戏初始化

为了让游戏设置得灵活一点,增加全局变量init_count,表示初始方块需赋值2或者4的个数,通常为2。

init_count = 2  # 初始值的个数


def init():
    """
    游戏初始化
    :return: None
    """
    print(f"""当前分数:{score}\n操作方式:q退出 n认输
       w(上)
a(左)  s(下)  d(右)""")
    for i in range(init_count):  # 随机生成init_count个初始值
        random_site()
    print_list()

7. 各方向移动操作

首先要明确,核心函数中第2节的相等相撞合并函数,仅仅针对一维列表,而整个游戏以二维列表为主,因此需要复用该代码

def merge():
    """合并操作,详见merge_single()函数"""
    for i in range(4):
        merge_single(after_source[i])

接下来逐一分析,左右上下操作如何实现...

(1)左(基础操作)

调用merge函数,完成滑动合并,然后比较前后矩阵相等来决定是否调用随机填充(即调用compare_matrix函数

def left():
    """向左操作"""
    merge()

(2)右

最暴力无脑的办法就是复制上述函数代码,然后更改,但是我一直写这些简单清晰的函数,就是为了复用,所以这里应该想办法复用merge等代码,我以向左操作为基础,仅看merge函数,假设每一行都逆转,再进行向左的核心操作,再逆转回去,不就可以了吗,比如:

[0, 2, 2, 4]向右移动操作后变成[0, 0, 4, 4]
[0, 2, 2, 4] >逆转> [4, 2, 2, 0] >merge> [4, 4, 0, 0] >逆转> [0, 0, 4, 4]

因此代码如下:

def reverse():
    """逆转2048二维列表中的每一行一维列表"""
    for i in range(4):
        after_source[i].reverse()
def right():
    """向右操作"""
    reverse()
    merge()
    reverse()

(3)上

同样为了复用,以向左操作为基础,仅看merge函数,假设进行矩阵转置,然后调用merge操作,再转置,比如:

[0, 2, 0, 0]
[4, 2, 0, 0]
[0, 0, 4, 0]
[4, 0, 0, 0]
转置后
[0, 4, 0, 4]
[2, 2, 0, 0]
[0, 0, 4, 0]
[0, 0, 0, 0]
调用merge后
[8, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
[0, 0, 0, 0]
再转置回来,得到结果
[8, 4, 4, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]

因此代码如下(3种转置均可):

def transposition():
    """二维列表转置(矩阵转置)"""
    for x in range(4):
        for y in range(x, 4):
            after_source[x][y], after_source[y][x] = after_source[y][x], after_source[x][y]

def transposition():
    """二维列表转置(矩阵转置)"""
    new_map = [list(item) for item in zip(*after_source)]
    after_source.clear()
    after_source.extend(new_map)

def transposition():
    """二维列表转置(矩阵转置)"""
    new_map = [list(item) for item in zip(*after_source)]
    after_source[:] = new_map
def up():
    """向上操作"""
    transposition()
    merge()
    transposition()

(3)下

根据上移和右移操作所得的灵感,同样是以左移为基础操作。假设进行矩阵转置,逆转后,调用merge操作,再逆转,再转置,比如:

[0, 2, 0, 0]
[4, 2, 0, 0]
[0, 0, 4, 0]
[4, 0, 0, 0]
转置后
[0, 4, 0, 4]
[2, 2, 0, 0]
[0, 0, 4, 0]
[0, 0, 0, 0]
逆转每一行后
[4, 0, 4, 0]
[0, 0, 2, 2]
[0, 4, 0, 0]
[0, 0, 0, 0]
调用merge后
[8, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
[0, 0, 0, 0]
再逆转回来
[0, 0, 0, 8]
[0, 0, 0, 4]
[0, 0, 0, 4]
[0, 0, 0, 0]
再转置回来,得到结果
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[8, 4, 4, 0]

因此代码如下:

def down():
    """向下操作"""
    transposition()
    reverse()
    merge()
    reverse()
    transposition()

五、最终代码

"""
    2048 GAME
"""
import random
import copy

score = 0  # 分数
init_count = 2  # 初始值的个数
before_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作前的矩阵
after_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作后的矩阵(当前打印的矩阵)
random_tuple = (2, 4)  # 初始添加的值、移动时添加的值


# Controller层
def zero_to_end(list_data):
    """
    重排序函数(核心算法)
    非0元素移至最前(保持顺序),0元素移至最后,充当中间人处理列表的角色
    :param list_data: list 一维列表
    :return: None
    """
    for i in range(3, -1, -1):
        if not list_data[i]:
            del list_data[i]
            list_data.append(0)


def merge_single(list_data):
    """
    合并元素函数(核心算法)
    重排序后,左边两个相邻相同的非0元素相加,后方补0,并加分(可diy)
    如果两个相邻的元素不同或者为0,则不做其他操作
    :param list_data: list 一维列表
    :return: None
    """
    zero_to_end(list_data)
    for i in range(3):
        if list_data[i] == 0: break
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2
            del list_data[i + 1]
            list_data.append(0)
            global score
            score += list_data[i]


def random_site():
    """
    随机填充0元素函数(非核心)
    随机挑选0元素的位置,进行随机填充random_list中的任意一个元素
    可通过增删改变random_list中的元素,从而影响到随机填充的数字
    :return: None
    """
    random_list_len = len(random_tuple)
    while True:
        x = random.randint(0, 3)
        y = random.randint(0, 3)
        if after_source[x][y] == 0:
            after_source[x][y] = random_tuple[random.randint(0, random_list_len - 1)]
            break


def merge():
    """合并操作,详见merge_single()函数"""
    for i in range(4):
        merge_single(after_source[i])


def reverse():
    """逆转2048二维列表中的每一行一维列表"""
    for i in range(4):
        after_source[i].reverse()


def transposition():
    """二维列表转置(矩阵转置)"""
    for x in range(4):
        for y in range(x, 4):
            after_source[x][y], after_source[y][x] = after_source[y][x], after_source[x][y]


def compare_matrix():
    """
    二维数组比较
    操作前后的二维数组(矩阵)进行比较
    如果不相等,说明有元素可移动,当移动时调用random_site()函数
    """
    if not (before_source == after_source):
        random_site()


def left():
    """向左操作"""
    merge()


def right():
    """向右操作"""
    reverse()
    merge()
    reverse()


def up():
    """向上操作"""
    transposition()
    merge()
    transposition()


def down():
    """向下操作"""
    transposition()
    reverse()
    merge()
    reverse()
    transposition()


# View层
def init():
    """
    游戏初始化
    :return: None
    """
    print(f"""当前分数:{score}\n操作方式:q退出 n认输
       w(上)
a(左)  s(下)  d(右)""")
    for i in range(init_count):  # 随机生成init_count个初始值
        random_site()
    print_list()


def print_list():
    """打印游戏过程中必看的矩阵信息"""
    for single_list in after_source:
        print(single_list)


def forfeit():
    """认输"""
    print(f"玩家已认输,最终得分:{score}")
    raise KeyboardInterrupt


def main():
    """程序入口:初始化 + 输入 + 输出"""
    init()
    while True:
        try:
            global before_source
            before_source = copy.deepcopy(after_source)
            key = input("键入:")
            if key == "a": left()
            if key == "d": right()
            if key == "w": up()
            if key == "s": down()
            if key == "n": forfeit()
            if key == "q": break
            compare_matrix()
            print(f"当前分数:{score}")
            print_list()
        except KeyboardInterrupt:
            break


main()

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


我最近重新拾起了计算机视觉,借助Python的opencv还有face_recognition库写了个简单的图像识别demo,额外定制了一些内容,原本想打包成exe然后发给朋友,不过在这当中遇到了许多小问题,都解决了,记录一下踩过的坑。 1、Pyinstaller打包过程当中出现warning,跟d
说到Pooling,相信学习过CNN的朋友们都不会感到陌生。Pooling在中文当中的意思是“池化”,在神经网络当中非常常见,通常用的比较多的一种是Max Pooling,具体操作如下图: 结合图像理解,相信你也会大概明白其中的本意。不过Pooling并不是只可以选取2x2的窗口大小,即便是3x3,
记得大一学Python的时候,有一个题目是判断一个数是否是复数。当时觉得比较复杂不好写,就琢磨了一个偷懒的好办法,用异常处理的手段便可以大大程度帮助你简短代码(偷懒)。以下是判断整数和复数的两段小代码: 相信看到这里,你也有所顿悟,能拓展出更多有意思的方法~
文章目录 3 直方图Histogramplot1. 基本直方图的绘制 Basic histogram2. 数据分布与密度信息显示 Control rug and density on seaborn histogram3. 带箱形图的直方图 Histogram with a boxplot on t
文章目录 5 小提琴图Violinplot1. 基础小提琴图绘制 Basic violinplot2. 小提琴图样式自定义 Custom seaborn violinplot3. 小提琴图颜色自定义 Control color of seaborn violinplot4. 分组小提琴图 Group
文章目录 4 核密度图Densityplot1. 基础核密度图绘制 Basic density plot2. 核密度图的区间控制 Control bandwidth of density plot3. 多个变量的核密度图绘制 Density plot of several variables4. 边
首先 import tensorflow as tf tf.argmax(tenso,n)函数会返回tensor中参数指定的维度中的最大值的索引或者向量。当tensor为矩阵返回向量,tensor为向量返回索引号。其中n表示具体参数的维度。 以实际例子为说明: import tensorflow a
seaborn学习笔记章节 seaborn是一个基于matplotlib的Python数据可视化库。seaborn是matplotlib的高级封装,可以绘制有吸引力且信息丰富的统计图形。相对于matplotlib,seaborn语法更简洁,两者关系类似于numpy和pandas之间的关系,seabo
Python ConfigParser教程显示了如何使用ConfigParser在Python中使用配置文件。 文章目录 1 介绍1.1 Python ConfigParser读取文件1.2 Python ConfigParser中的节1.3 Python ConfigParser从字符串中读取数据
1. 处理Excel 电子表格笔记(第12章)(代码下载) 本文主要介绍openpyxl 的2.5.12版处理excel电子表格,原书是2.1.4 版,OpenPyXL 团队会经常发布新版本。不过不用担心,新版本应该在相当长的时间内向后兼容。如果你有新版本,想看看它提供了什么新功能,可以查看Open
1. 发送电子邮件和短信笔记(第16章)(代码下载) 1.1 发送电子邮件 简单邮件传输协议(SMTP)是用于发送电子邮件的协议。SMTP 规定电子邮件应该如何格式化、加密、在邮件服务器之间传递,以及在你点击发送后,计算机要处理的所有其他细节。。但是,你并不需要知道这些技术细节,因为Python 的
文章目录 12 绘图实例(4) Drawing example(4)1. Scatterplot with varying point sizes and hues(relplot)2. Scatterplot with categorical variables(swarmplot)3. Scat
文章目录 10 绘图实例(2) Drawing example(2)1. Grouped violinplots with split violins(violinplot)2. Annotated heatmaps(heatmap)3. Hexbin plot with marginal dist
文章目录 9 绘图实例(1) Drawing example(1)1. Anscombe’s quartet(lmplot)2. Color palette choices(barplot)3. Different cubehelix palettes(kdeplot)4. Distribution
Python装饰器教程展示了如何在Python中使用装饰器基本功能。 文章目录 1 使用教程1.1 Python装饰器简单示例1.2 带@符号的Python装饰器1.3 用参数修饰函数1.4 Python装饰器修改数据1.5 Python多层装饰器1.6 Python装饰器计时示例 2 参考 1 使
1. 用GUI 自动化控制键盘和鼠标第18章 (代码下载) pyautogui模块可以向Windows、OS X 和Linux 发送虚拟按键和鼠标点击。根据使用的操作系统,在安装pyautogui之前,可能需要安装一些其他模块。 Windows: 不需要安装其他模块。OS X: sudo pip3
文章目录 生成文件目录结构多图合并找出文件夹中相似图像 生成文件目录结构 生成文件夹或文件的目录结构,并保存结果。可选是否滤除目录,特定文件以及可以设定最大查找文件结构深度。效果如下: root:[z:/] |--a.py |--image | |--cat1.jpg | |--cat2.jpg |
文章目录 VENN DIAGRAM(维恩图)1. 具有2个分组的基本的维恩图 Venn diagram with 2 groups2. 具有3个组的基本维恩图 Venn diagram with 3 groups3. 自定义维恩图 Custom Venn diagram4. 精致的维恩图 Elabo
mxnet60分钟入门Gluon教程代码下载,适合做过深度学习的人使用。入门教程地址: https://beta.mxnet.io/guide/getting-started/crash-course/index.html mxnet安装方法:pip install mxnet 1 在mxnet中使
文章目录 1 安装2 快速入门2.1 基本用法2.2 输出图像格式2.3 图像style设置2.4 属性2.5 子图和聚类 3 实例4 如何进一步使用python graphviz Graphviz是一款能够自动排版的流程图绘图软件。python graphviz则是graphviz的python实