Python学习笔记(一):命令行界面扫雷(详细)

  • Post author:
  • Post category:python




起因

因为最近刚刚开始学习Python,想编点东西来熟悉一下语法,想起来之前用C语言编过一个简单的命令行界面的扫雷,故用Python来重新编一个,大致效果如下图,我会在文章中梳理一下我当时的大致思路。

最终效果



过程

首先我们需要构建出来一个雷阵,我们需要的参数有两个,一个是雷阵的大小,另一个就是雷数。

    n = eval(input("请输入雷阵的大小"))
    m = eval(input("请输入雷的个数"))

我们首先要定义一个二维数组(行列数应比雷阵大一“圈”)

    mine = [([0] * (n + 2)) for i in range(0, n + 2)]  # 雷阵

因为我们需要一个数组给玩家看,所以需要再定义一个一样大数组,而且该数组到时候需要判断是否开过雷,为防止函数运用到数组之外的地方,因此数组最外一圈初始化为0.

    user = [(['#'] * (n + 2)) for i in range(0, n + 2)]  # 用户
    for i in range(0, n+2):
        user[0][i] = 0
        user[n + 1][i] = 0
        user[i][0] = 0
        user[i][n + 1] = 0

之后,我们就可以初始化 雷阵 了,该过程需要埋下雷,和使不是雷的方块可以显示四周方块中雷的个数

def build(list1, num1, num2):    # 雷阵初始化
    i = 0                        # list1为雷阵,num1为雷阵大小,num2为雷数
    while i < num2:              # 循环布雷
        m = randint(1, num1)
        n = randint(1, num1)
        if list1[m][n] != '*':
            list1[m][n] = '*'
            i += 1
        else:
            continue
    for m in range(1, num1 + 1):            # 为每一个非雷方块确定周围的雷数
        for n in range(1, num1 + 1):        # m代表数组的行数,n代表列数
            if list1[m][n] != '*':
                if list1[m - 1][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m - 1][n] == '*':
                    list1[m][n] += 1
                if list1[m - 1][n + 1] == '*':
                    list1[m][n] += 1
                if list1[m][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m][n + 1] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n + 1] == '*':
                    list1[m][n] += 1

之后我们就有了一个已经布好了雷的雷阵。接下去我们需要一个函数,它可以帮我们把我们打印出我们输出数组。因为游戏进行的过程中需要用到横纵坐标,因此我们在输出数组的时候,用第一排和第一列来表示横纵坐标。而且输出数组的时候出于美观,将0输出为空格。

def print_mine(list2, num):             # list2为要输出的数组,num为数组的大小
    for m in range(0, num + 1):
        for n in range(0, num + 1):
            if m == 0:                  # 横坐标
                print(n, end='  ')
                continue
            if n == 0:                  # 纵坐标
                print(m, end='  ')
                continue
            if list2[m][n] == 0:        # 将对应为0的元素用空格代替
                print('  ', end=' ')
            else:
                print(list2[m][n], end='  ')
        print('')

OK,现在我们就可以编写进行游戏的部分了。我们将进行游戏的主体部分交给函数来处理,这里命名为win,此函数的主要功能就是判断胜负,和进行游戏,其中落子的部分由play函数来处理。所以此函数需要自我调用,这里用flag== 0来表示输了,随即结束游戏;flag==1表示游戏可以继续进行。

def win(list_user, list_mine, num, flag, mine):        # 判断胜负
    i = 0                                              # num为数组的大小,mine为雷数
    print_mine(list_user, num)
    if flag == 1:                                      # 继续游戏
        for m in range(0, num+2):
            for n in range(0, num+2):
                if list_user[m][n] == "#" or list_user[m][n] == '@':
                    i += 1
        if i == mine:                                  # 当数组中未展开或者标记的元素等于雷数,宣布胜利
            print("你赢了!")   
            return 1
        else:
            a = play(list_user, list_mine, num)        # 进行一次落子
            win(list_user, list_mine, num, a, mine)    # 进行下一次判断
    else:                                              # 游戏结束
        print("你输了!")
        return 0

接下来编写play函数,play函数的功能有落子和标记,落子后需要判断落到的地方是否为雷,若不是,则调用open函数进行开雷处理;若是,则返回0。

def play(list_user, list_mine, num):                 # 落子函数
    m = input("请输入横坐标(需要标记请按@)")
    n = input("请输入纵坐标(需要标记请按@)")
    if m == '@' or n == '@':                         # 触发标记功能
        m1 = eval(input("请输入要标记的横坐标"))
        n1 = eval(input("请输入要标记的纵坐标"))
        list_user[m1][n1] = '@'
        print_mine(list_user, num)
        play(list_user, list_mine, num)
        return 1
    elif int(m) > num or int(n) > num:               # 数据不合理重新输入
        print("请重新输入")
        play(list_user, list_mine, num)
        return 1
    else:
        if list_mine[int(m)][int(n)] == '*':         # 踩雷了
            return 0
        else:
            open_user(list_user, list_mine, int(m), int(n))    # 开雷
            return 1

然后进行开雷函数的编写,开雷函数是将user数组中的’#’显示成mine数组中对应元素的数值,若为0,则向四周执行open函数,达到开雷的效果。

def open_user(list_user, list_mine, m, n):                    # 开雷函数
    if list_user[m][n] == '#' or list_user[m][n] == '@':
        if list_mine[m][n] == 0:
            list_user[m][n] = list_mine[m][n]
            open_user(list_user, list_mine, m - 1, n - 1)     # 向四周扩散
            open_user(list_user, list_mine, m - 1, n)
            open_user(list_user, list_mine, m - 1, n + 1)
            open_user(list_user, list_mine, m, n + 1)
            open_user(list_user, list_mine, m, n - 1)
            open_user(list_user, list_mine, m + 1, n - 1)
            open_user(list_user, list_mine, m + 1, n)
            open_user(list_user, list_mine, m + 1, n + 1)
        else:
            list_user[m][n] = list_mine[m][n]

