俄羅斯方塊(三):"流動"的方塊

来源:https://www.cnblogs.com/ansver/archive/2018/07/09/9152892.html
-Advertisement-
Play Games

問題的提出: 俄羅斯方塊允許90度的坡,是不是有點不夠科學#(滑稽) 想辦法加一種會“滑坡”的方塊 本文兩大部分: 詳細的描繪是怎樣的“流動” 寫代碼,並整合進游戲 本文基於我寫的 俄羅斯方塊(一):簡版 事先上兩個動圖, 說明下我想做什麼 第一部分 首先是假象圖 這是一個長條逐漸“癱軟”的過程 歸 ...


 

 

 問題的提出:

俄羅斯方塊允許90度的坡,是不是有點不夠科學#(滑稽)

想辦法加一種會“滑坡”的方塊

本文兩大部分:

詳細的描繪是怎樣的“流動”

寫代碼,並整合進游戲

 

 本文基於我寫的

俄羅斯方塊(一):簡版 

事先上兩個動圖, 說明下我想做什麼

 

 

第一部分

首先是假象圖

這是一個長條逐漸“癱軟”的過程

歸納規律,其實只有兩種移動

 

1下方沒有方塊時:向下方滑落

2下方有方塊時:向左下或者右下滑落

但是這兩條是不夠的

下麵展示一種例外

左邊的比右邊“更科學”

顯然,需要再加一條規則“規則三:下方的方塊先行動

但是,這個規則還不夠,我又找到兩個“不科學的”例子

左邊的比右邊“更科學”

不同行之間需要區分次序,同一行的也要區分次序!!!

行內從左向右,和從右向左都無法從根本解決問題,那就只能調整兩種移動的優先順序

再加一條規則“規則四:同一行的,下落移動的優先

接下來還有一個“不科學”

進去了。。。這是最不科學的一個。。

添加一條斜向移動的限制“左右有方塊不可斜向移動

然後我發現,規則四沒用了!!!

說明:1號方塊的斜向移動被2號阻止了,上面的情況變成“不可能發生”

它們解決的是同一個問題,水平相鄰方塊之間的次序問題

只是限制斜向移動的方法更為徹底!

規則彙總:

1下方沒有方塊時:向下方滑落

2下方有方塊且左下右下為空時且無阻礙時:向左下或者右下滑落

3多個方塊,行數小的方塊先行動

 我已經儘力的考慮每一種“例外”了,如有疏漏歡迎指出

 

第二部分

代碼的問題無非兩個:

怎麼寫

什麼時候執行

先說第二個問題

什麼時候執行

講流程,原來的“方塊落地-->清算分數-->下一個方塊”    新的  "方塊落地-->開始流動-->清算分數-->下一個方塊"

可是一次流動結束就太沒趣了   一次只流動一步

之前的下落過程的流程圖

新的

代碼重整

 

# flows list 全局變數 儲存"正在流動的方塊"
# block_type list 全局變數 決定活動方塊能否流動
def move_down():
    
    if flows:
# ---------流動過程--------- pass # ========================== else: # 下麵是常規的流程 x, y = centre x -= 1 for i, j in active: i += x j += y if background[i][j]: break else: centre.clear() centre.extend([x, y]) return # ----------落地後------- # 如果方塊狀態 為 流動 則寫入flows if block_type[0] == 2: x, y = centre for i, j in active: flows.append((x + i, y + j)) # === 將方塊信息 轉入 待流動列表 active.clear() return # 結束函數,防止進入下方"清算環節"
# - 否則走常規流程 else: x, y = centre for i, j in active: background[x + i][y + j] = 1 # -----檢查是否滿行----- l = [] for i in range(1, 20): if 0 not in background[i]: l.append(i) l.sort(reverse=True) for i in l: background.pop(i) background.append([0 for j in range(10)]) score[0] += scores_f(len(l)) pygame.display.set_caption("分數:%d" % (score[0])) active.clear() new = list(random.choice(all_block)) if random.randint(1, 10) >= 4 else list(random.choice(all_block_plus)) active.extend(new) block_type[0] = 1 if random.randint(1, 10) >= 5 else 2 # 取個隨機數,決定下個方塊的類型 1為普通方塊 2為流動方塊 centre.clear() centre.extend([20, 4]) x, y = centre for i, j in active: i += x j += y if background[i][j]: break else: return alive.append(1)

 

