300+行代码实现2048小游戏

笔者作为python小白,2048小游戏无疑是非常适合笔者的练手小项目,因此花了两天时间用python复现了2048小游戏。本文主要总结一下笔者做2048的一点经验,提供关于实现2048小游戏的资料,如果有如同笔者这样的小白的后来者,希望本文能给你一些帮助。

资料方面,参考过蓝桥云课的“Python200行代码实现2048”(链接:https://www.lanqiao.cn/courses/368),但蓝桥云课的2048主要采用curses终端图形编程库编程,画面比较简陋,仅由点、线和数字组成,因此只参考了蓝桥云课的算法,在此基础上,笔者用pygame库进行编程,优化了游戏画面(pygame库的学习参考《python从入门到实践》这本书的“外星人大战”项目)。

笔者认为2048的难点只有两个,一是棋盘操作算法,即棋盘内的数字跟随方向键移动、合并,这个算法我参考了蓝桥云课的算法,但也仅仅是参考,并没有套用,我用了numpy库的ndarray数组和zeros函数,生成4x4的棋盘模,通过修改、添加和删减数组内的0来达到2048游戏的效果,顺便添加了一个颜色字典color_dict和精灵(Sprite)组来达到不同颜色配不同数字的效果,而把0的颜色设置成背景色就能“去除0”(这是我的偷懒做法)。

第二个难点是判定gameover,即游戏结束时程序不会崩溃,而会跳出gameover。这个算法笔者基本照搬了蓝桥云课的算法,只做了稍稍的修改,核心是使用any()函数返回bool值来判定游戏是否结束。

笔者初学python,经验不足,虽然主要逻辑比较清晰,但代码块显的有点乱,代码也有点冗杂,有想码2048的小伙伴在码前最好整理一下代码块,笔者码到最后悔不当初,恨自己在最开始没有创建一个控制游戏状态game_stats的类,导致在最后码控制游戏状态的代码时,一股脑塞给了其中一个类,看着有点乱。

蓝桥云课的2048:

 笔者的2048:

 

话不多说,接下来直接上代码,整个的主题逻辑代码:

import pygame
import sys
from settings import Settings
from pygame.sprite import Group
import function
from number_action import Number
from scoreboard import Scoreboard
from gameover_board import Gameover_board
from board import Board
from win_board import Win_board

def run_game():
    pygame.init()
    screen_set = Settings()
    screen = pygame.display.set_mode((screen_set.length,screen_set.width))
    pygame.display.set_caption("2048")
    rectangles = Group()
    gameover = Gameover_board(screen,screen_set)
    board = Board(screen,screen_set)
    scoreboard = Scoreboard(screen_set,screen)
    win = Win_board(screen,screen_set)
    number = Number(scoreboard,screen_set,win,board)
    function.creat_fleet(rectangles, screen, screen_set, number)

    while 1:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()

            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE and board.keyspace_active:
                    board.game_active = True
                    board.status = False
                if event.key == pygame.K_r:
                    number.reset(scoreboard, screen_set,win,board)
                if event.key == pygame.K_q:
                    sys.exit()
                elif board.game_active:
                    left,right,up,down = function.check_movable(number)
                    if event.key == pygame.K_a:
                        if any(left):
                            function.update_left(number, scoreboard, screen_set)
                    if event.key == pygame.K_d:
                        if any(right):
                            function.update_right(number, scoreboard, screen_set)
                    if event.key == pygame.K_w:
                        if any(up):
                            function.update_up(number, scoreboard, screen_set)
                    if event.key == pygame.K_s:
                        if any(down):
                            function.update_down(number, scoreboard, screen_set)


        rectangles.empty()
        function.creat_fleet(rectangles, screen, screen_set, number)
        screen.fill(screen_set.bg_color)
        function.draw_rectangles(rectangles,number)
        scoreboard.draw_scoreboard()
        function.show_board(board)
        function.show_win(win)
        function.check_win(number, win, board)
        function.check_gameover(number, gameover)
        pygame.display.flip()

run_game()

 function:

from pygame.sprite import Sprite
import numpy as np
from square import Square
def creat_fleet(rectangles,screen,screen_set,number):
    for i in range(4):
        for j in range(4):
            rectangle = Square(screen)
            rectangle.square_color = screen_set.color_dict[number.init_number[i*4+j]]
            rectangle.rect.left += j*60
            rectangle.rect.top += i*60
            rectangle.point_list = [(rectangle.rect.left, rectangle.rect.top),
                               (rectangle.rect.right, rectangle.rect.top),
                               (rectangle.rect.right, rectangle.rect.bottom),
                               (rectangle.rect.left, rectangle.rect.bottom),
                               (rectangle.rect.left, rectangle.rect.top)]
            rectangles.add(rectangle)
def draw_rectangles(rectangles,number):
    i= 0
    for rectangle in rectangles:
        rectangle.prep_number(str(int(number.init_number[i])))
        rectangle.draw_square()
        i+=1
def update_left(number,scoreboard,screen_set):
        update_row_left(number)
        merge_left(number, scoreboard, screen_set)
        update_row_left(number)
        number.spawn()
def update_right(number,scoreboard,screen_set):
        invert(number)
        update_left(number, scoreboard, screen_set)
        invert(number)
def update_up(number,scoreboard,screen_set):
        transpose(number)
        update_left(number, scoreboard, screen_set)
        transpose(number)
def update_down(number,scoreboard,screen_set):
        transpose(number)
        update_right(number, scoreboard, screen_set)
        transpose(number)
def update_row_left(number):
    change_number = number.init_number.reshape(4,4)
    new_number = []
    for row in change_number:
        new_row = [i for i in row if i != 0]
        for i in range(4 - len(new_row)):
            new_row.append(0)
        new_number.append(new_row)
    number.init_number = np.array(new_number).reshape(16)
def merge_left(number,scoreboard,screen_set):
    change_number = number.init_number.reshape(4,4)
    new_number =[]
    pair = False
    for row in change_number:
        for i in range(len(row)):
            if pair:
                new_number.append(2*row[i])
                pair = False
            else:
                if i+1<len(row) and row[i]==row[i+1]:
                    pair=True
                    scoreboard.score+= 2*row[i]
                    scoreboard.prep_score(screen_set)
                    new_number.append(0)
                else:
                    new_number.append(row[i])
    number.init_number = np.array(new_number).reshape(16)
def invert(number):
    change_number = number.init_number.reshape(4,4)
    new_number = [row[::-1] for row in change_number]
    number.init_number = np.array(new_number).reshape(16)
def transpose(number):
    change_number = number.init_number.reshape(4,4)
    new_number = np.array(change_number).transpose()
    number.init_number = new_number.reshape(16)
def check_left_movable(number,new_number):
    change_number = number.init_number.reshape(4, 4)
    for row in change_number:
        for i in range(len(row)):
            if row[i]==0 or (i + 1 < len(row) and row[i] == row[i + 1]):
                new_number.append(1)
def check_right_movable(number,new_number):
    invert(number)
    check_left_movable(number,new_number)
    invert(number)
def check_up_movable(number,new_number):
    transpose(number)
    check_left_movable(number,new_number)
    transpose(number)
def check_down_movable(number,new_number):
    transpose(number)
    check_right_movable(number,new_number)
    transpose(number)
def check_movable(number):
    new_number1,new_number2,new_number3,new_number4 = [],[],[],[]
    check_left_movable(number, new_number1)
    check_right_movable(number, new_number2)
    check_up_movable(number, new_number3)
    check_down_movable(number, new_number4)
    return new_number1,new_number2,new_number3,new_number4
def check_gameover(number,gameover):
    if not any(check_movable(number)):
        gameover.draw_gameover()
def show_board(board):
    if board.status:
        board.draw_board()
def check_win(number,win,board):
    for i in number.init_number:
        if i >=2048:
            win.status = True
            board.game_active = False
            board.keyspace_active = False

def show_win(win):
    if win.status:
        win.draw_win()

number_action:

import random
import numpy as np
class Number():
    def __init__(self,scoreboard,screen_set,win,board):
        self.reset(scoreboard,screen_set,win,board)
    def spawn(self):
        self.new_element = 4 if random.randint(0, 100) > 80 else 2
        i = random.choice([i for i in range(16) if self.init_number[i]==0])
        self.init_number[i] = self.new_element
    def reset(self,scoreboard,screen_set,win,board):
        scoreboard.score = 0
        scoreboard.prep_score(screen_set)
        self.init_number = np.zeros(16)
        self.spawn()
        self.spawn()
        win.status = False
        board.game_active = True
        board.keyspace_active = True

settings:

class Settings():
    def __init__(self):
        self.width = 400
        self.length = 600
        self.bg_color = (230,230,230)
        self.color_dict = {0: (255, 255, 255),
                           2: (208, 193, 198),
                           4: (234, 208, 209),
                           8: (177, 122, 125),
                           16: (150, 164, 139),
                           32: (134, 150, 167),
                           64: (122, 114, 129),
                           128: (160, 106, 80),
                           256: (149, 88, 57),
                           512: (150, 84, 84),
                           1024: (144, 59, 28),
                           2048: (81, 31, 30)}

square:

import pygame
from pygame.sprite import Sprite
class Square(Sprite):
    def __init__(self,screen):
        super().__init__()
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.length,self.width = 60,60
        self.square_color = (230,230,230)
        self.text_color = (255, 255, 255)
        self.rect = pygame.Rect(0,0,self.length,self.width)
        self.font_1 = pygame.font.SysFont(None,48)
        self.font_2 = pygame.font.SysFont(None,35)
        self.rect.left = (self.screen_rect.right-4*60)/2
        self.rect.top = (self.screen_rect.bottom-4*60)/2
        self.point_list = [(self.rect.left,self.rect.top),
                           (self.rect.right,self.rect.top),
                           (self.rect.right,self.rect.bottom),
                           (self.rect.left,self.rect.bottom),
                           (self.rect.left,self.rect.top)]

    def draw_square(self):
        pygame.draw.rect(self.screen,self.square_color,self.rect)
        pygame.draw.aalines(self.screen,(0,0,0),True,self.point_list,blend=1)
        self.screen.blit(self.image,self.image_rect)

    def prep_number(self,number):
        if int(number)<1024:
            self.image = self.font_1.render(number,True,self.text_color,self.square_color)
        else:
            self.image = self.font_2.render(number,True,self.text_color,self.square_color)
        self.image_rect = self.image.get_rect()
        self.image_rect.center = self.rect.center

scoreboard:

import pygame
class Scoreboard():
    def __init__(self,screen_set,screen):
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.score = 0
        self.font = pygame.font.SysFont(None,45)
        self.score_color = (0,0,0)
        self.prep_score(screen_set)
    def prep_score(self,screen_set):
        self.image = self.font.render("Score:"+str(int(self.score)),True,self.score_color,screen_set.bg_color)
        self.imagereset = self.font.render("R:RESET", True, self.score_color, screen_set.bg_color)
        self.imagereset_rect = self.imagereset.get_rect()
        self.image_rect = self.image.get_rect()
        self.image_rect.top,self.image_rect.left = self.screen_rect.top,self.screen_rect.left
        self.imagereset_rect.top, self.imagereset_rect.left = self.screen_rect.top+45, self.screen_rect.left
    def draw_scoreboard(self):
        self.screen.blit(self.image,self.image_rect)
        self.screen.blit(self.imagereset, self.imagereset_rect)

win_board:

import pygame
class Win_board():
    def __init__(self,screen,screen_set):
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.color = (0,0,0)
        self.font = pygame.font.SysFont(None,60)
        self.prep_win(screen_set)
        self.status = False
    def prep_win(self,screen_set):
        self.image = self.font.render("YOU WIN!",True,self.color,screen_set.bg_color)
        self.image_rect = self.image.get_rect()
        self.image_rect.centerx = self.screen_rect.centerx
        self.image_rect.y = self.screen_rect.bottom - 60
    def draw_win(self):
        self.screen.blit(self.image,self.image_rect)

board:

import pygame
class Board():
    def __init__(self,screen,screen_set):
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.color = (0,0,0)
        self.font = pygame.font.SysFont(None,45)
        self.prep_board(screen_set)
        self.status = True
        self.game_active = False
        self.keyspace_active = True
    def prep_board(self,screen_set):
        self.image = self.font.render("SPACE:START",True,self.color,screen_set.bg_color)
        self.image_rect = self.image.get_rect()
        self.image_rect.centerx = self.screen_rect.centerx
        self.image_rect.y = self.screen_rect.bottom - 60
    def draw_board(self):
        self.screen.blit(self.image,self.image_rect)

gameover_board:

import pygame
class Gameover_board():
    def __init__(self,screen,screen_set):
        self.screen = screen
        self.screen_rect = self.screen.get_rect()
        self.color = (0,0,0)
        self.font = pygame.font.SysFont(None,60)
        self.prep_gameover(screen_set)
    def prep_gameover(self,screen_set):
        self.image = self.font.render("GAME OVER!",True,self.color,screen_set.bg_color)
        self.image_rect = self.image.get_rect()
        self.image_rect.centerx = self.screen_rect.centerx
        self.image_rect.y = self.screen_rect.bottom - 60
    def draw_gameover(self):
        self.screen.blit(self.image,self.image_rect)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值