C++学习(三十九)(C语言部分)之 游戏项目(2048游戏)
/***************************项目 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游戏)的更多相关文章
- Salesforce LWC学习(三十九) lwc下quick action的recordId的问题和解决方案
本篇参考: https://developer.salesforce.com/docs/component-library/bundle/force:hasRecordId/documentation ...
- 前端学习(三十九)移动端app(笔记)
移动端App 开发App的三种方式 Native App 原生 底层语言 java Android oc ...
- Java开发学习(三十九)----SpringBoot整合mybatis
一.回顾Spring整合Mybatis Spring 整合 Mybatis 需要定义很多配置类 SpringConfig 配置类 导入 JdbcConfig 配置类 导入 MybatisConfig ...
- “全栈2019”Java第三十九章:构造函数、构造方法、构造器
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...
- 学习笔记:CentOS7学习之十九:Linux网络管理技术
目录 学习笔记:CentOS7学习之十九:Linux网络管理技术 本文用于记录学习体会.心得,兼做笔记使用,方便以后复习总结.内容基本完全参考学神教育教材,图片大多取材自学神教育资料,在此非常感谢MK ...
- Java开发学习(三十六)----SpringBoot三种配置文件解析
一. 配置文件格式 我们现在启动服务器默认的端口号是 8080,访问路径可以书写为 http://localhost:8080/books/1 在线上环境我们还是希望将端口号改为 80,这样在访问的时 ...
- NeHe OpenGL教程 第三十九课:物理模拟
转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线 ...
- Java进阶(三十九)Java集合类的排序,查找,替换操作
Java进阶(三十九)Java集合类的排序,查找,替换操作 前言 在Java方向校招过程中,经常会遇到将输入转换为数组的情况,而我们通常使用ArrayList来表示动态数组.获取到ArrayList对 ...
- Gradle 1.12用户指南翻译——第三十九章. IDEA 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- SQL注入之Sqli-labs系列第三十八关、第三十九关,第四十关(堆叠注入)
0x1 堆叠注入讲解 (1)前言 国内有的称为堆查询注入,也有称之为堆叠注入.个人认为称之为堆叠注入更为准确.堆叠注入为攻击者提供了很多的攻击手段,通过添加一个新 的查询或者终止查询,可以达到修改数据 ...
随机推荐
- TabBar + TabBarView导航风格
import 'package:flutter/material.dart'; import 'News.dart'; import 'Video.dart'; import 'Chat.dart'; ...
- centos6.5下安装Redis
已有redis-3.2.1.tar.gz文件 拖到centos系统的桌面 现在在桌面目录下 tar -zxv -f redis-3.2.1.tar.gz以解压压缩包 cd redis-3.2.1以切换 ...
- Python作业
1使用while 循环输入1,2,3,4,5,6,,8,9,10 count = 0 while count<10: count+=1 if count ==7: continue print( ...
- [Hibernate] 分页查询
@Test public void test9(){ //根据部门编号进行分组,再根据每个部门总工资>5000 Session ss=HibernateUtil.getSession(); St ...
- SWUST OJ(963)
小偷的背包 #include<stdio.h> #include<stdlib.h> int s, flag, n, *a; //主函数之外定义的变量为全局变量 void df ...
- 打造springboot高性能服务器(spring reactor的使用)
推荐:https://www.cnblogs.com/ivaneye/p/5731432.htmlpom依赖: <dependency> <groupId>org.spring ...
- YII框架实现 RBAC
(1).在 common\config\main.php添加 'components' => [ ’authManager’ => [ ’class’ => ...
- JAVA 创建文件和文件夹,删除文件和文件夹的实用工具
package com.file; import java.io.File; import java.io.IOException; //创建新文件和目录 public class CCRDFile ...
- Linux c读取系统内存使用信息
系统的内存使用信息能够在虚拟文件系统/proc/meminfo中找到,如图 所以只要打开/proc/meminfo文件,然后从中读取信息就好了 #include <stdio.h>#inc ...
- Python *Mix_w2
1.循环: 执行流程: 1. 判断条件是否为真. 如果真. 执行代码块 2. 再次判断条件是否为真...... 3. 当条件为假.执行else 跳出循环. 循环结束. while 条件: 代码块(又叫 ...