c語言第一個小游戲:貪吃蛇小游戲05

貪吃蛇脫韁自動向右走:脫韁的野蛇

#include <curses.h>

#include <stdlib.h>

struct snake{

????????int hang;

????????int lie;

????????struct snake *next;

};

struct snake *head;

struct snake *tail;

void initNcurse()

{

????????initscr();

????????keypad(stdscr,1);

}

int ?hasSnakeNode(int i,int j)

{

????????struct snake *p;

????????p = head;

????????while(p != NULL){

????????????????if(p->hang==i && p->lie==j){

????????????????????????return 1;

????????????????}

????????????????p=p->next;

????????}

????????return 0;

}

void gamepic()

{

????????int hang;

????????int lie;

????????move(0,0);

????????for(hang=0;hang<20;hang++){

????????????????if(hang == 0){

????????????????????????for(lie=0;lie<20;lie++){

????????????????????????????????printw("--");

????????????????????????}

?????????????????printw("\n");

????????????????}

????????????????if(hang>=0 && hang<=19){

????????????????????????for(lie=0;lie<=20;lie++){

?????????????????????????????????if(lie==0||lie==20){

?????????????????????????????????????????printw("|");

?????????????????????????????????}else if(hasSnakeNode(hang,lie)){

????????????????????????????????????????printw("[]");

?????????????????????????????????}

?????????????????????????????????else{

?????????????????????????????????????????printw(" ?");

?????????????????????????????????}

????????????????????????}

????????????????????????printw("\n");

????????????????}

????????????????if(hang == 19){

????????????????????????for(lie=0;lie<20;lie++){

?????????????????????????????????printw("--");

????????????????????????}

????????????????????????printw("\n");

????????????????}

??????????}

??????????printw("by shijintao");

??????????printw("\n");

???}

void addNode()

{

????????struct snake *new;

????????new =(struct snake *)malloc(sizeof(struct snake));

????????new->hang=tail->hang;

????????new->lie=tail->lie+1;

????????tail->next = new;

????????tail = new;

????????new->next = NULL;

}

void ?initSnake()

{

????????struct snake *p;

????????while(head != NULL){

?????????????p=head;

?????????????head=head->next;

?????????????free(p);

????????}

????????head = (struct snake *)malloc(sizeof(struct snake));

????????head->hang=2;

????????head->lie=2;

????????head->next=NULL;

????????tail = head;

????????addNode();

????????addNode();

}

void deleteNode()

{

????????struct snake *p;

????????p = head;

????????head = head->next;

????????free(p);

}

void moveSnake()

{

????????addNode();

????????deleteNode();

????????if(tail->hang==0||tail->hang==20||tail->lie==20||tail->lie==0){

????????????????initSnake();

????????}

}

int main()

{

????????????????int con;

????????????????initNcurse();

????????????????initSnake();

????????????????gamepic();

????????????????while(1){

???????????????????????????moveSnake();

???????????????????????????gamepic();

???????????????????????????refresh();

???????????????????????????usleep(100000);

????????????????}

????????????????getch();

????????????????endwin();

????????????????return 0;

}

這邊最主要改動的代碼地方在于main函數里面的?

????????主要是我們要先想一下如果只是一味的想讓這個蛇自己往右邊走(這個右邊就是代碼效果,也沒有設置方向,就是新節點的插入導致一個向右邊走的效果),肯定要把監測右方向鍵給去掉,然后死循環就可以實現l,不斷的moveSnake(不斷的addNode,deleteNode)

while(1){

???????????????????????????moveSnake();

???????????????????????????gamepic();

???????????????????????????refresh();

???????????????????????????usleep(100000);

????????????????}

原始代碼中,getch()?函數的調用確實會導致屏幕刷新。這是因為?getch()?在等待用戶輸入時,會隱式觸發一次屏幕刷新

1.?getch()?的隱式刷新機制

在 ncurses 中,getch()?不僅用于獲取用戶輸入,還會執行以下操作:

  • 刷新屏幕:在等待按鍵時,ncurses 會自動將當前緩沖區內容渲染到屏幕上。
  • 處理輸入事件:捕獲用戶按下的鍵并返回其 ASCII 碼(或特殊鍵的編碼,如方向鍵)。

因此,當你在循環中調用?getch()?時,即使沒有顯式調用?refresh(),屏幕也會被刷新。這解釋了為什么你之前的代碼能正常顯示畫面。

2. 為什么現在需要顯式調用?refresh()

在你的新代碼中,循環結構發生了變化:

while(1){

????moveSnake();??????// 更新蛇位置

????gamepic();????????// 繪制畫面

????refresh();????????// 顯式刷新屏幕

????usleep(100000);???// 延時控制速度}

這里的關鍵區別是:蛇的移動不再依賴用戶輸入,而是通過定時器自動更新(usleep())。因此:

  • 如果沒有?refresh(),繪制操作(gamepic())的結果只會停留在緩沖區,無法顯示。
  • usleep()?是純延時函數,不會觸發屏幕刷新,必須手動調用?refresh()

3. 兩種實現方式的對比

舊代碼(依賴?getch()

新代碼(自動移動)

蛇僅在用戶按鍵時移動

蛇自動持續移動

getch()?隱式刷新屏幕

必須顯式調用?refresh()

游戲節奏由用戶控制

游戲節奏由?usleep()?控制

4. 最佳實踐建議

  • 始終顯式調用?refresh():即使在有?getch()?的場景中,顯式刷新能讓代碼邏輯更清晰,避免依賴隱式機制。
  • 分離輸入處理和畫面刷新:在自動移動的游戲中,用戶輸入(如方向鍵)和屏幕刷新是獨立的操作,需要分別處理。

總結

原始代碼之所以能正常顯示,是因為?getch()?在等待輸入時隱式刷新了屏幕。而在新的自動移動實現中,由于沒有依賴?getch(),必須通過?refresh()?手動刷新屏幕才能看到蛇的移動效果。這是 ncurses 編程中的一個常見細節,理解它有助于寫出更健壯的代碼!

貪吃蛇風騷走位之支持方向變換

????????那么問題來了,方向的改變要監測鍵盤的按鍵需要一個while(1)循環(不斷的檢測用戶的輸入)? ?還有,蛇的脫韁的走也需要一個while(1)循環支持,就是這兩個while(1)需要同時跑,那么我們現在單線程,就是目前無法一心二用,所以我們還做不了,需要學習多線程。

Linux多線程概念引入及編碼實現

我們做貪吃蛇的時候不是要需要控制按方向鍵和moveSnake(脫韁的野蛇)且 要同時運行且都是要用while死循環去進行接收嗎,那么我們單線程就無法做到了,這個時候要學習多線程,這樣我們就是可以同時執行兩個死循環。

所以我們先學習一下多線程的基本原理

想讓我們先看個單線程的例子

我想通過這個單線程的返回結果,和等下多線程的返回結果做對比就很容易理解多線程的意思了;

????????因為在單線程環境下,函數是順序執行的。當調用 FUNC1 時,即使里面有sleep操作,它也只是讓執行流程暫停一下,并不會切換到其他函數執行,所以會先輸出this is FUNC1,等待sleep時間過后再繼續輸出this is FUNC1

假如現在這段代碼變成了多線程

在多線程環境下,當一個線程執行到類似sleep操作時,它會暫停執行一段時間,這時操作系統會調度其他可運行的線程。

假設存在FUNC1FUNC2兩個函數分別在不同線程中執行:

  1. FUNC1所在線程執行到sleep時,該線程會進入休眠狀態,此時操作系統會尋找其他可運行的線程,很可能就會找到FUNC2所在線程(如果FUNC2線程處于可運行狀態),然后開始執行FUNC2
  2. FUNC2執行到類似sleep操作后,它也會暫停,操作系統又會重新調度,此時FUNC1線程休眠時間結束的話,可能會繼續執行FUNC1;如果FUNC1還沒結束休眠,且還有其他可運行線程,就可能會執行其他線程。

這種線程間的切換和執行順序,取決于操作系統的線程調度機制,沒有辦法精確地預測哪個線程會在特定時刻執行,除非使用線程同步機制(如鎖、條件變量、信號量等)來控制線程的執行順序。例如在 Java 中,可以使用join方法讓一個線程等待另一個線程執行完畢;或者使用同步塊和waitnotify方法來控制線程間的協作和執行順序。

通過下面的例子學習一下

運行后是這樣的 ?一直循環這個兩句話,就是說不一定每次調用的都是一樣的(在多核是同時運行,在單核是他們去爭奪cpu資源),但是可以保證他們可以同時運行的(這就夠了)

  • #include <pthread.h>?:引入 POSIX 線程庫的頭文件,該庫用于在 C 語言中實現多線程編程,后續用到的?pthread_t?類型以及?pthread_create?等函數聲明都在這個頭文件中。
  • func1?和?func2?是兩個線程函數。它們的返回類型為?void *?,這是 POSIX 線程庫中線程函數的標準返回類型要求。
  • 每個函數內部都有一個無限循環?while(1)?。在循環中,先通過?printf?輸出相應的提示信息("this is func1\n"?或?"this is func2\n"?),然后調用?sleep(1)?讓線程暫停 1 秒鐘。這使得線線程變量聲明pthread_t th1;?和?pthread_t th2;?聲明了兩個?pthread_t?類型的變量,pthread_t?用于標識線程,th1?和?th2?分別用來標識即將創建的兩個不同線程。
  • 線程創建
    • pthread_create(&th1,NULL,func1,NULL);?調用?pthread_create?函數創建了一個新線程,該線程的標識符存儲在?th1?中。第二個參數?NULL?表示使用默認的線程屬性;第三個參數?func1?指明這個新線程要執行的函數是?func1?;第四個參數?NULL?表示不向?func1?函數傳遞額外參數 。
    • 同理,pthread_create(&th2,NULL,func2,NULL);?創建了另一個線程,其標識符為?th2?,執行函數為?func2?。
  • 無限循環與程序結束while(1);?是一個無限循環,這里的作用是防止?main?函數執行結束。因為在多線程程序中,如果主線程(main?函數所在線程)執行完畢退出,那么其他子線程也會被操作系統強制終止。通過這個無限循環,讓主線程一直保持運行狀態,從而使得創建的?th1?和?th2?線程能持續執行它們各自的任務。return 0;?理論上不會執行到,不過按照?main?函數的規范,需要有返回值聲明。

整體功能

這段代碼實現了一個簡單的多線程程序,創建了兩個線程分別執行?func1?和?func2?函數,這兩個線程會并發地不斷打印各自的提示信息并間隔 1 秒。但代碼存在一些不足,比如沒有對線程的返回值進行處理,也沒有合理的方式來終止線程或主線程,實際應用中可以進一步完善,比如添加信號處理機制來結束程序等 。

  • 程會不斷重復打印信息并間隔 1 秒。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80893.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80893.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80893.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

react-diff-viewer 如何實現語法高亮

前言 react-diff-viewer 是一個很好的 diff 展示庫&#xff0c;但是也有一些坑點和不完善的地方&#xff0c;本文旨在描述如何在這個庫中實現自定義語法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…

coco數據集mAP評估

0 coco數據集劃分說明 1 用yolo自帶的評估 from ultralytics import YOLOmodel YOLO("../spatial-perception/checkpoints/yolo11n.pt")metrics model.val(data"./coco.yaml", save_jsonTrue) ## save_json為True,可以把預測結果存成json文件&#xff…

sensitive-word-admin v2.0.0 全新 ui 版本發布!vue+前后端分離

前言 sensitive-word-admin 最初的定位是讓大家知道如何使用 sensitive-word&#xff0c;所以開始想做個簡單的例子。 不過秉持著把一個工具做好的原則&#xff0c;也收到很多小伙伴的建議。 v2.0.0 在 ruoyi-vue&#xff08;也非常感謝若依作者多年來的無私奉獻&#xff09…

好消息!PyCharm 社區版現已支持直接選擇 WSL 終端為默認終端

在過去&#xff0c;PyCharm 社區版雖然提供了鏈接 Windows 子系統 Linux&#xff08;WSL&#xff09;終端的能力&#xff0c;但用戶無法在設置中直接指定 WSL 為默認終端&#xff0c;這一功能僅限于專業版使用者。 而現在&#xff0c;在 PyCharm 2025.1.1 版本中&#xff0c;Je…

【Redis】string 字符串

文章目錄 string 字符串常用命令設置和獲取setgetmget & mset 計數操作incr & incrbydecr & decrbyincrbyfloat 字符串操作appendstrlengetrangesetrange 應用場景 string 字符串 關于 Redis 的字符串&#xff0c;有幾點需要注意 Redis 所有的 key 的類型都是字符…

本地部署firecrawl的兩種方式,自托管和源碼部署

網上資料很多 AI爬蟲黑科技 firecrawl本地部署-CSDN博客 源碼部署 前提條件本地安裝py&#xff0c;node.js環境,嫌棄麻煩直接使用第二種 使用git或下載壓縮包 git clone https://github.com/mendableai/firecrawl.git 設置環境參數 cd /firecrawl/apps/api 復制環境參數 …

(三)毛子整潔架構(Infrastructure層/DapperHelper/樂觀鎖)

文章目錄 項目地址一、Infrastructure Layer1.1 創建Application層需要的服務1. Clock服務2. Email 服務3. 注冊服務 1.2 數據庫服務1. 表配置Configurations2. Respository實現3. 數據庫鏈接Factory實現4. Dapper的DataOnly服務實現5. 所有數據庫服務注冊 1.3 基于RowVersion的…

uni-app微信小程序登錄流程詳解

文章目錄 uni-app微信小程序登錄流程實戰詳解微信小程序登錄流程概述1. 獲取登錄憑證&#xff08;code&#xff09;2. 發送登錄請求3. 保存登錄態4. 登錄狀態管理5. 應用登錄狀態請求攔截器中添加 token自動登錄頁面路由守衛 使用 Vuex 集中管理登錄狀態登錄組件示例登錄流程最…

GUC并發編程和SpringCloud,二者之間的關系

一.提問 我認為&#xff0c;Java開發中&#xff0c;如果項目的每一個小模塊需要不同人員并行開發時&#xff0c;就需要使用SpringCloud&#xff1b;如果要解決系統用戶激增&#xff0c;就是用GUC并發編程。 這個說法對么&#xff1f; 二.解答 你的理解部分正確&#xff0c;但不…

在 Vue 3 中使用 canvas-confetti 插件

&#x1f389; 在 Vue 3 中使用 canvas-confetti 插件 canvas-confetti 是一個輕量、無依賴的 JavaScript 動畫庫&#xff0c;用于在網頁上展示彩帶、慶祝動畫。非常適合用于抽獎、支付成功、活動慶祝等場景。 本教程將指導你如何在 Vue 3 項目中集成并使用該插件。 &#x1…

深入解析Spring Boot項目目錄結構:從新手到規范實踐

一、標準項目結構全景圖 典型的Spring Boot項目&#xff08;Maven構建&#xff09;目錄結構如下&#xff1a; my-spring-project/ ├── src/ │ ├── main/ │ │ ├── java/ # 核心代碼 │ │ │ └── com/ │ │ │ └── exa…

【C語言】宏經典練習題,交換奇偶位

交換奇偶位 寫一個宏&#xff0c;可以將一個整數的二進制位的奇數位和偶數位交換。 #define Swap(x) x(((x&0x55555555)<<1)((x&0xaaaaaaaa)>>1)) int main() {int a 10;Swap(a);printf("%d\n", a);return 0; } 寫宏的思路&#xff1a; 假設…

VSCode-插件:codegeex:ai coding assistant / 清華智普 AI 插件

一、官網 https://codegeex.cn/ 二、vscode 安裝插件 點擊安裝即可&#xff0c;無需復雜操作&#xff0c;國內軟件&#xff0c;無需科學上網&#xff0c;非常友好 三、智能注釋 輸入 // 或者 空格---后邊自動出現注釋信息&#xff0c;&#xff0c;按下 Tab 鍵&#xff0c;進…

FFmpeg 與 C++ 構建音視頻處理全鏈路實戰(三)—— FFmpeg 內存模型

經過前面文章的 FFmpeg 編程實踐&#xff0c;相信你已經對AVPacket和AVFrame這兩個核心結構體不再陌生。當我們編寫代碼時&#xff0c;頻繁調用unref系列 API 釋放內存的操作&#xff0c;或許讓你心生疑惑&#xff1a;這些函數究竟是如何實現內存釋放的&#xff1f;又該在何時準…

c 中的哈希表

哈希是一種可以接受各種類型、大小的輸入&#xff0c;輸出一個固定長度整數的過程。你可以將哈希理解成一種特殊的映射&#xff0c;哈希映射&#xff0c;將一個理論無限的集合A映射到有限整數集合B上。 哈希函數&#xff1a;哈希函數是哈希過程的核心&#xff0c;它決定了哈希映…

【一次成功!】Ubuntu22.04安裝cartographer

之前在ubuntu20.04上成功安裝cartographer&#xff0c;但是翻遍全網都沒找到官方的22.04安裝教程&#xff0c;然后找到小魚的&#xff0c;試了一下&#xff0c;一次成功&#xff0c;連接如下&#xff1a; gd2l-ros2/docs/humble/chapt10/get_started/2.Carto介紹及安裝.md at …

【WPF】Opacity 屬性的使用

在WPF&#xff08;Windows Presentation Foundation&#xff09;中&#xff0c;Opacity 屬性是定義一個元素透明度的屬性&#xff0c;其值范圍是從 0.0&#xff08;完全透明&#xff09;到 1.0&#xff08;完全不透明&#xff09;。由于 Opacity 是在 UIElement 類中定義的&…

8天Python從入門到精通【itheima】-6~10

目錄 7節-開發出第一個Python程序: 1.在cmd窗口寫下第一個最簡單的程序:Hello World!!! 9節: 1.如何卸載python: 2.報錯:不是可運行的程序 ?編輯 3.報錯:無法初始化設備PRN: 4.報錯:語法錯誤——非法的字符 10節-python解釋器: 1.python解釋器的原理: 2.解…

Mac 3大好用的復制粘貼管理工具對比

剪貼板管理器是查看復制粘貼歷史記錄的工具&#xff0c;幾乎是每個蘋果電腦用戶必備工具。市面上的工具很多&#xff0c;我結合了功能豐富、設計簡潔、交互便利整理了目前3款頭部剪貼板應用 Paste、PasteNow、PasteMe。 Paste 優勢&#xff1a;老牌剪切板應用&#xff0c;功能…

2025年全國青少年信息素養大賽初賽模擬測試網站崩了的原因及應對比賽流程

2025年全國青少年信息素養大賽初賽模擬測試昨天開始&#xff0c;由于同一時間涌入太多的人&#xff0c;導致網站的服務器奔了&#xff0c;出現了各種狀況&#xff0c;導致很多人沒有模擬上&#xff0c;大家今天可以刷新或者提前打開網頁。 有的是一直“轉圈圈”&#xff0c;有的…