用Python寫一個貪吃蛇


最近在學Python,想做點什么來練練手,命令行的貪吃蛇一般是C的練手項目,但是一時之間找不到別的,就先做個貪吃蛇來練練簡單的語法。

  由於Python監聽鍵盤很麻煩,沒有C語言的kbhit(),所以這條貪吃蛇不會自己動,運行效果如下:

  

要求:用#表示邊框,用*表示食物,o表示蛇的身體,O表示蛇頭,使用wsad來移動

Python版本:3.6.1

系統環境:Win10

類:

  board:棋盤,也就是游戲區域

  snake:貪吃蛇,通過記錄身體每個點來記錄蛇的狀態

  game:游戲類

  本來還想要個food類的,但是food只需要一個坐標,和一個新建,所以干脆使用list來保存坐標,新建food放在game里面,從邏輯上也沒有太大問題

源碼

# Write By Guobao
# 2017/4//7
#
# 貪吃蛇
# 用#做邊界,*做食物,o做身體和頭部
# python 3.6.1

import copy
import random
import os
import msvcrt

# the board class, used to put everything
class board:

    __points =[]

    def __init__(self):
        self.__points.clear()
        for i in range(22):
            line = []
            if i == 0 or i == 21:
                for j in range(22):
                    line.append('#')
            else:
                line.append('#')
                for j in range(20):
                    line.append(' ')
                line.append('#')
            self.__points.append(line)

    def getPoint(self, location):
        return self.__points[location[0]][location[1]]

    def clear(self):
        self.__points.clear()
        for i in range(22):
            line = []
            if i == 0 or i == 21:
                for j in range(22):
                    line.append('#')
            else:
                line.append('#')
                for j in range(20):
                    line.append(' ')
                line.append('#')
            self.__points.append(line)

    def put_snake(self, snake_locations):
        # clear the board
        self.clear()

        # put the snake points
        for x in snake_locations:
            self.__points[x[0]][x[1]] = 'o'

        # the head
        x = snake_locations[len(snake_locations) - 1]
        self.__points[x[0]][x[1]] = 'O'

    def put_food(self, food_location):
        self.__points[food_location[0]][food_location[1]] = '*'

    def show(self):
        os.system("cls")
        for i in range(22):
            for j in range(22):
                print(self.__points[i][j], end='')
            print()

# the snake class
class snake:
    __points = []

    def __init__(self):
        for i in range(1, 6):
            self.__points.append([1, i])

    def getPoints(self):
        return self.__points

    # move to the next position
    # give the next head
    def move(self, next_head):
        self.__points.pop(0)
        self.__points.append(next_head)

    # eat the food
    # give the next head
    def eat(self, next_head):
        self.__points.append(next_head)

    # calc the next state
    # and return the direction
    def next_head(self, direction='default'):

        # need to change the value, so copy it
        head = copy.deepcopy(self.__points[len(self.__points) - 1])

        # calc the "default" direction
        if direction == 'default':
            neck = self.__points[len(self.__points) - 2]
            if neck[0] > head[0]:
                direction = 'up'
            elif neck[0] < head[0]:
                direction = 'down'
            elif neck[1] > head[1]:
                direction = 'left'
            elif neck[1] < head[1]:
                direction = 'right'

        if direction == 'up':
            head[0] = head[0] - 1
        elif direction == 'down':
            head[0] = head[0] + 1
        elif direction == 'left':
            head[1] = head[1] - 1
        elif direction == 'right':
            head[1] = head[1] + 1
        return head

# the game
class game:

    board = board()
    snake = snake()
    food = []
    count = 0

    def __init__(self):
        self.new_food()
        self.board.clear()
        self.board.put_snake(self.snake.getPoints())
        self.board.put_food(self.food)

    def new_food(self):
        while 1:
            line = random.randint(1, 20)
            column = random.randint(1, 20)
            if self.board.getPoint([column, line]) == ' ':
                self.food = [column, line]
                return

    def show(self):
        self.board.clear()
        self.board.put_snake(self.snake.getPoints())
        self.board.put_food(self.food)
        self.board.show()


    def run(self):
        self.board.show()

        # the 'w a s d' are the directions
        operation_dict = {b'w': 'up', b'W': 'up', b's': 'down', b'S': 'down', b'a': 'left', b'A': 'left', b'd': 'right', b'D': 'right'}
        op = msvcrt.getch()

        while op != b'q':
            if op not in operation_dict:
                op = msvcrt.getch()
            else:
                new_head = self.snake.next_head(operation_dict[op])

                # get the food
                if self.board.getPoint(new_head) == '*':
                    self.snake.eat(new_head)
                    self.count = self.count + 1
                    if self.count >= 15:
                        self.show()
                        print("Good Job")
                        break
                    else:
                        self.new_food()
                        self.show()

                # 反向一Q日神仙
                elif new_head == self.snake.getPoints()[len(self.snake.getPoints()) - 2]:
                    pass

                # rush the wall
                elif self.board.getPoint(new_head) == '#' or self.board.getPoint(new_head) == 'o':
                    print('GG')
                    break

                # normal move
                else:
                    self.snake.move(new_head)
                    self.show()
            op = msvcrt.getch()

game().run()

筆記:

  1.Python 沒有Switch case語句,可以利用dirt來實現

  2.Python的=號是復制,復制引用,深復制需要使用copy的deepcopy()函數來實現

  3.即使在成員函數內,也需要使用self來訪問成員變量,這和C++、JAVA很不一樣

 

2017.4.11 更新

  完成了貪吃蛇之后,我開始打一個簡單的學生信息管理系統,內容簡單,數據量小,但是可以用上MVC架構,又可以更好的訓練。過程中發現自己在貪吃蛇中有一個致命的問題,雖然在貪吃蛇中,這個問題並不會影響結果。

  先看board類中的部分代碼:

 1 class board:
 2 
 3     __points =[]
4 # 后面不重要

咋一看沒有問題,但是看初始化__point的位置,並不是在__init__()中第一次初始化,這種情況下,__point是一個類變量,而不是一個成員變量,如果__point是一個不可變的類型(如整形),那可能不會看出什么影響,但是當它是一個可變的類型,如list,就會有很大的問題,看我在命令行上的試驗:

①整形例子

到此為止,和我一開始的想法沒有沖突,請看下面:

驚訝的發現可以直接通過類名來訪問到num變量,而且還一直保持着最初的值,還能變

②list例子

試驗的時候心理活動:嗯→很正常→就該這樣→卧槽???

這時候可以再來一句:

分析:整形例子中,當我修改num的值,其實是讓num指向了新的內存,所以會有c1,c2有着各自的num值;

而在list例子中,當我修改cc的值,修改的是cc指向的內存的值,cc一直指向同一個內存

個人理解:不論哪一個例子,都是在復制類變量,所以會有這種現象

 

貪吃蛇中,因為Snake和board我都只有一個實例,所以沒有明顯的問題。

與此相關可以看這篇博客:http://www.cnblogs.com/duanv/p/5947525.html

函數的默認參數中也有相似的問題,可以看:http://blog.jobbole.com/42706/#article-comment


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM