1. 程式人生 > >關於2048小遊戲專案的一些心得

關於2048小遊戲專案的一些心得

這是我學習完標C和Unix C以後做的一個小遊戲,只能支援字元終端介面。在此將其貼出來,希望能夠跟大家分享一些心得。以下是我當時寫的一個專案文件

==========================2048 by Phoenix遊戲開發手冊==========================

1.遊戲在終端中的顯示:

遊戲按鍵設定:
     w
a   s   d
遊戲規則:
使用者通過按鍵移動方格中的數字,相鄰格子中的數字如果相同,則可以通過移動
相應的方向來實現相加並將這兩個數字合併,率先達到2048則獲得勝利。如果方格已
經被充滿,既無法相加也無法移動,則遊戲失敗
***************************************************************************
2.技術支援:
作業系統:Ubuntu
開發工具:vim,gcc,makefile
***************************************************************************
3.遊戲中包含的檔案:
標頭檔案:color.h,myheadfile.h
儲存檔案:SaveMap.bin,SaveScore.bin,HistoryHightscore.bin
介面建立:MapPrinter.c
邏輯判定:JudgeLogic.c
事件響應:GameLogic.c
檔案操作:FileOperate.c
主檔案(程式入口):2048.c
***************************************************************************
3.遊戲的邏輯功能實現(將相應功能模組獨立出來並形成相應的檔案):
a.遊戲邏輯:GameLogic.c(已實現)
  所包含的功能(函式)有move_top,move_down,move_lef,move_right,insert_num,
  run_map;
  move_*函式實現當用戶使用按鍵的時候,相應的移動效果的實現
  insert_num函式用於在移動後的空位中插入隨機的2或4
  run_map函式用於獲得使用者的操作並作出相應的邏輯實現

b.列印地圖:MapPrinter.c(已實現)
  所包含的功能(函式)有map_print,init_map,print,print_color;
  map_print用於打印出地圖
  init_map用於在遊戲開始時且使用者上次遊戲並未儲存的情況下,初始化地圖
  print用於列印地圖的“-----”
  print_color用於打印出不同顏色的數字

c.判定邏輯:JudgeLogic.c(已實現)
  所包含的功能(函式)有success_judge,fail_judge;
  success_judge如果滿足勝利條件則在螢幕上列印"You Win!!",並終止遊戲
  fail_judge如果滿足失敗判定條件,則終止遊戲,並在螢幕上列印“You Lose!!”
  
d.檔案操作:FileOperate.c(已實現)
  所包含的功能(函式)有open,save_hightscore,save_game;
  open:開啟檔案SaveMap.txt,檢視使用者是否有儲存上次遊戲的結果,如果有則將地
  圖載入進來,否則直接初始化地圖。
  save_hightscore:當檢測到使用者的成績高於歷史最高分時,將成績儲存下來
  save_game:使用者輸入q的時候,將地圖寫入到SaveMap.bin檔案中

e.程式入口:2048.c
  所包含的功能(函式)有:main
  實現功能:為程式的執行提供一個入口
****************************************************************************
4.函式原型設定:
GameLogic.c:
void move_top(int (*)[],int,int*,int*,int*);
void move_down(int (*)[],int,int*,int*,int*);
void move_left(int (*)[],int,int*,int*,int*);
void move_right(int (*)[],int,int*,int*,int*);
void run_map(int (*)[],int,int*,int*,int*,int*);
void insert_num(int (*)[],int);

MapPrinter.c:
static void print(int);
static void print_color(int (*)[],int,int);
void init_map(int (*)[],int);
void map_printer(int (*)[],int,int*,int*);

JudgeLogic.c:
int success_judge(int (*)[],int);
int fail_judge(int (*)[],int,int,int);

FileOperate.c:
int open(int (*)[],int,int*,int*);
int save_hightscore(int);
int save_game(int (*)[],int,int);

2048.c
int main(void);

****************************************************************************

以下是相應的檔案實現程式碼:

標頭檔案:

