C语言——2048

背景

昨天给自己放了个假,除了睡觉贼无聊,然后就开始找事情做了。

搜了一下c语言新手项目,看到2048就开始做了。

 

实现过程

1. 思路

用一维数组存储数字,每次操作通过对数组数字操作实现。

 

2. 主流程:

a. 按键检测;

b. 键值处理;

c. 成绩结算;

PS:有点废话。

 

3. 各函数介绍:

按键检测在main函数里面实现;

a.按键处理

1)通过传入的按键标志位,尝试移动数字。

有效移动,则生成新数字并判断数组是否未满;

        未满,显示新数组;

        数组满,结束游戏。

无效操作,则跳过。

2)清除标志位。

b.数字移动(上下左右)

以向左移动为例:

1)备份数组(用于判断移动是否有效);

2)对相邻相等项向左合并,例如数组a第一行为“2,0,2,4”,则将a[0] = a[0] + a[2],将a[2]清零,再接着跳过a[0]和a[2]中间数字的比较,直接判断a[3],因为一个数字最多进行一次合并。依次类推合并其他行;

3)拷贝数组的一行;

4)将原数组清零,并把拷贝的数组中非0元素左对齐放回圆数组;

5)比较移动后数组与移动前数组是否完全相等,完全相等则为无效移动,不等则为有效移动。

c.判断数组满

1)统计数组元素0的个数,同时备份数组。

2)原数组中没有0,则对备份数组进行上下左右的移动,若均为无效移动,说明数组已满。

d.随机生成新数字并放入数组空白位置

前面提到用一维数组,就是为了这里实现方便。

1)统计【原数组】中元素为0的下标,放到一个【临时数组】中;

2)随机选择【临时数组】中的一个元素,把【以该元素为下标的原数组元素】改为随机生成的2或4。

e.显示函数

 

源代码(代码有点乱)

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <conio.h>
#include <time.h>

#define true 1
#define false 0

void key_deal(char * state, unsigned int * buf);
char new_buf(unsigned int * p);
void game_show(unsigned int * p);
char key_left(unsigned int * p);
char key_right(unsigned int * p);
char key_up(unsigned int * p);
char key_down(unsigned int * p);
char buf_full(unsigned int * p);

int main(void) {
	HANDLE handle_in = GetStdHandle(STD_INPUT_HANDLE);      //获得标准输入设备句柄
	INPUT_RECORD keyrec;        //定义输入事件结构体
	DWORD res;      //定义返回记录
	
	char state = 0, i;
	unsigned int buf[16] = {
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
		0, 0, 0, 0,
	};

	srand((unsigned)time(NULL));	//以程序运行时间为随机数种子

	new_buf(buf);
	game_show(buf);

	while(state != -1) {
		//按键扫描
		ReadConsoleInput(handle_in, &keyrec, 1, &res);	//读取输入事件
                //如果当前事件是键盘事件
		if (keyrec.EventType == KEY_EVENT) {
			//当前事件是虚拟按键左方向键且当前状态是按下,不是释放
                        if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_LEFT	
				&& keyrec.Event.KeyEvent.bKeyDown == true) {
				//printf("< ");
				state = 1;
			}
                        //当前事件是虚拟按键右方向键且当前状态是按下,不是释放
			else if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_RIGHT	
				&& keyrec.Event.KeyEvent.bKeyDown == true) {
				//printf("> ");
				state = 2;
			}
                        //当前事件是虚拟按键上方向键且当前状态是按下,不是释放
			else if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_UP		
				&& keyrec.Event.KeyEvent.bKeyDown == true) {
				//printf("^ ");
				state = 3;
			}
                        //当前事件是虚拟按键下方向键且当前状态是按下,不是释放
			else if (keyrec.Event.KeyEvent.wVirtualKeyCode == VK_DOWN	
				&& keyrec.Event.KeyEvent.bKeyDown == true) {
				//printf("v ");
				state = 4;
			}
                        //当前事件是虚拟按键下 Q 键且当前状态是按下,不是释放
			else if (keyrec.Event.KeyEvent.wVirtualKeyCode == 'Q'					&& keyrec.Event.KeyEvent.bKeyDown == true) {
				//退出
				state = -1;
			}
		}

		key_deal(&state, buf);	//按键处理
    }
	for(i=0; i<16; i++) {
		if(buf[i] > buf[0])
			buf[0] = buf[i];
	}
	printf("\n您最后得分是:%d\n", buf[0]);
	printf("\n==============游戏结束==============\n\n");

	system("pause");
	return 0;
}

void key_deal(char *state, unsigned int * buf) {
	if(*state!=0 && *state!=-1) {
		char flag = 0;

		switch(*state) {
			case 1 :
				if(key_left(buf))
					flag = 1;	//左
			break;
			case 2 :
				if(key_right(buf))
					flag = 1;	//右
			break;
			case 3 :
				if(key_up(buf))
					flag = 1;	//上
			break;
			case 4 :
				if(key_down(buf))
					flag = 1;	//下
			break;
			default : printf("ERROR : 按键判断出错\n");
		}
		if(*state>0 && *state<5 && flag==0) {
			new_buf(buf);	//在空白处随机生成2或4
			system("cls");	//清屏
			game_show(buf);	//显示处理后的结果
			if(!buf_full(buf)) {	//返回值为0说明数组满了,游戏结束
				*state = -1;
				return;
			}
		}
		*state = 0;	//清除按键标志位
	}
}

//显示数组内容
void game_show(unsigned int * p) {
	char i, j;

	printf("===========【菜鸡版2048】===========\n\n");
	printf("操作说明:\n");
	printf("1. 方向键控制数字移动\n");
	printf("2. 蹲下按 Q 退出游戏\n\n");
	printf("祝您游戏愉快,遇到bug请忽略!\n\n");
	printf("====================================\n\n");
	for(i=0; i<16; i+=4) {
		printf(" |");
		for(j=i; j<i+4; j++) {
			if(p[j] != 0) {
				printf("%7d|", p[j]);
			}
			else
				printf("       |");	//7个空格
		}
		printf("\n\n");
	}
	printf("====================================\n");
}

//左方向键处理函数,为有效操作返回0,否则返回1
char key_left(unsigned int * p) {
	char i, j, n;
	unsigned int buf[4];
	unsigned int tmp[16];
	char count = 0;
	//备份数组
	for(i=0; i<16; i++)
		tmp[i] = p[i];

	for(i=0; i<16; i+=4) {
		//合并相邻的相等项
		for(j=i; j<i+4; j++) {
			for(n=1; n<(i+4)-j; n++) {
				if(p[j+n]!=0) {
					if(p[j]==p[j+n]) {
						p[j] = p[j] + p[j+n];
						p[j+n] = 0;
						j = j + n;
					break;
					}
					else
						break;
				}
			}
		}

		count = 0;
		//计算第i行有多少个0
		for(n=0; n<4; n++) {
			if(p[i+n] == 0)
				count++;
			buf[n] = p[i+n];
		}

		//如果i行不全为0
		if(count < 4) {
			//获得非零元素左对齐的数组
			for(n=0, j=i; n<4; n++) {
				if(buf[n] != 0) {
					p[i+n] = 0;		//把原数组中需要移动的元素所在位置清零
					p[j++] = buf[n];	//将非零元素左对齐
				}
			}
		}
	}

	//检查移动前移动后是否有变化,没变化说明不能移动
	for(i=0, count=0; i<16; i++)
		if(tmp[i] == p[i])
			count++;
	if(count == 16)
		return 1;
	else
		return 0;
}

//右方向键处理函数,为有效操作返回0,否则返回1
char key_right(unsigned int * p) {
	char i, j, n;
	unsigned int buf[4];
	unsigned int tmp[16];
	char count = 0;

	for(i=0; i<16; i++)
		tmp[i] = p[i];

	for(i=15; i>-1; i-=4) {
		//合并相邻的相等项
		for(j=i; j>i-4; j--) {
			for(n=1; n<j-(i-4); n++) {
				if(p[j-n]!=0) {
					if(p[j]==p[j-n]) {
						p[j] = p[j] + p[j-n];
						p[j-n] = 0;
						j = j - n;
						break;
					}
					else
						break;
				}
			}
		}

		count = 0;
		//计算第i行有多少个0
		for(n=0; n<4; n++) {
			if(p[i-n] == 0)
				count++;
			buf[3-n] = p[i-n];
		}

		//如果i行不全为0
		if(count < 4) {
			//获得非零元素右对齐的数组
			for(n=3, j=i; n>-1; n--) {
				if(buf[n] != 0) {
					p[i-(3-n)] = 0;		//把原数组中需要移动的元素所在位置清零
					p[j--] = buf[n];	//将非零元素右对齐
				}
			}
		}
	}

	//检查移动前移动后是否有变化,没变化说明不能移动
	for(i=0, count=0; i<16; i++)
		if(tmp[i] == p[i])
			count++;
	if(count == 16)
		return 1;
	else
		return 0;
}

//上方向键处理函数,为有效操作返回0,否则返回1
char key_up(unsigned int * p) {
	char i, j, n;
	unsigned int buf[4];
	unsigned int tmp[16];
	char count = 0;

	for(i=0; i<16; i++)
		tmp[i] = p[i];

	for(i=0; i<4; i++) {
		//合并相邻的相等项
		for(j=i; j<i+13; j+=4) {
			for(n=4; n<(i+13)-j; n+=4) {
				if(p[j+n]!=0) {
					if(p[j]==p[j+n]) {
						p[j] = p[j] + p[j+n];
						p[j+n] = 0;
						j = j + n;
						break;
					}
					else
						break;
				}
			}
		}

		count = 0;
		//计算第i行有多少个0
		for(n=0; n<4; n++) {
			if(p[i+(n*4)] == 0)
				count++;
			buf[n] = p[i+(n*4)];
		}

		//如果i行不全为0
		if(count < 4) {
			//获得非零元素顶对齐的数组
			for(n=0, j=i; n<4; n++) {
				if(buf[n] != 0) {
					p[i+(n*4)] = 0;		//把原数组中需要移动的元素所在位置清零
					p[j] = buf[n];		//将非零元素顶对齐
					j += 4;
				}
			}
		}
	}

	//检查移动前移动后是否有变化,没变化说明不能移动
	for(i=0, count=0; i<16; i++)
		if(tmp[i] == p[i])
			count++;
	if(count == 16)
		return 1;
	else
		return 0;
}

//上方向键处理函数,为有效操作返回0,否则返回1
char key_down(unsigned int * p) {
	char i, j, n;
	unsigned int buf[4];
	unsigned int tmp[16];
	char count = 0;

	for(i=0; i<16; i++)
		tmp[i] = p[i];

	for(i=15; i>11; i--) {
		//合并相邻的相等项
		for(j=i; j>i-13; j-=4) {
			for(n=4; n<j-(i-13); n+=4) {
				if(p[j-n]!=0) {
					if(p[j]==p[j-n]) {
						p[j] = p[j] + p[j-n];
						p[j-n] = 0;
						j = j - n;
						break;
					}
					else
						break;
				}
			}
		}

		count = 0;
		//计算第i行有多少个0
		for(n=0; n<4; n++) {
			if(p[i-(n*4)] == 0)
				count++;
			buf[3-n] = p[i-(n*4)];
		}

		//如果i行不全为0
		if(count < 4) {
			//获得非零元素底对齐的数组
			for(n=3, j=i; n>-1; n--) {
				if(buf[n] != 0) {
					p[i-(12-(n*4))] = 0;	//把原数组中需要移动的元素所在位置清零
					p[j] = buf[n];			//将非零元素底对齐
					j -= 4;
				}
			}
		}
	}

	//检查移动前移动后是否有变化,没变化说明不能移动
	for(i=0, count=0; i<16; i++)
		if(tmp[i] == p[i])
			count++;
	//count = 16说明数组没变,移动无效
	if(count == 16)
		return 1;
	else
		return 0;
}

//产生新的数字,为有效操作返回0,否则返回1
char new_buf(unsigned int * p) {
	unsigned int tmp[16];
	char n=0, count=-1;

	//将等于0的元素下标放在tmp中
	for(n=0; n<16; n++) {
		if(p[n] == 0)
			tmp[++count] = n;
	}
	count++;
	if(count != 0) {
		//生成[0, count)的随机数,即在p随机选取一个0,将其随机改为2或4
		p[tmp[rand()%count]] = (rand()%2) ? 2 : 4;
		return 0;
	}
	else
		return 1;
}

//数组是否满,满返回0,否则返回1
char buf_full(unsigned int * p) {
	/*
	 * 1. 数组中没有0
	 * 2. 对数组进行上下左右移动都不变
	 */
	char count = 0, i;
	unsigned int tmp[16];

	for(i=0; i<16; i++) {
		if(p[i] == 0)
			count++;
		tmp[i] = p[i];
	}

	if(count == 0) {
		if(!key_left(tmp))
			return 1;	//向左为有效移动,数组没满
		else if(!key_right(tmp))
			return 1;	//向右为有效移动,数组没满
		else if(!key_up(tmp))
			return 1;	//向上为有效移动,数组没满
		else if(!key_down(tmp))
			return 1;	//向下为有效移动,数组没满
		else
			return 0;	//数组满了
	}

	return 1;
}

 

运行截图(皮了一下)

2048截图01
2048截图01
2048截图02
2048截图02

 

完结撒花!!!

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值