下麵是流動的詳細內容:

 

PS:從流動列表移除的同時 , 應該寫入 background   "固化"成為背景

流動過程代碼(在最後有 紅色代碼額外的說明) 

        # 基於"遍歷過程中避免修改遍歷對象"的原則
        # 故遍歷 flows的 拷貝
        flows_copy = flows.copy()
        # - 流動原則三,行數小的先動,  故遍歷前先排序
        flows_copy.sort()

        for i, j in flows_copy:
            flows.remove((i, j)) # 1
            # '抹去'舊方塊位置信息
            
            if background[i - 1][j] or ((i - 1, j) in flows):
            # 如果下方有方塊(背景方塊,流動中的方塊都算) # 2 
                num = 0
                
                # 如果左側沒有方塊(背景方塊,流動中的方塊都算)num += 1
                if j >= 1 and (not background[i][j - 1]) and (not background[i - 1][j - 1]) \
                        and ((i, j - 1) not in flows) and ((i - 1, j - 1) not in flows): # (3)
                    num += 1
                    
                # 如果右側沒有方塊(背景方塊,流動中的方塊都算)num += 2
                if j <= 8 and (not background[i][j + 1]) and (not background[i - 1][j + 1]) \
                        and ((i, j + 1) not in flows) and ((i - 1, j + 1) not in flows):
                    num += 2
                
                #num可能的情況    0           1         2          3
                #對應          左右均不通   僅通左路  僅通右路   左右均通
                
                #如果左右均通 左右兩路隨機選一個
                if num == 3:
                    num = random.randint(1, 2)

                # 根據num取值 做出相應的移動
                if num == 0:
                    # 這裡與 流程圖 不一致 後面會說明原因 (4)
                    if (background[i - 1][j]) and (j <= 0 or background[i][j - 1] or background[i - 1][j - 1]) and \
                            (j >= 9 or background[i][j + 1] or background[i - 1][j + 1]):
                        background[i][j] = 3
                        #如果 三個方向 被背景方塊 堵死
                        #就"固化/停止流動/移出flows/寫入background"
                    else:
                        flows.append((i, j))
                
                elif num == 1:
                    flows.append((i - 1, j - 1))
                elif num == 2:
                    flows.append((i - 1, j + 1))
            else:
                # 否則就將算出 下方的坐標 添回flows
                flows.append((i - 1, j))

        # 如果flows仍有方塊存在
        # return 結束函數, 防止
        if flows:
            return

額外的說明:

1, 移動就是,  從flows除去舊的位置,添入新位置,,,為避免代碼重覆出現,迴圈第一步就移除舊位置

2,至此,方塊有兩種,background的背景方塊 flows的流動方塊     一個位置是否有方塊   需要查閱兩次    另外畫面繪製部分需要 遍歷 flows 繪製方塊

3,為避免 越界錯誤  ,索引background前 加個判斷

4流程圖未考慮到一個特殊情況

理想狀態下 兩個流動方塊應該 填上洞      但是按流程圖    1 號方塊三路堵死  固化

所以    因流動方塊 阻礙 造成的"假死"不用固化   /    單純被 背景堵死 才固化

 

完整代碼

import pygame, sys, random, time


def new_draw():
    screen.fill(white)

    for i in range(1, 21):
        for j in range(10):
            bolck = background[i][j]
            if bolck:
                pygame.draw.rect(screen, colors[bolck], (j * 25 + 1, 500 - i * 25 + 1, 23, 23))

    x, y = centre
    for i, j in active:
        i += x
        j += y
        pygame.draw.rect(screen, colors[block_type[0]], (j * 25 + 1, 500 - i * 25 + 1, 23, 23))

    for i, j in flows:
        pygame.draw.rect(screen, yellow, (j * 25 + 1, 500 - i * 25 + 1, 23, 23))

    pygame.display.update()


