C语言课程设计报告-游戏2048
发布时间:2015-01-15 09:17:50
发布时间:2015-01-15 09:17:50
东华理工大学
C语言课程设计 报告
学院: 国际教育学院学院
专业: 电子信息工程
班级: 1420606
学号: 201420060638
姓名: 钟天运
1、课程设计题目:游戏2048
2、课程设计要求:
a) 使用C语言编写2048这款游戏
b) 能够正常运行,拥有游戏界面。
c) 能正常进行游戏从开始到结束。
d) 用户操作方便
3、设计思路:
a) 游戏介绍:
i. 2048是一款简单的数字类游戏,界面是一个4*4的方形格子。每个格子里可以为空或者有一个2^n的数值。
ii. 用户可以输入4种指令,分别是:上下左右,游戏会根据用户的指定的方向,将格子中的数值向对应方向进行移动,直至移动到最边上的格子或者有其他数值占用,如果碰到等大数值,将会进行合并。此外,成功移动后,会在一个空格子随机生成一个2或者4
iii. 游戏目标是合成2048这个数值或者更大的数值。
b) 实现思路:
i. 可以使用二维数组来保存4*4格子中的数值
ii. 指令,可以通过输入字符函数,读取用户在键盘上的方向键,进行判断执行对应的代码。
iii. 游戏界面,可以使用简单的特殊制表符,来实现,并通过清屏函数来进行反复同位置打印界面。
iv. 需要判断游戏结束的函数,以及记录游戏分数和步骤的变量
v. 当游戏结束时,能够询问 用户是否重新开始。
vi. 随机生成一个新数,可以调用随机函数,使用时间做种子。
c) 实现难点:
i. 打印游戏界面,要实现灵活能根据棋盘数组里面的数据灵活打印。
ii. 执行操作时,数值的移动和合并。
4、流程图
5、C语言源代码
// 游戏2048.c
#include "windows.h"
#include "time.h"
#include "stdio.h"
#include "conio.h"
#include "string.h"
//宏定义常量 方向键值
//const int LEFT = 75, UP = 72, RIGHT = 77, DOWN = 80;
#define LEFT 75
#define UP 72
#define RIGHT 77
#define DOWN 80
const char error_str[] = "您上次输入的指令无法识别,请重新输入。";
struct board
{
int place[4][4];
long int stepn;
long int num; //存储游戏分数
long int time;
int dtk; //direction key 记录方向键,及操作方向
int over;
int zeronum;
};
//该函数为游戏运行函数,当只是玩游戏的时候。进入该函数,游戏控制函数。
int main()
{
//place数组为棋盘,其中为零代表空,-1代表不能合并的牌,其他2的倍数值为本身含义,初始化为全0。
struct board board1, board_backup;
int newgame(struct board *, int),
show(struct board *),
operate(struct board *);
char str[100] = "首次运行游戏"; //用于记录系统返回给用户的信息,例如:上一步执行向左合并,按键有误等
newgame(&board1, 0); //调用函数为新局初始化,第二个参数为初始化方式
show(&board1);
printf("\n%s\n\n请从键盘上单击方向键,指定下一步操作(ESC键退出游戏)……\n", str);
do
{
switch (_getch())
{
case 224:
{
switch (board1.dtk = _getch())
{
case LEFT: strcpy_s(str, sizeof(str), "您上次输入的方向键为:←,已执行左向合并。"); board_backup = board1; operate(&board1); break;
case UP: strcpy_s(str, sizeof(str), "您上次输入的方向键为:↑,已执行上向合并。"); board_backup = board1; operate(&board1); break;
case RIGHT: strcpy_s(str, sizeof(str), "您上次输入的方向键为:→,已执行右向合并。"); board_backup = board1; operate(&board1); break;
case DOWN: strcpy_s(str, sizeof(str), "您上次输入的方向键为:↓,已执行下向合并。"); board_backup = board1; operate(&board1); break;
default: strcpy_s(str, sizeof(str), error_str);
}
}break;
case 27: exit(27);
default: strcpy_s(str, sizeof(str), error_str);
}
system("cls");
show(&board1);
printf("\n%s\n\n请从键盘上单击方向键,指定下一步操作(ESC键退出游戏)……\n", str);
if (board1.over)
{
printf("\n游戏结束,是否重新开始?(y重新开始/n或ECS退出/其他键无效)");
while (1)
{
str[99] = _getch();
if (str[99] == 'y')
{
newgame(&board1, 0);
show(&board1);
break;
}
else if (str[99] == 'c')
{
change(&board1, &board_backup);
break;
}
else if (str[99] == 'n' || str[99] == 27)
break;
}
}
} while (!board1.over);
printf("\n按任意键退出……");
_getch();
return 0;
}
//该函数为主要打印函数,包括棋盘的打印,分数,等信息
int show(struct board * pboard)
{
int i, j, x, len,
numlen(int);
printf("游戏2048——游戏运行时间:%ld S\n", time(NULL) - (*pboard).time);
printf("游戏分数:%ld", (*pboard).num);
printf(" 已执行步骤数:%ld\n", (*pboard).stepn);
//开始绘制棋盘
printf("╔═══╦═══╦═══╦═══╗\n║ ║ ║ ║ ║\n");
for (i = 0; i < 7; ++i)
{
if (i % 2 == 0)
{
printf("║");
for (j = 0; j < 8; ++j)
{
if (j % 2 == 0)
{
if ((*pboard).place[i / 2][j / 2] == 0)
printf(" "); //如果值为0,输入六个空格
else //打印值时。调用numlen判断值的位数,控制数值前后的空格数量,最长为6,若为奇数,前面空格比后面多一个。
{
len = numlen((*pboard).place[i / 2][j / 2]);
for (x = 0; x < (6 - len + 1) / 2; ++x)
printf(" ");
printf("%d", (*pboard).place[i / 2][j / 2]);
for (x = 0; x < (6 - len) / 2; ++x)
printf(" ");
}
}
else
printf("║");
}
}
else
{
printf("║ ║ ║ ║ ║\n╠═══╬═══╬═══╬═══╣\n║ ║ ║ ║ ║");
}
printf("\n");
}
printf("║ ║ ║ ║ ║\n╚═══╩═══╩═══╩═══╝\n");
return 0;
}
//这是show函数的附属函数,用于求一个整数长度。
int numlen(int a)
{
int i, n = 1;
for (i = 1; i < 11; ++i)
{
n *= 10;
if (a == a%n)
return i;
}
return 0;
}
//开始新的游戏,将棋盘和数据初始化,或者载入棋盘,第二个参数决定初始化方案,默认0
int newgame(struct board * pboard, int project)
{
int i, j, t;
(*pboard).stepn = 0;
(*pboard).num = 0;
(*pboard).time = (long int)time(NULL); //取当前时间为开始时间
(*pboard).dtk = 0;
(*pboard).over = 0;
(*pboard).zeronum = 14;
for (i = 0; i < 4; ++i)
for (j = 0; j < 4; ++j)
(*pboard).place[i][j] = 0;
do
{
i = (int)(rand() / 32768.0 * 4);
j = (int)(rand() / 32768.0 * 4);
} while ((*pboard).place[i][j]);
t = 2 * ((int)(rand() / 32768.0 * 2) + 1);
(*pboard).place[i][j] = t;
do
{
i = (int)(rand() / 32768.0 * 4);
j = (int)(rand() / 32768.0 * 4);
} while ((*pboard).place[i][j]);
t = 2 * ((int)(rand() / 32768.0 * 2) + 1);
(*pboard).place[i][j] = t;
return 0;
}
//主要操作函数,调用该函数,将执行结构体中对应按键值的方向的操作,并增加分数和操作次数。
int operate(struct board * pboard)
{
int i, j, t, shun = -1, alter = 0, //alter变量是用来判断执行步骤的时候是否改变了棋局。shun用来记录已经进行合并的值对应的I或J,用来避免该值再次合并
randget(struct board *),
calczeronum(struct board *);
switch ((*pboard).dtk)
{
case UP:
{
for (j = 0; j < 4; ++j, shun = -1)
{
for (i = 1; i < 4; ++i)
{
if ((*pboard).place[i][j] != 0)
{
for (t = i - 1; t >= 0; --t)
{
if ((*pboard).place[t][j] != 0)
{
if ((*pboard).place[t][j] == (*pboard).place[i][j] && shun != t)
{
(*pboard).num += (*pboard).place[t][j] *= 2;
(*pboard).place[i][j] = 0;
shun = t;
alter = 1;
}
else if (t + 1 != i)
{
(*pboard).place[t + 1][j] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
}
break;
}
else if (t == 0)
{
(*pboard).place[t][j] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
break;
}
}
}
}
}
}break;
case DOWN:
{
for (j = 0; j < 4; ++j, shun = -1)
{
for (i = 2; i >= 0; --i)
{
if ((*pboard).place[i][j] != 0)
{
for (t = i + 1; t < 4; ++t)
{
if ((*pboard).place[t][j] != 0)
{
if ((*pboard).place[t][j] == (*pboard).place[i][j] && shun != t)
{
(*pboard).num += (*pboard).place[t][j] *= 2;
(*pboard).place[i][j] = 0;
shun = t;
alter = 1;
}
else if (t - 1 != i)
{
(*pboard).place[t - 1][j] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
}
break;
}
else if (t == 3)
{
(*pboard).place[t][j] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
break;
}
}
}
}
}
}break;
case RIGHT:
{
for (i = 0; i < 4; ++i, shun = -1)
{
for (j = 2; j >= 0; --j)
{
if ((*pboard).place[i][j] != 0)
{
for (t = j + 1; t < 4; ++t)
{
if ((*pboard).place[i][t] != 0)
{
if ((*pboard).place[i][t] == (*pboard).place[i][j] && shun != t)
{
(*pboard).num += (*pboard).place[i][t] *= 2;
(*pboard).place[i][j] = 0;
shun = t;
alter = 1;
}
else if (t - 1 != j)
{
(*pboard).place[i][t - 1] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
}
break;
}
else if (t == 3)
{
(*pboard).place[i][t] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
break;
}
}
}
}
}
}break;
case LEFT:
{
for (i = 0; i < 4; ++i, shun = -1)
{
for (j = 1; j < 4; ++j)
{
if ((*pboard).place[i][j] != 0)
{
for (t = j - 1; t >= 0; --t)
{
if ((*pboard).place[i][t] != 0)
{
if ((*pboard).place[i][t] == (*pboard).place[i][j] && shun != t)
{
(*pboard).num += (*pboard).place[i][t] *= 2;
(*pboard).place[i][j] = 0;
shun = t;
alter = 1;
}
else if (t + 1 != j)
{
(*pboard).place[i][t + 1] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
}
break;
}
else if (t == 0)
{
(*pboard).place[i][t] = (*pboard).place[i][j];
(*pboard).place[i][j] = 0;
alter = 1;
break;
}
}
}
}
}
}break;
default:return -1;
}
if (alter)
{
++(*pboard).stepn;
return randget(pboard);
}
return 1;
}
//计算place数组中有多少个零,即多少个空位,返回值等同于 board1.zeronum
int calczeronum(struct board * pboard)
{
int i, j;
for ((*pboard).zeronum = 0, i = 0; i < 4; ++i)
for (j = 0; j < 4; ++j)
if ((*pboard).place[i][j] == 0)
++(*pboard).zeronum;
return (*pboard).zeronum;
}
//新数生成函数。每次有效执行 操作函数 时,进行新的数生成
int randget(struct board * pboard)
{
int i, j, t, x,
ifover(struct board *);
calczeronum(pboard);
t = (int)(rand() / 32768.0 * (*pboard).zeronum) + 1;
for (x = 0, i = 0; i < 4; ++i)
for (j = 0; j < 4; ++j)
if ((*pboard).place[i][j] == 0)
if (++x == t)
{
(*pboard).place[i][j] = 2 * ((int)(rand() / 32768.0 * 2) + 1);
if (!(--(*pboard).zeronum) || (*pboard).zeronum <= 0)
ifover(pboard);
return 0;
}
return -1;
}
//用来判断游戏是否结束,当board.zeronum 的值为零的时候,调用该函数进行判断。游戏结束时返回1,over值为1,否则返回可操作方向值,DOWN或RIGHT
int ifover(struct board * pboard)
{
int i;
for (i = 0; i < 4; ++i)
{
if ((*pboard).place[i][0] == (*pboard).place[i][1] || (*pboard).place[i][1] == (*pboard).place[i][2] || (*pboard).place[i][2] == (*pboard).place[i][3])
{
(*pboard).over = 0;
return DOWN;
}
if ((*pboard).place[0][i] == (*pboard).place[1][i] || (*pboard).place[1][i] == (*pboard).place[2][i] || (*pboard).place[2][i] == (*pboard).place[3][i])
{
(*pboard).over = 0;
return RIGHT;
}
}
return (*pboard).over = 1;
}
6、执行效果
7、总结
原计划是要在此基础上再做个2048游戏AI,让电脑去玩这个游戏,但时间受限,以及难度较高,导致无法限时内完成。
通过清屏,打印,清屏,打印,实现了游戏的动画效果,尽管是非常单调简单的,但是只通过单纯的打印函数实现,还是蛮有意思的。