C语言零基础项目:涂格子(点灯)游戏!详细思路+源码分享

每天一个C语言小项目,提升自己的编程能力!

点灯游戏是一个十分有趣的智力游戏:有一行N行N列的灯,开始时全部是灭的,当你点击其中一盏灯时他的上下左右(若存在的话)状态全部改变,现在要求你在限定的时间内以最少地步数,将全部的灯点亮。

例如一开始有5×5共25盏灯,都处于关的状态,现在要想办法把25盏灯全打开,每次只能开/关一盏灯,但由于电路原因,和它相邻的四盏灯也会改变开/关状态,于是想把25盏灯全打开就有一定难度。

效果展示如下:

(本期代码有两个版本)

编译环境:Visual C++ 6.0 / VS2019/2022,EasyX插件

代码版本一:

#include  <graphics.h>
#include  <conio.h>
#define    MaxNum        14    //单边最大格子数
#define    G_length      30    //格子边长
#define    USER_LBUTTONDOWN  101
#define    USER_RBUTTONDOWN  102
#define    USER_MBUTTONDOWN  103
​
///
void PaintGrid(int Mid_x, int Mid_y, int num, int color);  // 绘制游戏格子,初始化格子
void OnLButtonDown(MOUSEMSG m, int num);          // 左键按下
void OnRButtonDown(int num);                // 右键按下
void OnMButtonDown();                    // 中键按下*暂无定义*
void welcome();                        // 显示游戏主界面
void goodbye(int num);                    // 显示结束画面
void NextLevel(int num);                  // 下一关
int   GetMessage(MOUSEMSG m);                // 获取鼠标信息
int  DispatchMessage(MOUSEMSG m, int opt);          // 分发鼠标信息
int  JudgeFull(int num, int array[MaxNum][MaxNum]);      // 格子是否填满
​
///
// 定义游戏格子结构体
struct Grid
{
  int left;  // 游戏区域边界
  int right;
  int top;
  int bottom;
  int array[MaxNum][MaxNum];  // 记录格子状态
  int num;  // 记录边界格子数目
}grid;
​
​
///
void main()
{
  int opt, end=0;
  grid.num=4; 
  welcome();
  PaintGrid(320, 240, grid.num, RGB(0,255,0));
  MOUSEMSG m;
  while(end!=1)
  {
    m = GetMouseMsg();
    opt = GetMessage(m);
    end=DispatchMessage(m, opt);  
  }
  goodbye(grid.num);
    closegraph();
}
​
​
///
// 获取鼠标信息
int GetMessage(MOUSEMSG m)
{
  //鼠标循环
  switch(m.uMsg)
  {
    case WM_LBUTTONDOWN:
      return USER_LBUTTONDOWN;
    case WM_RBUTTONDOWN:
      return USER_RBUTTONDOWN;
    case WM_MBUTTONDOWN:
      return USER_MBUTTONDOWN;
  }
  return 0;
}
​
​
///
// 分发消息
int DispatchMessage(MOUSEMSG m, int opt)
{
  switch(opt)
  {
    case USER_LBUTTONDOWN:
      // 左键填色
      OnLButtonDown(m, grid.num);  
      // 判断是否填满
      if(JudgeFull(grid.num, grid.array)==1)
        {
          grid.num++;
          // 格子数目超过最大值通关
          if(grid.num>MaxNum)
          {
            return 1;
            break;
          }
          else
            NextLevel(grid.num);
        }
      break;
​
    case USER_RBUTTONDOWN:
      // 右键清除
      OnRButtonDown(grid.num);
      break;
​
    case USER_MBUTTONDOWN:
      return 1;
      break;
  }
​
  return 0;
}
​
​
///
// 左键按下
void OnLButtonDown(MOUSEMSG m, int num)
{
  int nx, ny, x, y;  
  if(m.x>grid.left && m.x<grid.right && m.y>grid.top && m.y<grid.bottom)
  {
    // 计算位置
    nx=(int)(m.x - grid.left)/G_length; 
    ny=(int)(m.y - grid.top )/G_length;
    // 转换格子状态
    grid.array[nx][ny] = -grid.array[nx][ny];
    if(nx >= 0 && nx <  num-1) grid.array[nx+1][ny] = -grid.array[nx+1][ny];
    if(nx >  0 && nx <= num-1) grid.array[nx-1][ny] = -grid.array[nx-1][ny];
    if(ny >= 0 && ny <  num-1) grid.array[nx][ny+1] = -grid.array[nx][ny+1];
    if(ny >  0 && ny <= num-1) grid.array[nx][ny-1] = -grid.array[nx][ny-1];
    // 扫描填色
    for(nx=0; nx<num; nx++)
    for(ny=0; ny<num; ny++)
    {
      if(grid.array[nx][ny] == 1)
        setfillcolor(GREEN);
      else
        setfillcolor(BLACK);
      x= nx * G_length + grid.left;
      y= ny * G_length + grid.top;
      solidrectangle(x+1, y+1, x+G_length-1, y+G_length-1);
    }
  }
}
​
​
///
// 右键按下清空
void OnRButtonDown(int num)
{
  int x, y, nx, ny;
​
  for(x=0; x<num; x++)
    for(y=0; y<num; y++)
      grid.array[x][y] = -1;
​
  for(nx=0; nx<num; nx++)
    for(ny=0; ny<num; ny++)
    {
      setfillcolor(BLACK);
      x= nx*G_length + grid.left;
      y= ny*G_length + grid.top;
      solidrectangle(x+1, y+1, x+G_length-1, y+G_length-1);
    }
}
​
​
///
// 显示下一关
// 参数:
//  num:  下一关的边界格子数
void NextLevel(int num)
{
  // 清屏
  BeginBatchDraw();
  for(int y=0; y<=480; y+=5)
  {
    setlinecolor(RGB(0, 255, 0));
    settextcolor(RGB(0, 255, 0));
    line(0, y, 640, y);
    line(0, 480-y, 640, 480-y);
    outtextxy(300, y, "下一关");
    FlushBatchDraw();
    Sleep(16);
    setfillcolor(BLACK);
    solidrectangle(0, y+4, 640, y-5);
    solidrectangle(0, 480-y, 640, 480-y+5);
  }
  EndBatchDraw();
​
  // 绘制下一关格子
  PaintGrid(320, 240, num, RGB(0,255,0));
}  
​
​
///
// 判断格子是否填满
// 参数:
//  num:  单边格子数目
//  array:  生成数组接收实参
int JudgeFull(int num, int array[MaxNum][MaxNum])
{
  int c=-1;
  int nx=0, ny=0;
  while(nx<num && ny<num)
  {
    for(nx=0; nx<num; nx++)
      for(ny=0; ny<num; ny++)
        if(array[nx][ny]==1)
          continue;
        else 
          return c;
  }
  c=1;
  return c;
}
​
​
///
// 绘制游戏格子,初始化格子
// 参数:
//  Mid_x:  屏幕中心 x 坐标
//  Mid_y:  屏幕中心 y 坐标
//  num:  单边格子数目
//  color:  格子线条颜色
void PaintGrid(int Mid_x, int Mid_y, int num, int color)
{
  int x,y,nx,ny;
  // 游戏区域大小
    grid.left   = Mid_x-num*G_length/2;
  grid.right  = Mid_x+num*G_length/2;
  grid.top    = Mid_y-num*G_length/2;
  grid.bottom = Mid_y+num*G_length/2;
  // 绘制格子
  setlinecolor(color);
  for(x=grid.left; x<=grid.right;  x+=G_length)
  {
    line(x, grid.top, x, grid.bottom);
    Sleep(10);
  }
  for(y=grid.top;  y<=grid.bottom; y+=G_length)
  {
    line(grid.left, y, grid.right, y);
    Sleep(10);
  }
  // 外边框
  for(x=20;x>10;x--)
  {
    line(grid.left-x,  grid.top-x,    grid.right+x,  grid.top-x);
    line(grid.left-x,  grid.bottom+x,  grid.right+x,  grid.bottom+x);
    line(grid.left-x,  grid.top-x,    grid.left-x,  grid.bottom+x);
    line(grid.right+x,  grid.top-x,    grid.right+x,  grid.bottom+x);
    Sleep(5);
  }
  // 清空单元格
  for(x=0; x<num; x++)
    for(y=0; y<num; y++)
      grid.array[x][y] = -1;
  for(nx=0; nx<num; nx++)
  for(ny=0; ny<num; ny++)
  {
    setfillcolor(BLACK);
    x = nx * G_length + grid.left;
    y = ny * G_length + grid.top;
    solidrectangle(x+1, y+1, x+G_length-1, y+G_length-1);
  }
}
​
​
///
// 显示游戏主界面
void welcome()
{
  // 初始化窗口
  initgraph(640, 480);
​
  // 输出屏幕提示
  cleardevice();
  settextcolor(RGB(0,255,0));
  settextstyle(64, 0, "黑体");
  outtextxy(70, 50, "涂格子游戏(点灯)");
  settextcolor(WHITE);
  settextstyle(16, 0, "宋体");
  outtextxy(100, 200, "每点一个格子,上下左右的格子也会做出于现状相反的动作");
  outtextxy(100, 240, "总共11关,左键填色,右键重来,中键退出");
  outtextxy(100, 280, "只是一个功能演示版本,以后再改进");
  outtextxy(400, 320, "by:doufuguolyl");
  settextstyle(16, 0, "黑体");
  outtextxy(400, 340, "Ver 0.1");
​
  // 实现闪烁的"按任意键继续"
  int c=255;
  while(!kbhit())
  {
    settextcolor(RGB(0, c, 0));
    outtextxy(280, 400, "按任意键继续");
    c -= 8;
    if (c < 0) c = 255;
    Sleep(20);
  }
​
  getch();
  cleardevice();
}
​
​
///
// 显示结束画面
void goodbye(int num)
{
  int c=255;
​
  // 清屏
  BeginBatchDraw();
  for(int y=0; y<=480; y+=5)
  {
    setlinecolor(RGB(0, 255, 0));
    line(0, y, 640, y);
    line(0, 480-y, 640, 480-y);
    FlushBatchDraw();
    Sleep(16);
    setfillcolor(BLACK);
    solidrectangle(0, y+4, 640, y-5);
    solidrectangle(0, 480-y, 640, 480-y+5);
  }
  EndBatchDraw();
​
  //判断显示文字
  if(num == MaxNum+1)
  {
    settextcolor(RGB(0, c, 0));
    settextstyle(48, 0, "黑体");
    outtextxy(280, 200, "通关");
    settextstyle(20, 0, "黑体");
  }
  else
  {
    settextcolor(RGB(0, c, 0));
    settextstyle(48, 0, "黑体");
    outtextxy(200, 200, "再接再厉");
    settextstyle(20, 0, "黑体");
  }
​
  while(!kbhit())
  {
    settextcolor(RGB(0, c, 0));
    outtextxy(400, 400, "QQ: 372480348");
    c -= 8;
    if (c < 0) c = 255;
    Sleep(20);
  }
​
  getch();
}​

代码版本二:

#include<stdio.h>
#include<conio.h>
//easyx图形库
#include<graphics.h>
#include<mmsystem.h>//包含躲媒体1设备接口头文件
#pragma comment(lib,"winmm.lib")
#define WIN_WIDTH 640
#define WIN_HEIGHT 480
#define GRID_WIDTH 30//格子宽度
#define GRID_NUM 4//每一行格子的数量
struct Grid //格子结构,结构体,类首字母大写
{
	int top;//上面一条线的y
	int down;
	int left;
	int right;
	int foot;//玩家走了多少步
	int map[GRID_NUM][GRID_NUM];
	int x;//保存鼠标点击的数组下标
	int y;
}grid;
MOUSEMSG msg;
//int a=0;
IMAGE img;//保存背景图片
//欢迎界面
void Welcome()
{
	/*
	错误	1	error C2665: “loadimage”: 2 个重载中没有一个可以转换所有参数类型
	什么问题?字符集问题1,在字符串前面加上L
					  2,在字符串前面加上_T(str)  推荐
					  3,修改项目字符集属性
	*/
	mciSendString("open ./森林幻想曲.mp3 alias BGM",0,0,0);//向多媒体设备接口发送一个字符串
	mciSendString("play BGM repeat", 0, 0, 0);//重复播放音乐
	//Sleep(10000);
	//mciSendString("close BGM", 0, 0, 0);//关闭音乐
	loadimage(&img, "./bk.jpg", WIN_WIDTH, WIN_HEIGHT);//加载图片
	putimage(0, 0, &img);
	setbkmode(TRANSPARENT);//设置文字背景颜色透明
	settextcolor(GREEN);
	settextstyle(50, 0, "楷体");//设置字体大小,类型
	char str[] = "涂格子游戏(点灯游戏)";
	int twidth = textwidth(str)/2;//让文字居中显示
	outtextxy(WIN_WIDTH / 2 - twidth, 20, str);

	while (!_kbhit())//如果没有按键就一直循环
	{
		settextcolor(RGB(rand() % 256, rand() % 256,rand() % 256));
		outtextxy(180, 300, "按任意键继续...");
		Sleep(300);
	}	
	_getch();//等待按键,没有按键,就一直等待
}
//初始化数据
void GameInit()
{
	grid.top = WIN_HEIGHT / 2 - GRID_NUM*GRID_WIDTH / 2;//获取上面的y坐标
	grid.down = WIN_HEIGHT / 2 + GRID_NUM*GRID_WIDTH / 2;//获取下面的y坐标
	//理解不?
	grid.left = WIN_WIDTH / 2 - GRID_NUM*GRID_WIDTH / 2;
	grid.right = WIN_WIDTH / 2 + GRID_NUM*GRID_WIDTH / 2;
	grid.foot = 0;
	//双重循环初始化二维数组
	for (int i = 0; i < GRID_NUM; i++)
	{
		for (int k = 0; k < GRID_NUM; k++)
		{
			grid.map[i][k] = 1;//黑色为1,绿色为-1
		}
	}
}
//把数据通过图形界面的方式展示出来
void GameDraw()
{
	cleardevice();
	putimage(0, 0, &img);
	//绘制格子
	setlinecolor(RGB(0, 200, 0));
	//循环绘制格子线条
	for (int x = grid.left; x <= grid.right; x+=GRID_WIDTH)
	{
		line(x, grid.top, x, grid.down);//画竖线
	}
	for (int y = grid.top; y <= grid.down; y += GRID_WIDTH)
	{
		line(grid.left, y, grid.right,y);
	}
	//绘制界面边框
	for (int x = 20; x > 10; x--)
	{
		line(grid.left - x, grid.top - x, grid.right + x, grid.top - x);
		line(grid.left - x, grid.down + x, grid.right + x, grid.down + x);
		line(grid.left - x, grid.top - x, grid.left - x, grid.down + x);
		line(grid.right + x, grid.top - x, grid.right + x, grid.down + x);
	}
	grid.map[2][2] = -1;
	//填充游戏格子为黑色
	int x, y;
	for (int i = 0; i < GRID_NUM; i++)
	{
		for (int k = 0; k < GRID_NUM; k++)
		{
			y = i*GRID_WIDTH + grid.top;//得到的是什么
			x = k*GRID_WIDTH + grid.left;//得到的是什么
			if (grid.map[i][k] == 1)
			{
				setfillcolor(BLACK);
			}
			else if (grid.map[i][k] == -1)
			{
				setfillcolor(GREEN);
			}
			solidrectangle(x + 1, y + 1, x + GRID_WIDTH - 1, y + GRID_WIDTH - 1);
		}
	}
}
//游戏控制,鼠标点击
void GameControl()
{
	if (MouseHit())
	{
		msg = GetMouseMsg();
		//如果是鼠标左键点击,而且点击在游戏区域,格子区域
		if (msg.uMsg == WM_LBUTTONDOWN 
			&& msg.x>grid.left && msg.x<grid.right
			&& msg.y>grid.top && msg.y>grid.down)
		{
			grid.y = (msg.x - grid.left) / GRID_WIDTH;
			grid.x = (msg.y - grid.top) / GRID_WIDTH;
			grid.map[grid.x][grid.y] = -grid.map[grid.x][grid.y];//改变颜色
			if (grid.x >= 0 && grid.x < GRID_NUM - 1)
			{
				grid.map[grid.x+1][grid.y] = -grid.map[grid.x+1][grid.y];
			}
			if (grid.x > 0 && grid.x <= GRID_NUM - 1)
			{
				grid.map[grid.x - 1][grid.y] = -grid.map[grid.x - 1][grid.y];
			}
			if (grid.y >= 0 && grid.y < GRID_NUM - 1)
			{
				grid.map[grid.x][grid.y + 1] = -grid.map[grid.x ][grid.y + 1];
			}
			if (grid.y >= 0 && grid.y <= GRID_NUM - 1)
			{
				grid.map[grid.x][grid.y-1] = -grid.map[grid.x][grid.y-1];
			}
		}
	}
}
int main()
{
	initgraph(WIN_WIDTH, WIN_HEIGHT);
	Welcome();
	GameInit();
	BeginBatchDraw();
	while (1)
	{
		GameDraw();
		FlushBatchDraw();
		GameControl();
	}
	return 0;
}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!(↓↓↓↓↓↓)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值