C语言课程设计报告-游戏2048

发布时间: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重新开始/nECS退出/其他键无效)");

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用来记录已经进行合并的值对应的IJ,用来避免该值再次合并

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 的值为零的时候,调用该函数进行判断。游戏结束时返回1over值为1,否则返回可操作方向值,DOWNRIGHT

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,让电脑去玩这个游戏,但时间受限,以及难度较高,导致无法限时内完成。

通过清屏,打印,清屏,打印,实现了游戏的动画效果,尽管是非常单调简单的,但是只通过单纯的打印函数实现,还是蛮有意思的。

C语言课程设计报告-游戏2048

相关推荐