【閑暇一寫】用Python編寫2048游戲(命令行版)

来源:https://www.cnblogs.com/qinyu6/archive/2023/09/28/17736441.html
-Advertisement-
Play Games

本篇博文圍繞使用Python開發熱門游戲2048 GAME(命令行版本) 代碼未做任何優化(原生且隨意)、全程以面向過程、MVC的設計思想為主、開發環境是Ubuntu系統下的Pycharm 2048是我很久以前學習Python過程中的一個作業,接下來直入正題—— 一、瞭解游戲 1. 介紹 《2048 ...


本篇博文圍繞使用Python開發熱門游戲2048 GAME(命令行版本)

代碼未做任何優化(原生且隨意)、全程以面向過程MVC的設計思想為主、開發環境是Ubuntu系統下的Pycharm

2048是我很久以前學習Python過程中的一個作業,接下來直入正題——

一、瞭解游戲

1. 介紹

2048》是一款單人線上和移動端游戲,由19歲的義大利人Gabriele Cirulli於2014年3月開發。游戲任務是在一個網格上滑動小方塊來進行組合,直到形成一個帶有有數字2048的方塊(來源:維基百科

2. 玩法規則

  1. 通過方向鍵讓方塊整體上下左右移動
  2. 如果兩個帶有相同數字的方塊在移動中碰撞,則它們會相加合併為一個新方塊
  3. 每次出現方塊移動時,都會有一個值為2或者4的新方塊出現
  4. 初始開局時,4*4的方塊,隨機2個方塊賦值2或者4
  5. 其中所出現的數字都是2的冪,2,4,8,16......

二、MVC設計

Model:無
View:終端界面(有時間再研究一下pyQt),列印二維列表,輸入輸出控制
Controller:二維列表-矩陣、數據控制、上下左右操作、計分機制、方塊合併處理等等

三、核心函數

截圖_20230928194512

通過觀察游戲界面,可知數據由二維數組(線性代數--方陣)存儲,將上圖映射到如下代碼:

source = [
    [0, 0, 0, 0],
    [0, 0, 2, 2],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

通過玩法規則第1條可知,方向鍵上下左右移動,四種移動必然存在相似的操作,祁天暄講師通過分析向左移動來書寫後續代碼,我這裡也會以向左移動來分析如何寫後續的代碼。

截圖_20230928194603

[0, 0, 2, 2]

取上述一行,按左方向鍵移動後,可以看到兩個方塊2持續左移(如果左邊還有非0的方塊,那麼就會頂住該非0的方塊),然後相撞變成方塊4,因為有方塊移動,所以隨機挑選一個方塊0進行填充成2(不限於當前行,也可能發生在其他行):
截圖_20230928194619

本行規律:

[0, 0, 2, 2] >發生滑動> [2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0] >隨機填充> [4, 0, 0, 2]

經過多局游戲結合上方的規律,可以得知以下規律:

[0, 0, 2, 2] >發生滑動> [2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0] >隨機填充> [4, 0, 0, 2]
[4, 0, 2, 2] >發生滑動> [4, 2, 2, 0] >相等相撞求和> [4, 4, 0, 0] >隨機填充> [4, 4, 0, 4]
[4, 4, 2, 2] >發生滑動,不動> [4, 4, 2, 2] >相等相撞求和> [8, 4, 0, 0] >隨機填充> [4, 0, 0, 2]

1. 滑動處理

發生滑動的環節,可以得出一個規律,有0在非0元素的前方則必滑動,否則不動

[0, 0, 2, 2] >發生滑動> [2, 2, 0, 0]
[4, 0, 2, 2] >發生滑動> [4, 2, 2, 0]
[4, 4, 2, 2] >發生滑動,不動> [4, 4, 2, 2]

所以此處構造一個zero_to_end函數,功能就是將0移至末尾處,並保持非0元素應有的順序,先看一下中規中矩的方式(冒泡式的移動,時間複雜度較高):

def zero_to_end(list_data):
    for i in range(3, -1, -1):
        for j in range(i):
            if list_data[j] == 0:
                list_data[j], list_data[j + 1] = list_data[j + 1], list_data[j]

再看一下另一種寫法(採用該函數,時間複雜度為O(n)):

def zero_to_end(list_data):
    """
    重排序函數(核心演算法)
    非0元素移至最前(保持順序),0元素移至最後,充當中間人處理列表的角色
    :param list_data: list 一維列表
    :return: None
    """
    for i in range(3, -1, -1):
        if not list_data[i]:
            del list_data[i]
            list_data.append(0)

2. 相等相撞求和

經上一函數,每一行列表都被處理成:若幹非0元素有序在前,若幹0元素在後

[2, 2, 0, 0] >相等相撞求和> [4, 0, 0, 0]
[4, 2, 2, 0] >相等相撞求和> [4, 4, 0, 0]
[4, 4, 2, 2] >相等相撞求和> [8, 4, 0, 0]

相等相撞求和這個過程肯定要統一函數處理,增加復用性,因此需要詳細拆分該流程的細節:

[4, 2, 2, 0]
如果第1個元素等==第2個元素:
    則第1個元素 + 第2個元素,並賦給第1個元素的位置
    刪除第2個元素
    末尾追加一個0
[4, 2, 2, 0]
如果第2個元素==第3個元素(符合條件)
    則第2個元素 + 第3個元素,並賦給第2個元素的位置[4, 4, 2, 0]
    刪除第3個元素[4, 2, 0]
    末尾追加一個0[4, 2, 0, 0]
[4, 2, 0, 0]
如果第3個元素==第4個元素
    則第3個元素 + 第4個元素,並賦給第3個元素的位置
    刪除第4個元素
    末尾追加一個0

也就是說,相鄰且相等的兩個元素相加,應賦值給前方位置的元素,然後刪除後方位置的元素,刪除了一個,肯定還要湊回去的,根據游戲規則,補0即可

其實上方的邏輯還可以進行優化,當檢測到當前位置的元素為0時,直接打斷迴圈即可(因為已經被zero_to_end函數處理過了),封裝成merge_single函數如下:

def merge_single(list_data):
    """
    合併元素函數(核心演算法)
    重排序後,左邊兩個相鄰相同的非0元素相加,後方補0,並加分(可diy)
    如果兩個相鄰的元素不同或者為0,則不做其他操作
    :param list_data: list 一維列表
    :return: None
    """
    zero_to_end(list_data)  # 處理一維列表
    for i in range(3):
        if list_data[i] == 0: break  # 檢測到當前位置為0,後方就不管了,直接打斷
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2  # 等價於 += list_data[i + 1]
            del list_data[i + 1]  # 刪除 [i + 1]位置的元素
            list_data.append(0)  # 補0

有點不太放心,放一條數據進行測試:

[4, 4, 2, 2] >i = 0,相加> [8, 4, 2, 2] >刪除i + 1位置> [8, 2, 2] >補0> [8, 2, 2, 0] 
>迴圈結束第1次,i = 1,又相加> [8, 4, 2, 0] >又刪除i + 1位置> [8, 4, 0] >又補0> [8, 4, 0, 0]
> 迴圈結束第2次,i = 2,發現是0,直接跳出迴圈 

3. 隨機填充

玩法規則第3條,當游戲中,發生方塊滑動時,在0元素區域隨機抽取一個位置,隨機賦值2或者4。

因此,先定義全局變數,一個存2和4的元組,通過random模塊實現隨機索引獲取2或者4。

random_tuple = (2, 4)  # 初始添加的值、移動時添加的值

通過while迴圈不斷尋找隨機方格,直到發現該方格存儲0,那麼該方格將被賦予新值。

def random_site():
    """
    隨機填充0元素函數(非核心)
    隨機挑選0元素的位置,進行隨機填充random_list中的任意一個元素
    可通過增刪改變random_list中的元素,從而影響到隨機填充的數字
    :return: None
    """
    random_list_len = len(random_tuple)
    while True:
        x = random.randint(0, 3)
        y = random.randint(0, 3)
        if after_source[x][y] == 0:
            after_source[x][y] = random_tuple[random.randint(0, random_list_len - 1)]
            break

四、附加功能函數

通過以上三步,成功的完成了2048的核心功能,接下來逐一部署2048的初始化、游戲操作、用戶操作、列印等等函數。

1. 初始數據和矩陣比較

構造游戲初始數據,以全局變數表示:

score = 0  # 初始分數,後續累加即可
source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]

單純的一個source表示數據可能還不夠,我構造了兩個4*4的矩陣,分別命名為before_source和after_source:

before_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作前的矩陣
after_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作後的矩陣(當前列印的矩陣)

這兩個矩陣,用於用戶操作前的一個比較(後臺比較),假設用戶進行左向移動,那麼移動前的數據傳給before_source,移動後(即每一行調用merge_single函數後)的數據傳給after_source,after_source才是用戶需要看的,兩者在後臺進行比較後,倘若不同,則說明發生了“方塊移動”,那麼就要調用隨機填充的函數,如果相同,說明沒有方塊滑動,則不可隨機填充。假如對以下的數據發起左移操作後,是不存在元素移動的,即不會調用隨機填充:

[
    [4, 0, 0, 0],
    [0, 0, 0, 0],
    [2, 4, 2, 0],
    [8, 2, 0, 0]
]

根據上述分析,構造比較函數compare_matrix:

def compare_matrix():
    """
    二維數組比較
    操作前後的二維數組(矩陣)進行比較
    如果不相等,說明有元素可移動,當移動時調用random_site()函數
    """
    if not (before_source == after_source):
        random_site()

2. 矩陣數據列印

每次執行完移動操作後(無論是上下還是左右),肯定都要反饋給用戶數據界面,因此需要構造列印矩陣的函數:

def print_list():
    """列印游戲過程中必看的矩陣信息"""
    for single_list in after_source:
        print(single_list)

3. main入口(框架搭建)

調用程式總該需要一個入口,構造main函數。

迴圈開始階段,通過global關鍵字操作全局變數before_source,此處需要註意:應使用深拷貝(需要導入copy模塊),將當前的數據拷貝給before_source,如果採取淺拷貝,操作after_source後,before_source也會跟隨變化,這樣就導致before_source恆等於after_source。

上下左右以input輸入(w 、s、a、d)來進行移動,n表示主動認輸,q表示退出游戲。

每次執行完移動操作後,都需要進行反饋數據界面,所以迴圈末尾需要調用print_list函數和列印當前分數:

def main():
    """程式入口:初始化 + 輸入 + 輸出"""
    while True:
        global before_source
        before_source = copy.deepcopy(after_source)
        key = input("鍵入:")
        if key == "a": pass
        if key == "d": pass
        if key == "w": pass
        if key == "s": pass
        if key == "n": pass
        if key == "q": break  
        print_list()
            
            
main()  # 調用main函數,即正常游戲的入口

當準備輸入時,手動中止程式,會發現很煩人的紅色報錯

因此加上try和except簡單的處理一下:

def main():
    """程式入口:初始化 + 輸入 + 輸出"""
    while True:
        try:
            key = input("鍵入:")
            global before_source
            before_source = copy.deepcopy(after_source)
            if key == "a": pass
            if key == "d": pass
            if key == "w": pass
            if key == "s": pass
            if key == "n": pass
            if key == "q": break
            print(f"當前分數:{score}")
            print_list()
        except KeyboardInterrupt:
            break
        
            
            
main()  # 調用main函數,即正常游戲的入口

4. 實現認輸功能

構建forfeit函數,列印最終分數後,人為拋出KeyboardInterrupt異常,直接調到except執行break打斷迴圈(為了不在列印最終得分後,執行後續兩條語句):

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-JqFS4KKo-1674483797912)(/home/ronan/.config/Typora/typora-user-images/image-20230123214632123.png)]

