/***************************项目 2048**********************
c语言编写 图形库制作
时间:2019.04.03

准备工具: vs2013 图形库 ico素材(作为exe的图标) 背景图(jpg格式)
知识点: 循环 数组 函数 随机数

项目步骤分析:
2048 通过方向键 WASD控制方向合并相同数字直到生成2048 游戏结束
1、4X4的棋盘 存放数字 ---->数组 游戏操作 ---->键盘操作 界面 ---->需要操作结果

2、步骤
a、准备数组 生成两个隋杰的位置和随机的数字(2或4) //初始化操作
b、等待用户输入 根据上下左右处理数组
//添加一个判断输赢e
c、生成新的随机位置 和新的数字(2或4)
d、打印界面 当前输出结果 等待下一轮输入
e、输赢条件 输-->数组满,不能移动 赢-->出现2048,游戏就赢了

3、拆分函数 如果同一个功能或者相似的功能可以写成一个函数,减少代码量和难点
a 初始化

函数声明:

void init(int map[][4]);

函数定义:

void init(int map[][4])
{

rand();
int x, y;
for (int i = 0; i < 2;)
{
x = rand() % 4; 
y = rand() % 4;
if (map[x][y] == 0) 
{
map[x][y] = rand()%2*2+2; 
}
}
}

在主函数中测试效果:

int main()
{
int map[4][4];
init(map); 
for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
printf("%d\t", map[i][j]);
}
printf("%\n");
}

getchar();
return 0;
}

效果如图所示:

b 等待用户输入操作 随机位置

用switch进行键盘消息判断

各方向键的代码比对如下:

 

 

也可以用枚举法定义方向键并对其进行消息的判断

对比如下:

 

 

c 判断输赢

d 打印结果 显示地图

打印地图的函数:

暂时测试结果如下:

目的:
复习 总结 综合运用
***************************项目 2048**********************/

测试代码笔记如下:

 //**************************头文件部分*********************