color.h(定義了Linux中終端顏色顯示欄位)

#ifndef __COLOR__H
#define __COLOR__H
//定義字型顏色
#define NONE "\033[m" //取消列印的顏色,列印完數字後就要取消掉顏色列印
#define RED_2 "\033[0;40;31m"					//黑底紅色	2
#define GREEN_4 "\033[0;40;32m"					//黑底綠色	4
#define DARKYELLOW_8 "\033[0;40;33m"			//黑底土黃	8	
#define BLUE_16 "\033[0;40;36m"					//黑底藍色	16	
#define PURPLE_32 "\033[0;40;35m"				//黑底紫紅	32
#define LIGHT_ORANGE_64 "\033[1;40;31m"			//黑底淺橘  64
#define ORANGE_128 "\033[1;40;32m"				//黑底橘紅	128
#define YELLOW_256 "\033[1;40;33m"				//黑底黃色	256
#define LIGHT_PURPLE_512 "\033[1;40;34m"		//黑底淺紫	512
#define PINK_1024 "\033[1;40;35m"				//黑底粉字	1024
#define CAYN_2048 "\033[1;40;36m"				//黑底青色	2048
#define BACKGROUND "\033[1;40m"					//黑色背景
#endif //__COLOR__H
myheadfile.h(聲明瞭整個2048遊戲中的函式及其共享巨集變數)
#ifndef __MYHEADFILE__H
#define __MYHEADFILE__H
//共享的巨集變數定義
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#define SIZE 4
#define FILENAME map.txt
#define FILESCORE score.bin
//函式宣告
//GameLogic.c
void move_top(int (*)[],int,int*,int*,int*);
void move_down(int (*)[],int,int*,int*,int*);
void move_left(int (*)[],int,int*,int*,int*);
void move_right(int (*)[],int,int*,int*,int*);
void insert_num(int (*)[],int);
void run_map(int (*)[],int,int*,int*,int*,int*);
//FileOperate.c
int open(int (*)[],int,int*,int*);
int save_hightscore(int);
int save_game(int (*)[],int,int);
//MapPrinter.c
void init_map(int (*)[],int);
void map_printer(int (*)[],int,int*,int*);
//JudgeLogic.c
int success_judge(int (*)[],int);
int fail_judge(int (*)[],int,int,int);
#endif

C檔案:

2048,c(遊戲入口檔案)

#include "myheadfile.h"
int main(void){
	srand(time(0));
	int arr[SIZE][SIZE] = {};
	int IsMove,IsPlus,score = 0,hightscore = 0;
	if(open(arr,4,&score,&hightscore)){
		map_printer(arr,4,&score,&hightscore);
	}
	else{
		init_map(arr,4);
		map_printer(arr,4,&score,&hightscore);
	}
	run_map(arr,4,&score,&hightscore,&IsMove,&IsPlus);
	return 0;
}

MapPrinter.c(負責列印遊戲介面)

#include "color.h"
#include "myheadfile.h"
static void print(int size){
	int i;
	for(i = 0;i < size;i++){
		printf("-----");
	}
	printf("\n");
}
void init_map(int (*arr)[SIZE],int size){
	int randone = rand()%size;
	int randtwo = rand()%size;
	int randthree = rand()%size;
	int randfour = rand()%size;
	arr[randone][randtwo]=2;
	arr[randthree][randfour]=4;
}
static void print_color(int (*arr)[SIZE],int size,int row,int col){
	switch(arr[row][col]){
		case 0:
			printf("|"BACKGROUND"    "NONE);
			break;
		case 2:
			printf("|"RED_2"2   "NONE);
			break;
		case 4:
			printf("|"GREEN_4"4   "NONE);
			break;
		case 8:
			printf("|"DARKYELLOW_8"8   "NONE);
			break;
		case 16:
			printf("|"BLUE_16"16  "NONE);
			break;
		case 32:
			printf("|"PURPLE_32"32  "NONE);
			break;
		case 64:
			printf("|"LIGHT_ORANGE_64"64  "NONE);
			break;
		case 128:
			printf("|"ORANGE_128"128 "NONE);
			break;
		case 256:
			printf("|"YELLOW_256"256 "NONE);
			break;
		case 512:
			printf("|"LIGHT_PURPLE_512"512 "NONE);
			break;
		case 1024:
			printf("|"PINK_1024"1024"NONE);
			break;
		case 2048:
			printf("|"CAYN_2048"2048"NONE);
			break;
		default:
			break;
	}
}
void map_printer(int (*arr)[SIZE],int size,int *score,int *hightscore){
	int i,j;
	system("clear");
	printf("2048 by Phoenix\n");
	printf("Score:%d\tHightScore:%d\n",*score,*hightscore);
	print(size);
	for(i = 0;i < size;i++){
		for(j = 0;j < size;j++){
				print_color(arr,size,i,j);
		}
		printf("|\n");
		print(size);
	}
}

