冒泡排序動畫(基於python pygame實現)

本項目效果初始截圖如下
在這裏插入圖片描述
動畫見本人b站投稿:https://www.bilibili.com/video/av95491382
本項目對應github地址:https://github.com/BigShuang
python版本:3.6,pygame版本:1.9.3。(python版本一致應該就沒什麼問題)
樣例gif如下
在這裏插入圖片描述

======================= 大爽歌作,made by big shuang =======================

一、思路分析

冒泡排序的思路感覺沒有什麼需要多做贅述的了。
所以這裏主要講下可視化的問題
這裏爲了方便,採用面向對象的思路編寫代碼。
其實主要需要三個類就要。
1 - 要實現一個內部有數字的圓點,同時這個圓點大小和數字相關聯。
2 - 在實現一個管理這些圓點列表的,可以認爲和是和數組相對應的列表管理者。
3 - 實現可視化

二、代碼實現

1 在開頭,先導入需要的類再設置好相關的常量
import pygame
import random

FPS = 60  # 遊戲幀率

WIN_WIDTH = 1200  # 窗口寬度
WIN_HEIGHT = 980  # 窗口高度

BUBBLE_SPACE = 40  # 氣泡之間的間距
INIT_R = 10  # 氣泡初始大小-半徑
DR = 4  # 氣泡數字值一個單位對應的氣泡半徑增加量
NUMBER = 10  # 氣泡個數

COLORS = {
    "bg": (240, 255, 255),  # 背景顏色
    "bubble": (135, 206, 235),  # 氣泡顏色
    "select": (0, 139, 139),  # 被選擇的氣泡顏色
    "text": (0, 0, 0),  # 文本顏色
}

pygame.init()  # pygame 初始化,必須有,且必須在開頭
try:
    # 如果你電腦自帶有Arial字體,這裏可以直接運行,(一般電腦都會有的)
    font = pygame.font.SysFont("Arial", 20)
except Exception:
    # 如果你本地沒有Arial字體,你需要在項目裏面建立一個font文件夾存放simkai.ttf字體文件,
    # 字體文件可在我的github下載
    font = pygame.font.Font("./font/simkai.ttf", 20)
2 建立Bubble類實現氣泡

建立Bubble類,實現氣泡,即一個內部有數字的圓點

class Bubble:
    speed = 2  # 速度
    color = COLORS["bubble"]

    def __init__(self, master, x, y, v, r):
        self._master = master  # 父控件
        self.cx = x  # 氣泡中心橫座標 - x
        self.cy = y  # 氣泡中心縱座標 - y
        self.v = v  # 氣泡內部數字
        self.radius = r  # 氣泡半徑

        # 註釋說明1-1
        # 由於pygame自己繪製的圓鋸齒太大,看起來十分不光滑
        # 想要光滑的圓,只能從png文件中導入圓的圖片(圖片可前往我的github下載)
        # 這一部分對於單純的pygame動畫學習者而言,並不是非常重要,所以我把與此相關的代碼都給註釋掉了
        # 有需要的把所有標明註釋說明1-1的代碼解註釋就好
        # self.image = pygame.image.load("img/bubble.png")
        # self.select_image = pygame.image.load("img/select.png")
        # self.resize()

    def up(self):
        # 氣泡上浮
        self.cy -= DR

    def down(self):
        # 氣泡下沉
        self.cy += DR

    # 繪製氣泡
    def draw(self, current=False):
        text = font.render(str(self.v), 1, COLORS["text"])
        if current:
            pygame.draw.circle(self._master, COLORS["select"], (self.cx, self.cy), self.radius)
        else:
            pygame.draw.circle(self._master, self.color, (self.cx, self.cy), self.radius)
        text_width = text.get_width()
        text_height = text.get_height()
        self._master.blit(text, (self.cx - text_width // 2, self.cy - text_height // 2))

    # 見註釋說明1-1
    # def resize(self):
    #     self.image = pygame.transform.scale(self.image, (self.radius * 2, self.radius * 2))
    #     self.select_image = pygame.transform.scale(self.select_image, (self.radius * 2, self.radius * 2))
    # def draw_pic(self, current=False):
    #     if current:
    #         self._master.blit(self.select_image, (self.cx - self.radius, self.cy - self.radius))
    #     else:
    #         self._master.blit(self.image, (self.cx - self.radius, self.cy - self.radius))
    #
    #     text = font.render(str(self.v), 1, COLORS["text"])
    #     text_width = text.get_width()
    #     text_height = text.get_height()
    #     self._master.blit(text, (self.cx - text_width // 2, self.cy - text_height // 2))
3 建立BubbleManager類管理氣泡

建立BubbleManager類管理氣泡,並進行冒泡排序控制

class BubbleManager:
    def __init__(self, master, x, height, arr=[]):
        self._master = master  # 父控件窗體
        self.bubblelist = []  # bubble列表
        # 數組
        if arr:
            self.arr = arr
        else:
            self.arr = [i for i in range(1, NUMBER)]
            random.shuffle(self.arr)

        # 根據數組中的數字建立Bubble對象,添加到bubblelist中
        # 注意動畫界面是從下往上展示數組的元素的
        top_h = 0  # 上一個氣泡最高點到底部的距離(即圓心到底的距離加上氣泡半徑),初始爲0
        for i in range(NUMBER-1):
            v = self.arr[i]
            vr = INIT_R + v * DR  # 數字對應的半徑大小
            center_h = top_h + BUBBLE_SPACE + vr  # 當前氣泡圓心到界面底部的距離
            top_h = center_h + vr  # 記錄當前氣泡最高點到底部的距離,供下一個使用

            bubble_i = Bubble(master, x, WIN_HEIGHT - center_h, v, vr)
            self.bubblelist.append(bubble_i)

        # 用於記錄冒泡排序過程的值
        self.i = 0
        self.j = 0
        
        # 記錄當前氣泡j是否正在上浮
        self.bubbling = False
        
        # 控制動畫播放速度
        self.count = 0

    def draw(self):
        # 繪製所有氣泡
        for j in range(len(self.bubblelist)):
            db = self.bubblelist[j]
            db.draw(j==self.j)

    def bubble_done(self):
        # 檢查冒泡排序是否完成
        return self.i >= NUMBER - 1

    def bubble_j(self):
        # 氣泡j上浮動畫
        if not self.bubbling:
            return

        dbj0 = self.bubblelist[self.j]
        dbj1 = self.bubblelist[self.j+1]

        if dbj0.cy - dbj0.radius > dbj1.cy + dbj1.radius:
            # j氣泡在下
            dbj0.up()
            dbj1.down()
        elif dbj0.cy > dbj1.cy:
            # 氣泡相交
            cy0 = dbj0.cy
            dbj0.cy = dbj1.cy - dbj1.radius + dbj0.radius
            dbj1.cy = cy0 - dbj1.radius + dbj0.radius
        elif (dbj1.cy - dbj1.radius) - (dbj0.cy + dbj0.radius) < BUBBLE_SPACE:
            dbj0.up()
            dbj1.down()
        else:
            self.bubblelist[self.j] = dbj1
            self.bubblelist[self.j+1] = dbj0
            self.bubbling = False
            self.j += 1
            return

    def bubble_once(self):
        # 進行一次冒泡排序
        if self.j >= NUMBER - self.i - 2:
            self.i += 1
            self.j = 0
        else:
            if self.arr[self.j] > self.arr[self.j + 1]:
                if self.count > 5:
                    self.count = 0
                else:
                    self.count += 1
                    return
                self.bubbling = True
                self.arr[self.j], self.arr[self.j + 1] = self.arr[self.j + 1], self.arr[self.j]
                self.bubble_j()
            else:
                if self.count > 10:
                    self.count = 0
                    self.j += 1
                else:
                    self.count += 1
             
    # 見註釋說明1-1
    # def draw_pic(self):
    #     for j in range(len(self.bubblelist)):
    #         db = self.bubblelist[j]
    #         db.draw_pic(j==self.j)
4 實現可視化界面

這裏用一個Gui類去實現可視化
界面顯示後,點擊任意鍵盤按鍵即開始動畫

class Gui:
    def __init__(self, width, height, count, fps=FPS, arr=[]):
        self.win = pygame.display.set_mode((width, height))
        self.clock = pygame.time.Clock()
        self.fps = fps
        pygame.display.set_caption("Bubble sort animation - made by Big Shuang")

        self.bmlist = []
        for i in range(1, count):
            x = WIN_WIDTH // count * i
            bm = BubbleManager(self.win, x, WIN_HEIGHT)
            self.bmlist .append(bm)

        self.start = False

    def loop(self):
        while True:
            # 獲取所有事件
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    # 判斷當前事件是否爲點擊右上角退出鍵
                    pygame.quit()
                if event.type == pygame.KEYDOWN:
                	# 點擊任意鍵開始動畫
                    self.start = True

            if self.start:
                for bm in self.bmlist :
                    if bm.bubbling:
                        bm.bubble_j()
                    elif not bm.bubble_done():
                        bm.bubble_once()
                    else:
                        bm.j = -1

            self.win.fill(COLORS["bg"])
            for bm in self.bmlist:
                bm.draw()

            self.clock.tick(self.fps)
            pygame.display.update()

最後用如下調用一下就好

if __name__ == '__main__':
    gui = Gui(WIN_WIDTH, WIN_HEIGHT, 10)
    gui.loop()

運行後初始狀態截圖如下
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章