C语言之贪吃蛇(curses库函数)

上大学学习编程的初始目的就是冲着游戏来的~不过在刚学习C语言的时候,完全无法利用所学知识弄出一个可玩的游戏╮(╯_╰)╭,学了1年后仍然对最简单的贪吃蛇没有思路(当然不是说没有打代码的思路,而是没有办法弄出动态的东西,而且当时往算法那走了~),直到如今大二,利用寒假的一点时间入门Linux的时候,发现了curses库函数!当我看明白它的作用时,拍案而起,TM这玩意不就是我梦寐以求的可实现界面的东东么!当我学了一点点就觉悟到,用一个move()和printw()函数就完全可以把贪吃蛇这种级别的写出来。。。

既然要写就要写自己喜欢玩的~一条蛇太单调没有可玩价值,弄两条刚好有点挑战性!

游戏规则:

初始时两条蛇会在中间,一条蛇头是‘@‘,食物是‘$’;另一条蛇头是‘+‘,食物是‘*‘,蛇身都是‘#‘。WASD控制‘+‘蛇方向,方向键控制‘@‘蛇方向,每条蛇只能吃自己的食物。撞墙,碰到自己或另一条蛇的身体,吃到另一条蛇的食物都会结束游戏。

运行环境:Linux操作系统,请确认已安装curses库,没有的请在终端输入sudo apt-get libncurses5-dev 安装(移植性不好~可惜了)

编译指令:例如保存为snake.c    就在终端键入:g++ -o snake snake.c -lcurses

执行指令:在终端键入:./snake

思路:

怎样弄出一条蛇呢?我觉得用双链表还是比较方便的,每个结点记录的是当前蛇身的坐标跟前驱后继指针。这样在蛇移动时在头结点前添加一个结点,用move()移动到蛇头坐标,打印蛇头;接着如果没有吃到食物的话,把尾结点删掉,用move()移动到蛇尾,打印空值。之后~完了(高中语文都是及格低分飘过,再加上懒,只能写这么一点。。。)

感慨:

唉~想归想,实际动起手来还是有点麻烦的。主要是键盘响应上的问题,由于是刚刚接触curses库,学得还是很浅的。还是那句话,语文太差,连问题都不知道如何形容。。。反正就是按下任一方向键不松手,一段时间后松开,然后再按别的方向,它会先朝之前的方向移动,直到移动完后在去别的方向。移动一步的时间调得越慢,这种现象越明显。意思就是我不懂怎样在移动一步的时间内把多余的输入getch()忽略掉。还有一个蛋疼的就是让我在无数次尝试中发现了一个客观性的东西:C语言不支持函数的引用参数。我个蛋!害我无数次在终端中输入gcc开头的可爱句子找问题,结果可想而知。本来想加上计分的,后来发现,两条蛇的话,不知道怎样计才公平,唉~还需要一点点数学。。。最后想说一点就是:在编程的很多时候,我们并不是缺少能力,而是缺少工具。(像这个例子:我懂点C语言,但我不懂curses库,我就做不出东西),所以不敢说精通,但至少要熟练运用一门语言,之后再去找合适的工具,编程就不是什么难事情。写到这里,突然提醒了我,也提醒了大家,学编程,基础与实践很重要,反正切不可急!相信别人能做的,自己也可以!

PS:这只是刚写完版本的,bug众多~我觉得在继续学习curses的过程中为程序添加生机,这样才不那么枯燥。虽然curses库是基于文本的图形编程,但一样可以把游戏做得很好看。为QT打下基础先。。。

最后一点,CSND博客里的字符显示是不是出现了点问题。。。

转载请注明出处:http://blog.csdn.net/u013351484?viewmode=contents,谢谢!

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
#include <time.h>

int dir_y[] = {-1, 1, 0, 0};
int dir_x[] = {0, 0, -1, 1};
int vis[100][100];
int scord;

typedef struct body
{
	int y, x;
	struct body *pre, *sub;
} Body;

typedef struct
{
	Body *head, *tail;
	int DIRECTION;
	int HEAD_X;
	int HEAD_Y;
	int FOOD_X;
	int FOOD_Y;
	char food;
	char H;
} Snake;

//随机函数
int Get_Rand_Number(int k)
{
	static int first_time = 0;
	if(!first_time){
		first_time = 1;
		srand((unsigned int)(time(NULL)));
	}
	return rand()%k + 1;
}

void Add(Snake &snake)
{
	Body *s;
	s = (Body *)malloc(sizeof(Body));
	s->y = snake.HEAD_Y;
	s->x = snake.HEAD_X;
	s->pre = NULL;
	s->sub = snake.head;
	snake.head->pre = s;
	snake.head = s;
}

void Free(Body *head)
{
	if(NULL == head) return;
	Free(head->sub);
	free(head);
}

int Judge_Kill(int y, int x)
{
	if(y == LINES - 1 || x == COLS - 1 || y == 0 || x == 0) return 1;
	if(1 == vis[y][x]) return 1;
	return 0;
}

void Initialization(Snake &snake, int y, int x)
{
	snake.DIRECTION = 0;
	int &FOOD_Y = snake.FOOD_Y;
	int &FOOD_X = snake.FOOD_X;
	snake.HEAD_Y = y;
	snake.HEAD_X = x;

	while(1)
	{
		FOOD_Y = Get_Rand_Number(LINES - 3);
		FOOD_X = Get_Rand_Number(COLS - 3);
		if(!vis[FOOD_Y][FOOD_X]){
			vis[FOOD_Y][FOOD_X] = 2;
			break;
		}
	}
	move(FOOD_Y, FOOD_X);
	printw("%c", snake.food);

	snake.head = (Body *)malloc(sizeof(Body));
	snake.head->y = y;
	snake.head->x = x;
	snake.head->pre = snake.head->sub = NULL;
	snake.tail = snake.head;
	move(y, x);
	printw("%c", snake.H);
	refresh();
}

void New_Food(Snake &snake)
{
	scord++;
	int &FOOD_Y = snake.FOOD_Y;
	int &FOOD_X = snake.FOOD_X;
	while(1){
		FOOD_Y = Get_Rand_Number(LINES - 3);
		FOOD_X = Get_Rand_Number(COLS - 3);
		if(!vis[FOOD_Y][FOOD_X]){
			vis[FOOD_Y][FOOD_X] = 2;
			break;
		}
	}
	move(FOOD_Y, FOOD_X);
	printw("%c", snake.food);
}

void Reverse_Direction(Snake &snake)
{
	int &HEAD_Y = snake.HEAD_Y;
	int &HEAD_X = snake.HEAD_X;
	Body *&head = snake.head;

	HEAD_Y += dir_y[snake.DIRECTION];
	HEAD_X += dir_x[snake.DIRECTION];
	//在蛇长不为1时,下一个移动跟蛇运动方向相反
	if(head->sub && HEAD_Y == head->sub->y && HEAD_X == head->sub->x){
		if(HEAD_Y == head->y){
			if(HEAD_X > head->x) HEAD_X = head->x - 1;
			else HEAD_X = head->x + 1;
		}
		else{
			if(HEAD_Y > head->y) HEAD_Y = head->y - 1;
			else HEAD_Y = head->y + 1;
		}
	}
}

int Move(Snake &snake, int fy, int fx)
{
	int &FOOD_Y = snake.FOOD_Y;
	int &FOOD_X = snake.FOOD_X;
	int &HEAD_Y = snake.HEAD_Y;
	int &HEAD_X = snake.HEAD_X;
	Body *&head = snake.head;
	Body *&tail = snake.tail;

	//输出蛇头
	move(HEAD_Y, HEAD_X);
	printw("%c", snake.H);
	vis[HEAD_Y][HEAD_X] = 1;

	//如果有蛇身,输出之
	if(head->sub){
		move(head->sub->y, head->sub->x);
		printw("#");
	}

	if(HEAD_Y == FOOD_Y && HEAD_X == FOOD_X) New_Food(snake);//吃到食物
	else if(HEAD_Y == fy && HEAD_X == fx) return 1;//吃到另一条蛇的食物
	else{//什么都没有吃到
		move(tail->y, tail->x);
		printw(" ");
		vis[tail->y][tail->x] = 0;
		free(tail);
		if(tail->pre){
			tail = tail->pre;
			tail->sub = NULL;
		}
	}
	return 0;
}

int main()
{

	initscr();

	curs_set(0);//隐藏物理指针
	noecho();//不回显输入
	box(stdscr, '|', '-');//画框

	Snake snake1, snake2;
	snake1.food = '$';
	snake2.food = '*';
	snake1.H = '@';
	snake2.H = '+';
	//两条蛇的初始化位置(小蛇放中间比较舒服)
	int init1_y = LINES / 2;
	int init1_x = COLS / 2 - 1;
	int init2_y = LINES / 2;
	int init2_x = COLS / 2 + 1;
	vis[init1_y][init1_x] = vis[init2_y][init2_x] = 1;

	//初始化两条蛇位置跟食物位置
	Initialization(snake1, init1_y, init1_x);
	Initialization(snake2, init2_y, init2_x);

	keypad(stdscr, true);//开启键盘功能键
	int key = 0;
	key = getch();
	while(1)
	{
		if(KEY_UP == key) snake1.DIRECTION = 0;
		else if(KEY_DOWN == key) snake1.DIRECTION = 1;
		else if(KEY_LEFT == key) snake1.DIRECTION = 2;
		else if(KEY_RIGHT == key) snake1.DIRECTION = 3;
		else if('w' == key) snake2.DIRECTION = 0;
		else if('s'== key) snake2.DIRECTION = 1;
		else if('a' == key) snake2.DIRECTION = 2;
		else if('d' == key) snake2.DIRECTION = 3;

		//获取向前移动的坐标及判断是否跟蛇运动方向相反(一条长度不为1的运动着的蛇
		//是有一个方向是不能走的)
		Reverse_Direction(snake1);
		Reverse_Direction(snake2);

		//判断游戏是否结束
		if(Judge_Kill(snake1.HEAD_Y, snake1.HEAD_X) || Judge_Kill(snake2.HEAD_Y, snake2.HEAD_X)){
			//释放内存,很重要
			Free(snake1.head);
			Free(snake2.head);
			break;
		}

		//往各自的链表添加蛇身
		Add(snake1);
		Add(snake2);

		//移动蛇及判断是否吃到另一条蛇的食物
		int over1 = 0, over2 = 0;
		over1 = Move(snake1, snake2.FOOD_Y, snake2.FOOD_X);
		over2 = Move(snake2, snake1.FOOD_Y, snake1.FOOD_X);
		if(over1 || over2) break;

		refresh();
		usleep(150*1000);//停留150毫秒
		if(ERR != halfdelay(1)) key = getch();//在100毫秒内等待输入,如无输入,往下执行
	}
	sleep(3);
	endwin();
	exit(0);
}
时间: 2024-09-29 10:19:10

C语言之贪吃蛇(curses库函数)的相关文章

C语言实现贪吃蛇之结构链表篇

之前的两篇博客将运用的C语言知识限定在了一般的数组上,但如果已经完整地了解过C语言的话,运用结构和链表会让程序的结构更明了,逻辑更清晰.这篇博客就将介绍如何用结构和链表改善之前的程序. 首先,我们为蛇的节点定义一个结构: typedef struct node{ COORD cor; struct node *next; }node; COORD结构我在上一篇已经介绍过,这里就直接借用了. COORD food = { 3,5 }; node *head; food也相应地由COORD来定义,并

C语言实现贪吃蛇之全靠数组篇

贪吃蛇游戏的设计思路很简单,相信有过一些编程经验的同学都不至于束手无策,可在我刚刚接触编程时,这个小小的贪吃蛇游戏可是让我费了不少脑筋,如今学习编程已经快一年了,前几天又看了一遍K&R,打算写几个贪吃蛇程序巩固一下知识.我打算写若干篇贪吃蛇的博客,从简单粗糙的开始,不断改良,希望能给初学C语言的同学一点借鉴. 话不多说,我们现在就开始吧,首先我们整理一下思路.首先我们要明确,既然贪吃蛇游戏理论上可以无限继续下去,那么游戏主体一定就是一个循环.蛇的移动就在这个循环中完成.如果是初学编程的话,可能会

C语言实现贪吃蛇之图形界面篇

这已经是贪吃蛇系列的第五篇了,讲真一直写这个也挺无聊的,所以这一篇博文将是系列的最后一篇.虽然已经介绍了贪吃蛇的几种写法,但说到底我们的游戏还只是在一个黑框框里移动的星号.和我们平时玩的贪吃蛇游戏有不少差距.游戏嘛,画面也是很重要的一环.接下来就是让之前的贪吃蛇游戏脱胎换骨的时候了.话不多说,这就开干吧. 首先,为了摆脱无趣的黑框(控制台),我们这次新建一个win32项目,直接点击完成.初次接触win32项目的同学可能会感到一股扑面而来的伤害,满屏都是意义不明的字符,这TM还是C语言吗!先不要沮

C语言之贪吃蛇(conio.h)

写完基于Linux中curses库的贪吃蛇,发现只需要用到一个WIN的API(移动光标那个)改写move(int y, int x)函数,然后加上conio.h(一般编译器都会包含这个头文件)的getch(),就可以在Windows系统运行! Linux版链接:http://blog.csdn.net/u013351484/article/details/43940803 游戏规则: 初始时两条蛇会在中间,一条蛇头是'@',食物是'$':另一条蛇头是'+',食物是'*',蛇身都是'#'.WASD

C语言实现贪吃蛇

日期:2018.9.11 用时:150min 项目:贪吃蛇(C语言--数组   结构体实现) 开发工具:vs2013 关键知识:数组,结构体,图形库,键位操作 源代码: 1 #include<stdio.h> 2 #include<graphics.h> 3 #include<stdlib.h> 4 #include<conio.h> 5 #include<time.h> 6 7 #define N 200 8 int i, key; 9 int

C语言之贪吃蛇(ncurses)

声明: 以下内容可能会引起某些读者不适, 请小心阅读. 有些内容并没有详细介绍, 可能简单理解也是错误的, 但是这都是为了尽量简单. 前言: 代码是很久之前写的,属于边想边写的那种,很混乱. 推荐材料: NCURSES Programming HOWTO 贪吃蛇应该是我们这代人都玩过的游戏.而如果我们要写一个贪吃蛇最需要考虑的就是贪吃蛇是如何移动的.其实贪吃蛇的移动就是尾部的减少和头部的增加. 这篇文章: 介绍一些ncurses库的基础内容 贪吃蛇游戏的代码解释 介绍一些ncurses库的基础内

C语言实现贪吃蛇之局部刷新篇

上一篇博客里,我介绍了如何用数组存储坐标,不断全屏刷新以实现动态效果,这几乎是最显而易见的思路,但带来的副作用也十分明显,那就是始终伴随着游戏的闪烁现象,之所以会造成闪烁现象,原因在于频繁的清空与打印,其实贪吃蛇游戏里我们并不需要重打印整个界面,要实现蛇的移动,我们只要打印出新的蛇头,清除原来的蛇尾就好了.食物只有在被吃掉时才需要重新打印,边界更是只用打印一次.好了,既然我们看到了可提升的地方,就开始动手优化吧.显然,我们将需要一个可以自由移动光标的函数,这样我们才能做到在需要的地方打印.TC上

C语言之贪吃蛇

利用链表的贪吃蛇,感觉自己写的时候还是有很多东西不熟悉, 1.预编译 2.很多关于系统的头文件也不是很熟悉 3.关于内存 第一个是.h头文件 #ifndef _SNAKE_H_H_H #define _SNAKE_H_H_H //调节游戏界面大小,这里设置为15*15 int const COL=15; int const ROW=15; //蛇结点 typedef struct Node { //data域 int x; int y; //指针域 struct Node* pre; struc

架构练习:c语言实现贪吃蛇(三):封装蛇的移动方法

目前进展: 封装蛇的移动方法: typedef struct snakeinfo { int numParts;/* how many parts,蛇身体分多少个段 */ int lenParts[GAME_WIDTH];/* 蛇身体每段的长度 */ int xPartsHead[GAME_WIDTH];/* 蛇身体第i段的x坐标,初始值为1 */ int yPartsHead[GAME_WIDTH];/* 蛇身体第i段的y坐标,初始值为1 */ uchar direction;/* 蛇当前在像