puzzle:c语言拼图小游戏

写在前面:
这个小游戏是自己学习c时的一个小练习,算是记录吧,大牛还请见怪不怪。。。
还有,头文件中包有我自己另下的头文件,不是c官方的。
也就是这玩意:EasyX_2018。
有了它就可以很方便的用c做画图等操作了。
头文件如下:

#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#include<time.h>

话不多说,先看看效果。
puzzle:c语言拼图小游戏_第1张图片
如图:
有四块小图位置错乱,右下角置有小黑块。
(当只交换一对时,就可能会出现puzzle无解的情况,具体论证可在网上找到。)

好了,先说说整体思路。
1.建立窗口,大小足以包含两个原图。
2.导入图片,左图切分为N*N小块,右图用以参照。
3.将左图任意两对小块交换位置,左图右小角置黑块
4.当任一小块周围存在黑块时,可按‘w’,‘a’,‘s’,‘d’来控制小块向黑块方向移动。如果左图除右下角外其余小块都与右图相同,则游戏结束。

整体代码:

#include<graphics.h>
#include<conio.h>
#include<stdio.h>
#include<time.h>

#define N  5 //左图切割为N*N块的“N”
#define winw 400 //窗口宽度
#define winh 400 //窗口高度

int Isgameover(int layout[N][N]) //判断游戏是否结束
{
 int ret = 0;
 int i, j;
 for (i = 0; i < N; ++i)
 {
  for (j = 0; j < N; ++j)
  {
   if (layout[i][j] != i*N + j)//如果数组值不正确直接跳转
   {
    goto lp;
   }
  }
 }
lp: if ((i == N - 1 && j == N - 1))//如果此时i,j已经遍历到最后一个置ret为1
 {
  ret = 1;
 }
 return ret;
}

int Findblank(int layout[N][N])  //找到并返回小黑块的位置
{
 for (int i = 0; i < N; ++i)
 {
  for (int j = 0; j < N; ++j)
  {
   if (layout[i][j] < 0)
   {
    return i*N + j;  //返回其作为一维数组的下标值
   }
  }
 }
}

void Moveleft(int layout[N][N])  //将图片向左移动
{
 int index=Findblank(layout);
 int i = index / N;
 int j = index % N;
 int t;
 if (j + 1 < N)     //小黑块右边不是右边界外,才能有小块左移
 {
  t = layout[i][j];
  layout[i][j] = layout[i][j + 1];
  layout[i][j + 1] = t;
 }
}

void Moveright(int layout[N][N]) //将图片向右移动
{
 int index = Findblank(layout);
 int i = index / N;
 int j = index % N;
 int t;
 if (j -1 >= 0)     //小黑块左边不是左边界外,才能有小块右移
 {
  t = layout[i][j];
  layout[i][j] = layout[i][j - 1];
  layout[i][j - 1] = t;
 }
}

void Moveup(int layout[N][N]) //将图片向上移动
{
 int index = Findblank(layout);
 int i = index / N;
 int j = index % N;
 int t;
 if (i + 1 < N)    //小黑块下边不是下边界外,才能有小块上移
 {
  t = layout[i][j];
  layout[i][j] = layout[i + 1][j];
  layout[i + 1][j] = t;
 }
}

void Movedown(int layout[N][N])  //将图片向下移动
{
 int index = Findblank(layout);
 int i = index / N;
 int j = index % N;
 int t;
 if (i - 1 >= 0)     //小黑块上边不是上边界外,才能有小块下移
 {
  t = layout[i][j];
  layout[i][j] = layout[i - 1][j];
  layout[i - 1][j] = t;
 }
}

