c印记(六): 数组与递归联合应用的小游戏

目录

    • 目录
    • 一概述
    • 二汉诺塔Hanoi Tower
      • 将汉诺塔变为算法问题化描述
      • 分析
        • a 当n 1的时候
        • b 当n 2的时候
        • c 当n 3的时候
        • d 当n N的时候
      • 实现
    • 三迷宫
      • 迷宫的表示方式
      • 手动版迷宫
      • AI版迷宫

一、概述

前面分别讲述了数组和递归,他们在c语言程序设计与编程当中是非常有用的,出现的频率也比较高,

下面就用两个经典的小游戏(汉诺塔,迷宫)来说明一下数组和递归的具体应用。

二、汉诺塔(Hanoi Tower)

汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

这算是一个比较经典的递归问题了。下面就来简单说明一下其实现方式。

1.将汉诺塔变为算法问题化描述

假设,这里有A,B,C三根柱子,在柱子A上有n个由大到小的圆盘,现需要将A柱子上的圆盘全部移动到C柱子上,其具体规则如下:

- 圆盘要始终保持上小下大的顺序

- 一次只能移动一个圆盘

- 在从A移动到C的过程中可以记住柱子B进行中间移动

2. 分析

这里以数字来表示圆盘,数字的大小就是圆盘的大小,那么汉诺塔就可以简单的用字幕和数字表示为:

A B C

1

2

n - 1

n

a. 当n = 1的时候

直接将圆盘从A移动到C,A ——> C

b. 当n = 2的时候

移动顺序为:

圆盘 移动策略
1 A ——> B
2 A ——> C
1 B ——> C

c. 当n = 3的时候

可以将上面两个圆盘(1,2)看着一个整体,这样就可以像上面一样只处理两个圆盘,这里以大写的‘一’表示(1,2)两个圆盘的合体,以‘二’表示圆盘三,其移动顺序为:

圆盘 移动策略
A ——> B
A ——> C
B ——> C

如果完整的展开其移动顺序为:

圆盘 移动策略
1 A ——> C
2 A ——> B
1 C ——> B
3 A ——> C
1 B ——> A
2 B ——> C
1 A ——> C

d. 当n = N的时候

和上面一样,可以将前面N - 1个圆盘视为同意的一个圆盘,第N个圆盘视为第二个圆盘,那么其以供 顺序为为:

圆盘 移动策略
N-1 A ——> B
N A ——> C
N-1 B ——> C

这个移动策略就是汉诺塔归纳出来的通用移动策略,也就是说,汉诺塔游戏的递归函数只需要实现这样的移动策略就可以了

3. 实现

这里所说的小游戏并不是常规的有炫酷界面的游戏,这个汉诺塔小游戏是一个简单的控制台程序,既然是游戏,那么这里还是按照比较常用的MVC的架构来实现,其中:

- M: 模型(也就是数据部分),这里以一个二维数组来实现这个小游戏的数据模型,以数组的列分别表示柱子A,B,C,以数组的行表示圆盘(形如:int data_arrray[10][3])

- V:视图(也就是显示部分),这里将实现一个show函数,用于将二维数组显示与控制台界面上

- C:控制(也就是逻辑部分),这里将用一个递归函数实现移动圆盘的过程

接下来就将完整的c语言代码贴出来:

#include <stdio.h>
#include <stdlib.h>

 /**< the data struct, this is M in MVC model */
typedef struct my_hanoi_tower_s
{
    int row; /** the count of array row */
    int column; /** the count of array column */
    int column_name[3];
    int data_array[10][3];
}my_hanoi_tower_t;

/**
 *
 *@brief  show the hanoi tower data array.
 *
 *@param  mht [in] the data struct.
 *
 *@return None.
 *
 *@see
 *@note this is V in MVC model
 */
 void show(my_hanoi_tower_t* mht)
 {
    int i,j;
    int n;
    for (n = 0; n < mht->column; n++)
    {
        printf("%5c", mht->column_name[n]);/**< show the name of columns */
    }
    printf("\n---------------------\n"); /** show split line */
    for (i = 0; i < mht->row; i++)
    {
        for(j = 0; j < mht->column; j++)
        {
            printf("%5d", mht->data_array[i][j]);
        }
        printf("\n");
    }
    printf("\n");
 }

/**
 *
 *@brief  initialize the hanoi tower data array .
 *
 *@param  mht [in] the data struct.
 *@param  n   [in] the count of disc
 *
 *@return None.
 *
 *@see
 *@note this is M in MVC model
 */
void init(my_hanoi_tower_t* mht, int n)
{
    int j = 1;
    int i;
    for (i = n; i > 0; i--)
    {
        /** the set number means disc size */
        mht->data_array[mht->row - i][0] = j++; /** just initialize column ‘A‘ */
    }

    mht->column_name[0] = ‘A‘;
    mht->column_name[1] = ‘B‘;
    mht->column_name[2] = ‘C‘;
}

int get_zero_start_row(my_hanoi_tower_t* mht, int column)
{
    int ret = -1;
    int i;
    for (i = mht->row - 1; i >= 0; i--)
    {
        if (mht->data_array[i][column] == 0)
        {
            ret = i;
            break;
        }
    }

    return ret;
}
int get_noe_zero_start_row(my_hanoi_tower_t* mht, int column)
{
    int ret = -1;
    int i;
    for (i = 0; i < mht->row; i++)
    {
        if (mht->data_array[i][column] > 0)
        {
            ret = i;
            break;
        }
    }

    return ret;
}

void move_disc(my_hanoi_tower_t* mht, int from_col, int to_col, int from_row, int to_row)
{
    int to_val   = mht->data_array[to_row][to_col];
    int from_val = mht->data_array[from_row][from_col];
    mht->data_array[to_row][to_col] = from_val;
    mht->data_array[from_row][from_col] = to_val;
    printf("move: %c(%d) --> %c\n", mht->column_name[from_col], from_val, mht->column_name[to_col]);
    show(mht);
}

/**
 *
 *@brief  the AI to auomatic process hanoi tower.
 *
 *@param  mht [in] the data struct.
 *
 *@return None.
 *
 *@see
 *@note this is C in MVC model
 *@note 算法: 1. 当只有一个盘子的时候,只需要从将A塔上的一个盘子移到C塔上。

              2. 当A塔上有两个盘子时,先将A塔上的1号盘子(编号从上到下)移动到B塔上,
                 再将A塔上的2号盘子移动的C塔上,最后将B塔上的小盘子移动到C塔上。

              3. 当A塔上有3个盘子时,先将A塔上编号1至2的盘子(共2个)移动到B塔上(需借助C塔),
                 然后将A塔上的3号最大的盘子移动到C塔,最后将B塔上的两个盘子借助A塔移动到C塔上。

              4. 当A塔上有n个盘子是,先将A塔上编号1至n-1的盘子(共n-1个)移动到B塔上(借助C塔),
              然后将A塔上最大的n号盘子移动到C塔上,最后将B塔上的n-1个盘子借助A塔移动到C塔上。
 */
void hanoi_AI(my_hanoi_tower_t* mht,int n, int a, int b, int c)
{
    if (n < 1)
    {
        return ;
    }
    else if (1 == n)
    {
        int a_row = get_noe_zero_start_row(mht, a);
        int c_row = get_zero_start_row(mht, c);
        move_disc(mht, a, c, a_row, c_row);
    }
    else
    {
        hanoi_AI(mht, n - 1, a, c, b);

        int a_row = get_noe_zero_start_row(mht, a);
        int c_row = get_zero_start_row(mht, c);
        move_disc(mht, a, c, a_row, c_row);
        hanoi_AI(mht, n - 1, b, a, c);
    }
}

int main(int argc, char* argv[])
{
    my_hanoi_tower_t mht = {0};
    mht.column = 3;
    mht.row = 10;

    printf("Hello world!\n");
    init(&mht, 3);
    show(&mht);
    hanoi_AI(&mht, 3, 0, 1, 2);
    show(&mht);
    system("pause");
    return 0;
}

三、迷宫

迷宫也是一个比较经典的小游戏,其中自动走迷宫的算法,也算是寻路算法的简单雏形。

1. 迷宫的表示方式

这里的迷宫和上面的汉诺塔小游戏一样,都是使用一个二维数组来表示的。不同的是,在迷宫小游戏中二维数组里面会有几组不同的数据,首先,数组的大部分元素都是 0,起到‘背景’的作用,然后内部会 有一些值为2元素,它们散落在数组内的不同地方,起到‘墙壁’的作用,表示此方向在当前位置被阻断,无法通行,还有一个值为1的元素,初始化之后,其位置在[0][0]的地方,表示走迷宫的角色。形如

// ---- x
// |
// |
// y
{
    { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 2, 2, 2, 2, 0, 0, 0, 0 },
    { 0, 0, 2, 0, 0, 0, 2, 0, 0, 0 },
    { 0, 0, 0, 0, 2, 0, 2, 0, 2, 2 },
    { 2, 0, 2, 0, 0, 0, 2, 0, 0, 2 },
    { 0, 0, 0, 0, 2, 0, 0, 0, 0, 2 },
    { 0, 2, 0, 0, 0, 2, 0, 2, 0, 0 },
    { 0, 2, 0, 2, 0, 0, 0, 0, 2, 0 },
    { 0, 0, 0, 0, 2, 0, 2, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};

2. 手动版迷宫

先来这一种相对比较简单,这种情况相对来说比较简单,其原理就是,接收来自键盘输入的字母来确定角色‘1’ 应该怎样移动,使用x,y来表示坐标,x为横向从左至右依次递增(即 0 ——> x),用数组的列数 表示, x为从上至下依次递增(即0 ——> y),用数组的行数表示。角色在迷宫中移动需要注意以下几点:

  • 欲移动的新坐标中,x,y的值均不能小于0(否则就出现越界了),
  • 欲移动的新坐标中,x,y的值均不能大于,数组的最大行和列减1,例如迷宫使用数组 int a[M][N],

    那么,新坐标中,必须要有 x < N 或 x <= (N - 1), y < M或 y <= (M - 1)

  • 不能移动到数组表示墙的元素(如上面二维数组中的‘2’)的坐标上,如上面的二维数组中,角色就不能移动到坐标(2, 0)上,也就是二维数组的[0][2]位置
  • 每次只能移动一个单位坐标
  • 移动的方向只能是 上,下,左,右

有了上面几点限制条件之后,剩下的就是具体的移动实现了,这个比较简单,就是根据输入的字母判断应该向哪个方向移动,然后再判断欲将移动的新坐标是否满足上面几个条件,如果满足就移动,不满足,就原地不动。具体实现代码如下

/**
 *
 *@brief  role move a step with direction.
 *
 *@param  a         [in] the 2D array.
 *@param  x         [io] input orignal x coordinate,output move result.
 *@param  y         [io] input orignal y coordinate,output move result.
 *@param  direction [in] move direction.
 *
 *@return None.
 *
 *@see
 */
static void maze_move(int a[MAZE_ROW][MAZE_COLUMN],int* x, int* y, int direction)
{
    int org_x = *x; //old coordinate x
    int org_y = *y; // old coordinate y
    int role_x = org_x;
    int role_y = org_y;
    const char* dir_name = "unknown";
    switch (direction)
    {
    case ‘w‘:// up
        if ((role_y - 1) >= 0)
        {
            role_y--;
            dir_name = "up";
        }
        break;
    case ‘s‘: //down
        if ((role_y + 1) < MAZE_ROW)
        {
            role_y++;
            dir_name = "down";
        }
        break;
    case ‘a‘: //left
        if ((role_x - 1) >= 0)
        {
            role_x--;
            dir_name = "left";
        }
        break;
    case ‘d‘://right
        if ((role_x + 1) < MAZE_COLUMN)
        {
            role_x++;
            dir_name = "right";
        }
        break;
    default:
        printf("unspport this command:%c\n", direction);
        break;
    }

    if ((org_x != role_x) || (org_y != role_y)) //coordinate has change,then move
    {
        printf("move:%s\n", dir_name);
        int temp = a[org_y][org_x];
        a[org_y][org_x] = a[role_y][role_x];
        a[role_y][role_x] = temp;
        *x = role_x;
        *y = role_y;
    }

}

其中 MAZE_ROW, MAZE_COLUMN是宏定义,表示数组的行和列,可根据具体需要定义其值

3. AI版迷宫

这个版本将使用寻路算法实现自动计算走出迷宫的路径(当然,如果迷宫是死胡同,那自动寻路也将找不到走出迷宫的路径。然后寻路的实现也可分递归版本和循环版本),在数据表示方面和手动版迷宫一样,都是使用二维数组来实现,当然也有不同的地方,就是在AI版中,会在手动版的基础上,在定义一个一模一样(行列相同)的二维数组用于自动寻路算法使用,其中的数据也就会被初始化为和表示迷宫数据的二维数组一样。

既然要自动寻路,那么就需要来分析一下,迷宫寻路的规律或者实现方式:

  • 首先,忽略掉限制条件的情况下,在每一个坐标点,都可以有4种移动方式,上,下,左,右
  • 其次,移动的过程,就是将当前坐标的数组元素与计算出来的新坐标的数组元素进行交换
  • 再次,在移动过程中的限制条件和手动版是一样的
  • 然后,因为初始化之后,角色是在左上角(坐标为(0,0),数组位置为[0][0]),而走出迷宫的标志是走到右下角(坐标为(MAZE_COLUMN-1, MAZE_ROW-1),数组位置为[MAZE_ROW-1][MAZE_COLUMN-1]),所以,判 断一种能否移动的顺序为:右,下,左,上
  • 最后,需要防止移动的时候,出现回退的情况,即从位置A移动到位置B之后,在计算新的移动点C的时候,不能让其再次落到A位置的坐标上,否则会出现死循环

首先是判断各个方向能否进行移动的函数:

/**
 *
 *@brief  try move a step(just got x,y coordinate,not really move).
 *
 *@param  a         [in] the 2D array.
 *@param  x         [io] input orignal x coordinate,output move result.
 *@param  y         [io] input orignal y coordinate,output move result.
 *
 *@return 1:can move, 0:cann‘t move.
 *
 *@see
 */
int  try_move(int a[MAZE_ROW][MAZE_COLUMN], int* x, int *y)
{
    int ret = 1;
    int tempx = *x;
    int tempy = *y;
    //try order: right->down->left->up

    if (((tempx + 1) < MAZE_COLUMN) && (a[tempy][(tempx + 1)] < BARRIER))
    {//try right
        *x = tempx + 1;
    }
    else if (((tempy + 1) < MAZE_ROW) && (a[(tempy + 1)][tempx] < BARRIER))
    {//try down
        *y = tempy + 1;
    }
    else if (((tempx - 1) >= 0) && (a[tempy][(tempx - 1)] < BARRIER))
    {//try left
        *x = tempx - 1;
    }else if (((tempy - 1) >= 0) && (a[(tempy - 1)][tempx] < BARRIER))
    {//try up
        *y = tempy - 1;
    }
    else
    {
        ret = 0;
    }

    return ret;
}

其中 MAZE_ROW, MAZE_COLUMN是宏定义,表示数组的行和列,可根据具体需要定义其值。宏BARRIER表示迷宫中的‘墙’,其值为2,之所以判断小于BARRIER,而不是等于,是因为之后还需要定义一个比BARRIER大的宏记录寻路算法走过的路径,且不能重复的走已经走过的路径。

然后是递归寻路函数:

/**
 *
 *@brief  the AI of pathfinding (recursive implement)
 *
 *@param  a         [in] the 2D array.
 *@param  x         [in] the x coordinate of role.
 *@param  y         [in] the y coordinate of role.
 *
 *@return 1: find, 0: doesn‘t.
 *
 *@see
 */
int maze_pathfinding_ai_recursive(int a[MAZE_ROW][MAZE_COLUMN], int x, int y)
{
    a[y][x] = AI_OBJ;

    if (((MAZE_COLUMN - 1) == x) && ((MAZE_ROW - 1) == y)) //recursive end condition
    {
        return 1;
    }
    else
    {
        if (try_move(a, &x, &y)) //check try move.
        {
            return maze_pathfinding_ai_recursive(a, x, y);
        }

    }

    return 0;
}

其中宏AI_OBJ的值为3,用来记录寻路算法走过的路径以及防止重复走过的路径(也就是死循环)

上面有提供寻路算法有递归和循环两个版本,而循环的算法实现方式为:

/**
 *
 *@brief  the AI of pathfinding (loop implement)
 *
 *@param  a         [in] the 2D array.
 *@param  x         [in] the x coordinate of role.
 *@param  y         [in] the y coordinate of role.
 *
 *@return 1: find, 0: doesn‘t.
 *
 *@see
 */
int maze_pathfinding_ai_loop(int a[MAZE_ROW][MAZE_COLUMN], int x, int y)
{
    int ret = 0;
    int loop_cnt = 0;
    int max_loop = MAZE_ROW * MAZE_COLUMN; //maximum move steps, to avoid endless loop.

    while (loop_cnt++ < max_loop)
    {
        //end condition,it is same with recursive ai.
        if (((MAZE_COLUMN - 1) == x) && ((MAZE_ROW - 1) == y))
        {
            ret = 1;
            break;
        }

        if (try_move(a, &x, &y)) //check try move
        {
            a[y][x] = AI_OBJ;
        }
    }

    return ret;
}

在最后将整个迷宫小游戏的全部代码都贴出来:

#include <stdio.h>
#include <stdlib.h>

//maze data array
#define MAZE_ROW    10
#define MAZE_COLUMN 10

//playing maze mode: 1: AI, 0: human
#define MAZE_PLAY_MODE 1

//pathfinding ai type(AI only): 1: recursive, 0: loop
#define MAZE_PATHFINDING_AI_TYPE 1

#define ROLE_OBJ 1
#define AI_OBJ   3 //this value  must bigger than BARRIER
#define BARRIER  2
#define MAZE_PAD 0

//orignal coordinate
#define ROLE_ORG_X 0  //in array column
#define ROLE_ORG_Y 0  //in array row

#define show_obj(obj) printf("%4d ", obj)

// ---- x
// |
// |
// y

static int g_maze_data[MAZE_ROW][MAZE_COLUMN] =
{
    { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 2, 2, 2, 2, 0, 0, 0, 0 },
    { 0, 0, 2, 0, 0, 0, 2, 0, 0, 0 },
    { 0, 0, 0, 0, 2, 0, 2, 0, 2, 2 },
    { 2, 0, 2, 0, 0, 0, 2, 0, 0, 2 },
    { 0, 0, 0, 0, 2, 0, 0, 0, 0, 2 },
    { 0, 2, 0, 0, 0, 2, 0, 2, 0, 0 },
    { 0, 2, 0, 2, 0, 0, 0, 0, 2, 0 },
    { 0, 0, 0, 0, 2, 0, 2, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};

static int g_maze_ai_data[MAZE_ROW][MAZE_COLUMN];

/**
 *
 *@brief  show all element of 2D array.
 *
 *@param  a [in] the 2D array.
 *
 *@return None.
 *
 *@see
 */
void show(int a[MAZE_ROW][MAZE_COLUMN])
{
    printf("\n----------------------------------------------------\n");
    int i,j;
    for (i = 0; i < MAZE_ROW; i++)
    {
        for (j = 0; j < MAZE_COLUMN; j++)
        {
            show_obj(a[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

/**
 *
 *@brief  initialize maze ai data arrray with a 2D array.
 *
 *@param  a [in] the 2D array.
 *
 *@return None.
 *
 *@see
 */
void init_ai_data(int a[MAZE_ROW][MAZE_COLUMN])
{
    int i, j;
    for (i = 0; i < MAZE_ROW; i++)
    {
        for (j = 0; j < MAZE_COLUMN; j++)
        {
           g_maze_ai_data[i][j] = a[i][j];
        }
    }
}

/**
 *
 *@brief  role move a step with direction.
 *
 *@param  a         [in] the 2D array.
 *@param  x         [io] input orignal x coordinate,output move result.
 *@param  y         [io] input orignal y coordinate,output move result.
 *@param  direction [in] move direction.
 *
 *@return None.
 *
 *@see
 */
static void maze_move(int a[MAZE_ROW][MAZE_COLUMN],int* x, int* y, int direction)
{
    int org_x = *x; //old coordinate x
    int org_y = *y; // old coordinate y
    int role_x = org_x;
    int role_y = org_y;
    const char* dir_name = "unknown";
    switch (direction)
    {
    case ‘w‘:// up
        if ((role_y - 1) >= 0)
        {
            role_y--;
            dir_name = "up";
        }
        break;
    case ‘s‘: //down
        if ((role_y + 1) < MAZE_ROW)
        {
            role_y++;
            dir_name = "down";
        }
        break;
    case ‘a‘: //left
        if ((role_x - 1) >= 0)
        {
            role_x--;
            dir_name = "left";
        }
        break;
    case ‘d‘://right
        if ((role_x + 1) < MAZE_COLUMN)
        {
            role_x++;
            dir_name = "right";
        }
        break;
    default:
        printf("unspport this command:%c\n", direction);
        break;
    }

    if ((org_x != role_x) || (org_y != role_y)) //coordinate has change,then move
    {
        printf("move:%s\n", dir_name);
        int temp = a[org_y][org_x];
        a[org_y][org_x] = a[role_y][role_x];
        a[role_y][role_x] = temp;
        *x = role_x;
        *y = role_y;
    }

}

/**
 *
 *@brief  try move a step(just got x,y coordinate,not really move).
 *
 *@param  a         [in] the 2D array.
 *@param  x         [io] input orignal x coordinate,output move result.
 *@param  y         [io] input orignal y coordinate,output move result.
 *
 *@return 1:can move, 0:cann‘t move.
 *
 *@see
 */
int  try_move(int a[MAZE_ROW][MAZE_COLUMN], int* x, int *y)
{
    int ret = 1;
    int tempx = *x;
    int tempy = *y;
    //try order: right->down->left->up

    if (((tempx + 1) < MAZE_COLUMN) && (a[tempy][(tempx + 1)] < BARRIER))
    {//try right
        *x = tempx + 1;
    }
    else if (((tempy + 1) < MAZE_ROW) && (a[(tempy + 1)][tempx] < BARRIER))
    {//try down
        *y = tempy + 1;
    }
    else if (((tempx - 1) >= 0) && (a[tempy][(tempx - 1)] < BARRIER))
    {//try left
        *x = tempx - 1;
    }else if (((tempy - 1) >= 0) && (a[(tempy - 1)][tempx] < BARRIER))
    {//try up
        *y = tempy - 1;
    }
    else
    {
        ret = 0;
    }

    return ret;
}

/**
 *
 *@brief  the AI of pathfinding (recursive implement)
 *
 *@param  a         [in] the 2D array.
 *@param  x         [in] the x coordinate of role.
 *@param  y         [in] the y coordinate of role.
 *
 *@return 1: find, 0: doesn‘t.
 *
 *@see
 */
int maze_pathfinding_ai_recursive(int a[MAZE_ROW][MAZE_COLUMN], int x, int y)
{
    a[y][x] = AI_OBJ;

    if (((MAZE_COLUMN - 1) == x) && ((MAZE_ROW - 1) == y)) //recursive end condition
    {
        return 1;
    }
    else
    {
        if (try_move(a, &x, &y)) //check try move.
        {
            return maze_pathfinding_ai_recursive(a, x, y);
        }

    }

    return 0;
}

/**
 *
 *@brief  the AI of pathfinding (loop implement)
 *
 *@param  a         [in] the 2D array.
 *@param  x         [in] the x coordinate of role.
 *@param  y         [in] the y coordinate of role.
 *
 *@return 1: find, 0: doesn‘t.
 *
 *@see
 */
int maze_pathfinding_ai_loop(int a[MAZE_ROW][MAZE_COLUMN], int x, int y)
{
    int ret = 0;
    int loop_cnt = 0;
    int max_loop = MAZE_ROW * MAZE_COLUMN; //maximum move steps, to avoid endless loop.

    while (loop_cnt++ < max_loop)
    {
        //end condition,it is same with recursive ai.
        if (((MAZE_COLUMN - 1) == x) && ((MAZE_ROW - 1) == y))
        {
            ret = 1;
            break;
        }

        if (try_move(a, &x, &y)) //check try move
        {
            a[y][x] = AI_OBJ;
        }
    }

    return ret;
}

void maze_walk_out(int real_data[MAZE_ROW][MAZE_COLUMN], int ai_data[MAZE_ROW][MAZE_COLUMN])
{
    int x = 0;
    int y = 0;
    int loop_cnt = 0;
    int max_loop = MAZE_ROW * MAZE_COLUMN; //maximum move steps, to avoid endless loop.
    ai_data[ROLE_ORG_Y][ROLE_ORG_X] = ROLE_OBJ;

    while (loop_cnt++ < max_loop)
    {
        //end condition,it is same with recursive ai.
        if (((MAZE_COLUMN - 1) == x) && ((MAZE_ROW - 1) == y))
        {
            break;
        }

        /** move order: right->down->left->up. */

        //try right
        if (((x + 1) < MAZE_COLUMN) && (ai_data[y][(x + 1)] == AI_OBJ))
        {
            ai_data[y][(x + 1)] = MAZE_PAD; //reset ai path value.
            maze_move(real_data, &x, &y, ‘d‘); //move right
            show(real_data);
        }

        //try down
        if (((y + 1) < MAZE_ROW) && (ai_data[(y + 1)][x] == AI_OBJ))
        {
            ai_data[(y + 1)][x] = MAZE_PAD;//reset ai path value.
            maze_move(real_data, &x, &y, ‘s‘); //move down
            show(real_data);
        }

        //try left
        if (((x - 1) >= 0) && (ai_data[y][(x - 1)] == AI_OBJ))
        {
            ai_data[y][(x - 1)] = MAZE_PAD;//reset ai path value.
            maze_move(real_data, &x, &y, ‘a‘); //move left
            show(real_data);
        }

        //try up
        if (((y - 1) >= 0) && (ai_data[(y - 1)][x] == AI_OBJ))
        {
            ai_data[(y - 1)][x] = MAZE_PAD;//reset ai path value.
            maze_move(real_data, &x, &y, ‘w‘); //move up
            show(real_data);
        }
    }
}

/**
 *
 *@brief  human play the game.
 *
 *@param  None
 *
 *@return None
 *
 *@see
 */
void human_play()
{
    int x = ROLE_ORG_X;
    int y = ROLE_ORG_Y; //role, initialize coordinate
    printf("\n------------------------------------\n");
    printf("input:w,s,a,d to play the game:\n ");
    printf("w --> up\n");
    printf("s --> down\n");
    printf("a --> left\n");
    printf("d --> right\n");
    printf("note: aways need to input the key of \‘ENTER\‘ as end");
    printf("\n------------------------------------\n");

    while (1)
    {
        int ch = getchar();
        getchar(); //skip the key of ‘enter‘
        maze_move(g_maze_data,&x, &y, ch);
        show(g_maze_data);
    }
}

/**
 *
 *@brief  AI play the game.
 *
 *@param  None
 *
 *@return None
 *
 *@see
 */
void ai_play()
{
    int ret = 0;
    int x = ROLE_ORG_X;
    int y = ROLE_ORG_Y;

    init_ai_data(g_maze_data); //initialize ai data array.

#if MAZE_PATHFINDING_AI_TYPE  //if 1, recursive implement
    ret = maze_pathfinding_ai_recursive(g_maze_ai_data, x, y);
#else//if 0, loop implement.
    ret = maze_pathfinding_ai_loop(g_maze_ai_data, x, y);
#endif

    if (1 == ret)
    {
        printf("congratulations! you can walk out the maze!\n");

        printf("show AI data array\n");
        show(g_maze_ai_data);

        printf("starting demonstrate the procedure of walk out maze...\n");
        maze_walk_out(g_maze_data, g_maze_ai_data);

    }
    else
    {
        printf("unfortunate! cann‘t find any path to walk out the maze!\n");
    }
    //show(g_maze_data);
}

int main(void)
{
    printf("show maze default data array\n");

    show(g_maze_data);
#if MAZE_PLAY_MODE //1:ai play mode
    ai_play();
#else //0: human play mode.
    human_play();
#endif

    system("pause");
    return 0;
}
时间: 2024-10-12 20:48:44

c印记(六): 数组与递归联合应用的小游戏的相关文章

12月28日 二维数组的应用:第一个小游戏(推箱子)

小游戏:******推箱子******** static void Main(string[] args) { int i, j; int[,] a = new int[10, 10]                  //二维数组的定义           类型[,] 数组名 = new  类型 [行数, 列数] {赋值}:   或单个赋值 a[i,j]=1; { {1,1,1,1,1,1,1,1,1,1}, {1,0,0,0,0,0,0,0,0,1}, {1,0,2,0,0,8,0,0,0,

笨鸟学php(六) 数组

一.数组概述 1.1 数组是复合类型 1.2 数组中可以存储任意长度的数据, 也可以存储任意类型的数据 二.数组的类型 2.1 索引数组: 下标是顺序整数作为索引 <?php $user[0] = 1; $user[1] = "zhangsan"; $user[2] = "[email protected]"; echo '<pre>'; print_r($user); echo '</pre>'; ?> 2.2 关联数组: 下标

每日一题12:用数组加速递归

许多程序设计教学书上都用斐波那契数列(数列中第一二项都是1,其它任意一项都是其前两项之和)作为讲解递归的例子,作为教学例子,它确实十分合适,但是如果用在实际计算中,那么递归实现的斐波那契数列求值实在是太慢了,其中主要的原因是重复计算太多,这样的递归算法不仅速度效率低下,还容易造成栈溢出.如果能够保留下已经计算过的值,但需要时直接取用而不是重复计算,那么必然会提高程序性能. 对于斐波那契数列求解使用一个一维数组来保存之前的计算值,是十分合适的: #include "stdafx.h" #

js选择颜色小游戏(随机生成不含重复数字的数组,通过数组中的数控制定义好的数组)

<!DOCTYPE html><html> <head> <meta charset="utf-8"> <title>js网页版小游戏</title> <style media="screen"> .wrap { width: 577px; outline: 1px solid hotpink; margin: 100px auto; box-shadow: 0 0 5px; } .

小游戏●贪吃蛇1(利用二维数组制作)

利用二维数组编写简单贪吃蛇小游戏,由于是初学C#,用的是单线程,所以蛇不会自动前进 代码及简要分析如下: 1 //定义地图,0为空,1为墙,2为蛇,3为食物 2 int[,] map = new int[15, 15]{ 3 {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}, 4 {1,2,0,0,0,0,0,0,0,0,0,0,0,0,1}, 5 {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1}, 6 {1,0,0,0,0,0,0,0,0,0,0,0,0,0,1},

Lichee (六) 配置内核时的一点小优化

我们在分析<Lichee(二) 在sun4i_crane平台下的编译 >的时候,居然没有一个步骤是在配置内核 make ARCH=arm menuconfig 仔细的读过的代码的会发现,在build_kernel有这么一段话 if [ ! -e .config ]; then echo -e "\n\t\tUsing default config... ...!\n" cp arch/arm/configs/sun4i_crane_defconfig .config fi

小游戏●推箱子(利用二维数组制作)

利用数组制作的简单推箱子游戏 代码及简要分析如下: 1 //推箱子小游戏 2 //定义一个三维数组存放地图,三维数组由单独的二维数组组成,本游戏中只有三个地图 3 int[][,] a = new int[3][,]; 4 //用二维数组创建地图,0是空位,1是墙,2是人,3是箱子,4是终点 5 int[,] b0 = new int[10, 10]{ 6 {1,1,1,1,1,1,1,1,1,1}, 7 {1,0,0,0,1,0,1,0,0,1}, 8 {1,0,0,0,1,0,1,0,0,1

第六回 煤渣场对垒藏高人 小饭馆煮面论英雄[林大帅作品连载]

第六回 煤渣场对垒藏高人 小饭馆煮面论英雄    诗曰: 古道热肠情谊浓,铁砚豪杰事已空. 今宵何似煮酒夜,五魁声里忆峥嵘.        却说林二二人前往蹴鞠场,当日石头路旧院比不得擢英砺青宽敞,这场也只饭堂前方寸之地.日后林二有幸在哲理场上驰骋,自不可同日而语.不过今日难得见小谢兴致不错,自是一番热情相陪.林二方进文会时,每日只知街上晃荡,书店里耽搁.不过也敬这小谢治学功夫,他也常劝林二,多做些文章才是.平日里也不忘邀林二探讨点学问,或彼此切磋,故而甚相契重.但这蹴鞠场上,林二却是好手,不说

【2048小游戏】——原生js爬坑之遍历算法显示二维数组内容

引言:做2048小游戏会将横纵方向的数字内容,存储在一个二维数组中,要将这个二维数组中的内容显示在页面上,就一定要用遍历算法来实现了. 一.二维数组存储    首先考虑用二维数组存储所有行数,列数  →  var  RN=4,CN=4; 然后再定义一个变量data 来保存这个二维数组  →  var  data; 游戏的所有主要执行程序都保存在start()函数下 → 启动游戏 保存存有行数,列数的二维数组到data中    关键代码 ↓ function start(){ data=[]; /