GameLogic.c(實現遊戲的相應操作)

#include "myheadfile.h"
//向上移動
void move_top(int (*arr)[SIZE],int size,int* score,int* IsPlus,int *IsMove){
	int i,j,k;
	for(i = 0;i < size;i++){
		k=0;
		while(arr[k][i]&&k<size){	//找到同列不同行中值為0的數並得到行號
			k++;
		}
		for(j = k+1;j < size;j++){
			if(arr[j][i]!=0){
				arr[k][i] = arr[j][i];
				arr[j][i] = 0;
				k++;
				*IsMove = 2;
			}
		}
		for(k=0;k<size-1;k++){
			if(arr[k][i] == arr[k+1][i]&&arr[k][i]!=0){
				arr[k][i] *= 2;
				*score += arr[k][i];
				arr[k+1][i] = 0;
				*IsPlus = 1;
				*IsMove = 1;
			}
		}
	}
	for(i = 0;i < size;i++){
		k=0;
		while(arr[k][i]&&k<size){
			k++;
		}
		for(j = k+1;j < size;j++){
			if(arr[j][i]!=0){
				arr[k][i] = arr[j][i];
				arr[j][i] = 0;
				k++;
				*IsMove = 1;
			}
		}
	}
}
//向下移動
void move_down(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
	int i,j,k;
	for(i = 0;i < size;i++){
		k=size-1;
		while(arr[k][i]&&k>=0){	//找到同列不同行中值為0的數並得到行號
			k--;
		}
		for(j = k-1;j >= 0;j--){
			if(arr[j][i]!=0){
				arr[k][i] = arr[j][i];
				arr[j][i] = 0;
				k--;
				*IsMove = 1;
			}
		}
		for(k=size-1;k>0;k--){
			if(arr[k][i] == arr[k-1][i]&&arr[k][i]!=0){
				arr[k][i] *= 2;
				*score += arr[k][i];
				arr[k-1][i] = 0;
				*IsPlus = 1;
				*IsMove = 1;
			}
		}
	}
	for(i = 0;i < size;i++){
		k=size-1;
		while(arr[k][i]&&k>=0){
			k--;
		}
		for(j = k-1;j >= 0;j--){
			if(arr[j][i]!=0){
				arr[k][i] = arr[j][i];
				arr[j][i] = 0;
				k--;
				*IsMove = 1;
			}
		}
	}
}
//向左移動
void move_left(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
	int i,j,k;
	for(i = 0;i < size;i++){
		k = 0;
		while(arr[i][k]&&k<size){	//找到同列不同行中值為0的數並得到行號
			k++;
		}
		for(j = k+1;j < size;j++){
			if(arr[i][j]!=0){
				arr[i][k] = arr[i][j];
				arr[i][j] = 0;
				k++;
				*IsMove = 1;
			}
		}
		for(k=0;k<size-1;k++){
			if(arr[i][k] == arr[i][k+1]&&arr[i][k]!=0){
				arr[i][k] *= 2;
				*score += arr[i][k];
				arr[i][k+1] = 0;
				*IsPlus = 1;
				*IsMove = 1;
			}
		}
	}
	for(i = 0;i < size;i++){
		k=0;
		while(arr[i][k]&&k<size){
			k++;
		}
		for(j = k+1;j < size;j++){
			if(arr[i][j]!=0){
				arr[i][k] = arr[i][j];
				arr[i][j] = 0;
				k++;
				*IsMove = 1;
			}
		}
	}
}
//向右移動先移動,再判斷相加,最後再移動一次
void move_right(int (*arr)[SIZE],int size,int* score,int* IsPlus,int* IsMove){
	int i,j,k;
	for(i = 0;i < size;i++){
		k=size-1;
		while(arr[i][k]&&k>=0){	//找到同列不同行中值為0的數並得到行號
			k--;
		}
		//向右移動
		for(j = k-1;j >= 0;j--){
			if(arr[i][j]!=0){
				arr[i][k] = arr[i][j];
				arr[i][j] = 0;
				k--;
				*IsMove = 1;
			}
		}
		/****************************************************************/
		//判斷相加
		for(k=size-1;k>0;k--){
			if(arr[i][k] == arr[i][k-1]&&arr[i][k]!=0){
				arr[i][k] *= 2;
				*score += arr[i][k];
				arr[i][k-1] = 0;
				*IsPlus = 1;
				*IsMove = 1;
			}
		}
		/******************************************************************/
	}
	//移動
	for(i = 0;i < size;i++){
		k=size-1;
		while(arr[i][k]&&k>=0){
			k--;
		}
		for(j = k-1;j >= 0;j--){
			if(arr[i][j]!=0){
				arr[i][k] = arr[i][j];
				arr[i][j] = 0;
				k--;
				*IsMove = 1;
			}
		}
	}
}
/************************************************************************/
void insert_num(int (*arr)[SIZE],int size){
	int randX,randY,randnum;
	randnum = rand()%2;			//randnum的值為0或1,0表示在,1表示4
	do{
		randX=rand()%size;
		randY=rand()%size;
		if(arr[randX][randY] == 0){
			if(randnum == 0){
				arr[randX][randY] = 2;
				break;
			}
			else{
				arr[randX][randY] = 4;
				break;
			}
		}
	}while(1);
}
void run_map(int (*arr)[SIZE],int size,int *score,int *hightscore,int *IsMove,int *IsPlus){
	char input;
	int i,j;
	int save = 0;
	while(1){
		*IsMove = 0;
		*IsPlus = 0;
		scanf("%c",&input);
		scanf("%*[^\n]");
		scanf("%*c");
		switch(input){
			case 'w':
				move_top(arr,4,score,IsPlus,IsMove);
				break;
			case 's':
				move_down(arr,4,score,IsPlus,IsMove);
				break;
			case 'a':
				move_left(arr,4,score,IsPlus,IsMove);
				break;
			case 'd':
				move_right(arr,4,score,IsPlus,IsMove);
				break;
			case 'q':
				printf("Saving……,Please wait!!\n");
				save = save_game(arr,size,*score);
				sleep(1);
				if(save){
					printf("The game has been saved\n");
					exit(0);
				}
				else{
					printf("Save failed!!\n");
				}
				break;
			default:
				printf("Input error!!\n");
				break;
		}
		if(*IsMove){
			insert_num(arr,4);
		}
		map_printer(arr,4,score,hightscore);
		if(success_judge(arr,4)){
			printf("You Win!!\n");
			for(i=0;i<size;i++){
				for(j=0;j<size;j++){
					arr[i][j] = 0;
				}
			}
			save_hightscore(*score);	//儲存最高分數
			*score = 0;
			save_game(arr,size,*score);
			exit(0);
		}
		if(fail_judge(arr,4,*IsMove,*IsPlus)){
			printf("You Lose!!\n");
			for(i=0;i<size;i++){
				for(j=0;j<size;j++){
					arr[i][j] = 0;
				}
			}
			save_hightscore(*score);	//儲存最高分
			*score = 0;
			save_game(arr,size,*score);
			exit(0);
		}
	}
}
JudgeLogic.c(實現對遊戲輸贏的判定)
#include "myheadfile.h"
int success_judge(int (*arr)[SIZE],int size){
	int i,j,success = 0;
	for(i = 0;i < size;i++){
		for(j = 0;j < size;j++){
			if(arr[i][j] == 2048){
				success = 1;	
			}
		}
	}
	return success;
}
int fail_judge(int (*arr)[SIZE],int size,int IsMove,int IsPlus){
	int i,j,tmp = 0,fail = 0,col = 0,row = 0;
	//迴圈判斷陣列中是否還有空位,有則tmp=1,無則tmp=0
	for(i = 0;i < size;i++)
		for(j = 0;j < size;j++){
			if(!arr[i][j])
				tmp = 1;
		}
	for(i = 0;i < size;i++)
		for(j = 0;j < size-1;j++){
			if(arr[i][j] == arr[i][j+1] && arr[i][j] != 0){
				col = 1;
			}
			if(arr[j][i] == arr[j+1][i] && arr[j][i] != 0){
				row = 1;
			}
		}
	if(!IsMove && !IsPlus && !tmp && !col && !row){
		fail = 1;
	}
	return fail;
}

