项目日记 | 基于Linux系统的2048小游戏

提示:源码资源可以点击下列地址:

源码链接: 2048小游戏资源

📕内容讲解

1、游戏登入界面

代码入口 main.c

登入界面

代码部分:
#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "initgame.h"

int array[4][4] = {0};

int main(){
    init(array);
}
代码解释:
(一)预处理部分:
1、#include<stdio.h> 和 #include<stdlib.h> 作为最基本头文件不多做赘述
2、#include<string.h> 专门快速处理字符串操作的头文件
3、#include<curses.h> linux图形编程的头文件,可以完成终端的图形化设计,让界面更加好看
4、#include“initgame.h” 是自己设计的登入界面的函数,会在下文介绍到

知识点补充:#include<> 和 #include"" 的区别:【对找工作的同学们挺重要的】

1、引用的头文件不同:
	前者:引用编译器的类库路径的头文件
	后者:引用的是程序所在目录的头文件
2、用法不同(个人感觉这个非强制,但是还是养成习惯):
	前者:引用包含标准头文件
	后者:引用包含非标准头文件
3、调用文件的顺序不同:
	前者:编译程序会先到标准函数库中调用文件
	后者:编译程序会先从当前目录中调用文件
4、预处理程序的指示不同:
	前者:指示预处理程序到预定义的缺省路径下寻找文件
	后者:指示预处理程序先到当前目录下寻找文件,再到预定义的缺省路径下寻找文件

📕总结:尖括号的标准,去库路径寻找;双引号的非标准,在当前目录下寻找
(二)全局变量:
1、int array[4][4] = {0};	由于我的代码设计的4x4大小的游戏界面所以定义的是4x4的二维数组

在这里插入图片描述

(三)函数部分:
int main(){
	/*init函数是initgame.c内的函数,可以当作游戏的(API)接口,下文会有讲解 */
    init(array);
}

游戏登入界面初始化 initgame

代码部分

h文件

#ifndef __INITGAME_H__
#define __INITGAME_H__

void initjudge1(int array[4][4]);
int init(int array[4][4]);


#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "initui.h"

void initjudge1(int array[4][4]){
    char key = getch();
    switch (key){
    case 27:
        clear();
        refresh();
        endwin();
        break;
    case '\n':
        clear();
        refresh();
        gamestart(array);
        break;
    default:
        attron(A_BLINK);
        mvaddstr(LINES * 0.7, COLS * 0.40,"Please try again......");
        attroff(A_BLINK);
        initjudge1(array);
        break;
    }
}

int init(int array[4][4]){

  initscr();
  intrflush(stdscr,false);
  cbreak();
  keypad(stdscr,true);
  box(stdscr,'+','+');
  noecho();  
  curs_set(0);

  attron(A_UNDERLINE);
  mvaddstr(LINES * 0.3, COLS * 0.4, "Welcome To 2048 Game !!!");
  attroff(A_UNDERLINE); 
  mvaddstr(LINES * 0.45, COLS * 0.45, "Start : (Enter)");
  mvaddstr(LINES * 0.5, COLS * 0.46, "Exist : (Esc)");
  mvaddstr(LINES * 0.6, COLS * 0.35,"Please input string Enter/Esc");

  initjudge1(array);

 return 0;
}
代码解释:
(一)预处理部分:
1、#include<stdio.h> 和 #include<stdlib.h> 作为最基本头文件不多做赘述
2、#include<string.h> 专门快速处理字符串操作的头文件
3、#include<curses.h> linux图形编程的头文件,可以完成终端的图形化设计,让界面更加好看
4、#include“initui.h” 是自己设计的游戏运行界面的函数,会在下文介绍到
(二)函数部分:
1、游戏登入界面文字初始化: int init(int array[4][4])

 initscr();							// 此函数是curses编程的初始化,必须使用这个函数来启动curses模式 
 intrflush(stdscr,false);			// 类似中断处理,但建议不需要打开
 cbreak();							// 除DELETE或CTRL等一些特殊控制字元外能被读取
 keypad(stdscr,true);				// 可以使用方向键,将方向键转化为以KEY_开头的常量
 box(stdscr,'+','+');				// 自动绘画方框,方便你设计边框边界
 noecho();  						// 按键输入时不实时显示在屏幕
 curs_set(0);						// 隐藏游标显示

/* 以上代码可以视作图形编程的初始化代码,如果弄不懂的可以不强求弄懂*/

 attron(A_UNDERLINE);													// 打开字体设置并将下文设置成自带下划线
 mvaddstr(LINES * 0.3, COLS * 0.4, "Welcome To 2048 Game !!!");			// 在显示屏幕中显示文字 “Welcome To 2048 Game !!!”
 attroff(A_UNDERLINE); 													// 关闭字体设置
 mvaddstr(LINES * 0.45, COLS * 0.45, "Start : (Enter)");				// 在显示屏幕中显示文字 “Start : (Enter)”
 mvaddstr(LINES * 0.5, COLS * 0.46, "Exist : (Esc)");					// 在显示屏幕中显示文字 “Exist : (Esc)”
 mvaddstr(LINES * 0.6, COLS * 0.35,"Please input string Enter/Esc");	// 在显示屏幕中显示文字 “Please input string Enter/Esc”

 initjudge1(array);														// 判断按键函数,下文会有解释
------------------------------------------------------------------------------------------------------------------------------
2、游戏登入界面按键判断 void initjudge1(int array[4][4])

char key = getch();														// 获得按键信息
switch (key){															// 状态机判断运行
case 27:																// 如果是按下Esc键																	
    clear();															// 将整个屏幕清屏
    refresh();															// 将整个屏幕清屏(与上面工作功能差不多) 
    endwin();															// 结束curse编程的函数
    break;
case '\n':																// 如果是按下Enter键
    clear();
    refresh();
    gamestart(array);													// 打开游戏运行界面,这个是在initui.c的一个函数,下文会有介绍。
    break;
default:																// 如果是按下除Esc键和Enter键以外的键
    attron(A_BLINK);													// 打开字体设置并将下文设置成自闪
    mvaddstr(LINES * 0.7, COLS * 0.40,"Please try again......");		// 在显示屏幕中显示文字 “Please try again......”
    attroff(A_BLINK); 													// 关闭字体设置
    initjudge1(array); 													// 重新进行按键判断
    break;
}

2、游戏运行界面

游戏运行界面初始化 initui

代码部分

h文件

#ifndef __INITUI_H__
#define __INITUI_H__

extern WINDOW * subwindow;
void initborder(WINDOW * subwindow);
void initarray(int array[4][4], WINDOW * subwindow);
int gamestart(int array[4][4]);

#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "keyjudge.h"
#include "gamejudge.h"
#include "randomint.h"

void initborder(WINDOW * subwindow){
    wmove(subwindow, 4, 1);
    whline(subwindow, '-', 27);
    wmove(subwindow, 8, 1);
    whline(subwindow, '-', 27);
    wmove(subwindow, 12, 1);
    whline(subwindow, '-', 27);
    wmove(subwindow, 16, 1);
    whline(subwindow, '-', 27);

    wmove(subwindow, 1, 7);
    wvline(subwindow, '|', 15);
    wmove(subwindow, 1, 14);
    wvline(subwindow, '|', 15);
    wmove(subwindow, 1, 21);
    wvline(subwindow, '|', 15);
}

void initarray(int array[4][4], WINDOW * subwindow){
    for(int i = 1; i <= 4; i++){
        for(int j = 1; j <= 4; j++){
            if(array[i-1][j-1] != 0){
                wmove(subwindow, (4 * i) - 2, (7 * j) - 5);
                wprintw(subwindow, "%-5d", array[i-1][j-1]);
            }
        }
    }

}

WINDOW *subwindow = NULL;
int oldarray[4][4] = {0};

void gamestart(int array[4][4]){
    
    
    initscr();
    cbreak();
    noecho();
    curs_set(0);
    subwindow = subwin(stdscr, 24, 29, LINES * 0.07, COLS * 0.35);
    initborder(subwindow);
    box(subwindow,'|','-');
    wmove(subwindow,17, 7);
    wprintw(subwindow, "[W]:up [S]:down");
    wmove(subwindow,19, 5);
    wprintw(subwindow, "[L]:left [D]:right");
    wmove(subwindow,22, 8);
    wprintw(subwindow, "Exit : (Esc)");
    RandomNum(array);
    RandomNum(array);
    initarray(array, subwindow);
    
    while(1){
        
        for (int i = 0; i < 4; i++){
            for (int j = 0; j < 4; j++)
            {
                oldarray[i][j] = array[i][j];
            }
            
        }
        key(array);
        Gamejudge(array);
        int flag = 0;
        for (int i = 0; i < 4; i++){
            for (int j = 0; j < 4; j++)
            {
                if(oldarray[i][j] != array[i][j]){
                    flag = 1;
                }
            }
        }
        if(flag){
            RandomNum(array);
        }
        else{
            
        }
        clear();
        wmove(subwindow,17, 7);
        wprintw(subwindow, "[W]:up [S]:down");
        wmove(subwindow,19, 5);
        wprintw(subwindow, "[L]:left [D]:right");
        wmove(subwindow,22, 8);
        wprintw(subwindow, "Exit : (Esc)");
        box(subwindow,'|','-');
        initborder(subwindow);
        initarray(array, subwindow);
        
    }
}
代码解释:
(一)预处理部分:
1、#include<stdio.h> 和 #include<stdlib.h> 作为最基本头文件不多做赘述
2、#include<string.h> 专门快速处理字符串操作的头文件
3、#include<curses.h> linux图形编程的头文件,可以完成终端的图形化设计,让界面更加好看
4、#include“keyjudge.h” 、#include“gamejudge.h” 和#include“randomint.h” 分别是按键判断、游戏逻辑判断和随机生成“2”位置的函数,会在下文介绍到
(二)全局变量:
1、WINDOW *subwindow = NULL;	由上图可见,我是在原先的主窗口有创建了一个子窗口,此变量就是子窗口的变量
2、int oldarray[4][4] = {0};	用来保存之前二维数组的缓存
(三)函数部分:
1、游戏边框ui代码::void initborder(WINDOW * subwindow)

wmove(subwindow, 4, 1);
whline(subwindow, '-', 27);
wmove(subwindow, 8, 1);
whline(subwindow, '-', 27);
wmove(subwindow, 12, 1);
whline(subwindow, '-', 27);
wmove(subwindow, 16, 1);
whline(subwindow, '-', 27);

/* 以上代码将方框划分出4四行*/

wmove(subwindow, 1, 7);
wvline(subwindow, '|', 15);
wmove(subwindow, 1, 14);
wvline(subwindow, '|', 15);
wmove(subwindow, 1, 21);
wvline(subwindow, '|', 15);

/* 以上代码将方框划分出4四列*/
-----------------------------------------------------------------------------------------------------
2、数字ui内容填写:void initarray(int array[4][4], WINDOW * subwindow)

/*将之前定义的二维数组进行遍历*/
for(int i = 1; i <= 4; i++){
        for(int j = 1; j <= 4; j++){
            if(array[i-1][j-1] != 0){								// 挑选出非零数值进行显示
                wmove(subwindow, (4 * i) - 2, (7 * j) - 5);			// 移动光标至对应位置
                wprintw(subwindow, "%-5d", array[i-1][j-1]);		// 将数值显示
            }
        }
    }
-----------------------------------------------------------------------------------------------------
3、游戏运行界面总函数:void gamestart(int array[4][4])

initscr();															// curse编程的初始化
cbreak();							·								// 除DELETE或CTRL等一些特殊控制字元外能被读取
noecho();															// 按键输入时不实时显示在屏幕
curs_set(0);														// 隐藏游标
/* 以上可视为子窗口的初始化*/

subwindow = subwin(stdscr, 24, 29, LINES * 0.07, COLS * 0.35);		// 创建子窗口大小
initborder(subwindow);												// 初始化ui界面的边框
box(subwindow,'|','-');												// 快速绘画边界边框

wmove(subwindow,17, 7);												
wprintw(subwindow, "[W]:up [S]:down");								
wmove(subwindow,19, 5);												
wprintw(subwindow, "[L]:left [D]:right");							
wmove(subwindow,22, 8);												
wprintw(subwindow, "Exit : (Esc)");									
/* 以上为控制提示的ui设计 */

RandomNum(array);
RandomNum(array);
/* 随机给二维数组2个数字,此函数是位于randomint.c里(后面会有讲解) */

initarray(array, subwindow);										//数字ui界面填写
while(1){
   
   for (int i = 0; i < 4; i++){
       for (int j = 0; j < 4; j++)
       {
           oldarray[i][j] = array[i][j];
       }
       
   }
   /* 保存最新的二维数组 */
   key(array);														// 此函数是在keyjudge.c里的函数,用于方向判断和操作(后面会有讲解)
   Gamejudge(array);												// 此函数是在gamejudge.c里的函数,用于判断游戏是否结束(后面会有讲解)
   int flag = 0;													// 是否改变的标志位

	/* 判断是否和之前的数组一样 */
   for (int i = 0; i < 4; i++){
       for (int j = 0; j < 4; j++)
       {
           if(oldarray[i][j] != array[i][j]){
               flag = 1;
           }
       }
   }
   if(flag){ 														// 如果不同这随机在给一个数字
       RandomNum(array);
   }
   else{															// 反之不进行任何操作
       
   }
   clear();															// 进行清屏刷新数据
   
   /* 以下是将新界面进行呈现 */ 
   wmove(subwindow,17, 7);
   wprintw(subwindow, "[W]:up [S]:down");
   wmove(subwindow,19, 5);
   wprintw(subwindow, "[L]:left [D]:right");
   wmove(subwindow,22, 8);
   wprintw(subwindow, "Exit : (Esc)");
   box(subwindow,'|','-');
   initborder(subwindow);
   initarray(array, subwindow);
   
}

