今天看了一下我之前關注過的一位大神的blog,看到他轉發的一篇貪喫蛇遊戲,感覺很有趣,於是將代碼放到linux系統下運行了一下,很不錯,mark一下!哈哈。
原文連接:點擊打開鏈接
/*********************************************************************************
* Copyright: (C) 2017 zoulei
* All rights reserved.
*
* Filename: snake.c
* Description: This file
*
* Version: 1.0.0(2017年09月09日)
* Author: zoulei <[email protected]>
* ChangeLog: 1, Release initial version on "2017年09月09日 17時05分19秒"
*
********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <curses.h>
#include <signal.h>
#include <sys/time.h>
#define NUM 60
struct direct //用來表示方向的
{
int cx;
int cy;
};
typedef struct node //鏈表的結點
{
int cx;
int cy;
struct node *back;
struct node *next;
}node;
void initGame(); //初始化遊戲
int setTicker(int); //設置計時器
void show(); //顯示整個畫面
void showInformation(); //顯示遊戲信息(前兩行)
void showSnake(); //顯示蛇的身體
void getOrder(); //從鍵盤中獲取命令
void over(int i); //完成遊戲結束後的提示信息
void creatLink(); //(帶頭尾結點)雙向鏈表以及它的操作
void insertNode(int x, int y);
void deleteNode();
void deleteLink();
int ch; //輸入的命令
int hour, minute, second; //時分秒
int length, tTime, level; //(蛇的)長度,計時器,(遊戲)等級
struct direct dir, food; //蛇的前進方向,食物的位置
node *head, *tail; //鏈表的頭尾結點
int main()
{
initscr();
initGame();
signal(SIGALRM, show);
getOrder();
endwin();
return 0;
}
void initGame()
{
cbreak(); //把終端的CBREAK模式打開
noecho(); //關閉回顯
curs_set(0); //把光標置爲不可見
keypad(stdscr, true); //使用用戶終端的鍵盤上的小鍵盤
srand(time(0)); //設置隨機數種子
//初始化各項數據
hour = minute = second = tTime = 0;
length = 1;
dir.cx = 1;
dir.cy = 0;
ch = 'A';
food.cx = rand() % COLS;
food.cy = rand() % (LINES-2) + 2;
creatLink();
setTicker(20);
}
//設置計時器(這個函數是書本上的例子,有改動)
int setTicker(int n_msecs)
{
struct itimerval new_timeset;
long n_sec, n_usecs;
n_sec = n_msecs / 1000 ;
n_usecs = ( n_msecs % 1000 ) * 1000L ;
new_timeset.it_interval.tv_sec = n_sec;
new_timeset.it_interval.tv_usec = n_usecs;
n_msecs = 1;
n_sec = n_msecs / 1000 ;
n_usecs = ( n_msecs % 1000 ) * 1000L ;
new_timeset.it_value.tv_sec = n_sec ;
new_timeset.it_value.tv_usec = n_usecs ;
return setitimer(ITIMER_REAL, &new_timeset, NULL);
}
void showInformation()
{
tTime++;
if(tTime >= 1000000) //
tTime = 0;
if(1 != tTime % 50)
return;
move(0, 3);
//顯示時間
printw("time: %d:%d:%d %c", hour, minute, second);
second++;
if(second > NUM)
{
second = 0;
minute++;
}
if(minute > NUM)
{
minute = 0;
hour++;
}
//顯示長度,等級
move(1, 0);
int i;
for(i=0;i<COLS;i++)
addstr("-");
move(0, COLS/2-5);
printw("length: %d", length);
move(0, COLS-10);
level = length / 3 + 1;
printw("level: %d", level);
}
//蛇的表示是用一個帶頭尾結點的雙向鏈表來表示的,
//蛇的每一次前進,都是在鏈表的頭部增加一個節點,在尾部刪除一個節點
//如果蛇吃了一個食物,那就不用刪除節點了
void showSnake()
{
if(1 != tTime % (30-level))
return;
//判斷蛇的長度有沒有改變
bool lenChange = false;
//顯示食物
move(food.cy, food.cx);
printw("@");
//如果蛇碰到牆,則遊戲結束
if((COLS-1==head->next->cx && 1==dir.cx)
|| (0==head->next->cx && -1==dir.cx)
|| (LINES-1==head->next->cy && 1==dir.cy)
|| (2==head->next->cy && -1==dir.cy))
{
over(1);
return;
}
//如果蛇頭砬到自己的身體,則遊戲結束
if('*' == mvinch(head->next->cy+dir.cy, head->next->cx+dir.cx) )
{
over(2);
return;
}
insertNode(head->next->cx+dir.cx, head->next->cy+dir.cy);
//蛇吃了一個“食物”
if(head->next->cx==food.cx && head->next->cy==food.cy)
{
lenChange = true;
length++;
//恭喜你,通關了
if(length >= 50)
{
over(3);
return;
}
//重新設置食物的位置
food.cx = rand() % COLS;
food.cy = rand() % (LINES-2) + 2;
}
if(!lenChange)
{
move(tail->back->cy, tail->back->cx);
printw(" ");
deleteNode();
}
move(head->next->cy, head->next->cx);
printw("*");
}
void show()
{
signal(SIGALRM, show); //設置中斷信號
showInformation();
showSnake();
refresh(); //刷新真實屏幕
}
void getOrder()
{
//建立一個死循環,來讀取來自鍵盤的命令
while(1)
{
ch = getch();
if(KEY_LEFT == ch)
{
dir.cx = -1;
dir.cy = 0;
}
else if(KEY_UP == ch)
{
dir.cx = 0;
dir.cy = -1;
}
else if(KEY_RIGHT == ch)
{
dir.cx = 1;
dir.cy = 0;
}
else if(KEY_DOWN == ch)
{
dir.cx = 0;
dir.cy = 1;
}
setTicker(20);
}
}
void over(int i)
{
//顯示結束原因
move(0, 0);
int j;
for(j=0;j<COLS;j++)
addstr(" ");
move(0, 2);
if(1 == i)
addstr("Crash the wall. Game over");
else if(2 == i)
addstr("Crash itself. Game over");
else if(3 == i)
addstr("Mission Complete");
setTicker(0); //關閉計時器
deleteLink(); //釋放鏈表的空間
}
//創建一個雙向鏈表
void creatLink()
{
node *temp = (node *)malloc( sizeof(node) );
head = (node *)malloc( sizeof(node) );
tail = (node *)malloc( sizeof(node) );
temp->cx = 5;
temp->cy = 10;
head->back = tail->next = NULL;
head->next = temp;
temp->next = tail;
tail->back = temp;
temp->back = head;
}
//在鏈表的頭部(非頭結點)插入一個結點
void insertNode(int x, int y)
{
node *temp = (node *)malloc( sizeof(node) );
temp->cx = x;
temp->cy = y;
temp->next = head->next;
head->next = temp;
temp->back = head;
temp->next->back = temp;
}
//刪除鏈表的(非尾結點的)最後一個結點
void deleteNode()
{
node *temp = tail->back;
node *bTemp = temp->back;
bTemp->next = tail;
tail->back = bTemp;
temp->next = temp->back = NULL;
free(temp);
temp = NULL;
}
//刪除整個鏈表
void deleteLink()
{
while(head->next != tail)
deleteNode();
head->next = tail->back = NULL;
free(head);
free(tail);
}
這裏主要依賴的是linux提供的libcurses庫。用gcc snake.c -o snake -lcurses命令進行編譯。
測試效果圖:
通過基於這個庫我們還可以編寫坦克大戰,堆箱子等一些小遊戲,感覺蠻有意思的,萬萬沒想到小時候買的遊戲機裏面的遊戲也就這麼點代碼就能實現,小時候覺得特別神奇的玩意兒,沒想到自己也能夠編寫代碼去實現,哈哈,我相信以後程序員一定會是我特別喜歡熱愛的的一個職業,絕不僅僅是因爲工作!!!