def forfeit():
    """認輸"""
    print(f"玩家已認輸,最終得分:{score}")
    raise KeyboardInterrupt

5. 加分機制

每發生方塊碰撞合併後,相加數字即為當局加分,比如初始為0,兩個相鄰的方塊2碰撞合併後變成方塊4,當前分數+4,即分數為4。

在merge_single函數中(參考2.2的函數),list_data.append(0)語句後添加下述代碼:

global score
score += list_data[i]

即:

def merge_single(list_data):
    """
    合併元素函數(核心演算法)
    重排序後,左邊兩個相鄰相同的非0元素相加,後方補0,並加分(可diy)
    如果兩個相鄰的元素不同或者為0,則不做其他操作
    :param list_data: list 一維列表
    :return: None
    """
    zero_to_end(list_data)
    for i in range(3):
        if list_data[i] == 0: break
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2
            del list_data[i + 1]
            list_data.append(0)
            global score
            score += list_data[i]

6. 游戲初始化

為了讓游戲設置得靈活一點,增加全局變數init_count,表示初始方塊需賦值2或者4的個數,通常為2。

init_count = 2  # 初始值的個數


def init():
    """
    游戲初始化
    :return: None
    """
    print(f"""當前分數:{score}\n操作方式:q退出 n認輸
       w(上)
a(左)  s(下)  d(右)""")
    for i in range(init_count):  # 隨機生成init_count個初始值
        random_site()
    print_list()

7. 各方向移動操作

首先要明確,核心函數中第2節的相等相撞合併函數,僅僅針對一維列表,而整個游戲以二維列表為主,因此需要復用該代碼:

def merge():
    """合併操作,詳見merge_single()函數"""
    for i in range(4):
        merge_single(after_source[i])

接下來逐一分析,左右上下操作如何實現...

(1)左(基礎操作)

調用merge函數,完成滑動合併,然後比較前後矩陣相等來決定是否調用隨機填充(即調用compare_matrix函數)

def left():
    """向左操作"""
    merge()

(2)右

最暴力無腦的辦法就是複製上述函數代碼,然後更改,但是我一直寫這些簡單清晰的函數,就是為了復用,所以這裡應該想辦法復用merge等代碼,我以向左操作為基礎,僅看merge函數,假設每一行都逆轉,再進行向左的核心操作,再逆轉回去,不就可以了嗎,比如:

[0, 2, 2, 4]向右移動操作後變成[0, 0, 4, 4]
[0, 2, 2, 4] >逆轉> [4, 2, 2, 0] >merge> [4, 4, 0, 0] >逆轉> [0, 0, 4, 4]

因此代碼如下:

def reverse():
    """逆轉2048二維列表中的每一行一維列表"""
    for i in range(4):
        after_source[i].reverse()
def right():
    """向右操作"""
    reverse()
    merge()
    reverse()

(3)上

同樣為了復用,以向左操作為基礎,僅看merge函數,假設進行矩陣轉置,然後調用merge操作,再轉置,比如:

[0, 2, 0, 0]
[4, 2, 0, 0]
[0, 0, 4, 0]
[4, 0, 0, 0]
轉置後
[0, 4, 0, 4]
[2, 2, 0, 0]
[0, 0, 4, 0]
[0, 0, 0, 0]
調用merge後
[8, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
[0, 0, 0, 0]
再轉置回來,得到結果
[8, 4, 4, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]

因此代碼如下(3種轉置均可):

def transposition():
    """二維列表轉置(矩陣轉置)"""
    for x in range(4):
        for y in range(x, 4):
            after_source[x][y], after_source[y][x] = after_source[y][x], after_source[x][y]

def transposition():
    """二維列表轉置(矩陣轉置)"""
    new_map = [list(item) for item in zip(*after_source)]
    after_source.clear()
    after_source.extend(new_map)

def transposition():
    """二維列表轉置(矩陣轉置)"""
    new_map = [list(item) for item in zip(*after_source)]
    after_source[:] = new_map
def up():
    """向上操作"""
    transposition()
    merge()
    transposition()

(3)下

根據上移和右移操作所得的靈感,同樣是以左移為基礎操作。假設進行矩陣轉置,逆轉後,調用merge操作,再逆轉,再轉置,比如:

[0, 2, 0, 0]
[4, 2, 0, 0]
[0, 0, 4, 0]
[4, 0, 0, 0]
轉置後
[0, 4, 0, 4]
[2, 2, 0, 0]
[0, 0, 4, 0]
[0, 0, 0, 0]
逆轉每一行後
[4, 0, 4, 0]
[0, 0, 2, 2]
[0, 4, 0, 0]
[0, 0, 0, 0]
調用merge後
[8, 0, 0, 0]
[4, 0, 0, 0]
[4, 0, 0, 0]
[0, 0, 0, 0]
再逆轉回來
[0, 0, 0, 8]
[0, 0, 0, 4]
[0, 0, 0, 4]
[0, 0, 0, 0]
再轉置回來,得到結果
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[8, 4, 4, 0]

因此代碼如下:

def down():
    """向下操作"""
    transposition()
    reverse()
    merge()
    reverse()
    transposition()

五、最終代碼

"""
    2048 GAME
"""
import random
import copy

score = 0  # 分數
init_count = 2  # 初始值的個數
before_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作前的矩陣
after_source = [
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0],
    [0, 0, 0, 0]
]  # 操作後的矩陣(當前列印的矩陣)
random_tuple = (2, 4)  # 初始添加的值、移動時添加的值


# Controller層
def zero_to_end(list_data):
    """
    重排序函數(核心演算法)
    非0元素移至最前(保持順序),0元素移至最後,充當中間人處理列表的角色
    :param list_data: list 一維列表
    :return: None
    """
    for i in range(3, -1, -1):
        if not list_data[i]:
            del list_data[i]
            list_data.append(0)


def merge_single(list_data):
    """
    合併元素函數(核心演算法)
    重排序後,左邊兩個相鄰相同的非0元素相加,後方補0,並加分(可diy)
    如果兩個相鄰的元素不同或者為0,則不做其他操作
    :param list_data: list 一維列表
    :return: None
    """
    zero_to_end(list_data)
    for i in range(3):
        if list_data[i] == 0: break
        if list_data[i] == list_data[i + 1]:
            list_data[i] *= 2
            del list_data[i + 1]
            list_data.append(0)
            global score
            score += list_data[i]


def random_site():
    """
    隨機填充0元素函數(非核心)
    隨機挑選0元素的位置,進行隨機填充random_list中的任意一個元素
    可通過增刪改變random_list中的元素,從而影響到隨機填充的數字
    :return: None
    """
    random_list_len = len(random_tuple)
    while True:
        x = random.randint(0, 3)
        y = random.randint(0, 3)
        if after_source[x][y] == 0:
            after_source[x][y] = random_tuple[random.randint(0, random_list_len - 1)]
            break


def merge():
    """合併操作,詳見merge_single()函數"""
    for i in range(4):
        merge_single(after_source[i])


def reverse():
    """逆轉2048二維列表中的每一行一維列表"""
    for i in range(4):
        after_source[i].reverse()


def transposition():
    """二維列表轉置(矩陣轉置)"""
    for x in range(4):
        for y in range(x, 4):
            after_source[x][y], after_source[y][x] = after_source[y][x], after_source[x][y]


def compare_matrix():
    """
    二維數組比較
    操作前後的二維數組(矩陣)進行比較
    如果不相等,說明有元素可移動,當移動時調用random_site()函數
    """
    if not (before_source == after_source):
        random_site()


def left():
    """向左操作"""
    merge()


def right():
    """向右操作"""
    reverse()
    merge()
    reverse()


def up():
    """向上操作"""
    transposition()
    merge()
    transposition()


def down():
    """向下操作"""
    transposition()
    reverse()
    merge()
    reverse()
    transposition()