def move_LR(n):
    """n=-1代表向左,n=1代表向右"""
    x, y = centre
    y += n
    for i, j in active:
        i += x
        j += y
        if j < 0 or j > 9 or background[i][j]:
            break
    else:
        centre.clear()
        centre.extend([x, y])


def rotate():
    x, y = centre
    l = [(-j, i) for i, j in active]
    for i, j in l:
        i += x
        j += y
        if j < 0 or j > 9 or background[i][j]:
            break
    else:
        active.clear()
        active.extend(l)


def move_down():
    if flows:
        flows_copy = flows.copy()
        flows_copy.sort()
        for i, j in flows_copy:
            flows.remove((i, j))
            if background[i - 1][j] or ((i - 1, j) in flows):
                num = 0
                if j >= 1 and (not background[i][j - 1]) and (not background[i - 1][j - 1]) \
                        and ((i, j - 1) not in flows) and ((i - 1, j - 1) not in flows):
                    num += 1
                if j <= 8 and (not background[i][j + 1]) and (not background[i - 1][j + 1]) \
                        and ((i, j + 1) not in flows) and ((i - 1, j + 1) not in flows):
                    num += 2

                if num == 3:
                    num = random.randint(1, 2)

                if num == 0:
                    if (background[i - 1][j]) and (j <= 0 or background[i][j - 1] or background[i - 1][j - 1]) and \
                            (j >= 9 or background[i][j + 1] or background[i - 1][j + 1]):
                        background[i][j] = 3
                    else:
                        flows.append((i, j))
                if num == 1:
                    flows.append((i - 1, j - 1))
                if num == 2:
                    flows.append((i - 1, j + 1))
            else:
                flows.append((i - 1, j))

        if flows:
            return

    else:
        x, y = centre
        x -= 1
        for i, j in active:
            i += x
            j += y
            if background[i][j]:
                break
        else:
            centre.clear()
            centre.extend([x, y])
            return

        if block_type[0] == 2:
            x, y = centre
            for i, j in active:
                flows.append((x + i, y + j))
            active.clear()
            return
        else:
            x, y = centre
            for i, j in active:
                background[x + i][y + j] = 1

    l = []
    for i in range(1, 20):
        if 0 not in background[i]:
            l.append(i)

    l.sort(reverse=True)

    for i in l:
        background.pop(i)
        background.append([0 for j in range(10)])

    score[0] += scores_f(len(l))
    pygame.display.set_caption("分數:%d" % (score[0]))

    active.clear()
    new = list(random.choice(all_block)) if random.randint(1, 10) >= 4 else list(random.choice(all_block_plus))
    active.extend(new)
    block_type[0] = 1 if random.randint(1, 10) >= 5 else 2
    centre.clear()
    centre.extend([20, 4])

    x, y = centre
    for i, j in active:
        i += x
        j += y
        if background[i][j]:
            break
    else:
        return
    alive.append(1)


def scores_f(n):
    if n in scores:
        return scores[n]
    else:
        return scores_f(n-4)


pygame.init()
screen = pygame.display.set_mode((250, 500))
pygame.display.set_caption("俄羅斯方塊")
fclock = pygame.time.Clock()

all_block = (((0, 0), (0, -1), (0, 1), (0, 2)),
             ((0, 0), (0, 1), (-1, 0), (-1, 1)),
             ((0, 0), (0, -1), (-1, 0), (-1, 1)),
             ((0, 0), (0, 1), (-1, -1), (-1, 0)),
             ((0, 0), (0, 1), (1, 0), (0, -1)),
             ((0, 0), (1, 0), (-1, 0), (1, -1)),
             ((0, 0), (1, 0), (-1, 0), (1, 1)))
all_block_plus = (((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1),),
                  ((-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)),
                  ((-1, 0), (0, -1), (0, 0), (0, 1), (1, 0), (-2, 0)),
                  ((0, -1), (0, 0), (0, 1), (1, -1), (1, 1)),
                  ((-1, 1), (0, -1), (0, 0), (0, 1), (1, -1)),
                  ((-1, -1), (0, -1), (0, 0), (0, 1), (1, 1)),
                  ((-1, -1), (-1, 1), (0, 0), (1, -1), (1, 1)),
                  ((-1, -1), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 1)),
                  ((-1, -1), (0, -1), (0, 0), (0, 1), (1, -1))

                  )
