python9(异常处理和2048游戏)

1.异常处理

#什么时异常?
在程序运行过程中影响程序正常运行的内容,

#为什么需要异常处理?
可以让你的程序更加健壮, 可以清晰的快速修复异常。
1)异常处理的语句
1.普通的异常处理:

import  time
try:
    # 如果你觉得代码可能出现问题, 那么放在try语句中, 只执行一次;
    print(s)
    # print("hello")
except NameError as e:   # 对于异常进行一个重命名;记录了异常的详细信息;
    # 可能执行一次, 也可能不执行;
    print("名称错误")
    with open("except.log", 'w') as f:
        f.write(time.ctime() + ' ')
        f.write(str(e))
finally:
    # 无论是否出现异常, 肯定会执行一次,
    print("处理结束")
名称错误
处理结束

2.复杂的异常处理

import  time
try:
    # 如果你觉得代码可能出现问题, 那么放在try语句中, 只执行一次;
    print('hello')
    with open('/etc/aa') as f:
        print(f.read()[:5])

    print("文件读取结束")
    li = [1, 2, 3, 4]   # try语句中一旦出现问题, 后面的语句(try里面的)不执行
    print(li[5])
    print(s)
    print("hello")
except (NameError, IndexError,FileNotFoundError) as e:   # 对于异常进行一个重命名;记录了异常的详细信息;
    # 可能执行一次, 也可能不执行;
    # print("名称错误")
    with open("except.log", 'a+') as f:
        f.write(time.ctime() + ' ' + str(e) + '\n')
finally:
    # 无论是否出现异常, 肯定会执行一次,
    print("处理结束")
hello
处理结束

2)python内置异常结构

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration
      +-- StopAsyncIteration
      +-- ArithmeticError
      |    +-- FloatingPointError
      |    +-- OverflowError
      |    +-- ZeroDivisionError
      +-- AssertionError
      +-- AttributeError
      +-- BufferError
      +-- EOFError
      +-- ImportError
      |    +-- ModuleNotFoundError
      +-- LookupError
      |    +-- IndexError
      |    +-- KeyError
      +-- MemoryError
      +-- NameError
      |    +-- UnboundLocalError
      +-- OSError
      |    +-- BlockingIOError
      |    +-- ChildProcessError
      |    +-- ConnectionError
      |    |    +-- BrokenPipeError
      |    |    +-- ConnectionAbortedError
      |    |    +-- ConnectionRefusedError
      |    |    +-- ConnectionResetError
      |    +-- FileExistsError
      |    +-- FileNotFoundError
      |    +-- InterruptedError
      |    +-- IsADirectoryError
      |    +-- NotADirectoryError
      |    +-- PermissionError
      |    +-- ProcessLookupError
      |    +-- TimeoutError
      +-- ReferenceError
      +-- RuntimeError
      |    +-- NotImplementedError
      |    +-- RecursionError
      +-- SyntaxError
      |    +-- IndentationError
      |         +-- TabError
      +-- SystemError
      +-- TypeError
      +-- ValueError
      |    +-- UnicodeError
      |         +-- UnicodeDecodeError
      |         +-- UnicodeEncodeError
      |         +-- UnicodeTranslateError
      +-- Warning
           +-- DeprecationWarning
           +-- PendingDeprecationWarning
           +-- RuntimeWarning
           +-- SyntaxWarning
           +-- UserWarning
           +-- FutureWarning
           +-- ImportWarning
           +-- UnicodeWarning
           +-- BytesWarning
           +-- ResourceWarning

2.python中的虚拟环境

# 1. 被人可以正常使用的模块, 你们使用不了;
# 2. 可能会用python的不同版本去写项目;
# 3. 项目里面用的python3.6 --------- itchat
#           - itchat3.10
#           - itchat4.0


# 为什么使用Anaconda?

Python易用,但用好却不易,其中比较头疼的就是包管理和Python不同版本的问题,特别是当你使用Windows的时候。为了解决这些问题,有不少发行版的Python,比如WinPython、Anaconda等,这些发行版将python和许多常用的package打包,方便pythoners直接使用,此外,还有virtualenv、pyenv等工具管理虚拟环境。



常见的问题:
- python解释器中相关模块使用有问题;
- 一台电脑需要编写多个项目, 每个项目的python解释器版本都不同;
- 多个项目中, python解释器版本相同, 但是安装的模块(eg:itchat)的版本不同等问题;




# 什么是Anaconda?

Anaconda是一个用于科学计算的Python发行版,支持 Linux, Mac, Windows系统,提供了包管理与环境管理的功能,可以很方便地解决多版本python并存、切换以及各种第三方包安装问题。Anaconda利用工具/命令 conda 来进行package和environment的管理,并且已经包含了Python和相关的配套工具。




## Anaconda和conda?
- conda 可以理解为一个工具,也是一个可执行命令,其核心功能是包管理与环境管理。
	- 包管理与pip的使用类似;
	- 环境管理则允许用户方便地安装不同版本的python并可以快速切换。

- Anaconda则是一个打包的集合,里面预装好了conda、某个版本的python、众多packages、科学计算工具等等,所以也称为Python的一种发行版。



# 如何使用?
- 1). 创建一个虚拟环境, python版本为3.6, 项目,名为2048;
conda create --name 2048 python==3.6


- 2). 进入虚拟环境
anaconda3:   conda activate  虚拟环境名称
anaconda2:    source activate  虚拟环境名称


- 3). 退出虚拟环境
ctrl+D



- 4). anaconda安装好的虚拟环境存储在哪里?
~/anaconda2/envs/


- 5). 制定虚拟机环境的python解释器位置?
~/anaconda2/envs/虚拟环境名称/bin/python



- 6).

conda install 包名 [-n 虚拟环境名称]
conda search 包名 [-n 虚拟环境名称]
conda remove 包名 [-n 虚拟环境名称]
conda update 包名 [-n 虚拟环境名称]
conda list

7). # 安装pymysql
# 如何配置国内镜像源?

3.2048游戏
1)棋盘绘制

# 1, 画它的分隔符
import random
def draw_sep():
    print("+-----" * 4 + '+')

# 2. 画每一行的格子
def draw_one_row(row):  # [0, 2, 0, 0]   |    |  2  |    |    |
    print("".join([ '|  %d  '%(item) if item != 0 else '|     ' for item in row ]) + '|')

# draw_one_row([0, 2, 0, 0])

# 3. 创建棋盘的数据, 默认情况下时4*4, 数值全为0;
field = [[0 for j in range(4)] for i in range(4)]
# print(field)

# 4. 开始游戏时, 棋盘数据会随机生成2或者4,
def random_create():
    # field[0][2] = 2
    while True:
        firstIndex = random.choice(range(4))
        secondIndex = random.choice(range(4))
        if field[firstIndex][secondIndex] == 0:
            value = random.choice([2, 4, 2, 2, 2])
            field[firstIndex][secondIndex] = value
            break

def game():
    random_create()
    random_create()
    print(field)
    for row in field:
        draw_sep()
        draw_one_row(row)
    draw_sep()


if __name__ == '__main__':
    game()
[[0, 0, 0, 0], [2, 0, 0, 4], [0, 0, 0, 0], [0, 0, 0, 0]]
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+
|  2  |     |     |  4  |
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+

2)判断棋盘是否可以向左移动

# 判断棋盘是否可以向左移动?
#  1). 只要棋盘的任意一行可以向左移动, 就返回True;
#          目标: 如何判断棋盘的一行是否可以向左移动?
#  2). 只要这一行的任意两个元素可以向左移动, 则返回True;
#           目标: 如何两个元素可以向左移动?
#           #  - 如果第一个数值为0, 第二个数值不为0, 则说明可以向左移动;
#           #  - 如果第一个数值不为0, 第二个数值与第一个元素相等, 则说明可以向左移动;
def is_row_left(row):  # [0, 2,2,0]
    # 任意两个元素可以向左移动?
    def is_change(index):  # index时索引值, [0,1,2,3]
        # - 如果第一个数值为0, 第二个数值不为0, 则说明可以向左移动;
        if row[index] == 0 and row[index + 1] != 0:
            return True
        # - 如果第一个数值不为0, 第二个数值与第一个元素相等, 则说明可以向左移动;
        if row[index] != 0 and row[index + 1] == row[index]:
            return True
        return False
    # 只要这一行的任意两个元素可以向左移动, 则返回True;
    return any([is_change(index) for index in range(3)])

def is_move_left(field):
    # 只要棋盘的任意一行可以向左移动, 就返回True;
    return any([is_row_left(row) for row in field])


if __name__ == "__main__":
    try:
        assert is_move_left([[0, 0, 0, 0], [0, 2, 0, 0], [0, 0, 0, 0], [0, 0, 0, 4]]) == True, "棋盘向左移动失败"
        assert is_row_left([2, 2, 2, 2]) == True, 'Error'
        assert is_row_left([2, 4, 2, 4]) == False, 'Error'
    except AssertionError as e:
        print(e)
    else:
        print("测试用例完成.....")
测试用例完成.....

3)判断棋盘是否可以向右移动

def invert(field):
    """矩阵的反转"""
    return  [row[::-1] for row in field]

def is_move_right(field):
    # 对棋盘的每一行元素进行反转;
    invertField = invert(field)
    return  is_move_left(invertField)

if __name__ == "__main__":
    try:
        assert is_move_right([[0, 0, 0, 0], 
                              [0, 2, 0, 0], 
                              [0, 0, 0, 0], 
                              [0, 0, 0, 4]]) == True, "棋盘向右移动失败"
    except AssertionError as e:
        print(e)
    else:
        print("测试用例完成.....")
测试用例完成.....

4)判断棋盘是否可以向上移动

def transpose(field):
    """实现矩阵的转置"""
    # zip: 实现
    # *field对列表进行解包;
    return  [move_row_left(row) for row in field]

def is_move_up(field):
    # 对棋盘的每一行元素进行转置;
    transposeField = transpose(field)
    return  is_move_left( transposeField)

if __name__ == "__main__":
    try:
        assert is_move_up([[0, 2, 0, 0],
                            [0, 2, 0, 0],
                            [0, 4, 0, 0],
                            [0, 4, 0, 0]]) == True, "棋盘向上移动失败"
    except AssertionError as e:
        print(e)
    else:
        print("测试用例完成.....")
测试用例完成.....

5)判断棋盘是否可以向下移动

def is_move_down(field):
    # 判断能否向下移动, 也就是对于元素进行转置, 判断转置后的棋盘能否向右移动;
    # 对棋盘的每一行元素进行反转;
    transposeField = transpose(field)
    return is_move_right(transposeField)

if __name__ == "__main__":
    try:
        assert is_move_down([[0, 2, 0, 0],
                            [0, 2, 0, 0],
                            [0, 4, 0, 0],
                            [0, 4, 0, 0]]) == True, "棋盘向下移动失败"
    except AssertionError as e:
        print(e)
    else:
        print("测试用例完成.....")
测试用例完成.....

6)实现棋盘向左移动

# 棋盘向左移动?
#  1). 只要棋盘的每一行可以向左移动, e;
#          目标: 如何让棋盘的每一行向左移动?
#           [2, 2, 2, 2] === [4, 4, 0 ,0 ]
#           讨论:
#               1). 先把这一行的非0 数字向前放, 0向后放;   ==== [2, 2, 2, 2]
#               2). 依次循环判断两个数是否相等, 如果相等, 第一个*2, 第二个数为0;  【4, 0, 4, 0】
#               3).先把这一行的非0数字向前放, 0向后放;[4, 4, 0 ,0 ]
score = 0
def invert(field):
    """矩阵的反转"""
    return  [row[::-1] for row in field]

def transpose(field):
    """实现矩阵的转置"""
    # zip: 实现
    # *field对列表进行解包;
    return  list(zip(*field))


# 1). 先把这一行的非0 数字向前放, 0向后放;   ==== [2, 2, 2, 2]
def tight(row):  # [2,0,2,0]
    return sorted(row, key=lambda x: 1 if x == 0 else 0)


# 2). 依次循环判断两个数是否相等, 如果相等, 第一个*2, 第二个数为0;  【4, 0, 4, 0】
def merge(row):
    for index in range(3):
        if row[index] == row[index + 1]:
            row[index] *= 2
            row[index + 1] = 0
            # 如果合并完成, 分数增加row[index]
            global score
            score += row[index]
    return row


def move_row_left(row):
    return tight(merge(tight(row)))

def move_left(field):
    return  [move_row_left(row) for row in field]

def move_right(field):
    field = invert(field)
    return  invert(move_left(field))

def move_up(field):
    field = transpose(field)
    return  transpose(move_left(field))


def move_down(field):
    field = transpose(field)
    return transpose(move_right(field))

if __name__ == '__main__':
    print(move_right([[0, 0, 0, 0], [0, 2, 0, 0], [0, 0, 0, 0], [0, 0, 0, 4]]))
    print(move_right([[0, 0, 2, 2], [0, 2, 4, 4], [0, 2, 0, 0], [2, 2, 0, 4]]))
    print(move_up([[0, 0, 0, 0], [0, 2, 0, 0], [0, 0, 0, 0], [0, 0, 0, 4]]))
    print(move_up([[0, 0, 2, 2], [0, 2, 4, 4], [0, 2, 0, 0], [2, 2, 0, 4]]))
    print(move_down([[0, 0, 2, 2], [0, 2, 4, 4], [0, 2, 0, 0], [2, 2, 0, 4]]))
[[0, 0, 0, 0], [0, 0, 0, 2], [0, 0, 0, 0], [0, 0, 0, 4]]
[[0, 0, 0, 4], [0, 0, 2, 8], [0, 0, 0, 2], [0, 0, 4, 4]]
[(0, 2, 0, 4), (0, 0, 0, 0), (0, 0, 0, 0), (0, 0, 0, 0)]
[(2, 4, 2, 2), (0, 2, 4, 8), (0, 0, 0, 0), (0, 0, 0, 0)]
[(0, 0, 0, 0), (0, 0, 0, 0), (0, 2, 2, 2), (2, 4, 4, 8)]

7)游戏结束条件

# 1. 何时用户游戏胜利?(当棋盘中出现num=2048时, 则代表用户胜利)
from itertools import  chain
field = [[2048, 0, 0, 0], [0, 0, 0, 0], [0, 0, 4, 0], [0, 0, 0, 4]]

def is_win(field):
    """
    list(chain(*[[2048, 0, 0, 0], [0, 0, 0, 0], [0, 0, 4, 0], [0, 0, 0, 4]]))
    [2048, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4]
    :param field:
    :return:
    """
    # 遍历所有的元素, 有一个元素大于等于2048, 即游戏胜利!
    # for row in field:
    #     for item in row:
    #         if item >= 2048:
    #             return  True
    # else:
    #     return  False
    return  max(chain(*field) >= 2048)
print(is_win(field))
True
# 2. 何时game over?(当用户在任何方向都不能移动时, 则代表游戏结束, 用户失败)
# 只要有任意一个方向可以移动, 那就没有结束
def is_over():
    return  not any([is_move_left(field),is_move_right(field),
                     is_move_up(field),is_move_down(field), ])
True
import curses
import random
from itertools import chain


class GameField(object):
    def __init__(self, width=4, height=4, win_score=2048):
        self.width = width
        self.height = height
        self.score = 0  # 当前得分
        self.highscore = 0  # 最高分
        self.win_score = win_score
        # 存储判断各个方向是否可移动的函数?
        self.moves = {}
        self.moves['Left'] = self.is_move_left
        self.moves['Right'] = self.is_move_right
        self.moves['Up'] = self.is_move_up
        self.moves['Down'] = self.is_move_down
        #  # 存储执行各个方向移动的函数?
        self.movesDict = {}
        self.movesDict['Left'] = self.move_left
        self.movesDict['Right'] = self.move_right
        self.movesDict['Up'] = self.move_up
        self.movesDict['Down'] = self.move_down

    # 重置棋盘, 重新开始游戏时, 执行的操作;
    def reset(self):
        # 2). 是否要更新最高分, 当前分数为0;
        if self.score > self.highscore:
            self.highscore = self.score
        self.score = 0

        # 3). 创建棋盘的数据, 默认情况下时4*4, 数值全为0;
        self.field = [[0 for j in range(self.height)] for i in range(self.width)]

        self.random_create()
        self.random_create()

    # 开始游戏时, 棋盘数据会随机生成2或者4,
    def random_create(self):
        # field[0][2] = 2
        while True:
            firstIndex = random.choice(range(self.height))
            secondIndex = random.choice(range(self.width))
            if self.field[firstIndex][secondIndex] == 0:
                value = random.choice([2, 4, 2, 2, 2])
                self.field[firstIndex][secondIndex] = value
                break

    # 画棋盘
    def draw(self, stdstr):
        def draw_sep():
            # print("+-----" * 4 + '+')
            stdstr.addstr("+-----" * self.width + '+' + '\n')

        # 2. 画每一行的格子
        def draw_one_row(row):  # [0, 2, 0, 0]   |    |  2  |    |    |
            stdstr.addstr("".join(['|  %d  ' % (item) if item != 0 else '|     ' for item in row]) + '|\n')

        stdstr.clear()
        stdstr.addstr(''.center(42, "*")+'\n')
        stdstr.addstr('2048游戏'.center(40, '*')+'\n')
        stdstr.addstr(''.center(42, '*')+'\n')
        # 3. 绘制棋盘
        for row in self.field:
            draw_sep()
            draw_one_row(row)
        draw_sep()
        stdstr.addstr("\n当前分数: %s" % (self.score))
        stdstr.addstr("\n当前最高分数: %s" % (self.highscore))
        stdstr.addstr(" \n游戏帮助: 上下左右键  (R)estart E(xit) ")
        if self.is_win():
            stdstr.addstr("\n游戏胜利\n")
        if self.is_gameover():
            stdstr.addstr("游戏失败\n")

    def is_win(self):
        # return max(chain(*self.field)) >= 2048
        return max(chain(*self.field)) >= self.win_score

    def is_gameover(self):
        return not any([self.is_move_left(self.field),
                        self.is_move_right(self.field),
                        self.is_move_up(self.field),
                        self.is_move_down(self.field)])


    def is_row_left(self, row):  # [0, 2,2,0]
        # 任意两个元素可以向左移动?
        def is_change(index):  # index时索引值, [0,1,2,3]
            # - 如果第一个数值为0, 第二个数值不为0, 则说明可以向左移动;
            if row[index] == 0 and row[index + 1] != 0:
                return True
            # - 如果第一个数值不为0, 第二个数值与第一个元素相等, 则说明可以向左移动;
            if row[index] != 0 and row[index + 1] == row[index]:
                return True
            return False

        # 只要这一行的任意两个元素可以向左移动, 则返回True;
        return any([is_change(index) for index in range(self.width-1)])



    def invert(self, field):
        """矩阵的反转"""
        return [row[::-1] for row in field]


    def transpose(self, field):
        """实现矩阵的转置"""
        # zip: 实现
        # *field对列表进行解包;
        return [ list(row) for row in zip(*field)]



    def is_move_left(self, field):
        # 只要棋盘的任意一行可以向左移动, 就返回True;
        return any([self.is_row_left(row) for row in field])
    def is_move_right(self, field):
        # 对棋盘的每一行元素进行反转;
        invertField = self.invert(field)
        return  self.is_move_left(invertField)

    def is_move_up(self, field):
        # 对棋盘的每一行元素进行转置;
        transposeField = self.transpose(field)
        return  self.is_move_left( transposeField)

    def is_move_down(self, field):
        # 判断能否向下移动, 也就是对于元素进行转置, 判断转置后的棋盘能否向右移动;
        # 对棋盘的每一行元素进行反转;
        transposeField = self.transpose(field)
        return self.is_move_right(transposeField)

    # 1). 先把这一行的非0 数字向前放, 0向后放;   ==== [2, 2, 2, 2]
    def tight(self, row):  # [2,0,2,0]
        return sorted(row, key=lambda x: 1 if x == 0 else 0)

    # 2). 依次循环判断两个数是否相等, 如果相等, 第一个*2, 第二个数为0;  【4, 0, 4, 0】
    def merge(self, row):
        for index in range(self.width-1):
            if row[index] == row[index + 1]:
                row[index] *= 2
                row[index + 1] = 0
                # 如果合并完成, 分数增加row[index]
                self.score += row[index]
        return row

    def move_row_left(self, row):
        return self.tight(self.merge(self.tight(row)))

    def move_left(self, field):
         return [self.move_row_left(row) for row in field]

    def move_right(self, field):
        field = self.invert(field)
        return self.invert(self.move_left(field))

    def move_up(self, field):
        field = self.transpose(field)
        return self.transpose(self.move_left(field))

    def move_down(self, field):
        field = self.transpose(field)
        return self.transpose(self.move_right(field))


    def move(self, direction):
        # 1). 判断这个方向是否可以移动?
        # 2). 执行移动的操作
        # 3). 再随机生成一个2或者4

        # 确保是上下左右的按键
        if direction in self.moves:
            # 1).判断这个方向是否可以移动?
            if self.moves[direction](self.field):
                self.field = self.movesDict[direction](self.field)
                self.random_create()
        else:
            return False


def get_user_action(stdstr):
    # 获取用户键盘输入的内容
    action = stdstr.getch()
    if action == curses.KEY_UP:
        return 'Up'
    if action == curses.KEY_DOWN:
        return 'Down'
    if action == curses.KEY_LEFT:
        # stdstr.addstr("left")
        return 'Left'
    if action == curses.KEY_RIGHT:
        # stdstr.addstr("right")
        return 'Right'
    # 获取字母r的ASCII码
    if action == ord('r'):
        # stdstr.addstr("重新开始")
        return 'Restart'
    if action == ord('e'):
        # stdstr.addstr("退  出")
        return 'Exit'


def main(stdstr):
    game_field = GameField(width=4, height=4, win_score=2048)

    def init():
        game_field.reset()
        game_field.draw(stdstr)
        return 'Game'

    def game():
        # 重新绘制棋盘
        game_field.draw(stdstr)
        action = get_user_action(stdstr)
        if action == 'Restart':
            return 'Init'
        if action == 'Exit':
            return 'Exit'
        if game_field.move(action):
            # move函数
            if game_field.is_win():
                return 'Win'
            if game_field.is_gameover():
                return 'Gameover'
        return 'Game'

    def not_game():
        action = get_user_action(stdstr)
        if action == 'Restart':
            return 'Init'
        if action == 'Exit':
            return 'Exit'

    state = 'Init'

    state_dict = {
        'Init': init,
        'Game': game,
        'Win':not_game,
        'Gameover':not_game,
        'Exit': exit
    }

    while True:
        state = state_dict[state]()


        # if state == 'Init':
        #     # 通过初始化函数, 进入游戏状态;
        #     state = init()
        # if state == 'Game':
        #     # 执行game函数, 判断用户的操作;
        #     # 1). 继续游戏; (upo, down, left, right)
        #     # 2). 重新开始;(R)
        #     # 3). 退出;(Q)
        #
        #     state = game()
        #
        # if state == 'Win':
        #     # 没有进行游戏, 只有两种状态;
        #     # 1). 重新开始;(R)
        #     # 2). 退出;(Q)
        #     state = not_game()
        # if state == 'Gameover':
        #     state = not_game()
        # if state == 'Exit':
        #     exit()

curses.wrapper(main)
# 注意:pycharm不能接收键盘输入,必须要用脚本运行
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+
|     |     |     |     |
+-----+-----+-----+-----+
|  2  |  2  |     |     |
+-----+-----+-----+-----+

当前分数: 0
当前最高分数: 0
游戏帮助: 上下左右键  (R)estart Q(uit) 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值