# View層
def init():
    """
    游戲初始化
    :return: None
    """
    print(f"""當前分數:{score}\n操作方式:q退出 n認輸
       w(上)
a(左)  s(下)  d(右)""")
    for i in range(init_count):  # 隨機生成init_count個初始值
        random_site()
    print_list()


def print_list():
    """列印游戲過程中必看的矩陣信息"""
    for single_list in after_source:
        print(single_list)


def forfeit():
    """認輸"""
    print(f"玩家已認輸,最終得分:{score}")
    raise KeyboardInterrupt


def main():
    """程式入口:初始化 + 輸入 + 輸出"""
    init()
    while True:
        try:
            global before_source
            before_source = copy.deepcopy(after_source)
            key = input("鍵入:")
            if key == "a": left()
            if key == "d": right()
            if key == "w": up()
            if key == "s": down()
            if key == "n": forfeit()
            if key == "q": break
            compare_matrix()
            print(f"當前分數:{score}")
            print_list()
        except KeyboardInterrupt:
            break


main()


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

-Advertisement-
Play Games
更多相關文章
  • 趁著國慶前夕整了一個vite4結合react18搭建後臺管理模板,搭配上位元組團隊react組件庫ArcoDesign,整體操作功能非常絲滑。目前功能支持多種模板佈局、暗黑/亮色模式、國際化、許可權驗證、多級路由菜單、tabview標簽欄快捷菜單、全屏控制等功能。極簡非凡的佈局界面、高定製化模塊,用心打 ...
  • 針對改動範圍大、影響面廣的需求,我通常會問上線了最壞情況是什麼?應急預案是什麼?你帶開關了嗎?。當然開關也是有成本的,接下來本篇跟大家一起交流下高頻發佈支撐下的功能開關技術理論與實踐結合的點點滴滴。 ...
  • RPC,Remote Procedure Call 即遠程過程調用,與之相對的是本地服務調用,即LPC(Local Procedure Call)。本地服務調用比較常用,像我們應用內部程式(註意此處是程式而不是方法,程式包含方法)互相調用即為本地過程調用,而遠程過程調用是指在本地調取遠程過程進行使用... ...
  • 使用雙指針進行原地移除元素 題目描述 給定一個數組 nums 和一個值 val,需要將數組中所有等於 val 的元素原地刪除,並返回刪除後數組的新長度。 要求: 不使用額外的數組空間 只能使用 O(1) 額外空間 數組中超過新長度後面的元素可以忽略 示例 1: 輸入:nums = [3,2,2,3] ...
  • 在Python中,字元串可以用單引號或雙引號括起來。'hello' 與 "hello" 是相同的。您可以使用print()函數顯示字元串文字: 示例: print("Hello") print('Hello') 將字元串分配給變數是通過變數名後跟等號和字元串完成的: 示例 a = "Hello" p ...
  • 前言 ThreadLocal可以用來存儲線程的本地數據,做到線程數據的隔離 ThreadLocal的使用不當可能會導致記憶體泄漏,排查記憶體泄漏的問題,不僅需要熟悉JVM、利用好各種分析工具還耗費人工 如果能明白其原理並正確使用,就不會導致各種意外發生 本文將從使用場景、實現原理、記憶體泄漏、設計思想等層 ...
  • 目錄場景1 定義全部異常處理類2 替換request引用3 代碼優化總結 場景 本文前端用的是阿裡的Ant-Design框架,其他框架也有全局攔截器,思路是相同,具體實現自行百度下吧 因為每次都需要調介面,都需要單獨處理異常情況(code !=0),因此前端需要對後端返回的通用響應進行統一處理,比如 ...
  • 在Dart學習的第02天,我們通過基礎語法說明和樣例代碼的方式,學習了Dart的16個基礎語法,這些基礎語法給我們後面編寫的Flutter程式打下來堅實基礎。今天,我們繼續深入學習Dart乃至所有編程語言都非常重要的部分:可迭代的集合…… ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...