最后,因为想不结束程序而重新进入游戏,所以将游戏初始化的部分设置为一个函数。

def begin():                                             # 游戏进行
    n = eval(input("请输入雷阵的大小"))
    m = eval(input("请输入雷的个数"))
    mine = [([0] * (n + 2)) for i in range(0, n + 2)]    # 雷阵
    user = [(['#'] * (n + 2)) for i in range(0, n + 2)]  # 用户
    for i in range(0, n+2):
        user[0][i] = 0
        user[n + 1][i] = 0
        user[i][0] = 0
        user[i][n + 1] = 0
    build(mine, n, m)
    win(user, mine, n, 1, m)

最后

begin()
if (input("是否重来(y/n)")) == 'y':
    begin()

完整代码如下

from random import *


def build(list1, num1, num2):    # 雷阵初始化
    i = 0                        # list1为雷阵,num1为雷阵大小,num2为雷数
    while i < num2:              # 循环布雷
        m = randint(1, num1)
        n = randint(1, num1)
        if list1[m][n] != '*':
            list1[m][n] = '*'
            i += 1
        else:
            continue
    for m in range(1, num1 + 1):            # 为每一个非雷方块确定周围的雷数
        for n in range(1, num1 + 1):        # m代表数组的行数,n代表列数
            if list1[m][n] != '*':
                if list1[m - 1][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m - 1][n] == '*':
                    list1[m][n] += 1
                if list1[m - 1][n + 1] == '*':
                    list1[m][n] += 1
                if list1[m][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m][n + 1] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n - 1] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n] == '*':
                    list1[m][n] += 1
                if list1[m + 1][n + 1] == '*':
                    list1[m][n] += 1


def print_mine(list2, num):             # list2为要输出的数组,num为数组的大小
    for m in range(0, num + 1):
        for n in range(0, num + 1):
            if m == 0:                  # 横坐标
                print(n, end='  ')
                continue
            if n == 0:                  # 纵坐标
                print(m, end='  ')
                continue
            if list2[m][n] == 0:        # 将对应为0的元素用空格代替
                print('  ', end=' ')
            else:
                print(list2[m][n], end='  ')
        print('')


def open_user(list_user, list_mine, m, n):                    # 开雷函数
    if list_user[m][n] == '#' or list_user[m][n] == '@':
        if list_mine[m][n] == 0:
            list_user[m][n] = list_mine[m][n]
            open_user(list_user, list_mine, m - 1, n - 1)     # 向四周扩散
            open_user(list_user, list_mine, m - 1, n)
            open_user(list_user, list_mine, m - 1, n + 1)
            open_user(list_user, list_mine, m, n + 1)
            open_user(list_user, list_mine, m, n - 1)
            open_user(list_user, list_mine, m + 1, n - 1)
            open_user(list_user, list_mine, m + 1, n)
            open_user(list_user, list_mine, m + 1, n + 1)
        else:
            list_user[m][n] = list_mine[m][n]


def play(list_user, list_mine, num):                 # 落子函数
    m = input("请输入横坐标(需要标记请按@)")
    n = input("请输入纵坐标(需要标记请按@)")
    if m == '@' or n == '@':                         # 触发标记功能
        m1 = eval(input("请输入要标记的横坐标"))
        n1 = eval(input("请输入要标记的纵坐标"))
        list_user[m1][n1] = '@'
        print_mine(list_user, num)
        play(list_user, list_mine, num)
        return 1
    elif int(m) > num or int(n) > num:               # 数据不合理重新输入
        print("请重新输入")
        play(list_user, list_mine, num)
        return 1
    else:
        if list_mine[int(m)][int(n)] == '*':         # 踩雷了
            return 0
        else:
            open_user(list_user, list_mine, int(m), int(n))    # 开雷
            return 1


def win(list_user, list_mine, num, flag, mine):        # 判断胜负
    i = 0                                              # num为数组的大小,mine为雷数
    print_mine(list_user, num)
    if flag == 1:                                      # 继续游戏
        for m in range(0, num+2):
            for n in range(0, num+2):
                if list_user[m][n] == "#" or list_user[m][n] == '@':
                    i += 1
        if i == mine:                                  # 当数组中未展开或者标记的元素等于雷数,宣布胜利
            print("你赢了!")
            return 1
        else:
            a = play(list_user, list_mine, num)        # 进行一次落子
            win(list_user, list_mine, num, a, mine)    # 进行下一次判断
    else:                                              # 游戏结束
        print("你输了!")
        return 0


def begin():                                             # 游戏进行
    n = eval(input("请输入雷阵的大小"))
    m = eval(input("请输入雷的个数"))
    mine = [([0] * (n + 2)) for i in range(0, n + 2)]    # 雷阵
    user = [(['#'] * (n + 2)) for i in range(0, n + 2)]  # 用户
    for i in range(0, n+2):
        user[0][i] = 0
        user[n + 1][i] = 0
        user[i][0] = 0
        user[i][n + 1] = 0
    build(mine, n, m)
    win(user, mine, n, 1, m)


begin()
if (input("是否重来(y/n)")) == 'y':
    begin()



后记

这是第一次在CSDN上写博客,对Python的学习也才刚刚开始,其中难免有些错误,多多包容。

下一步的打算是使雷阵在每次操作完可以刷新,而不是重新输出一个。还有将该游戏图形化的打算。(等我学到了再说)



版权声明:本文为qq_43777823原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。