C語言之貪吃蛇(ncurses)


這篇文章原先部署於github, 后來覺得博客園比較快, 就用博客園了. 

 https://coinsjack.github.io/2017/03/01/C%E8%AF%AD%E8%A8%80%E4%B9%8B%E8%B4%AA%E5%90%83%E8%9B%87%EF%BC%88ncurses%EF%BC%89/

聲明: 以下內容可能會引起某些讀者不適, 請小心閱讀. 有些內容並沒有詳細介紹, 可能簡單理解也是錯誤的, 但是這都是為了盡量簡單。

前言: 代碼是很久之前寫的,屬於邊想邊寫的那種,很混亂。

推薦材料:

 



貪吃蛇應該是我們這代人都玩過的游戲。而如果我們要寫一個貪吃蛇最需要考慮的就是貪吃蛇是如何移動的。其實貪吃蛇的移動就是尾部的減少和頭部的增加。

 

篇文章:

  • 介紹一些ncurses庫的基礎內容
  • 貪吃蛇游戲的代碼解釋

    介紹一些ncurses庫的基礎內容

    ncurses

    curses是一個在命令行下面的圖形函數庫,而ncurses的意思是 new curses。

ncurses的一些函數的簡單解釋

 

#include <ncurses.h>
int main()
{	int ch;
	initscr();			/* Start curses mode 		*/
	raw();				/* Line buffering disabled	*/
	keypad(stdscr, TRUE);		/* We get F1, F2 etc..		*/
	noecho();			/* Don't echo() while we do getch */
    	printw("Type any character to see it in bold\n");
	ch = getch();			/* If raw() hadn't been called
					 * we have to press enter before it
					 * gets to the program 		*/
	if(ch == KEY_F(1))		/* Without keypad enabled this will */
		printw("F1 Key pressed");/*  not get to us either	*/
					/* Without noecho() some ugly escape
					 * charachters might have been printed
					 * on screen			*/
	else
	{	printw("The pressed key is ");
		attron(A_BOLD);
		printw("%c", ch);
		attroff(A_BOLD);
	}
	refresh();			/* Print it on to the real screen */
    	getch();			/* Wait for user input */
	endwin();			/* End curses mode		  */
	return 0;
}

  


我們應該注意到這個程序並沒有導入標准輸入輸出庫,而是導入了ncurses.h文件。在這個程序里面也沒有使用printf(format printing),而是使用了printw這個函數(print window)就是打印到標准屏幕的意思這是我從這里拿到的一個例子。

  • initscr:初始化屏幕,啟動curses模式
  • raw:關閉行緩沖,一般的我們在命令行中輸入數據的時候,只有當我們按下回車之后,才可以把數據提交給程序
  • noecho: 負責按鍵事務,我不太懂
  • noecho: 關閉回顯(按下一個字符之后,屏幕並不顯示。比如說在普通的情況下按下a,a會被顯示在屏幕上)
  • getch: 獲取一個字符
  • attron: 在后面的打印中增加打印屬性,這里是粗體的意思
  • attroff: 移除屬性
  • refresh:之前的printw只是打印到邏輯屏幕,而refresh函數會依據邏輯屏幕的內容,刷新到物理屏幕。
  • endwin:退出curses模式

其他的可以自己慢慢去學習

ncurses庫的安裝和使用

安裝

如果你是使用debian系列的linux發行版,例如ubuntu,應該直接鍵入命令:

sudo apt-get install libncurses5-dev

  


 就可以了。
如果是別的系列的版本,建議下載源碼包,編譯安裝。或者百度搜索正確的答案。

使用

一般的,只需要在gcc編譯命令的后面加上-lncurses就可以正確導入ncurses庫了。

1
gcc -o demo demo.c -std=c99 -lncurses

-std=c99的意思是使用c99的標准來編譯代碼。
沒有使用過命令行編譯操作的同學需要自己去學習。

 

貪吃蛇的代碼解釋

從哪里得到

點擊這里。這份代碼是我寫的,也確實寫的不好。但也可以隨便看看。

碼結構

snake.h