void Drawpic(int layout[N][N],IMAGE* img) //画图。以及当图片有变动时的刷新,即再重新画一遍。
{
 int x, y, w, h, x1, y1;
 w = winw / N;
 h = winh / N;       //得到小块的大小
 setfillcolor(RGB(0, 0, 0));    //设置填充色黑色
 for (int i = 0; i < N; ++i)
 {
  for (int j = 0; j < N; ++j)     //遍历布局数组
  {
   x = j*w;
   y = i*h;        //x,y分别为目标小块的坐标值
   if (layout[i][j] >= 0)
   {
    x1 = layout[i][j] % N*w;
    y1 = layout[i][j] / N*h;   //x1,y1分别为布局数组第[i][j]项值对应的原图小块坐标值
    putimage(x, y, w, h, img, x1, y1); //将x1,y1对应的小块抠出贴到目标x,y坐标位置
   }
   else
   {
    fillrectangle(x, y, x + w, y + h); //填充小黑块
   }
  }
 }

 int x2, y2;
 for (int i = 0; i < N - 1; ++i)
 {
  x1 = 0;
  x2 = winw;
  y1 = y2 = winh / N*(i + 1);
  line(x1, y1, x2, y2);     //画N-1条横线
 }
 for (int i = 0; i < N - 1; ++i)
 {
  y1 = 0;
  y2 = winw;
  x1 = x2 = winw / N*(i + 1);
  line(x1, y1, x2, y2);     //画N-1条竖线
 }
}

void Disorganize(int layout[N][N])  //将左图打乱顺序
{
 int t;
 srand((unsigned)time(NULL));  //随机数种子
 int a, b, c, d;
 a = rand() % N;
 b = rand() % N;
 c = rand() % N;
 d = rand() % N;
 t = layout[a][b];
 layout[a][b] = layout[c][d];
 layout[c][d] = t;
 t = layout[b][a];
 layout[b][a] = layout[d][c];
 layout[d][c] = t;
}

int main()        //主函数
{
 initgraph(winw*2, winh);   //绘制窗口大小
 IMAGE img;
 loadimage(&img,_T("./getz.jpg")); //加载图片
 putimage(winw, 0, &img);   //画出右边样图
  /*int layout[N][N]=
 {
  { 0 , 1 , 2 , 3 , 4 },
  { 5 , 6 , 7 , 8 , 9 },
  { 10, 11, 12, 13, 14},
  { 15, 16, 17, 18, 19},
  { 20, 21, 22, 23, -1}
 };*/
  int layout[N][N] = { 0 };   //定义一个关于各个小块位置的二维数组用于布局
 for (int i = 0; i < N; ++i)
 {
  for (int j = 0; j < N; ++j)
  {
   layout[i][j] = i*N + j;
  }
 }
 layout[N-1][N-1] = -1;   //给布局数组赋值,即上面屏掉的结果(-1代表黑块)
 Disorganize(layout);   //调用乱序函数
 Drawpic(layout, &img);   //调用画图函数(画出以布局数组分布的左图,及用于分割的(N-1)*2条直线) 
 while (!Isgameover(layout))  //若游戏结束条件不满足,则一直循环
 {
  char key;
  key = _getch();    //从键盘接收一个字符
  switch (key)
  {
  case'a':
  case'A':
   Moveleft(layout);  //如果是a或A则调用左移函数
   break;
  case'd':
  case'D':
   Moveright(layout);  //如果是d或D则调用右移函数
   break;
  case'w':
  case'W':
   Moveup(layout);   //如果是w或W则调用上移函数
   break;
  case's':
  case'S':
   Movedown(layout);  //如果是s或S则调用下移函数
   break;
  }
  Drawpic(layout, &img);  //每次移动,即图片顺序变动,即布局数组变动,则重新绘图
 }
  closegraph();     //关闭窗口
 puts("你真是个小天才!");  //若能到这一行说明结束条件满足
}

关于代码中的灵魂函数putimage,详情可看下面链接:
C语言EasyX_2018中的putimage(x, y, w, h, img, x1, y1)函数
谢观!

你可能感兴趣的:(puzzle:c语言拼图小游戏)