zoukankan      html  css  js  c++  java
  • python小练习——2048

    今天跟着实验楼的python小项目走了一遍2048小游戏,感觉收获颇丰,

    在这里分享一下代码及遇到问题及感悟

    首先是跟着实验楼在linux上敲了一遍整体的代码

    没有pycharm的自动补全,还得自己注意PEP-8规范,着实写的很累,但很爽

    直接在linux记事本上敲代码,难免语法错误频发,其实这都是很不应该的。

    在后期调试时也都一一修改并反思了

    这个小项目代码不多,适合现阶段去练习,十分推荐,强烈安利

    里面涉及到的知识95%都是学过的,只有些许未涉及,但都实验楼代码中都有注释可以参考

    1.未涉及知识补充

    curse模块:用来在终端上显示图形界面

    collection模块中的defaultdict:提供了一个字典的子类defaultdict。可以指定key值不存在时,value的默认值

    any()函数:用于判断给定的可迭代参数 iterable 是否全部为 False,则返回 False,如果有一个为 True,则返回 True。

     

    2.代码分享

    以下是实现的代码

    import curses
    from random import randrange, choice
    from collections import defaultdict
    letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
    actions = ['Up', 'Left', 'Down', 'Right', 'Restart', 'Exit']
    actions_dict = dict(zip(letter_codes, actions * 2))
    
    def get_user_action(keyboard):
        char = "N"
        while char not in actions_dict:
            char = keyboard.getch()
        return actions_dict[char]
    
    
    def transpose(field):
        return [list(row) for row in zip(*field)]
    
    
    def invert(field):
        return [row[::-1] for row in field]
    
    class GameField(object):
        def __init__(self, height=4, width=4, win=2048):
            self.height = height
            self.width = width
            self.win_value = 2048
            self.score = 0
            self.highscore = 0
            self.reset()
    
    
        def spawn(self):
            new_element = 4 if randrange(100) > 89 else 2
            (i, j) = choice([(i, j) for i in range(self.width) for j in range(self.height) if self.field[i][j] == 0])
            self.field[i][j] = new_element
    
        def reset(self):
            if self.score > self.highscore:
                self.highscore = self.score
            self.score = 0
            self.field = [[0 for i in range(self.width)] for j in range(self.height)]
            self.spawn()
            self.spawn()
        def move(self,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(len(row) - 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])
                            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
                return tighten(merge(tighten(row)))
    
            moves = {}
            moves['Left'] = lambda field: [move_row_left(row) for row in field]
            moves['Right'] = lambda field: invert(moves['Left'](invert(field)))
            moves['Up'] = lambda field: transpose(moves['Left'](transpose(field)))
            moves['Down'] = lambda field: transpose(moves['Right'](transpose(field)))
    
            if direction in moves:
                if self.move_is_possible(direction):
                    self.field = moves[direction](self.field)
                    self.spawn()
                    return True
                else:
                    return False
    
        def is_win(self):
            return any(any(i >= self.win_value for i in row) for row in self.field)
    
        def is_gameover(self):
            return not any(self.move_is_possible(move) for move in actions)
    
        def move_is_possible(self, direction):
            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))
    
            check = {}
            check['Left'] = lambda field: any(row_is_left_movable(row) for row in field)
            check['Right'] = lambda field: check['Left'](invert(field))
            check['Up'] = lambda field: check['Left'](transpose(field))
            check['Down'] = lambda field: check['Right'](transpose(field))
    
            if direction in check:
                return check[direction](self.field)
            else:
                return False
    
        def draw(self, screen):
            help_string1 = '(W)Up (S)Down (A)Left (D)Right'
            help_string2 = '     (R)Restart (Q)Exit      '
            gameover_string = '           GAME OVER      '
            win_string = '          YOU WIN!       '
    
            def cast(string):
                screen.addstr(string + '
    ')
    
    
            def draw_hor_separator():
                line = '+' + ('+------' * self.width + '+')[1:]
                cast(line)
    
    
            def draw_row(row):
                cast(''.join('|{: ^5} '.format(num) if num > 0 else '|      ' for num in row) + '|')
    
            screen.clear()
            cast('SCORE: ' + str(self.score))
            if 0 != self.highscore:
                cast('HIGHSCORE: ' + str(self.highscore))
    
            for row in self.field:
                draw_hor_separator()
                draw_row(row)
            draw_hor_separator()
    
    
            if self.is_win():
                cast(win_string)
            else:
                if self.is_gameover():
                    cast(gameover_string)
                else:
                    cast(help_string1)
            cast(help_string2)
    
    def main(stdscr):
        def init():
            game_field.reset()
            return 'Game'
    
        def not_game(state):
            game_field.draw(stdscr)
            action = get_user_action(stdscr)
            responses = defaultdict(lambda: state)
            responses['Restart'], responses['Exit'] = 'Init', 'Exit'
            return responses[action]
    
        def game():
            game_field.draw(stdscr)
            action = get_user_action(stdscr)
    
            if action == 'Restart':
                return 'Init'
            if action == 'Exit':
                return 'Exit'
            if game_field.move(action):  # move successful
                if game_field.is_win():
                    return 'Win'
                if game_field.is_gameover():
                    return 'Gameover'
            return 'Game'
    
    
        state_actions = {
                'Init': init,
                'Win': lambda: not_game('Win'),
                'Gameover': lambda: not_game('Gameover'),
                'Game': game
            }
        curses.use_default_colors()
    
        game_field = GameField(win=2048)
    
    
        state = 'Init'
    
    
    

        while state != 'Exit':
            state = state_actions[state]()
    
    curses.wrapper(main)

    3.调试结果

    由于是在linux记事本下直接敲代码,没有pycharm的自动补全和时时提醒错误标黄,语法错误犯了很多

    在调试中才会发现,这其实是很不应该的

    在对应文件夹下 python3 2048.py

    然后开始错误频发,不停的回去调试

    调试完成并运行成功

     

    在终端操作通关游戏

    同样 又在pycharm上敲了一遍 并在mac的终端上运行了

     实验楼python2048地址:https://www.shiyanlou.com/courses/368

    后面还有附上一份实验楼的用面向对象重构的代码一份

    # -*- coding: utf-8 -*-
    import random
    import curses
    from itertools import chain
    
    
    class Action(object):
    
        UP = 'up'
        LEFT = 'left'
        DOWN = 'down'
        RIGHT = 'right'
        RESTART = 'restart'
        EXIT = 'exit'
    
        letter_codes = [ord(ch) for ch in 'WASDRQwasdrq']
        actions = [UP, LEFT, DOWN, RIGHT, RESTART, EXIT]
        actions_dict = dict(zip(letter_codes, actions * 2))
    
        def __init__(self, stdscr):
            self.stdscr = stdscr
    
        def get(self):
            char = "N"
            while char not in self.actions_dict:
                char = self.stdscr.getch()
            return self.actions_dict[char]
    
    
    class Grid(object):
    
        def __init__(self, size):
            self.size = size
            self.cells = None
            self.reset()
    
        def reset(self):
            self.cells = [[0 for i in range(self.size)] for j in range(self.size)]
            self.add_random_item()
            self.add_random_item()
    
        def add_random_item(self):
            empty_cells = [(i, j) for i in range(self.size) for j in range(self.size) if self.cells[i][j] == 0]
            (i, j) = random.choice(empty_cells)
            self.cells[i][j] = 4 if random.randrange(100) >= 90 else 2
    
        def transpose(self):
            self.cells = [list(row) for row in zip(*self.cells)]
    
        def invert(self):
            self.cells = [row[::-1] for row in self.cells]
    
        @staticmethod
        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(len(row) - 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])
                        # 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
            return tighten(merge(tighten(row)))
    
        def move_left(self):
            self.cells = [self.move_row_left(row) for row in self.cells]
    
        def move_right(self):
            self.invert()
            self.move_left()
            self.invert()
    
        def move_up(self):
            self.transpose()
            self.move_left()
            self.transpose()
    
        def move_down(self):
            self.transpose()
            self.move_right()
            self.transpose()
    
        @staticmethod
        def row_can_move_left(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))
    
        def can_move_left(self):
            return any(self.row_can_move_left(row) for row in self.cells)
    
        def can_move_right(self):
            self.invert()
            can = self.can_move_left()
            self.invert()
            return can
    
        def can_move_up(self):
            self.transpose()
            can = self.can_move_left()
            self.transpose()
            return can
    
        def can_move_down(self):
            self.transpose()
            can = self.can_move_right()
            self.transpose()
            return can
    
    
    class Screen(object):
    
        help_string1 = '(W)up (S)down (A)left (D)right'
        help_string2 = '     (R)Restart (Q)Exit'
        over_string = '           GAME OVER'
        win_string = '          YOU WIN!'
    
        def __init__(self, screen=None, grid=None, score=0, best_score=0, over=False, win=False):
            self.grid = grid
            self.score = score
            self.over = over
            self.win = win
            self.screen = screen
            self.counter = 0
    
        def cast(self, string):
            self.screen.addstr(string + '
    ')
    
        def draw_row(self, row):
            self.cast(''.join('|{: ^5}'.format(num) if num > 0 else '|     ' for num in row) + '|')
    
        def draw(self):
            self.screen.clear()
            self.cast('SCORE: ' + str(self.score))
            for row in self.grid.cells:
                self.cast('+-----' * self.grid.size + '+')
                self.draw_row(row)
            self.cast('+-----' * self.grid.size + '+')
    
            if self.win:
                self.cast(self.win_string)
            else:
                if self.over:
                    self.cast(self.over_string)
                else:
                    self.cast(self.help_string1)
    
            self.cast(self.help_string2)
    
    
    class GameManager(object):
    
        def __init__(self, size=4, win_num=2048):
            self.size = size
            self.win_num = win_num
            self.reset()
    
        def reset(self):
            self.state = 'init'
            self.win = False
            self.over = False
            self.score = 0
            self.grid = Grid(self.size)
            self.grid.reset()
    
        @property
        def screen(self):
            return Screen(screen=self.stdscr, score=self.score, grid=self.grid, win=self.win, over=self.over)
    
        def move(self, direction):
            if self.can_move(direction):
                getattr(self.grid, 'move_' + direction)()
                self.grid.add_random_item()
                return True
            else:
                return False
    
        @property
        def is_win(self):
            self.win = max(chain(*self.grid.cells)) >= self.win_num
            return self.win
    
        @property
        def is_over(self):
            self.over = not any(self.can_move(move) for move in self.action.actions)
            return self.over
    
        def can_move(self, direction):
            return getattr(self.grid, 'can_move_' + direction)()
    
        def state_init(self):
            self.reset()
            return 'game'
    
        def state_game(self):
            self.screen.draw()
            action = self.action.get()
    
            if action == Action.RESTART:
                return 'init'
            if action == Action.EXIT:
                return 'exit'
            if self.move(action):
                if self.is_win:
                    return 'win'
                if self.is_over:
                    return 'over'
            return 'game'
    
        def _restart_or_exit(self):
            self.screen.draw()
            return 'init' if self.action.get() == Action.RESTART else 'exit'
    
        def state_win(self):
            return self._restart_or_exit()
    
        def state_over(self):
            return self._restart_or_exit()
    
        def __call__(self, stdscr):
            curses.use_default_colors()
            self.stdscr = stdscr
            self.action = Action(stdscr)
            while self.state != 'exit':
                self.state = getattr(self, 'state_' + self.state)()
    
    
    if __name__ == '__main__':
        curses.wrapper(GameManager())
  • 相关阅读:
    jquery的$.与$.fn的区别
    jquery加载页面的方法
    创业股权究竟如何分配--新浪创业训练营
    创业者要处理好的10大关系
    洪泰基金投资经理殷鹏:肯定不投的八类项目
    创业初期股权如何分配-------陈楠心血总结
    排序总结
    如何快速进入一门领域,学习新的知识
    虚拟机的设置
    华为大数据项目fusionInsight
  • 原文地址:https://www.cnblogs.com/heirenxilou/p/12776237.html
Copyright © 2011-2022 走看看