C语言写一个2048游戏

简介: C语言写一个2048游戏

大部分同学学习C语言编程以后不知道能通过什么样的项目才可以锻炼自己的思维功力,2048相信大家都应该熟悉,不管是手机上还是网页版的相信大家都玩过,这个简单的控制台版本的游戏是我曾经在伟易达上班时一个嵌入式应用游戏部门的大佬设计的,适合于喜欢用C语言写一些简易的游戏的朋友,逻辑性很强。

一、2048游戏原理

在最初的游戏, 它始于一个空4 x 4游戏板。


1)在空位置的游戏板上,每一轮游戏产生一个“2”或“4”随机的数字。


2)接下来,玩家输入的上移,下移,左移或右移命令移动块。两个相邻块相同的号码,若是Q,可以组合成一个块数量2Q。


3)如果没有空间产生一个新的数字块,玩家则game over。


4)想赢得游戏,玩家需要产生一块2048数字块。

二、2048游戏文档

当然,这些游戏的逻辑不是大家闷着脑子就能空想出来的,它一定有很规范的说明文档,由专业的人来书写,最后软件工程师参考对应的文档编写自己的代码

640.png

640.png

篇幅有限,详细的下载链接:

链接:https://pan.baidu.com/s/1Daan58WN-A95BeYQcmSyDA
提取码:m6ie

当然也可以访问Github网站,这是一个开源的项目,后面各位牛逼的大佬经过移植后,运行在各个平台下,原版本链接如下:

http://gabrielecirulli.github.io/2048/

三、2048游戏源代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <conio.h>
//num
#define FALSE 0
#define TRUE  1
#define EMPTY_CELL      0
#define GMAE_ROW    4
#define GMAE_COL    4
//GameState
#define STATE_SELECT    0
#define STATE_PREPARE   1
#define STATE_PALYING   2
#define STATE_EXIT      3
//GameMode
#define MODE_NONE       0
#define MODE_NORMAL     1
#define MODE_DEBUG      2
//Select Index
#define INDEX_MAXNUM    3
#define INDEX_NORMAL    0
#define INDEX_DEBUG     1
#define INDEX_EXIT      2
//Command
#define COM_LEFT    'a'
#define COM_RIGHT   'd'
#define COM_UP      'w'
#define COM_DOWN    's'
#define COM_QUIT    'q'
//direction
#define DIR_HEAD        0xe0
#define KEY_UP      0xe048
#define KEY_DOWN    0xe050
#define KEY_LEFT    0xe04b
#define KEY_RIGHT   0xe04d
#define ESC       0x1B
#define ENTER     0x0D
//type
typedef unsigned int    Uint;
typedef unsigned short  Ushort;
typedef unsigned char   Uchar;
//declaration
static void GM_Init(void);
static void GM_End(void);
static Uint GM_SelectInit(void);
static Uint GM_SelectHandle(void);
static Uint GM_SelectEnd(void);
static Uint GM_PrepareInit(void);
static Uint GM_PrepareHandle(void);
static Uint GM_PrepareEnd(void);
static Uint GM_PlayingInit(void);
static Uint GM_PlayingHandle(void);
static Uint GM_PlayingEnd(void);
static Uint GM_SelectHandleEnter(void);
static Uint GM_SelectHandleEsc(void);
static void GM_PrintSelectMode(void);
static void GM_RandAddOneNum(void);
static Uchar GM_FromFileAddNum(void);
static Uchar GM_InputAddOneNum(void);
static Uchar GM_NotMoreMove(void);
static void GM_PrintBoard(void);
static Uchar GM_CheckWin2048(void);
static Uchar GM_PlayingPull(void);
static Uchar GM_CombineRight(Uint *array, int num);
static Uchar GM_CombineLeft(Uint *array, int num);
static Uchar GM_MoveRight(Uint *array, int num);
static Uchar GM_MoveLeft (Uint *array, int num);
//struct
typedef  struct gameinfo {
  Uint  Board[GMAE_ROW][GMAE_COL];
  Uchar GameState;
  Uchar GameMode;
  Uchar StateSelectIndex;
  Uint  PlayingCommand;
}GameInfo, *P_GameInfo;
GameInfo GM;
int main(void)
{
  GM_Init();
  while(1)
  { 
    switch(GM.GameState)
    {
      case STATE_SELECT:    
        GM_SelectHandle();  
        break;
      case STATE_PREPARE:
        GM_PrepareHandle();
        break;  
      case STATE_PALYING:
        GM_PlayingHandle();
        break;  
      case STATE_EXIT:
          goto GAME_EXIT;
      default:
        break;      
    }            
    }
GAME_EXIT:
    GM_End();
    return 0; 
}
static void GM_Init(void)
{
  memset(&GM, 0, sizeof(GameInfo));
  srand((int)time(NULL));
  GM_SelectInit();
}
static void GM_End(void)
{ 
  memset(&GM, 0, sizeof(GameInfo)); 
  fflush(stdin);  
  printf("\nCommand [q] can quit\n");
  while('q' != getch());
}
static Uint GM_SelectInit(void)
{
  GM.GameState = STATE_SELECT;
    GM.StateSelectIndex = INDEX_NORMAL;
  GM_PrintSelectMode();
}
static Uint GM_SelectHandle(void)
{ 
  GM_PrintSelectMode(); 
    fflush(stdin);
    Uchar  ch1 = getch();
  if( ENTER == ch1)
  {
    GM_SelectHandleEnter(); 
  }
  else if( ESC == ch1 )
  { 
    GM_SelectEnd();
    GM.GameState = STATE_EXIT;
  }
  else if ( DIR_HEAD == ch1)
  {
    Uchar  ch2 = getch();
    Ushort Key = (ch1 << 8)&0xff00 | ch2;
    switch(Key)
    {     
      case KEY_UP:
        GM.StateSelectIndex = (GM.StateSelectIndex + INDEX_MAXNUM - 1) % INDEX_MAXNUM;
        break;
      case KEY_DOWN:
        GM.StateSelectIndex = (GM.StateSelectIndex + 1) % INDEX_MAXNUM;
        break;
      default:
        break;  
    }
  }
}
static Uint GM_SelectEnd(void){}
static Uint GM_PrepareInit(void)
{
  Uchar OldState = GM.GameState;
    GM.GameState = STATE_PREPARE;
  //from STATE_SELECT -->  STATE_PREPARE
  if(STATE_SELECT == OldState)
  {
    if(MODE_NORMAL == GM.GameMode)
    {
      GM_RandAddOneNum();
      GM_RandAddOneNum(); 
    }
    else
    {
      GM_FromFileAddNum();  
    }
  } 
  //from STATE_PALYING -->  STATE_PREPARE
  else
  {
    if(MODE_NORMAL == GM.GameMode)
    {
      GM_RandAddOneNum();
    }
    else
    {
      GM_PrintBoard();    
      while(FALSE == GM_InputAddOneNum());
    } 
  }
  GM_PrintBoard();
}
static Uint GM_PrepareHandle(void)
{
  if(TRUE != GM_NotMoreMove())
  {
      GM_PrepareEnd();
    GM_PlayingInit();
  }
  else
  {
    printf("Game Over!\n"); 
    GM.GameState = STATE_EXIT;  
  } 
}
static Uint GM_PrepareEnd(void){}
static Uint GM_PlayingInit(void)
{
  GM.GameState = STATE_PALYING;
  printf( "PULL: [a]LEFT [d]RIGHT [w]UP [s]DOWN [q]QUIT\n" ); 
  printf( "Command: ");
    fflush(stdout);
}
static Uint GM_PlayingHandle(void)
{   
  fflush(stdin);
  GM.PlayingCommand = getch();
  switch(GM.PlayingCommand)
  {
    case COM_LEFT:
    case COM_RIGHT:
    case COM_UP:
    case COM_DOWN:
      if( FALSE == GM_PlayingPull())
      { 
        printf("[Error] invalid direction\n");    
        printf( "Command: "); 
      }
      else
      {
        if( TRUE == GM_CheckWin2048() )
        {
          GM_PrintBoard();
          printf("you win !\n");
          GM.GameState = STATE_EXIT;
        }
        else
        {
          GM_PlayingEnd();
          GM_PrepareInit();
        }
      }
      break;
    case COM_QUIT:
      printf("Bye !\n");  
      GM.GameState = STATE_EXIT;  
      break;
    default:
      printf("[Error] Command is a, d, w, s, q \n");
      printf( "Command: ");
      fflush(stdout);
      break;
  }
  //GM_PrintBoard();
}
static Uint GM_PlayingEnd(void)
{
  GM.PlayingCommand = 0;
}
static Uint GM_SelectHandleEnter(void)
{
  switch(GM.StateSelectIndex)
  {
    case INDEX_NORMAL:      
    case INDEX_DEBUG:
        if(INDEX_NORMAL == GM.StateSelectIndex)
      {
        GM.GameMode = MODE_NORMAL;
      }
        else
      {
        GM.GameMode = MODE_DEBUG;
      }
      GM_SelectEnd();
      GM_PrepareInit();
      break;
    case INDEX_EXIT:
      GM_SelectEnd();
      GM.GameState = STATE_EXIT;  
      break;
    default:
      printf("error\n");
      break;
  }         
}
static Uint GM_SelectHandleEsc(void){}
static void GM_PrintSelectMode(void)
{
  system("cls");
  printf("# -  -  -  -  -  -  -  - #\n");
  printf("#     welcome to 2048    #\n"); 
  printf("# -  -  -  -  -  -  -  - #\n");   
  printf("      MODU  SELECT        \n");
  printf("\n      ");
  printf(GM.StateSelectIndex==INDEX_NORMAL?"-->NORMAL":"   NORMAL");   
    printf("\n      "); 
  printf(GM.StateSelectIndex==INDEX_DEBUG? "-->DEBUG ":"   DEBUG ");
  printf("\n      "); 
  printf(GM.StateSelectIndex==INDEX_EXIT?  "-->EXIT  ":"   EXIT  ");
}
static void GM_RandAddOneNum(void)
{
  int row, col;
  while (1)
  {
    row = rand() % GMAE_ROW;
    col = rand() % GMAE_COL;
    if ( GM.Board[row][col] == EMPTY_CELL )
    {
      GM.Board[row][col] = ((rand() % 2) + 1) * 2;
      break;
    }
  } 
}
static Uchar GM_FromFileAddNum(void)
{
  FILE  *infp;
  Uchar tmp[6],tmp1;
  Uchar ret = 0;
  Uchar i,j;
  if(infp = fopen("map.txt", "rb"))
  {
      for(i = 0; i < GMAE_ROW * GMAE_COL; i++)
    {
      j = 0;
      memset(tmp, 0, sizeof(tmp));
      while(1)
      {
        if(!fread(&tmp[j], 1, 1, infp))
          ret |= 0x02;
        if(tmp[j] == ' ' || tmp[j] == '\n' || tmp[j] == 0)
          break;
        j++;  
      } 
      *(&GM.Board[0][0]+i) =  atoi((const char *)tmp);    
    }
  }
  else
  {
    ret |= 0x01;
  }
  if(NULL != infp)
  {
    fclose(infp);
  }
  if(ret != 0)
  {
    printf("read map txt fail\n");
  }
  return ret;
}
static Uchar GM_InputAddOneNum(void)
{
  int row, col, value;
  int ret = TRUE;
  printf("please input add one num!\n");
  printf("Row,Col,Value :");
  fflush(stdout);
  fflush(stdin);
  scanf("%d,%d,%d", &row, &col, &value);
  if(row >= GMAE_ROW || row < 0)
  {
    printf("[Error] Row is between 0 and %d !\n", GMAE_ROW-1);
    ret = FALSE;
  }
  if(col >= GMAE_COL || col < 0)
  {
    printf("[Error] Col is between 0 and %d !\n", GMAE_COL-1);
    ret = FALSE;
  } 
  if(ret == TRUE && GM.Board[row][col] != 0)
  {
    printf("[Error] ( %d , %d ) is occupied!\n", row, col);
    ret = FALSE;
  }
  if(value != 2 && value != 4)
  {
    printf("[Error] Cell Value is either 2 or 4\n");
    ret = FALSE;
  }
  if(ret == TRUE)
  {
    GM.Board[row][col] = value;
  }
  return ret;
}
static Uchar GM_NotMoreMove(void)
{
  int NotMoreMove = TRUE;
  int row, col;
  for ( row = 0; row < GMAE_ROW; row++)
  {
    for ( col = 0; col < GMAE_COL; col++)
    {   
      if(GM.Board[row][col] == 0)
      {
        NotMoreMove = FALSE;
        break;  
      }
      if( col+1 < GMAE_COL && GM.Board[row][col] == GM.Board[row][col+1])
      {
        NotMoreMove = FALSE;
        break;
      } 
      if( row+1 < GMAE_ROW && GM.Board[row][col] == GM.Board[row+1][col])
      {
        NotMoreMove = FALSE;  
          break;  
      }             
    }
    if(FALSE == NotMoreMove)
      break;
  } 
  return NotMoreMove;
}
static void GM_PrintBoard(void)
{
  int row, col; 
  system("cls");
  printf("# -  -  -  -  -  -  -  - #\n");
  printf("#     welcome to 2048    #\n"); 
  printf("# -  -  -  -  -  -  -  - #\n"); 
  for ( row = 0; row < GMAE_ROW; row++)
  {
    for ( col = 0; col < GMAE_COL; col++)
    {
      printf(" + - -", GM.Board[row][col]);
    }
    printf(" +\n");
    for ( col = 0; col < GMAE_COL; col++)
    {
      if(0 == GM.Board[row][col])
        printf(" |    ");
      else  
        printf(" |%4d", GM.Board[row][col]);
    }
    printf(" |\n");
  }
  printf(" + + + + + + + + + + + + + \n");  
}
static Uchar GM_CheckWin2048(void)
{
  int row,col;
  for ( row = 0; row < GMAE_ROW; row++)
  {
    for ( col = 0; col < GMAE_COL; col++)
    {
      if( GM.Board[row][col] == 2048 )
      {
        return TRUE;  
      }     
    }
  }     
  return FALSE;
}
static Uchar GM_PlayingPull(void)
{
  //GMAE_ROW  行 4
  //GMAE_COL  列 4 
  int index;
  int col, row;
  Uchar PullFlag = FALSE;
  Uint array[GMAE_ROW > GMAE_COL? GMAE_ROW:GMAE_COL];
  //******************COM_LEFT*******************
  if( COM_LEFT == GM.PlayingCommand)  
  for ( row = 0; row < GMAE_ROW; row++)
  {
    PullFlag |= GM_MoveLeft( (Uint *)GM.Board[row], GMAE_COL );
    PullFlag |= GM_CombineLeft( (Uint *)GM.Board[row], GMAE_COL );
    PullFlag |= GM_MoveLeft( (Uint *)GM.Board[row], GMAE_COL ); 
  }
  //******************COM_RIGHT****************** 
  else if( COM_RIGHT == GM.PlayingCommand)  
  for ( row = 0; row < GMAE_ROW; row++)
  { 
    PullFlag |= GM_MoveRight( (Uint *)GM.Board[row], GMAE_COL );
    PullFlag |= GM_CombineRight( (Uint *)GM.Board[row], GMAE_COL );
    PullFlag |= GM_MoveRight( (Uint *)GM.Board[row], GMAE_COL );    
  }
  //******************COM_UP*********************
  else if( COM_UP == GM.PlayingCommand)     
  for ( col = 0; col < GMAE_COL; col++)
  {
    for ( row = 0; row < GMAE_ROW; row++)
    {
      array[row] = GM.Board[row][col];        
    }
    //a col move Left   
    PullFlag |= GM_MoveLeft( (Uint *)array, GMAE_ROW );
    PullFlag |= GM_CombineLeft( (Uint *)array, GMAE_ROW );
    PullFlag |= GM_MoveLeft( (Uint *)array, GMAE_ROW ); 
    //write a col
    for ( row = 0; row < GMAE_ROW; row++)
    {
      GM.Board[row][col] = array[row];        
    }     
  }   
  //******************COM_DOWN******************
  else if( COM_DOWN == GM.PlayingCommand)     
  for ( col = 0; col < GMAE_COL; col++)
  { 
    //read a col
    for ( row = 0; row < GMAE_ROW; row++)
    {
      array[row] = GM.Board[row][col];        
    }
    //a col move right    
    PullFlag |= GM_MoveRight( (Uint *)array, GMAE_ROW );
    PullFlag |= GM_CombineRight( (Uint *)array, GMAE_ROW ); 
    PullFlag |= GM_MoveRight( (Uint *)array, GMAE_ROW );
    //write a col
    for ( row = 0; row < GMAE_ROW; row++)
    {
      GM.Board[row][col] = array[row];        
    }                 
  }   
  return  PullFlag; 
}
static Uchar GM_CombineLeft(Uint *array, int num)
{
  int i;
  Uchar CombineFlag = FALSE;
  for ( i = 0; i < num-1; i++ )
  {
    if( array[i] != 0 && array[i] == array[i+1] )
    {
      array[i]  *= 2;
      array[i+1] = 0;
      CombineFlag = TRUE;
    }           
  }   
  return CombineFlag;
}
static Uchar GM_CombineRight(Uint *array, int num)
{
  int i;
  Uchar CombineFlag = FALSE;
  for ( i = num-1; i >= 1; i-- )
  {
    if( array[i] != 0 && array[i] == array[i-1] )
    {
      array[i]  *= 2;
      array[i-1] = 0;
      CombineFlag = TRUE;
    }           
  }   
  return CombineFlag;
}
static Uchar GM_MoveRight(Uint *array, int num)
{
  int i;
  int index = num - 1;
  Uchar moveflg = FALSE;
  for(i = num-1; i >= 0; i--)
  {
    if(array[i] != 0)
    {
      if(array[i] != array[index])
      {
        array[index] = array[i];              
        moveflg = TRUE;
      } 
      index--;      
    }   
  }
  while(index != -1)
  {
    array[index] = 0;
    index--;
  }
  return moveflg;
}
static Uchar GM_MoveLeft(Uint *array, int num)
{
  int i;
  int index = 0;
  Uchar moveflg = FALSE;
  for(i = 0; i < num; i++)
  {
    if(array[i] != 0)
    {
      if(array[i] != array[index])
      {
        array[index] = array[i];              
        moveflg = TRUE;
      } 
      index++;      
    }   
  }
  while(index != num)
  {
    array[index] = 0;
    index++;
  }
  return moveflg;
}