FileOperate.c(通過檔案來儲存相應的玩家資訊及歷史最高分)

#include "myheadfile.h"
int open(int (*arr)[SIZE],int size,int *score,int *hightscore){
	FILE *p_file = fopen("SaveMap.bin","r");
	int i,j,failed = 0;
	if(NULL == p_file){
		printf("File open error!!\n");
		return 0;
	}
	else{
		for(i = 0;i < size;i++){
			failed = fread(arr[i],sizeof(int),size,p_file);
		}
		for(i = 0;i < size;i++){
			for(j = 0;j < size-1;j++){
				if(arr[i][j] == arr[i][j-1] && !arr[i][j]){
					failed = 0;
				}
				else{
					failed = 1;
				}
			}
		}
		fclose(p_file);
		p_file = fopen("SaveScore.bin","r");
		if(NULL == p_file){
			printf("ERROR:Can not read SaveScore.bin\n");
			return 0;
		}
		else{
			fread(score,sizeof(int),1,p_file);
			fclose(p_file);
			p_file = fopen("HistoryHightscore.bin","r");
			if(NULL == p_file){
				printf("ERROR:Can not read HistoryHightscore.bin\n");
				return 0;
			}
			else{
				fread(hightscore,sizeof(int),1,p_file);
				printf("11111111111111111:%d\n",*hightscore);
				fclose(p_file);
				p_file = NULL;
			}
		}
		return failed;
	}
}
int save_hightscore(int score){
	FILE *p_file = fopen("HistoryHightscore.bin","r+");
	int hightscore;
	if(NULL == p_file){
		printf("ERROR:Can not open HistoryHightscore.c!!\n");
		return 0;
	}
	else{
		fread(&hightscore,sizeof(int),1,p_file);
		if(score > hightscore){
			rewind(p_file);
			fwrite(&score,sizeof(int),1,p_file);
		}
		fclose(p_file);
		p_file = NULL;
		return 1;
	}
}
int save_game(int (*arr)[SIZE],int size,int score){
	FILE *p_file = fopen("SaveMap.bin","w");
	int i;
	if(NULL == p_file){
		printf("ERROR:Can not open SaveMap.bin!!\n");
		return 0;
	}
	else{
		for(i = 0;i < size;i++){
			fwrite(arr[i],sizeof(int),size,p_file);
		}
		fclose(p_file);
		p_file = fopen("SaveScore.bin","w");
		if(NULL == p_file){
			printf("ERROR:Can not open SaveScore.bin!!\n");
			return 0;
		}
		else{
			fwrite(&score,sizeof(int),1,p_file);
			fclose(p_file);
			p_file = NULL;
			return 1;
		}
	}
}

最終的程式執行結果: