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

  

小结

这份代码写的太差劲,所以我也没有也不想过多的去说它。

转载于:https://www.cnblogs.com/litran/p/10541110.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的贪吃程序,使用C语言编写。该程序使用ncurses库来实现游戏的图形界面,可以在Linux系统中编译和运行。 ``` #include <stdio.h> #include <stdlib.h> #include <time.h> #include <ncurses.h> #define ROWS 20 #define COLS 40 #define SNAKE_LEN 5 typedef struct { int row; int col; } point_t; enum direction {UP, DOWN, LEFT, RIGHT}; int main() { // 初始化ncurses initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); curs_set(FALSE); // 初始化游戏界面 clear(); refresh(); mvprintw(0, 0, "Press any key to start..."); getch(); // 初始化随机数生成器 srand(time(NULL)); // 初始化贪吃 point_t snake[SNAKE_LEN]; snake[0].row = ROWS / 2; snake[0].col = COLS / 2; for (int i = 1; i < SNAKE_LEN; i++) { snake[i].row = snake[0].row; snake[i].col = snake[0].col - i; } enum direction dir = RIGHT; int grow = 0; // 初始化食物 point_t food; food.row = rand() % ROWS; food.col = rand() % COLS; // 开始游戏循环 while (1) { // 绘制游戏界面 clear(); for (int r = 0; r < ROWS; r++) { for (int c = 0; c < COLS; c++) { if (r == snake[0].row && c == snake[0].col) { mvprintw(r, c, "H"); } else if (grow && r == snake[SNAKE_LEN-1].row && c == snake[SNAKE_LEN-1].col) { mvprintw(r, c, "O"); grow = 0; } else if (r == food.row && c == food.col) { mvprintw(r, c, "X"); } else { for (int i = 1; i < SNAKE_LEN; i++) { if (r == snake[i].row && c == snake[i].col) { mvprintw(r, c, "o"); break; } } } } } refresh(); // 处理用户输入 int ch = getch(); if (ch == KEY_UP && dir != DOWN) { dir = UP; } else if (ch == KEY_DOWN && dir != UP) { dir = DOWN; } else if (ch == KEY_LEFT && dir != RIGHT) { dir = LEFT; } else if (ch == KEY_RIGHT && dir != LEFT) { dir = RIGHT; } // 移动贪吃 for (int i = SNAKE_LEN-1; i > 0; i--) { snake[i].row = snake[i-1].row; snake[i].col = snake[i-1].col; } switch (dir) { case UP: snake[0].row--; break; case DOWN: snake[0].row++; break; case LEFT: snake[0].col--; break; case RIGHT: snake[0].col++; break; } // 检查贪吃是否撞墙或吃到食物 if (snake[0].row < 0 || snake[0].row >= ROWS || snake[0].col < 0 || snake[0].col >= COLS) { mvprintw(ROWS/2, COLS/2-4, "Game Over"); break; } else if (snake[0].row == food.row && snake[0].col == food.col) { grow = 1; do { food.row = rand() % ROWS; food.col = rand() % COLS; } while (food.row == snake[0].row && food.col == snake[0].col); } // 暂停一段时间,控制游戏速度 usleep(100000); } // 清理ncurses endwin(); return 0; } ``` 运行该程序,将会在终端中显示一个贪吃游戏界面。玩家可以使用方向键来控制贪吃的移动,吃到食物会让贪吃变长,撞到墙或自己则游戏结束。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值