四、运行结果

640.png

游戏主菜单界面,通过方向键选择,分别有NORMAL(正常进行游戏)、DEBUG(调试模式)、EXIT(退出游戏)

640.png

按回车键进入对应的模式。

用字母a、d、w、s、q分别代替左右上下以及退出键。

如果最后游戏成功了,则会提示成功,如果失败则会退出程序。

详细的游戏逻辑可通过代码以及文档进行了解。

往期精彩

嵌入式系统软件架构设计(长篇深度好文)


专为MCU项目开发提速的代码框架BabyOS


嵌入式C语言代码优化方案(深度好文,建议花时间研读并收藏)


C语言表驱动法编程实践(精华帖,建议收藏并实践)


嵌入式工程师买车、用车的总结

目录
相关文章
|
6天前
|
算法 编译器 C语言
猜数字游戏C语言代码实现
猜数字游戏C语言代码实现
|
4天前
|
存储 C语言
C语言实战 | “贪吃蛇”游戏重构
在程序设计中,模块化思维至关重要,尤其对于复杂项目,它帮助分解任务,便于团队协作。以“贪吃蛇”游戏为例,游戏涉及两个角色:蛇和食物。使用数组存储蛇的位置,变量存储食物位置。游戏流程分为初始化、显示和更新数据。初始化时,食物位置随机,蛇的位置根据数组设定。显示数据则根据这些信息在屏幕上呈现角色。更新数据时,处理蛇的移动和增长以及食物的生成和消失。类似地,通过模块化方法可开发“打砖块”游戏,涉及球、球拍和砖墙,每个角色都有相应数据结构和更新逻辑。通过这种方式,游戏开发就像搭建积木,遵循框架逐步实现。
18 0
C语言实战 | “贪吃蛇”游戏重构
|
6天前
|
C语言
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
6 1
|
6天前
|
C语言
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
【海贼王编程冒险 - C语言海上篇】C语言如何实现简单的三子棋游戏?
7 1
|
6天前
|
存储 安全 Serverless
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
扫雷游戏C语言代码实现——万字长文超详细,手把手教你实现,新手也能学会
|
7天前
|
存储 定位技术 API
C语言实战 -- 经典贪吃蛇游戏(含完整源码)
C语言实战 -- 经典贪吃蛇游戏(含完整源码)
9 1
|
3天前
|
C语言
C语言实现猜数字游戏:代码详解与函数解析
C语言实现猜数字游戏:代码详解与函数解析
7 0
|
4天前
|
存储 C语言
C语言实战 | “俄罗斯方块”游戏重构
摘要(Markdown格式): 在之前的游戏中,全局变量的过度使用导致存储浪费和低代码通用性。以“贪吃蛇”为例,显示功能依赖全局变量,限制了函数的复用。通过参数传递代替全局变量,如在“俄罗斯方块”等游戏中控制物体运动的函数,可提升代码重用性和模块化。重构过程中,即使小到变量命名和代码精简的改进,也能逐步带来程序质量的显著提升。
7 0
|
7天前
|
存储 C语言
C语言实战——扫雷游戏
C语言实战——扫雷游戏
9 0
|
7天前
|
算法 编译器 C语言
【C语言】:详解随机数的生成和猜数字游戏的实现
【C语言】:详解随机数的生成和猜数字游戏的实现
7 0