python2048游戏代码分析&知识点总结

代码分析

状态机:

共分为四个状态:init,game, not game(win, gameover), exit(退出程序)

  • Init:

    1. game_field.reset()
    2. 返回game状态
  • Game:

    1. game_field.draw(stdscr)

    2. action = get_user_action(stdscr) 获取输入

    3. 根据action选择返回对应的状态

    4. 如果是restart就返回init

    5. exit则返回exit

    6. 根据action移动,再判断是赢是输返回对应状态

    7. 都不是,则返回game状态

  • Notgame:

    1. game_field.draw(stdscr)
    2. action = get_user_action(stdscr)
    3. 根据action选择返回对应的状态

主要操作:

主要操作为draw,move,get_user_action,is_win,is_gameover,主要都集成在game_field类里面

game_field:
  • init:height, width, win_value, high_score, score, reset()(生成初始的field)
  • Reset:
    1. 保留了最高分
    2. 将分数重置为0
    3. field数组重置为0
    4. 随机生成了2个位置的2或4
  • Draw:
    1. 清屏&输出当前分数&(如果最高分不为0,输出最高分)
    2. 根据field数组画图(draw_hor_separator(), draw_row(row))
    3. 判断是赢还是输,输出对应语句(is_win(),is_gameover())
  • Move:
    1. 定义了一个move[direction]的函数字典(move_row_left(row))
    2. Move_row_left(row): 定义了两个函数tighten()和merge()。tighten将非零元素删除,merge将相同元素相加。最后返回tighten(merge(tighten(row)))
    3. 判断是否可以移动,可以的话就移动,修改field并且返回True,否则返回False。(is_move_possible())
  • is_win&is_gameover:判断是否达到win_value|任意方向都不可移动
  • Spawn():在0的位置随机生成2或4
    1. 随机生成2或4
    2. 遍历field找到0的位置
    3. 随机选择0位置赋值随机数
Get_user_action():

获取用户输入,转换成right、left、up、down、quit和restart操作。