'''3x3,十,十,凹,2,5,X,H,T'''

scores = {0: 0, 1: 1, 2: 3, 3: 6, 4: 10}
background
= [[0 for i in range(10)] for j in range(24)] background[0] = [1 for i in range(10)] active = list(random.choice(all_block)) centre = [20, 4] score = [0] flows = [] block_type = [1] black = 0, 0, 0 white = 255, 255, 255 blue = 0, 0, 255 yellow = 255, 215, 0 colors = {1: blue, 2: yellow, 3: (139, 105, 20)} times = 0 alive = [] press = False while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit() elif event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: move_LR(-1) elif event.key == pygame.K_RIGHT: move_LR(1) elif event.key == pygame.K_UP: rotate() elif event.key == pygame.K_DOWN: press = True elif event.type == pygame.KEYUP: if event.key == pygame.K_DOWN: press = False if press: times += 20 if times >= 100: move_down() times = 0 else: times += 1 if alive: pygame.display.set_caption("over %d分" % (score[0])) time.sleep(3) break new_draw() fclock.tick(100)

PS : 標紅的是一些細枝末節     有需要的話我再解釋

流動方塊降低了難度,,所以加入了一些 特殊形狀的 方塊增加難度

 

 

 

這篇急於發表   畢竟寫了一個月了...有問題有空再更新

#


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 首先感謝老觀眾水中盜影和其它幾位的回覆,我媽還沒見到我娶媳婦就走了,也是我的不孝啊! 這一個月前半都在跑我媽的後事,後半都是強制996的加班中 天天坐火車回去當天再回來,雖然忙但還挺充實,也沒想什麼,公證處要什麼材料我就去各單位去開各種證明... 但是公證跑完了之後,後半個月的兩周我幾乎天天失眠,甚 ...
  • Angular中的裝飾器是一個函數,它將元數據添加到類、類成員(屬性、方法)和函數參數。 用法:要想應用裝飾器,把它放在被裝飾對象的上面或左邊。 Angular使用自己的一套裝飾器來實現應用程式各部件之間的相互操作。 這個地方是前面幾個模塊(Modules), 指令(Diretives)、組件(Co ...
  • 一、面向對象簡介 Python設計之初,就是一門面向對象的語言,在Python中一切皆對象,而且在Python中創建一個對象也很簡單,今天我們就來學習一下Python的面向對象的知識。 二、兩種編程方式 在C#、Java中,只能使用面向對象編程,在Ruby、Python中可以使用函數編程以及面向對象 ...
  • 原創 題目為:()()()+()()()=()()() 將1~9這9個數字填入括弧,每個數字只能用一次。 枚舉: 1 public class Test { 2 public static void main(String[] args){ 3 int a[]=new int[9]; 4 int f ...
  • 創建一個socketserver 至少分以下幾步 First, you must create a request handler class by subclassing the BaseRequestHandlerclass and overriding its handle() method; ...
  • 1、引言 點陣圖是使用位(bit)數組來對數據進行統計,排序和去重,其結構圖如下: 其中點陣圖的索引映射需要存儲的值,點陣圖索引所在位置的值表示索引對應的值是否已經存儲。 2、介面 3、實現 定義靜態byte數組常量,用於快速檢驗點陣圖上索引對應的值: 聲明欄位: 其中size為點陣圖的大小;當點陣圖的size ...
  • O’Reilly的電子書《Reactive Microservices Architecture》講述了微服務/分散式系統的一些設計原則,本文是筆者閱讀完此書後的理解。 微服務相比傳統的單體應用能夠帶來快速的響應,以小的系統產生大的影響。而隨著網路加速、磁碟成本降低、RAM成本降低、多核技術的發展、 ...
  • 1 /* 2 queue.h -- Queue介面 3 */ 4 5 #ifndef QUEUE_H 6 #define QUEUE_H 7 8 #define MAXQUEUE 10 9 10 typedef int Item; 11 12 typedef struct node 13 { 14 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...