小彩球游戏

来源:互联网 发布:一元购完整源码 编辑:程序博客网 时间:2024/05/21 06:50

1.实验题目:彩球游戏实现过程

1.1问题描述(游戏规则):

(1)游戏区域为7*7-9*9(具体可由键盘输入确定),共有七种颜色的彩球随机出现,其中状态为5个,以后每次出现3个。

(2)用鼠标选中某个彩球,在选择一个空白区域作为目标位置,如果从源到目标位置有通路可走,将彩球移动到目标位置;如果没有通路可走,则不移动并给出提示。
(3)当同色彩球在横向,纵向,斜向达到5个及以上时,可以消除,同时得到相应的分数。
(4)当棋盘中没有空位时,游戏结束。

1.2题目要求:

1.2.1整体要求:
(1)所有小题放在一个程序中,以菜单的形式进行选择
(2)右侧得分,预告彩球以及统计信息。
(3)用伪图形界面进行游戏设计。

1.2.2 显示要求:
(1)被选中的彩球要有不同效果
(2)彩球移动是,要有动画效果沿着通路进行移动
(3)消除时要有相应的动画效果
(4)打印球时以不同的颜色输出
(5)伪图形界面中,鼠标在棋盘上移动实时显示当前所在的行,列位置。

1.2.3游戏规则具体要求:
(1)连续5个及以上则消除,得分规则自定义(具体实现中,我的规则是消除数量为n,则得分为(n-1)*(n-2),其中交叉点要重复计数)。
(2)若本次移动得分,则不产生新球,否则随机产生三个新球。
(3)鼠标左键选择,右键退出。
(4)小彩球只允许在空白位置进行移动,且只可横或竖走,不允许对角线走。

1.2.4 代码要求
(1)尽量使各菜单项中的程序公用函数,用参数解决细微差异。
(2)各函数代码长度尽量不超过50行
(3)不允许使用全局变量,全局指针以及全局数组,允许使用宏定义。
(4)只允许使用目前为止讲过的内容(包括课后补充知识。)

2.整体设计思路
写这个程序首先要考虑的是它的本质,也就是数据在机器内的存储形式。考虑多方面因素,最合适的应该是二维数组,形象的可以看成是10*10的行列矩阵(二维数组事实上还是连续排列的数据存储,这里为了方便理解)。其中数组的元素的值代表不同颜色的小球,0代表空位。程序设计过程中,可以通过数组相应位置(棋盘相应位置)的数值进行是否有球,是否同色等的判断,同时当球移动时,也可以通过移动路径中相应位置的值的变化来实现,同样包括当判断五个相同颜色的小球同线是消去的实现。
整体的过程如下图:

整体流程

3.主要功能的实现

3.1寻路

3.1.1 利用递归算法找到通路
自定义函数找到鼠标左键选择的起点和终点后,将起点和终点的坐标(即在二维数组内部存储的i,j值)存入两个大小为2 的数组begin[2]和final[2].
确定好起点和终点后,就可以开始寻路了。将整一个过程拆分成多个小过程,每个过程由两步组成—-起点走一步到起点2;起点2走到终点。不需要考虑每一个小过程到底发生了什么。
函数返回类型设置为int型,0代表无返回,也就是没找到路;1代表找到路。
找到之后原路返回,一路return 同时存储路径上的点 的坐标。算法完成之后,路径上所有的点逆序存放在rout数组之中。

例如下图:

递归流程

具体函数代码如下

int search(int(*p)[10], int(*rout)[2], int x1, int y1, int x2, int y2, int rows, int cols, int *num_pace){    if (x1 >= 0 && x1 < cols&&y1 >= 0 && y1 < rows)    {        if (x1 == x2&&y1 == y2)        {            rout[*num_pace][0] = y1;            rout[*num_pace][1] = x1;            (*num_pace)++;            return 1;        }        else            p[y1][x1] = -1;        if (p[y1][x1 + 1] == 0)        {            search(p, rout, x1 + 1, y1, x2, y2, rows, cols, num_pace);            if (*num_pace)            {                rout[*num_pace][0] = y1;                rout[*num_pace][1] = x1;                (*num_pace)++;                return 1;            }        }        if (p[y1 - 1][x1] == 0)        {            search(p, rout, x1, y1 - 1, x2, y2, rows, cols, num_pace);            if (*num_pace)            {                rout[*num_pace][0] = y1;                rout[*num_pace][1] = x1;                (*num_pace)++;                return 1;            }        }        if (p[y1][x1 - 1] == 0)        {            search(p, rout, x1 - 1, y1, x2, y2, rows, cols, num_pace);            if (*num_pace)            {                rout[*num_pace][0] = y1;                rout[*num_pace][1] = x1;                (*num_pace)++;                return 1;            }        }        if (p[y1 + 1][x1] == 0)        {            search(p, rout, x1, y1 + 1, x2, y2, rows, cols, num_pace);            if (*num_pace)            {                rout[*num_pace][0] = y1;                rout[*num_pace][1] = x1;                (*num_pace)++;                return 1;            }        }        return 0;    }    else        return 0;}

3.1.2找通路时内部存储变化
每次查找过程中,首先判断是否为结束条件,如果不是,则将内部数组该位置上的数值设置为-1,避免重复判断查找,保证不走同一个位置。而在判断能不能走的条件时,首先,要走的点对应的数值需要为0,同时不超出棋盘界限,所以当search完之后,需要将所有值为-1 的恢复成为原先的值,也就是0.
除此之外,起点和终点的值要进行互换。
不是所有值为-1的都是路径上的点,还有可能是探了一下但没有成功的点! 真正路径上的点都存储在相应的数组中。通过路径数组中存储的位置值,也就是对应二维数组中的i,j值对输出形式,位置进行选择。

3.1.3伪图形显示移动路径
通过路径数组中存储的位置值,也就是对应二维数组中的i,j值确定路径!由于存储时是先存终点后存起点,所以要逆方向读取。
首先需要判断路径上有几个点,用简单的循环和条件就可以判断。其次从倒数第二个开始,对应伪图形界面中的坐标,打印相应颜色的字符串,同时在倒数第一个位置打印空字符串,以此类推,一直到路径数组中的点全都对应完毕。

3.2判断得分
在新的一颗子落下之前,没有达到可以消除的条件;这颗子落下后,消除条件形成,发生消除动作。所以,每次寻路成功之后,以此次寻路中的终点作为第一颗子进行展开,判断是否满足消除条件。

判断得分

具体代码如下

void judge(int(*p)[10], char final[2], const int rows, const int cols, int *score, int *eve_score){    int new_chess = p[final[0] - 'A'][final[1] - '1'];    int x = final[1] - '1', y = final[0] - 'A';    int i1 = 1, i2 = 1, i3 = 1, i4 = 1, j1 = 1, j2 = 1, j3 = 1, j4 = 1, sum = 0;    if (x >= 1 && new_chess == p[y][x - 1] || x<cols - 1 && new_chess == p[y][x + 1])    {        while (x - i1 >= 0 && p[y][x - i1] == new_chess)            i1++;        while (x + j1<cols&&p[y][x + j1] == new_chess)            j1++;    }    if (y >= 1 && p[y - 1][x] == new_chess || y < rows - 1 && p[y + 1][x] == new_chess)    {        while (p[y - i2][x] == new_chess && y - i2 >= 0)            i2++;        while (p[y + j2][x] == new_chess && y + j2<rows)            j2++;    }    if (x >= 1 && y >= 1 && p[y - 1][x - 1] == new_chess || x < cols - 1 && y < rows - 1 && new_chess == p[y + 1][x + 1])    {        while (p[y + i3][x + i3] == new_chess && y + i3<rows && x + i3<cols)            i3++;        while (p[y - j3][x - j3] == new_chess &&y - j3 >= 0 && x - j3 >= 0)            j3++;    }    if (x<cols - 1 && y >= 1 && p[y - 1][x + 1] == new_chess || x >= 1 && y < rows - 1 && new_chess == p[y + 1][x - 1])    {        while (p[y - i4][x + i4] == new_chess && y - i4 >= 0 && x + i4<cols)            i4++;        while (p[y + j4][x - j4] == new_chess && x - j4 >= 0 && y + j4<rows)            j4++;    }    if (i1 + j1 >= 6)    {        sum += (i1 + j1 - 1);        for (int i = x + 1 - i1; i < x + j1; i++)            p[y][i] = 0;    }    if (i2 + j2 >= 6)    {        sum += (i2 + j2 - 1);        for (int i = y - i2 + 1; i < y + j2; i++)            p[i][x] = 0;    }    if (i3 + j3 >= 6)    {        sum += (i3 + j3 - 1);        for (int i = 1 - j3; i < i3; i++)            p[y + i][x + i] = 0;    }    if (i4 + j4 >= 6)    {        sum += (i4 + j4 - 1);        for (int i = 1 - i4; i<j4; i++)            p[y + i][x - i] = 0;    }    if (sum)    {        *eve_score = (sum - 1)*(sum - 2);        *score += (sum - 1)*(sum - 2);    }    else        *eve_score = 0;}

(如图只是为解释,该情况在实际中是不可能出现的)
判断时,从黑色小球出发,以上下为例,分两个方向进行搜索,分别定义i=1,j=1,循环判断p[y-i][x]?=p[y][x],不等于循环结束,记录i值;等于则i++,继续搜索。。。j同理,向黑色小球的下方进行搜索,最终纵向的小球个数为i+j-1;其他方向都是同理。
由于交叉点需要重复计数,所以选择先判断完所有方向,记录所有可以消除的球(包括交叉点的多次计数)后再对内部数组进行改变,防止交叉小球满足一个条件后就被消除,对潜在的其他方向的消除条件判断造成影响。
记录完之后计算此次得分,以此次得分是否为零,用以后续判断是否进行了消除。

代码较长,故仅贴出部分重要的函数
一下是在实现伪图形的游戏界面时的主要代码,其中包括比较多的没有贴出来的函数,我会尽量做出注释。

void seven(int(*p)[10]){    HANDLE hout = GetStdHandle(STD_OUTPUT_HANDLE);    const HANDLE hin = GetStdHandle(STD_INPUT_HANDLE);    int rows, cols, X, Y;//X,Y用于存放打印完棋盘后的光标坐标,方便打印结束语    char begin[2], final[2];//存放鼠标输入的起点坐标和终点坐标    int next[3], rout[81][2];//next用于存放下一轮将会出现的球的信息;rout用于存放寻到的路上的点坐标    init(&rows, &cols, p, 5);//初始化内部数组+随机产生5个球    const int *row_ptr = &rows;//防止不小心对cols rows篡改    const int *col_ptr = &cols;    int score = 0, eve_score = 0;//总得分,每次得分    int leap = 0;    print_console2(rows, cols, p);//打印棋盘    getxy(hout, X, Y);    print_console3(rows, cols, next, 0);//打印得分,预示等    for (int i = 0; i < 3; i++)        next[i] = rand() % 7;    print_console3(rows, cols, next, 4);    while (1)    {        int num_pace = 0;        bgn_fnl_mouse(begin, final, rows, cols, Y, p);//输入起点和终点的坐标        int begin_value = p[begin[0] - 'A'][begin[1] - '1'];//将起点值先存起来,寻完路后再赋给终点,防止寻路过程中值被“破坏        if (search(p, rout, begin[1] - '1', begin[0] - 'A', final[1] - '1', final[0] - 'A', *row_ptr, *col_ptr, &num_pace))        {//search返回1代表寻到路,否则没寻到路!但不论是否寻到路,这个函数中的操作已经执行,也就是路径上的点的值已被改变            reset_array(p, col_ptr, row_ptr);//search是吧探索过的店全都变成了-1要变回来,包括起始点            p[final[0] - 'A'][final[1] - '1'] = begin_value;            show_rout(rout, begin_value);//把存在rout里的路径上的点坐标按顺序(加延时)显示,即移动路径            for (int i = 0; i < 81; i++)                for (int j = 0; j < 2; j++)                    rout[i][j] = -1;//清空存储的路径记录            judge(p, final, *row_ptr, *col_ptr, &score, &eve_score);//判断能否得分,若得分,设置为0,更新内部数组            if (eve_score == 0)            {                update_array(p, next, col_ptr, row_ptr, 1);//将next中存的值随机赋给"棋盘"中空白的位置,也就是给内部数组p                for (int i = 0; i < 3; i++)                    next[i] = rand() % 7+1;////                print_console3(rows, cols, next, 4);//4用于控制打印位置            }            else            {/*遍历棋盘,把刚消去球的位置打印空格*/                for (int i = 0; i < rows; i++)                    for (int j = 0; j < cols; j++)                        if (p[i][j] == 0)                            showstr(hout, 2 + j * 4, (i + 1) * 2, "  ", COLOR_HWHITE, COLOR_HWHITE);                setcolor(hout, COLOR_HWHITE, COLOR_BLACK);                gotoxy(hout, 4 * cols + 12, 2);                cout << score;            }        }        else//如果没有找到路        {            p[begin[0] - 'A'][begin[1] - '1']=begin_value;            showstr(hout, 2 +(begin[1] - '1') * 4, (begin[0] - 'A' + 1) * 2, "○", begin_value + 1, COLOR_HWHITE);            reset_array(p, col_ptr, row_ptr);            continue;        }        if (game_over(p, row_ptr, col_ptr,Y-1))            break;    }    end();}
0 0
原创粉丝点击