随机生成数字“2” randomint

代码部分

h文件

#ifndef __RANDOMINT_H__
#define __RANDOMINT_H__

void Random(int array[4][4]);
int RandomNum(int array[4][4]);


#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

void Random(int array[4][4]){
    int flag = 0;
    int num[10] = {2,2,2,2,2,2,2,2,2,4};
    for (int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++){
            if(array[i][j]  == 0){flag++;}
        }
    }

    if(flag == 0){

    }
    else{
        flag = random() % flag;
        int key = 1;
        int k = 0;
        for (int i = 0; i < 4; i++){
            for (int j = 0; j < 4; j++){
                if(key == 1){
                    if(array[i][j]  == 0 && k != flag){
                    k++;
                    }
                else if (array[i][j]  == 0 && k == flag){
                    array[i][j] = num[random() % 10];
                    key = 0;
                    }
                }
            }
        }
    }
}

int RandomNum(int array[4][4]){
    int i, j;
    srandom(time(NULL));
    Random(array);
}
代码解释:
(一)预处理部分:
1、#include<stdio.h> 和 #include<stdlib.h> 作为最基本头文件不多做赘述
2、#include<string.h> 专门快速处理字符串操作的头文件
3、#include<curses.h> linux图形编程的头文件,可以完成终端的图形化设计,让界面更加好看
4、#include<time.h>  专门处理时间查询的函数库
(二)函数部分:
1、二维数组生成随机数字“2” void Random(int array[4][4])

int flag = 0;													// 判断是否有数字的标志位
int num[10] = {2,2,2,2,2,2,2,2,2,4};							// 这个是随机生成数字的“数字池”,随机生成的数字就是来源于此

/* 遍历数组,查找有多少个位置没有数据 */
for (int i = 0; i < 4; i++){
    for (int j = 0; j < 4; j++){
        if(array[i][j]  == 0){flag++;}
    }
}


if(flag == 0){													// 如果没有位置是空的就不作任何操作

}
else{															// 如果有位置是空进行随机数赋值
    flag = random() % flag;										// 在没有位置的地方随机选择位置赋值
    int key = 1;												// 是否赋值的标志位
    int k = 0;													// 由于判断是否到达我要赋值的位置的标志位

	/* 开始遍历赋值 */
    for (int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++){
            if(key == 1){										// 如果还没有赋值
                if(array[i][j]  == 0 && k != flag){				// 如果位置是空的但是没有到我想要赋值的位置
                k++;											// 位置加1
                }
            else if (array[i][j]  == 0 && k == flag){			// 如果位置是空的且到达我想赋值的位置
                array[i][j] = num[random() % 10];				// 从上述的数据池随机选择一个
                key = 0;										// 将是否赋值的标志位赋值0,让他不要赋值了
                }
            }
        }
    }
}
----------------------------------------------------------------------------------------------------------------------
2、随机生成总函数 int RandomNum(int array[4][4])

srandom(time(NULL));											// 生成随机种子
Random(array);													// 二维数组随机生成数字“2”

判断游戏是否结束 gamejudge

代码部分

h文件

#ifndef __GAMEJUDGE_H__
#define __GAMEJUDGE_H__

void Gamejudge(int array[4][4]);


#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "win.h"
#include "lose.h"

void Gamejudge(int array[4][4]){
    int i;
      for (i = 0; i < 4; ++i) {
          int j;
          for (j = 0; j < 3; ++j) {
              /* 横向和纵向比较挨着的两个元素是否相等,若有相等则游戏不结束 */
              if (array[i][j] == array[i][j + 1] || array[j][i] == array[j + 1][i]) {
                 return;
             }
         }
     }
     for(int i = 0; i < 4; i++){
        for (int j = 0; j < 4; j++){
            if(array[i][j] == 2048){
                clear();
                refresh();
                endwin();
                Win(array);
            }
        }

    clear();
    refresh();
    endwin();
    Lose(array);
    }    
}
代码解释:
(一)预处理部分:
1、#include<stdio.h> 和 #include<stdlib.h> 作为最基本头文件不多做赘述
2、#include<string.h> 专门快速处理字符串操作的头文件
3、#include<curses.h> linux图形编程的头文件,可以完成终端的图形化设计,让界面更加好看
4、#include"win.h"和#include"lose.h" 分别是游戏胜利界面和游戏失败界面的函数(下文会有讲解)
(二)函数部分:
1、游戏判断代码 void Gamejudge(int array[4][4])

int i;
for (i = 0; i < 4; ++i) {
    int j;
    for (j = 0; j < 3; ++j) {
        /* 横向和纵向比较挨着的两个元素是否相等,若有相等则游戏不结束 */
        if (array[i][j] == array[i][j + 1] || array[j][i] == array[j + 1][i]) {
           return;
       }
   }
}
/* 判断每个元素与它的下面的元素和左边的元素是否相同,如果有相同的就退出判断,否则进行下一个判断 */

for(int i = 0; i < 4; i++){
  for (int j = 0; j < 4; j++){
      if(array[i][j] == 2048){
          clear();
          refresh();
          endwin();
          Win(array);											// 此函数是win.c内的函数,下文会有介绍
      }
  }
/* 遍历数组,如果二维数组中有一个元素是2048,游戏胜利 */

clear();
refresh();
endwin();
Lose(array);													// 此函数是lose.c内的函数,下文会有介绍
/* 反之游戏失败 */
}  

3、游戏结束界面

由于结束界面和登入界面基本一致,所以大家可以类比游戏登入界面函数,以下只提供h文件和c文件

胜利界面 win

代码部分

h文件

#ifndef __WIN_H__
#define __WIN_H__

void initjudge3(int array[4][4]);
int Win(int array[4][4]);


#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "initui.h"

void initjudge3(int array[4][4]){
    int newarray[4][4] = {0};
    char key = getch();
    switch (key){
    case 27:
        clear();
        refresh();
        endwin();
        exit(0);
        break;
    case '\n':
        clear();
        refresh();
        endwin();
        gamestart(newarray);
        break;
    default:
        attron(A_BLINK);
        mvaddstr(LINES * 0.7, COLS * 0.40,"Please try again......");
        attroff(A_BLINK);
        initjudge3(array);
        break;
    }
    endwin();
}

int Win(int array[4][4]){
  initscr();
  cbreak();
  keypad(stdscr,true);
  box(stdscr,'+','+');
  noecho(); 
  curs_set(0);
  
  mvaddstr(LINES * 0.3, COLS * 0.47, "You Win !!!");
  mvaddstr(LINES * 0.45, COLS * 0.45, "Again : (Enter)");
  mvaddstr(LINES * 0.5, COLS * 0.46, "Exist : (Esc)");
  mvaddstr(LINES * 0.6, COLS * 0.35,"Please input string Enter/Esc");
  move(LINES - 1,COLS * 0.5);
 
  initjudge3(array);

 return 0;
}

失败界面 lose

代码部分

h文件

#ifndef __LOSE_H__
#define __LOSE_H__

void initjudge2(int array[4][4]);
int Lose(int array[4][4]);


#endif

c文件

#include <curses.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "initui.h"

void initjudge2(int array[4][4]){
    int newarray[4][4] = {0};
    char key = getch();
    switch (key){
    case 27:
        clear();
        refresh();
        endwin();
        exit(0);
        break;
    case '\n':
        clear();
        refresh();
        endwin();
        gamestart(newarray);
        break;
    default:
        attron(A_BLINK);
        mvaddstr(LINES * 0.7, COLS * 0.40,"Please try again......");
        attroff(A_BLINK);
        initjudge2(array);
        break;
    }
    
}

int Lose(int array[4][4]){
  initscr();
  cbreak();
  keypad(stdscr,true);
  box(stdscr,'+','+');
  noecho(); 
  curs_set(0);

  mvaddstr(LINES * 0.3, COLS * 0.47, "You Lose !!!");
  mvaddstr(LINES * 0.45, COLS * 0.45, "Again : (Enter)");
  mvaddstr(LINES * 0.5, COLS * 0.46, "Exist : (Esc)");
  mvaddstr(LINES * 0.6, COLS * 0.35,"Please input string Enter/Esc");
  move(LINES - 1,COLS * 0.5);

  initjudge2(array);

 return 0;
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值