|-- hungry_snake // 可執行文件,
|-- main.c
|-- snake.c
|-- snake.h
`-- wellcome.h
 
0 directories, 5 files

  

hungry_snake是一個可執行文件,在debian下編譯的,在centos下面可能需要重新編譯。
main.c是程序的主要邏輯部分。
wellcome.h包含了歡迎界面的代碼實現。

/*
 * snake.h
 */
#ifndef SNAKE_H
#define SNAKE_H
#include <ncurses.h>
// 這個枚舉類型我其實沒有用
typedef enum state
  {
    goleft,
    goright,
    goup,
    godown
  }state;
// 這個是蛇的一節,也就是一個坐標
typedef struct body
{
  int y;
  int x;
  struct body *prev;
  struct body *next;
}Body ,*Pbody;
// 這個是蛇的結構定義,有一個虛蛇頭,蛇頭和蛇尾,還有蛇的長度啊,運動狀態啊
typedef struct snake
{
  Pbody vir_head;
  Pbody head;
  Pbody tail;
  int length;
  state movement;					// 我沒有用
  char uping;
  char downing;
  char lefting;
  char righting;
}Snake, *Psnake;
int FOOD[1000][1000]; 				// 食物坐標,標記哪個位置有食物
int food_num; 						// 食物數量
/*
** body part
*/
Pbody init_body();					 // 初始化蛇身
void add_body(Pbody jack, Pbody bd); // 增加一節
Pbody bd(int lines,int cols);		// 構造一個蛇節
void print_snake(Pbody jack);	// 顯示那條蛇
void print_bd(Pbody bd);	// 顯示蛇節
Pbody get_head(Pbody jack);	// 得到蛇頭部節點
Pbody add_head(Pbody oldhead,Pbody newhead); // 增加節點到頭部
Pbody del_tail(Pbody jack); // 刪除蛇尾
Pbody get_tail(Pbody jack); // 得到蛇尾節點指針
/*
** snake part
*/
Psnake init_snake();	//初始化蛇
int snake_go_right(Psnake snake);	// 蛇的移動
int snake_go_left(Psnake jack);
void show_snake(Psnake jack);	// 屏幕上顯示蛇
void init_show_snake(Psnake jack); // 忘了
int snake_go_down(Psnake jack);	// 蛇的移動
int snake_go_up(Psnake jack);
int snake_can_move(Psnake jack, char ch); // 判斷蛇能否往目標方向移動
int body_in_snake(int row, int col, Psnake jack); // 判斷蛇身是否覆蓋坐標
/* food part */
void putfood(Psnake jack); // 隨機投放食物
int count_food(); // 食物技術
/* game_over */
void game_over(Psnake jack); // 游戲結束
#endif // SNAKE_H

  


main.c
這里有一些結構定義和函數聲明。

下面是程序主體邏輯部分:

#include <unistd.h>
#include <stdlib.h>
#include "snake.h"
#include <ncurses.h>
#include "wellcome.h"
#include <pthread.h>
int ch;
int GAMEOVER;
/*
void get_command(void *i)
{
  ch = getch();
}
*/
int main()
{
  initscr();
  start_color();
  keypad(stdscr,TRUE);
  curs_set(0);
  noecho();
  halfdelay(4);
  init_pair(1,COLOR_WHITE,COLOR_BLACK);
  init_pair(2,COLOR_BLACK,COLOR_WHITE);
  print_snake_logo(stdscr);
  mvprintw(LINES-1,COLS-20,"Producted by Jack");
  mvprintw(LINES-4,(COLS-21)/2,"|-> START");
  printw(" (? for help)");
  refresh();
  int choice;
  char goout = 0;
  while (!goout){
  switch (choice = getch() ){
  case 'q':
    endwin();
    return 0;
  case 'e':
    goout = 1;
    break;
  }
  }
  /*
  pthread_t id;
  pthread_create(&id,NULL,get_command,NULL);
  */
  
  Psnake jack = init_snake();
  init_show_snake(jack);
  ch = mvgetch(LINES-1,0);
  srand((unsigned)time(0));
  
  for (int i = 0; i<10; i++)
    putfood(jack);
  refresh();
  int noout = 1;
  int c = 0;
  while(noout){
	ch = getch();
  switch (ch){
  case KEY_RIGHT:
    c = snake_go_right(jack);
    if ( c == -1){
	noout = 0;
	GAMEOVER = 1;
	break;
    }
      continue;
  case KEY_DOWN :
       c = snake_go_down(jack);
      if ( c == -1){
	noout = 0;
	GAMEOVER = 1;
	break;
      }
      continue;
  case KEY_LEFT:
    c = snake_go_left(jack);
      if ( c == -1){
	noout = 0;
	GAMEOVER = 1;
	break;
      }
      continue;
  case KEY_UP:
    c = snake_go_up(jack);
      if ( c == -1){
	noout = 0;
	GAMEOVER = 1;
	break;
      }
      continue;
  case 'q':
    game_over(jack);
    endwin();
    return 0;
  }
  if (jack->righting){
    c = snake_go_right(jack);
    if (c == -1){
      GAMEOVER = 1;
      break;
    }
  }
  else if (jack->lefting){
    c = snake_go_left(jack);
    if (c == -1){
      GAMEOVER = 1;
      break;
    }
  }
  else if(jack->uping){
    c = snake_go_up(jack);
    if (c == -1){
      GAMEOVER = 1;
      break;
    }
  }
  else if(jack->downing){
    c = snake_go_down(jack);
    if (c == -1){
      GAMEOVER = 1;
      break;
    }
  }
  if (food_num < 30)
    putfood(jack);
  mvprintw(LINES-1,2,"-- socre %d --",jack->length);
  refresh();
  }
  if(GAMEOVER)
    game_over(jack);
    
  refresh();
  
  endwin();
  return 0;
}

  


編譯
 

gcc -o snake.out snake.h snake.c main.c wellcome.h -lncurses -std=c99

  

小結

這份代碼寫的太差勁,所以我也沒有也不想過多的去說它。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM