2048游戏(C语言LINUX环境下,键盘读取实例)

2048游戏(C语言LINUX环境下,键盘读取实例)

一、Linux键盘值的调用 “get_keyboard.h”

​ 在Linux下是没有自带的调取键盘的库的,所以我们得自己写一个头文件“get_keyborad.h”来获取键盘的值。当然如果在Windows环境下,直接调用“conio.h”头文件中就可以了。

// get_keyboard.h在linux下没有直接的get_ch()读取键盘,所以要自己写一个。

#ifndef GETCH_H
#define GETCH_H
#include <stdio.h>
#include <termios.h>
#include <stdlib.h>
#include <unistd.h>

typedef enum KEYBOARD
{
	KEY_UP    		= 183,
	KEY_DOWN  		= 184,
	KEY_RIGHT 		= 185,
	KEY_LEFT  		= 186,
	KEY_BACKSPACE	= 127,
	KEY_ENTER 		= 10,
	KEY_0           = 48,
	KEY_1           = 49,
	KEY_2           = 50,
	KEY_3           = 51,
	KEY_4           = 52,
	KEY_5           = 53,
	KEY_6           = 54,
	KEY_7           = 55,
	KEY_8           = 56,
	KEY_9           = 57,
	KEY_A           = 65,
	KEY_B           = 66,
	KEY_C           = 67,
	KEY_D           = 68,
	KEY_E           = 69,
	KEY_F           = 70,
	KEY_G           = 71,
	KEY_H           = 72,
	KEY_I           = 73,
	KEY_J           = 74,
	KEY_K           = 75,
	KEY_L           = 76,
	KEY_M           = 77,
	KEY_N           = 78,
	KEY_O           = 79,
	KEY_P           = 80,
	KEY_Q           = 81,
	KEY_R           = 82,
	KEY_S           = 83,
	KEY_T           = 84,
	KEY_U           = 85,
	KEY_V           = 86,
	KEY_W           = 87,
	KEY_X           = 88,
	KEY_Y           = 89,
	KEY_Z           = 90,
	KEY_a           = 97,
	KEY_b           = 98,
	KEY_c           = 99,
	KEY_d           = 100,
	KEY_e           = 101, 
	KEY_f           = 102,
	KEY_g           = 103,
	KEY_h           = 104,
	KEY_i           = 105,
	KEY_j           = 106,
	KEY_k           = 107,
	KEY_l           = 108,
	KEY_m           = 109,
	KEY_n           = 110,
	KEY_o           = 111,
	KEY_p           = 112,
	KEY_q           = 113,
	KEY_r           = 114,
	KEY_s           = 115,
	KEY_t           = 116,
	KEY_u           = 117,
	KEY_v           = 118,
	KEY_w           = 119,
	KEY_x           = 120,
	KEY_y           = 121,
	KEY_z           = 122
}KEYBOARD;

//此函数能立即从键盘不回显的接收数据
static int get_keyboard(void)
{
	//接收系统调用的执行结果
	int ret = 0;
	//存储终端设备的配置信息
	struct termios old;
	//通过系统调用获取终端的配置信息
	ret=tcgetattr(STDIN_FILENO,&old);
	if(0 > ret)
	{
		perror("tcgetattr");
		return -1;
	}
	//初始化新的终端配置信息
	struct termios new = old;
	//取消回显并立即获取
	new.c_lflag &= ~(ICANON|ECHO);
	//设置新的终端配置信息
	ret= tcsetattr(STDIN_FILENO,TCSANOW,&new);
	if(0 > ret)
	{
		perror("tcsetattr");
		return -2;
	}
	//在新的模式下从终端获取数据
	int key_value = 0;
	do
	{
		key_value += getchar();
	//由于和系统对FILE结构体的实现各不相同
	//linux系统 while(stdin->_IO_read_end - stdin->_IO_read_ptr);
	//OS系统 while(stdin->_r);
	}while(stdin->_IO_read_end - stdin->_IO_read_ptr);
	//还原终端的配置信息
	ret = tcsetattr(STDIN_FILENO,TCSANOW,&old);
	if(0 > ret)
	{
		perror("tcsetattr");
		return -3;
	}
	//返回获取到的数据
	return key_value;
}
#endif//GETCH_H

二、代码基本思路

  1. 先初始化加载游戏界面,用二维char类型数组(char map [row1] [col1])来储存交互界面。

  2. 再建一个数值的二维数组(int nmap [row2] [col2])来储存每个格子的数值。

  3. 将数值数组和界面数组建立映射关系,就可以把数值直接反应到交互界面。

  4. 生成随机数,存放到nmap中,刚开始生成3个,之后一次生成一个随机数(2、4、8)。

  5. 方向键的调用与游戏运行的函数设计

    • key = get_keyboard();获取方向值

    • 调用函数判断,对是否能进行移动进行判断。

    • 此处,我采取的是==将每一个方向重新抽离出来==,这样就可以四个方向进行一起判断,再重新写回

    • 如果能移动,那么是否存在两个相邻或者其中间隔若干个空格的两数相同,如果存在则合并

    • 先合并完成再进行移动。

    • 顺便计算一下得分情况。
      请添加图片描述