//头文件 宏定义 类型定义 全局变量 //头文件
#include<stdio.h>
#include<memory.h> //memset的头文件 或者string.h
#include<stdlib.h> //srand函数 rand函数
#include<time.h> //time函数
#include<conio.h> //getch函数
#include<graphics.h> //图形库
//#define COLOR1 RGB(238,106,80)
//#define COLOR2 RGB(238,169,184)
//#define COLOR3 RGB(131,139,131)
//#define COLOR4 RGB(193,205,205) //类型定义 枚举
enum dir
{
UP = , DOWN = , LEFT = , RIGHT =
}; //定义方向键 IMAGE img;//存放图片变量 //**************************函数声明***********************
//写个函数的声明
void init(int map[][]); //用函数给数组赋值初始化 void play(int map[][]); //处理用户输入 void drawMap(int map[][]); //打印 画图 void newNum(int map[][]); //随机一个新元素 int isGameOver(int map[][]);//判断游戏是否结束的一个函数 //**************************主函数*************************
//main函数
int main()
{
int map[][]; //准备数组 大小4X4的地图
init(map); //传参(实参) 传的是数组名 while () //死循环
{
drawMap(map);
play(map);
//system("cls"); //清屏函数 用于控制台
//newNum(map);
if (isGameOver(map) != ) break;//结束循环
} if (isGameOver(map) == )
{
//赢得游戏
drawMap(map);
//printf("赢得游戏\n");
MessageBox(GetHWnd(), L"OK", L"WIN", MB_OK);
//第一个 是窗口句柄 表示显示在这个窗口前面 可以写NULL 或者0
//第二个参数 是文本框内容
//第三次参数 窗口标题
//最后一个参数是按钮形式 }
else
{
//输了游戏
drawMap(map);
//printf("输了游戏\n");
MessageBox(GetHWnd(), L"OOPS", L"LOSE", MB_OK);
} //getchar();
closegraph();//关闭窗口
return ;
} //**************************函数定义************************
//上述函数的定义放在这里 //给数组赋值 初始化
void init(int map[][]) //int (*map[4])(形参) 是数组指针 两种形式都可以
{
//给数组赋值 第一种方式用循环
//for (int i = 0; i < 4; ++i) //数组用 下标进行循环
//{
// for (int j = 0; j < 4; ++j)
// {
// map[i][j] = 0; //刚开始全部赋值为0
// }
//} //第二种方式用函数 memset 初始化一块内存 头文件是stdlib,h
memset(map, , sizeof(int)* * ); //三个参数 内存首地址 初值 字节大小
//随机两个2和4 null用于指针 随机数 借用两个函数srand rand --->头文件stlib.h 还需要一个函数 time(时间函数)--->头文件time.h
srand((unsigned)time(NULL)); //设置随机数种子 不要写在循环里面 设置一次就够了
//得到某个范围内的随机数 用求余控制范围 0-->100之间 int x=rand()%101; 66-->100之间 -->66+(0-->43) rand()%35+66
//得到随机位置
int x, y;
for (int i = ; i < ;)
{
x = rand() % ; //下标3至3 所以对4求余
y = rand() % ;
if (map[x][y] == ) //这个位置没有元素
{
map[x][y] = rand()%*+; //2或4 2=1*2 4=2*2
i++; //赋值之后才算一次
}
//++i放上面 只能确保循环两次 不能确保赋值两次 所以放下面
}
} //处理用户输入
void play(int map[][])
{
//方式 键盘操作
//getch 读取键盘中的一个字符 conio.h
//kbhit conio.h 检测当前是否有键盘消息
//dir di; //枚举变量
switch (getch()) //判断键盘消息
{
case 'w':
case 'W'://往上
for (int j = ; j < ; ++j) //四列
{
for (int i = ; i < ; ++i) //判断前三个能否和后面的合并
{
if (map[i][j] == ) //判断是否为0
{
//到后面找一个不是0的元素 换过来
int k = i + ;
for (; k < ; ++k)
{
if (map[k][j] != )
{
map[i][j] = map[k][j];
map[k][j] = ;
break;
}
}
if (k == )break; //表明这一行全是0
}
//判断map[i][j]和后面的元素是否可以合并
for (int k = i + ; k < ; ++k) //判断后面的元素
{
if (map[k][j] == ) continue;
else if (map[i][j] == map[k][j])
{
//相同 合并
map[i][j] *= ; //合并
map[k][j] = ;
break;
}
else break; //不相等 往后找 退出
}
}
}
newNum(map);
break;
case 's':
case 'S'://往下
for (int j = ; j < ; ++j)//四列
{
//每一行进行合并
for (int i = ; i >; --i)//判断前三个能否和后面的进行合并
{
if (map[i][j] == )//判断map[i][j]是否为0
{
//到后面找一个不是0的元素 换到这个位置
int k = i - ;
for (; k >= ; --k)
{
if (map[k][j] != )
{
map[i][j] = map[k][j];
map[k][j] = ;
break;
}
}
if (k == )break;//表明这一行全是0 直接找下一行
}
//判断map[i][j] 和后面的元素能否合并
for (int k = i - ; k >= ; --k)//判断后面的几个元素
{
if (map[k][j] == ) continue;
else if (map[i][j] == map[k][j])
{
map[i][j] *= ;//合并
map[k][j] = ;
break;
}
else break;//不相等 往后找 退出
}
}
}
newNum(map);
break;
case 'a':
case 'A'://往左
for (int i = ; i < ; ++i) //四行
{
//每一行进行合并 从左往右 1、判断第一个元素是不是0,如果是0,就到右边找到第一个不是0的元素,放到为0的位置上 不是0,就进行下一步,(没有找到 说明全是0,那么就直接下一行)
//2、找到剩下的位置中不是0的元素 如果和这个位置的相同的话合并到下一个位置
for (int j = ; j < ; ++j) //判断前三个能否和后面的合并
{
if (map[i][j] == ) //判断是否为0
{
//到后面找一个不是0的元素 换过来
int k = j + ;
for (; k < ; ++k)
{
if (map[i][k] != )
{
map[i][j] = map[i][k];
map[i][k] = ;
break;
}
}
if (k == )break; //表明这一行全是0
}
//判断map[i][j]和后面的元素是否可以合并
for (int k = j + ; k < ; ++k) //判断后面的元素
{
if (map[i][k] == ) continue;
else if (map[i][j] == map[i][k])
{
//相同 合并
map[i][j] *= ; //合并
map[i][k] = ;
break;
}
else break; //不相等 往后找 退出
}
}
}
newNum(map);
break;
case 'd':
case 'D'://往右
for (int i = ; i < ; ++i) //四行
{
for (int j = ; j >; --j) //判断前三个能否和后面的合并
{
if (map[i][j] == ) //判断是否为0
{
//到后面找一个不是0的元素 换过来
int k = j - ;
for (; k >=; --k)
{
if (map[i][k] != )
{
map[i][j] = map[i][k];
map[i][k] = ;
break;
}
}
if (k == -)break; //表明这一行全是0
}
//判断map[i][j]和后面的元素是否可以合并
for (int k = j - ; k >=; --k) //判断后面的元素
{
if (map[i][k] == ) continue;
else if (map[i][j] == map[i][k])
{
//相同 合并
map[i][j] *= ; //合并
map[i][k] = ;
break;
}
else break; //不相等 往后找 退出
}
}
}
newNum(map);
break;
case : //表示使用方向键 方向键是组合件 用getch视为两个部分
switch (getch())
{
case UP:
for (int j = ; j < ; ++j)//四列
{
//每一行进行合并
for (int i = ; i < ; ++i)//判断前三个能否和后面的进行合并
{
if (map[i][j] == )//判断map[i][j]是否为0
{
//到后面找一个不是0的元素 换到这个位置
int k = i + ;
for (; k<; ++k)
{
if (map[k][j] != )
{
map[i][j] = map[k][j];
map[k][j] = ;
break;
}
}
if (k == )break;//表明这一行全是0 直接找下一行
}
//判断map[i][j] 和后面的元素能否合并
for (int k = i + ; k < ; ++k)//判断后面的几个元素
{
if (map[k][j] == ) continue;
else if (map[i][j] == map[k][j])
{
map[i][j] *= ;//合并
map[k][j] = ;
break;
}
else break;//不相等 往后找 退出
}
}
}
newNum(map);
break;
case DOWN:
for (int j = ; j < ; ++j)//四列
{
//每一行进行合并
for (int i = ; i >; --i)//判断前三个能否和后面的进行合并
{
if (map[i][j] == )//判断map[i][j]是否为0
{
//到后面找一个不是0的元素 换到这个位置
int k = i - ;
for (; k >= ; --k)
{
if (map[k][j] != )
{
map[i][j] = map[k][j];
map[k][j] = ;
break;
}
}
if (k == )break;//表明这一行全是0 直接找下一行
}
//判断map[i][j] 和后面的元素能否合并
for (int k = i - ; k >= ; --k)//判断后面的几个元素
{
if (map[k][j] == ) continue;
else if (map[i][j] == map[k][j])
{
map[i][j] *= ;//合并
map[k][j] = ;
break;
}
else break;//不相等 往后找 退出
}
}
}
newNum(map);
break;
case LEFT:
for (int i = ; i < ; ++i)//四行
{
//每一行进行合并
for (int j = ; j < ; ++j)//判断前三个能否和后面的进行合并
{
if (map[i][j] == )//判断map[i][j]是否为0
{
//到后面找一个不是0的元素 换到这个位置
int k = j + ;
for (; k<; ++k)
{
if (map[i][k] != )
{
map[i][j] = map[i][k];
map[i][k] = ;
break;
}
}
if (k == )break;//表明这一行全是0 直接找下一行
}
//判断map[i][j] 和后面的元素能否合并
for (int k = j + ; k < ; ++k)//判断后面的几个元素
{
if (map[i][k] == ) continue;
else if (map[i][j] == map[i][k])
{
map[i][j] *= ;//合并
map[i][k] = ;
break;
}
else break;//不相等 往后找 退出
}
}
}
newNum(map);
break;
case RIGHT:
for (int i = ; i < ; ++i)//四行
{
//每一行进行合并
for (int j = ; j >; --j)//判断前三个能否和后面的进行合并
{
if (map[i][j] == )//判断map[i][j]是否为0
{
//到后面找一个不是0的元素 换到这个位置
int k = j - ;
for (; k >= ; --k)
{
if (map[i][k] != )
{
map[i][j] = map[i][k];
map[i][k] = ;
break;
}
}
if (k == -)break;//表明这一行全是0 直接找下一行
}
//判断map[i][j] 和后面的元素能否合并
for (int k = j - ; k >= ; --k)//判断后面的几个元素
{
if (map[i][k] == ) continue;
else if (map[i][j] == map[i][k])
{
map[i][j] *= ;//合并
map[i][k] = ;
break;
}
else break;//不相等 往后找 退出
}
}
}
newNum(map);
break;
default:
break;
}
break;
default:
break;
} } //打印 贴图
void drawMap(int map[][])
{
/*for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
printf("%d\t", map[i][j]);
}
printf("\n");
}
printf("\n\n");*/ //cleardevice();//图形库清除屏幕内容 //如果加上 有闪屏的问题
putimage(, , &img);//贴背景图
char arr[];//准备字符串
for (int i = ; i < ; ++i)
{
for (int j = ; j < ; ++j)
{
//加数字进来
//outtextxy根据坐标输出字符串
//itoa 将int转换成字符串
/*if (map[i][j] != 0)
{
sprintf(arr,"%d",map[i][j]);
outtextxy(160 * j+20, 160 * i+20, arr);
}*/ //有素材 switch 根据不同数字进行贴图
//通过数字确定背景
//238 106 80
//238 169 184
//131 139 131
//193 205 205 /*switch (map[i][j])
{
case 0:
case 32:
setfillcolor(COLOR1);
fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
break;
case 2:
case 64:
setfillcolor(COLOR2);
fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
break;
case 4:
case 128:
setfillcolor(COLOR3);
fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
break;
case 8:
case 256:
setfillcolor(COLOR4);
fillrectangle(160 * j, 160 * i, 160 * j + 160, 160 * i + 160);
break;
}*/ sprintf(arr, "%d", map[i][j]);
outtextxy( * j + , * i + , arr[]);
//根据数字 确定不同的背景图 然后在背景图上 写数字
//if ()
//2^1 2^2 2^3 2^4
//2^5 2^6 //debug 调试版本 比较慢
//release 发行版本 -->主要发给别人用的
}
}
} //随机一个新元素
void newNum(int map[][])
{
//判断是不是满
int empty = ;
for (int i = ; i < ; ++i)
{
for (int j = ; j < ; ++j)
{
if (map[i][j] == ) empty = ;
}
if (empty == ) break;//如果找到这个空的位置 不需要继续循环
}
if (empty == ) return; int x, y;
do
{
x = rand() % ;
y = rand() % ;
} while (map[x][y] != );
map[x][y] = rand() % * + ; //随机2和4
//如果地图满的话 我们不能随机元素 所以最后 加上一个判断地图满的函数
} //判断游戏是否结束
int isGameOver(int map[][])
{
//赢 返回1 输 -1 还没赢 还没输 返回0
//游戏输赢条件
//出现2048 游戏赢
//如果游戏不能走动 游戏输掉
int empty = ;//如果判断的时候 map[i][j]==0 empty置为1
//如果有相邻元素 并且相同的话 也将empty置为1
for (int i = ; i < ; ++i)
{
for (int j = ; j < ; ++j)
{
if (map[i][j] >= )//赢的条件
{
//赢得游戏
return ;
}
}
}
//条件1 数字全满 并且 相邻没有同样的数字
for (int i = ; i < ; ++i)
{
for (int j = ; j < ; ++j)
{
if (map[i][j] == ) empty = ;
if (i + < && map[i][j] == map[i + ][j]) empty = ;
if (j + < && map[i][j] == map[i][j + ]) empty = ;
}
}
if (empty == )
{
//游戏还没有结束
return ;
}
else
{
//游戏结束 //输
return -;
}
}

结果展示:

注:代码部分仅供学习参考,完全复制下来不一定能够实现

2019-04-03  12:04:42

C++学习(三十九)(C语言部分)之 游戏项目(2048游戏)的更多相关文章

  1. Salesforce LWC学习(三十九) lwc下quick action的recordId的问题和解决方案

    本篇参考: https://developer.salesforce.com/docs/component-library/bundle/force:hasRecordId/documentation ...

  2. 前端学习(三十九)移动端app(笔记)

    移动端App    开发App的三种方式    Native App         原生        底层语言        java         Android        oc      ...

  3. Java开发学习(三十九)----SpringBoot整合mybatis

    一.回顾Spring整合Mybatis Spring 整合 Mybatis 需要定义很多配置类 SpringConfig 配置类 导入 JdbcConfig 配置类 导入 MybatisConfig ...

  4. “全栈2019”Java第三十九章:构造函数、构造方法、构造器

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 学习笔记:CentOS7学习之十九:Linux网络管理技术

    目录 学习笔记:CentOS7学习之十九:Linux网络管理技术 本文用于记录学习体会.心得,兼做笔记使用,方便以后复习总结.内容基本完全参考学神教育教材,图片大多取材自学神教育资料,在此非常感谢MK ...

  6. Java开发学习(三十六)----SpringBoot三种配置文件解析

    一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...

  7. NeHe OpenGL教程 第三十九课:物理模拟

    转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...

  8. Java进阶(三十九)Java集合类的排序,查找,替换操作

    Java进阶(三十九)Java集合类的排序,查找,替换操作 前言 在Java方向校招过程中,经常会遇到将输入转换为数组的情况,而我们通常使用ArrayList来表示动态数组.获取到ArrayList对 ...

  9. Gradle 1.12用户指南翻译——第三十九章. IDEA 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  10. SQL注入之Sqli-labs系列第三十八关、第三十九关,第四十关(堆叠注入)

    0x1 堆叠注入讲解 (1)前言 国内有的称为堆查询注入,也有称之为堆叠注入.个人认为称之为堆叠注入更为准确.堆叠注入为攻击者提供了很多的攻击手段,通过添加一个新 的查询或者终止查询,可以达到修改数据 ...

随机推荐

  1. 【C++】回看面向对象与C++

    本文将记录在C++与面向对象的重新学习过程中的要点: 未定义行为

  2. variable 'o' used without having been completely initialized Compiling Vertex program

    variable 'o' used without having been completely initialized Compiling Vertex program   v2f vert (ap ...

  3. ssm 整合(方案二 maven)

    通过maven来整合ssm方便很多,至少不用去找jar包 具体架构如下: 1.配置pom.xml <project xmlns="http://maven.apache.org/POM ...

  4. js 过滤器

    ,,]; ary[] = ; ary.filter(function(x) { return x === undefined; }); // A. [undefined x 7] // B. [0, ...

  5. vue中关于v-for性能优化---track-by属性

    vue中关于v-for性能优化---track-by属性 最近看了一些react,angular,Vue三者的对比文章,对比来说Vue比较突出的是轻量级与易上手. 对比Vue与angular,Vue有 ...

  6. Vue.js 基本功能了解一下~

    一.写在前面 隔了这么久才来出Vue的第二篇文章,真是堕落了,自己先惩罚下/(ㄒoㄒ)/~~ 回过头看自己第一篇相关文章<初试 Vue.js>(http://www.cnblogs.com ...

  7. 获取当前页面url并截取所需字段

    let url = window.location.href; // 动态获取当前url // 例: "http://i.cnblogs.com/henanyundian/web/app/# ...

  8. Charles 抓包工具

    参考博客: https://blog.csdn.net/mxw2552261/article/details/78645118 发包与改包: https://blog.csdn.net/b722305 ...

  9. vue中提示toFixed不是函数

     vue中toFixed获取小数点后两位 错误提示:.toFixed is not a function解决办法:Number(_this.group_cash).toFixed(2) 转自:http ...

  10. Php基本类型学习

    1.8种基本的数据类型 1)四种基本类型 boolean (布尔类型) integer(整形类型) double  (双精度类型) string  (字符串类型) 2)两种复合类型 array (数组 ...