自己代码与原始代码的对比:

  • 两个对数组做转秩操作的函数其中的左右倒序:

    def reverse(array):
        return [list(reversed(row)) for row in array] # reversed(row)返回迭代器list_reverseiterator object 
        # return [row[::-1] for row in array]

    利用reversed函数返回的是迭代器类型,后续对转秩数组操作如果采用下标索引会出错。并且迭代器在使用一次list(reversed(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

    用了两层if判断,一是与前一个元素相同,新数组写进两者之和,计分;二是与前一元素不同,再判断。如果与下一元素相同,新数组写0,修改标签;否则新数组写入当前元素。

    #我的代码:
    def merge(row): 
        flag=False
        new_row=[]
        for i in range(len(row)):
            try:
                if flag:
                    new_row[i-1]=0
                    new_row.append(row[i]*2)
                    self.score+=row[i]*2
                    flag=False
                    else:
                        flag=True if row[i]==row[i+1] else False
                        new_row.append(row[i])
                        except:
                            new_row.append(row[i])
                            return new_row

    一层if判断,区别在于与不考虑是否与后一元素是否相等,先写入原始元素并且判断,等到遍历到下一元素时再修改前一元素。

  • 判断赢了没:

    def is_win(self):
        return any([self.field[i][j]>= self.win_value for i in range(self.height) for j in range(self.width)])
    	# return any(any(i >= self.win_value for i in row) for row in self.field)

    要习惯用迭代值来遍历list

  • 画分隔符():

    def draw_hor_separator():
        line = '+' + ('+------' * self.width + '+')[1:]
        separator = defaultdict(lambda: line)
        if not hasattr(draw_hor_separator, "counter"):
            draw_hor_separator.counter = 0
            cast(separator[draw_hor_separator.counter])
            draw_hor_separator.counter += 1

    这里使用了一种[函数名].[变量名]的格式声明并引用变量counter,这种格式它意味着函数本身也是对象,也可以像其他对象一样拥有属性;在这种情况下,可能的目的是拥有一些其他语言所称的"静态"变量——一个对函数本身存在全局性的变量,而不是一个单独的调用。可以在外部调用。

    def example():
        pass
    example.foo ="hello"
    print(example.foo) 

    作者的意思大概是生成一个默认字典defauldict,每次修改counter值作为默认字典的输入,然后输出一行分隔符。但是好像没啥必要?反正最后都是要在循环中调用n次,为什么不直接输出一行分隔符呢?我的代码直接输出目前没发现什么问题。

  • 判断当前方向是否可移动中的某一行是否可移动:

    #原始代码
    def row_is_left_movable(row):
        def change(i):  # true if there'll be change in i-th tile
            if row[i] == 0 and row[i + 1] != 0:  # Move
                return True
            if row[i] != 0 and row[i + 1] == row[i]:  # Merge
                return True
            return False
        return any(change(i) for i in range(len(row) - 1))

    判别单个元素是否具备可移动的特征,但是其实可以写成某一行是否具备可移动特征。

    #我的代码
    def is_row_movable(row):
        if 0 in row:
            return True
        for i in range(self.width-1):
            if row[i]==row[i+1]:
                return True
            return False

    不用反复调用我觉得还挺简单的23333

  • 原始代码中move操作有一个返回值,是否移动成功。加了这个返回值就可以省略主程序中调用is_move_possible函数进行判断。

  • 原始代码中not_game在判断是win还是gameover时使用了默认字典。默认是当前状态,如果没有行为就会一直在当前界面循环。我是用if…elif…else实现的。感觉默认字典看起来要厉害一些233333

学到的知识点:

  • Python_函数做字典的值 :当需要用到3个及以上的if…elif…else时就要考虑该方法进行简化,通过将函数名称当做字典的值,利用字典的关键字查询,可以快速定位函数,然后执行

  • 默认字典:

    访问字典中某个‘键’时,若键不存在则会报错。解决方法有两种:字典自带的setdefault函数和collections模块的defaultdict函数。

    • 其中setdefault能为某一个键设置默认值,再次赋值可以被修改。

    • defaultdict可以为所有不存在的key设置一个默认值。
      defaultdict的构造函数接受一个工厂函数作为参数。一个类型名可以作为一个工厂函数,例如collections.defaultdict(int)默认返回0;collections.defaultdict(tuple)默认返回(); collections.defaultdict(str)默认返回空字符串。

      该函数在生成默认值时进行无参调用。因此如果使用类型名作为工厂函数,该类型名一定要具有无参构造函数。

      class Test:
          def __init__(self): #无参构造函数
              print("testing")
      dd = collections.defaultdict(Test) #使用自定义类型Test作为工厂函数
      print(dd['a']) #运行到这里输出testing
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 推箱子游戏是一种经典的智力游戏,在该游戏中,玩家需要通过移动箱子,将所有的箱子推到指定位置,才能完成游戏。而Python语言正是一种非常适合用来编写推箱子代码的工具。 在编写推箱子代码时,我们首先需要定义地图,地图可以使用一个二维数组来表示。在这个地图上,我们需要定义一些特殊的符号,比如墙、箱子、目的地等等。然后,我们需要检测玩家移动的有效性,如果玩家移动的位置被障碍物挡住了,那么就不能继续移动。 在推箱子游戏中,我们还需要定义一个状态:即哪些箱子已经被推到了目标位置,哪些还没有。当所有的箱子都被推到目标位置时,游戏就成功了。 在编写推箱子代码时,我们还需要考虑一些附加功能,比如计算玩家的分数、记录操作历史等等。这些功能可以通过Python的函数和类来实现。 总之,Python是一种非常强大的编程语言,它可以帮助我们实现各种复杂的程序,包括推箱子游戏。如果你想要学习如何编写推箱子代码,那么Python是一个非常不错的选择。 ### 回答2: 推箱子是一款非常经典的休闲益智游戏,通过推动箱子到目标点中来过关,非常考验玩家的思维能力和耐心。Python是目前应用广泛的一门编程语言,可以用来设计推箱子代码。 推箱子游戏的基本思路是将地图分为目标点、箱子、墙壁和角色四种元素,在控制角色移动的过程中将箱子推向目标点即可。Python可以通过图形界面库Tkinter来制作可视化的游戏界面,同时使用数组来保存游戏地图和箱子位置等数据。 对于Python设计推箱子代码的具体步骤,可以按照以下流程进行: 1. 设计游戏地图,使用二维数组(列表)保存地图信息,其中0表示空地,1表示墙壁,2表示目标点,3表示箱子,4表示角色。 2. 使用Tkinter库绘制游戏图形界面,并在界面上显示游戏地图。 3. 设计角色移动函数,根据玩家输入的方向和当前角色位置,计算目标点的位置并更新角色位置。 4. 判断移动后的位置是否与箱子重叠,如果是则根据箱子方向继续移动箱子。 5. 判断游戏胜利条件,即所有箱子都被推到目标点后游戏结束。 6. 加入游戏音效和背景音乐等元素,提高游戏体验度。 Python设计推箱子代码需要具备良好的编程基础和逻辑思维能力,同时还需要对Tkinter等相关工具的使用有一定了解。只有通过不断的实践和总结,才能提高自身的编程水平和效率,实现优质的代码编写。 ### 回答3: 推箱子这个游戏代码实现其实并不难,只需要具备一些基本的 Python 编程知识即可。下面我将分步骤介绍如何用 Python 设计推箱子代码。 第一步:初始化场景。首先,我们需要定义一个地图,表示推箱子游戏中的场景。这可以使用二维数组来实现。具体而言,我们可以将地图中的空地用数字 0 表示,将箱子用数字 1 表示,将目标点用数字 2 表示,将人用数字 3 表示。在定义好地图后,我们需要用 Python 将场景呈现出来。 第二步:定义人的动作。接下来,我们需要定义人在推箱子游戏中可能的动作。人可以向上、下、左、右四个方向移动,当然,人的行动需要遵循一些规律,比如人不能移动到箱子或墙上,人只能推着箱子行走等。因此,我们需要根据这些规则实现人的动作。 第三步:定义箱子的动作。箱子是推箱子游戏中的重要元素,它有时候会被人推着动,有时候用于达到目标点。因此,箱子的动作和人的动作规则有些不同。箱子不能移动到墙或另一个箱子上,而且只有被推着动才能移动。 第四步:完成游戏。当人成功地将所有箱子都推到目标点上时,游戏即为胜利。因此,在程序中需要实现判断是否胜利的条件以及相应的提示。 以上是设计推箱子代码的主要步骤,当然,实践中你还需要考虑其他细节和异常处理。总的来说,Python 设计推箱子代码需要掌握一定的 Python 编程基础,而推箱子游戏的实现相对简单,适合 Python 初学者尝试。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值