三、源代码

#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#include<string.h>
#include"get_keyboard.h" //#include <conio.h>    windows下通过这个头文件获取键盘值。

#define MAX_ROW 6        //规格MAX_ROW*MAX_ROW
#define EACH_ROW 3       //每个格子的高度
#define EACH_COL 7       //每个格子的宽度
#define TOTAL_ROW MAX_ROW*(1+EACH_ROW)+1   //总共需要的行数   
#define TOTAL_COL MAX_ROW*(1+EACH_COL)+1   //总共需要的列数
#define EACH_TIME_NUM 3   //初始化数字个数<EACH_TIME_NUM

void load();

int multi(int a,int b){ //求指数
	int i;
	int s = 1;
	for(i=0;i<b;i++){
		s*=a;
	}
	return s;
}

void findplace(int *px,int *py,int flag){ //数值和地图位置的映射关系
	*px = (flag/MAX_ROW)*(EACH_ROW+1)+EACH_ROW/2+1;
	*py = (flag%MAX_ROW)*(EACH_COL+1)+EACH_COL/2+1;
}

void printf_map(char (*map)[]){ //打印游戏地图
	int i,j;
	char (*board)[TOTAL_COL] = map;
	for(i=0;i<TOTAL_ROW;i++){
		for(j=0;j<TOTAL_COL;j++){
			printf("%c",board[i][j]);
		}
		printf("\n");
	}
}

int num_len(int num){  //求int的位数
	int cnt = 0;
	while(num>0){
		num/=10;
		++cnt;
	}
	return cnt;
}

int can_arr_move(int arr[]){
	int i=0;
	int cnt = 0;
	for(i=0;i<MAX_ROW;i++){
		while(arr[i]==0){
			cnt++;
			i++;
			if(cnt==MAX_ROW) return 0;
		}	
		if(cnt>0 && arr[i]!=0 && i<MAX_ROW){
			return 1;
		}
		if(i<MAX_ROW-1 && arr[i] == arr[i+1] && arr[i]!=0){
			return 1;
		}
	}
	return 0;
}

int arr_move(int arr[],int (*nmap)[MAX_ROW],int *pscore){
	int i;
	int p;
	int n=0;
	int temp;
	for(i=0;i<MAX_ROW;i++){
		while(i<MAX_ROW-1 && arr[i]==0){//找到首非零元素
			i++;
		}
		if(i == MAX_ROW-1) break;
		p = i+1;
		while(p<MAX_ROW && arr[p]==0) ++p;//找下一个非零元素的位置
		if(p==MAX_ROW) break;
		if(arr[p]==arr[i]){//如果两数相等则合并
			arr[i] = arr[i]*2;
			*pscore += arr[i];
			arr[p] = 0;
			i = p;
		}	
	}
	//合并完成后,开始移动位置
        for(i=1;i<MAX_ROW;i++){
                temp = i;
		while(arr[temp-1]==0&&temp>0){
           		 arr[temp-1] = arr[temp];
                      	 arr[temp] = 0;
                         --temp;
		} 
	}
}

int can_move(int (*nmap)[MAX_ROW],int dirc,int mod,int *pscore){
	int i,j;
	int x,y;
	int arr[MAX_ROW]={0};
	int res = 0;
	int cnt=0;
	// 把所有序列转存到新的数组arr
	for(i=0;i<MAX_ROW;i++){	
		for(j=0;j<MAX_ROW;j++){
			switch(dirc){
				case KEY_UP: 
					arr[j] = nmap[j][i];
					break;
				case KEY_DOWN:
					arr[j] = nmap[MAX_ROW-j-1][i];
					break;
				case KEY_LEFT:
					arr[j] = nmap[i][j];
					break;
				case KEY_RIGHT:
					arr[j] = nmap[i][MAX_ROW-j-1];
					break;
			}
		}
		res = can_arr_move(arr);
		if(mod && res) return 1;
		cnt += res;
		// 如果可以移动,反向存回nmap(4*4数字表)
		if(res){
			arr_move(arr,nmap,pscore);
	        	for(j=0;j<MAX_ROW;j++){	
				switch(dirc){
					case KEY_UP: 
						nmap[j][i] = arr[j];
						break;
					case KEY_DOWN:
						nmap[MAX_ROW-j-1][i] = arr[j];
						break;
					case KEY_LEFT:
						nmap[i][j] = arr[j];
						break;
					case KEY_RIGHT:
						nmap[i][MAX_ROW-j-1] = arr[j];
						break;
				}
			}
		}
	}
	return cnt;
}

int move(char (*map)[TOTAL_COL],int (*nmap)[MAX_ROW],int dirc,int *pscore){ //运动
	int i,j,k;
	int cnt0=0;//记录0的个数
	int x=0,y=0;
	int nlen=0;
	int m2;
	if(can_move(nmap,dirc,0,pscore)){
		srand((unsigned)time(NULL));
		int a = rand()%3+1;
		m2 = multi(2,a); //2^1~2^3
		int p = rand()%(MAX_ROW*MAX_ROW);
		while(nmap[p/MAX_ROW][p%MAX_ROW]!=0){
			p = rand()%(MAX_ROW*MAX_ROW);
		}
		nmap[p/MAX_ROW][p%MAX_ROW] = m2;
		//将nmap 4*4的int数组 映射到map里。
		for(i=0;i<MAX_ROW;i++){
			for(j=0;j<MAX_ROW;j++){
				findplace(&x,&y,i*MAX_ROW+j);
				nlen = num_len(nmap[i][j]);
				for(k=0;k<EACH_COL-2;k++){
					map[x][y-EACH_COL/2+k] = ' ';//每次清空上次数据 
				}
				if(nmap[i][j]!=0){
					for(k=0;k<nlen;k++){
						map[x][y-nlen/2+k] = nmap[i][j]/multi(10,nlen-k-1)%10+'0';//以字符的形式传入
					}
				}
			}
		}
	}
	for(i=0;i<MAX_ROW*MAX_ROW;i++){
		if(nmap[i/MAX_ROW][i%MAX_ROW]==0){
			++cnt0;
			break;
		}
	}
	
	printf("   SCORE: %d\n",*pscore);
	printf_map(map);	
	if(cnt0==0 && can_move(nmap,KEY_UP,1,pscore)==0 && can_move(nmap,KEY_LEFT,1,pscore)==0){
		return -1;
	}
}


void play(char (*map)[],int (*nmap)[],int *pscore){
	int key;
	int r=0;
	while(1){
		key = get_keyboard();  //windows 用getch()  ->别忘了头文件  #include<conio.h>
		switch(key){
			case KEY_UP:
			case KEY_DOWN:
			case KEY_LEFT:
			case KEY_RIGHT:
				system("clear"); //windows system("cls")
				r = move(map,nmap,key,pscore);
				break;	
			case KEY_R:
			case KEY_r:
				system("clear"); //windows system("cls")
			        load();
				break;
			case KEY_Q:
			case KEY_q: exit(0);
		}
		if(r==-1) {
			printf("Game over ~\n");
			key = get_keyboard();  //windows 用getch()  ->别忘了头文件  #include<conio.h>  
			if(key == KEY_R || key == KEY_r) load();
			else exit(0);
		}
	}
}

void load(){
	int i,j;
	int m,p;
	int m2;
	int score = 0;
	char map[TOTAL_ROW][TOTAL_COL];
	system("clear"); //windows system("cls")
	printf("小游戏1024\n");
	printf("press 'r' to restart , press 'q' to quit.");
	printf("\nAre you ready? Press any key to start ...\n");
	get_keyboard(); //windows 用getch()  ->别忘了头文件  #include<conio.h>
	system("clear"); //windows system("cls")
	printf("   SCORE: %d\n",score);
	//初始化 游戏界面设计
	for(i=0;i<TOTAL_ROW;i++){
		for(j=0;j<TOTAL_COL;j++){
			if(i%(1+EACH_ROW)==0||i==TOTAL_ROW-1){
				map[i][j] = '-';
			}
			else{
				if(j%(1+EACH_COL)==0||j==TOTAL_COL-1){
					map[i][j] = '|';
				}
				else{
					map[i][j] = ' ';
				}
			}
		}
	}
	//初始化值
	srand((unsigned)time(NULL)); 
	int x=0;
	int y=0;
	int nmap[MAX_ROW][MAX_ROW] = {0};
	for(i=0;i<EACH_TIME_NUM;i++){
		m = rand()%3+1;
		m2 = multi(2,m);
		p = rand()%16;
		findplace(&x,&y,p);
		map[x][y] = m2+'0'; 
		nmap[p/MAX_ROW][p%MAX_ROW] = m2;
	}
	printf_map(map);
	play(map,nmap,&score);
}

int main(){
	load();
	return 0;
}

四、运行结果

请添加图片描述
请添加图片描述
hexo_blog

  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时而癫狂的匡匡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值