[C + +] 2048 game series - fourth draft of optimization module [supplementary arrangement]

Posted by gotornot on Fri, 10 Dec 2021 14:10:43 +0100

2048 - optimize four drafts - activate maximum score output [supplementary document operation]

(updating...)
Reference blog: https://blog.csdn.net/qq_39151563/article/details/104283217
Currently completed:

Supplement the previously omitted (historical) highest score output, focusing on file operation

1, Basic file operation template

After reading a lot of blogs about C + + file operation, I feel that C + + is not easy to use, so I still use the C file operation format. C code can be used in C + +

#include <iostream>
using namespace std;
const char FILE_NAME[10] = "test.txt";

int main()
{
	//Using fprintf function to write formatted data to file 
	FILE* fp = fopen(FILE_NAME,"w");//"w" means write 
	fprintf(fp,"This is a test, enter:%d",30);
	fclose(fp); 
	
	//Use the fscanf function to read the formatted data from the file (equivalent to matching)
	int a = 0; 
	fp = fopen(FILE_NAME,"r"); //"r" means read, read
	fscanf(fp,"This is a test, enter:%d",&a);
	cout << a << endl; 
	
	return 0;
} 

2, Activate highest score output

The focus is to record the highest score in history, so it involves file operation

2.1 - Code overview after class encapsulation

The red line in the figure is the part to be changed:
1. At the beginning of the game, read the highest score in history to m through the LoadRecord() function_ TopSocre
2. After merging numbers each time, judge whether the current score is greater than the highest score in history (in the Move() function). If greater than, save the highest score through the GameSave() function
3. Read m when drawing the image_ TopSocre

2.2-GameSave() and LoadRecord() functions

Please refer to the following codes for details

const char RECORD_FILE_NAME[15] = "RECORD.txt";//Archive file name (in ConstDate.h)

void Game2048::GameSave()//Game save
{
	FILE* fp = fopen(RECORD_FILE_NAME,"w");
	if(fp==NULL)
	{
		this->RestartDate();
		fclose(fp);
		return;
	}
	fprintf(fp,"TopSocre:%d",this->m_TopScore);
	fclose(fp);	
} 

void Game2048::LoadRecord()//File reading
{
	FILE* fp = fopen(RECORD_FILE_NAME,"r");
	if(fp==NULL)
	{
		this->RestartDate();
		fclose(fp);
		return;
	}
	fscanf(fp,"TopSocre:%d",&this->m_TopScore);
	fclose(fp);
}

3, Delete unnecessary code

  • Delete / / void PrintGrid()// Print function, void ShowInfo()// Displays information about the two functions
  • Project - > project settings - > Win32 graphical interface program (so there is no console)

4, All code and running results

The relevant codes and settings have not been deleted when recording

#ifndef CONST_DATE_H
#define CONST_DATE_H

const char RECORD_FILE_NAME[15] = "RECORD.txt";//Archive Filename  

const int DEVIDE = 15;					//interval 
const int GRID_WIDTH = 106;				//Lattice width 

const int WIDTH  = 500+2*DEVIDE;		//Total interface width 
const int HEIGHT = 660+3*DEVIDE; 		//Total interface height 

const int BOARD_X = 0+DEVIDE;			//Chessboard start point x
const int BOARD_Y = 160+2*DEVIDE;		//Chessboard starting point y
const int LOGO_X = 25+DEVIDE;			//2048logo starting point x 
const int LOGO_Y = 20+DEVIDE;			//2048logo starting point y

const int RESTART_X = 0+DEVIDE;			//restart start point x
const int RESTART_Y = 80+LOGO_Y;		//restart start point y
const int RESTART_WIDTH = 220;			//restart width
const int RESTART_HEIGHT = 50;			//restart height 
 
const int SCOREGB_X = 230+DEVIDE;		//Score background starting point x 
const int SCOREGB_Y = 0+DEVIDE; 		//Starting point y of fractional background image 

const color_t TEXT_COLOR = EGERGB(241, 231, 214); //Font color
const color_t BG_COLOR = EGERGB(250,248, 239);//Background color light yellow 

const int MAXVALUE_X = SCOREGB_X+160;	//Maximum starting point x 
const int MAXVALUE_Y = SCOREGB_Y+112; 	//Maximum starting point y 

const int SCORE_X = MAXVALUE_X;			//Starting point of this score x 
const int SCORE_Y = MAXVALUE_Y-45;		//Starting point of this score y 

const int TOP_SCORE_X = SCORE_X;		//Highest score starting point x 
const int TOP_SCORE_Y = SCORE_Y-45;		//Highest score starting point y 

/*-----------------------------------------------------------*/ 
//Use in move function 
static int x0[4] = {0, 0, 3, 0};
static int y0[4] = {0, 0, 0, 3};
static int firstOffset[4][2]  = {{1,0},{0,1},{-1,0},{0,-1}};
static int secondOffset[4][2] = {{0,1},{1,0},{0,1} ,{1,0}};
/*-----------------------------------------------------------*/ 
/*-----------------------------------------------------------*/ 
//Picture storage 
PIMAGE BlockImgs[18];//0-background, 1-17 digital 
PIMAGE GameOverImg;//Game end chart 
PIMAGE GameLogoImg;//Game Logo 
PIMAGE RestartImg;//Restart diagram
PIMAGE AIImg;//AI key diagram 
PIMAGE ScoreBgImg;//Score background 
/*-----------------------------------------------------------*/
#endif	//CONST_DATE_H
#include <iostream>
#include "graphics.h"
#include <cmath>
//#include <fstream> 
#include "ConstDate.h"

using namespace std;
int Grid[4][4] = {0};			//[global variable] 4 * 4 matrix

class Game2048{
	//private:
	public:
		int m_Dir;								// Direction: 0-left, 1-up, 2-right, 3-down 
		int m_EmptyBlock;		//Number of spaces 
		int m_Score;			//Record score 
		int m_MaxValue;			//Record the maximum value of the composite
		int m_TopScore;			//Record the highest score 
		
	public:
		Game2048();//initialization 
		int CalculateEmpty();//Calculate space function 	
		int GetMaxValue();//Calculate the maximum value of the composite 
		void Move(int dir);//Moving function 
		void Addnum(int n);//Add new number
		void LoadImgs();//Load picture
		void ReleaseImgs();//Release picture 
		void Draw();//Draw image 
		bool isGameOver();//Judge the end of the game 
		void RestartDate();//restart
		bool isClicInRectangle(int xClick,int yClick,int Rectangle_X,int Rectangle_Y,
                       int Rectangle_WIDTH,int Rectangle_HEIGHT);//Check whether the click is in the rectangular area
		void GameSave();//Game save 
		void LoadRecord();//File reading
		void AI();//AI automatically to 2048 
		
};

int main()
{
/*Initialization-------------------------------------------------------------------------------*/ 
	initgraph(WIDTH, HEIGHT);
	setcaption("2048Game");								//Set game title 
	setbkcolor(BG_COLOR); 
	setfont(25,0,"Blackbody");  
	setbkmode(TRANSPARENT);								//Text output is transparent 
	
	Game2048 MainObj; 
	MainObj.Addnum(2); 									//Add a new number at 2 random locations 
	MainObj.m_MaxValue = MainObj.GetMaxValue();			//Update maximum 
	MainObj.LoadImgs();									//Load picture 
	MainObj.LoadRecord();
	MainObj.Draw(); 									//Draw the initial interface 
/*-------------------------------------------------------------------------------initialization*/ 

/*Start the game-------------------------------------------------------------------------------*/ 	  
    for ( ; is_run(); delay_fps(60) )
	{	
		//Key detection	
		while(kbmsg())
		{
		    key_msg keyMsg = getkey();
		    if(keyMsg.msg == key_msg_down)
		    {
		        switch(keyMsg.key)
		        {
		            case 'A':case key_left  : MainObj.m_Dir = 0; break;//Left 
		            case 'W':case key_up  	: MainObj.m_Dir = 1; break;//upper 
		            case 'D':case key_right : MainObj.m_Dir = 2; break;//right 
		            case 'S':case key_down	: MainObj.m_Dir = 3; break;//lower 
		        }
		    }
		}   
		if(MainObj.m_Dir!=-1)
		{
		    //Graphic update           
		    bool flag = false;    //Move flag bit 
			int tempGrid[4][4];	 //Temporary array 
			int i,j; 
			for(i=0;i<4;i++)
				for(j=0;j<4;j++)
					tempGrid[i][j] = Grid[i][j];
			MainObj.Move(MainObj.m_Dir);
			//compare 
			for(i=0; i<4; i++)
				for(j=0; j<4; j++)
					if(Grid[i][j]!=tempGrid[i][j])	
					{
						flag = true;
						break;
					} 
			if(flag)	MainObj.Addnum(1);	
			MainObj.Draw();
			MainObj.m_Dir = -1;//Set Dir to invalid
		}
		
		//Mouse detection 
		bool RestartClickflag = false;
		while (mousemsg())
	    {
				mouse_msg msg = getmouse();
	    		if (msg.is_left() && msg.is_down()&&
					MainObj.isClicInRectangle(msg.x,msg.y,RESTART_X,RESTART_Y,RESTART_WIDTH,RESTART_HEIGHT))
	    		{
	                RestartClickflag = true;
	    		} 
	    }
	    	    
	    if(RestartClickflag)
	    {
	       MainObj.RestartDate();
	       MainObj.Draw();
	    }
	    
		if(MainObj.isGameOver())
		{
			putimage_withalpha(NULL,GameOverImg,120,200);
			break;
		}		
	} 
/*------------------------------------------------------------------------------Start the game-*/ 
	MainObj.ReleaseImgs();
	getch();
    closegraph();  
    return 0;	
} 

Game2048::Game2048()//initialization 
{
	this->m_Dir = -1;
	this->m_EmptyBlock = 16;
	this->m_MaxValue = 0;
	this->m_Score = 0;
	this->m_TopScore = 0;
} 

int Game2048::CalculateEmpty()//Calculate space function 
{
	int cnt = 0;
    for(int i=0; i<4; i++)
        for(int j=0; j<4; j++)
            if(Grid[i][j]==0)   cnt++;
    return cnt;
} 

int Game2048::GetMaxValue()//Calculate the maximum value of the composite 
{
	int temp=0;
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
		{
			if(Grid[i][j]>temp)	temp = Grid[i][j];
		}
	return temp;
}

void Game2048::Move(int dir)//Moving function 
{
	if(dir==-1)	return;
    int tx, ty;
    int t1x, t1y;
    int t2x, t2y;
    for(int i=0; i<4; i++)
    {
        tx = x0[dir] + i*secondOffset[dir][0];
        ty = y0[dir] + i*secondOffset[dir][1];

        t1x = tx;
        t1y = ty;
        
        t2x = tx + firstOffset[dir][0]; 
        t2y = ty + firstOffset[dir][1];
        for( ;t2x>=0&&t2x<=3&&t2y>=0&&t2y<=3; t2x+=firstOffset[dir][0],t2y+=firstOffset[dir][1])
        {
            if(Grid[t2y][t2x]!=0)
            {
                if(Grid[t1y][t1x]==0)
                {
                    Grid[t1y][t1x] = Grid[t2y][t2x];
                    Grid[t2y][t2x] = 0;
                }
                else if(Grid[t1y][t1x]==Grid[t2y][t2x])
                {
                	
                    Grid[t1y][t1x]++;//merge 
                    
                    this->m_MaxValue = this->GetMaxValue();
                    this->m_Score += (int)pow(2,Grid[t1y][t1x]); 
                    if(this->m_TopScore<this->m_Score)
                    {
                    	this->m_TopScore = this->m_Score;
                    	this->GameSave();
					} 
					                  
                    Grid[t2y][t2x] = 0;
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                   // moved = true;
                }
                else if(t1x+firstOffset[dir][0]!=t2x||t1y+firstOffset[dir][1]!=t2y)
                {
                    Grid[t1y+firstOffset[dir][1]][t1x+firstOffset[dir][0]] = Grid[t2y][t2x];
                    Grid[t2y][t2x] = 0;
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                    //cout << "Move Test" << endl;
                   // moved = true;
                }
                else
                {
                    t1x += firstOffset[dir][0];
                    t1y += firstOffset[dir][1];
                }
            }
            
        }
    }
    //return moved;
}

void Game2048::Addnum(int n)//Add new number
{
	while(n--)//Add n
	{
		this->m_EmptyBlock = this->CalculateEmpty();
		if(this->m_EmptyBlock<=0)	return;		
		int cnt = random(this->m_EmptyBlock)+1;//Randomly get a number within the number of spaces	
		int *p = &Grid[0][0]-1;//Record the first address of the matrix, because the following p will be returned when it is found++ 
		for(int i=0; i<4&&cnt; i++)
			for(int j=0; j<4&&cnt; j++)
			{
				if(Grid[i][j]==0 && cnt)//If there are spaces and cnt is valid	
				{
					cnt--;//Find one, scratch one
				}
                p++;//p points to the next one for judgment
			}
        //At the end of the loop, p points to the space we randomly specified before
		*p = (random(10)==0)?2:1;// The probability of 0.1 is 2 and the probability of 0.9 is 1
		//*p = (random(10)==0)+1;// It's OK to write like this
		this->m_EmptyBlock--;
	}
}

void Game2048::LoadImgs()//Load picture
{
	char imgAdress[40];
	for(int i=1,num=2; i<18; i++,num*=2)
	{	
		sprintf(imgAdress,"..\\image\\block_%d.png",num);
		BlockImgs[i] = newimage();;
      	getimage(BlockImgs[i], imgAdress);
		//cout << imgAdress << endl;
	}
	BlockImgs[0] = newimage();
	GameOverImg = newimage();
	GameLogoImg = newimage();
	RestartImg = newimage();
	ScoreBgImg = newimage();
	getimage(BlockImgs[0], "..\\image\\background.png");	
	getimage(GameOverImg,  "..\\image\\gameOver.png");
	getimage(GameLogoImg,  "..\\image\\gamelogo.png"); 
	getimage(RestartImg,   "..\\image\\restart.png");
	getimage(ScoreBgImg,   "..\\image\\scorebg.png"); 
} 

void Game2048::ReleaseImgs()//Release picture 
{
	for(int i=0; i<18; i++)
	{
		delimage(BlockImgs[i]);
	}
	delimage(GameOverImg);
	delimage(GameLogoImg);
	delimage(RestartImg);
	delimage(ScoreBgImg);	
}

void Game2048::Draw()//Draw image 
{
	cleardevice();
	putimage_withalpha(NULL, BlockImgs[0], BOARD_X,   BOARD_Y);//Grid background 
	putimage_withalpha(NULL, GameLogoImg,  LOGO_X,    LOGO_Y);//Logo diagram 
	putimage_withalpha(NULL, RestartImg,   RESTART_X, RESTART_Y);//Restart diagram 
	putimage_withalpha(NULL, ScoreBgImg,   SCOREGB_X, SCOREGB_Y);//Scoring background 
	
	//MaxValue = GetMaxValue();// Update maximum
	setcolor(TEXT_COLOR);
	xyprintf(MAXVALUE_X, MAXVALUE_Y, "%4d",(int)pow(2,this->m_MaxValue));//Maximum number of currently synthesized 
	xyprintf(SCORE_X,    SCORE_Y,    "%4d",this->m_Score);//Current score
	xyprintf(TOP_SCORE_X,TOP_SCORE_Y,"%4d",this->m_TopScore);//Highest score in history 

	for(int i=0;i<4;i++) 
	{	
		for(int j=0;j<4;j++)
		{
			int x = (j+1)*DEVIDE + j*GRID_WIDTH + BOARD_X ;
			int y = (i+1)*DEVIDE + i*GRID_WIDTH + BOARD_Y ;
			//cout << "(x,y) = " << "(" << x << ","<< y << ")" << endl;
			if(Grid[i][j]!=0)
			{
				putimage_withalpha(NULL,BlockImgs[Grid[i][j]],x,y);
			}
		}
	}
	
}

bool Game2048::isGameOver()//Judge the end of the game 
{
	this->m_EmptyBlock = this->CalculateEmpty();
	if(this->m_EmptyBlock>0)	return false;
	for(int i=0;i<4;i++)
	{
		int t1=0,t2=1;
		while(t2<=3)
		{
			if(Grid[i][t1]==Grid[i][t2] || Grid[t1][i]==Grid[t2][i])//  Horizontal and vertical 
			{
				return false;
			}
			else
			{
				t1++;
				t2++;
			}
		}
	}
	return true;
}

void Game2048::RestartDate()//restart
{
	for(int i=0; i<4; i++)
		for(int j=0; j<4; j++)
			Grid[i][j] = 0;
	Addnum(2);
	this->m_MaxValue = this->GetMaxValue();
	this->m_Dir = -1;
	this->m_EmptyBlock = 14;
	this->m_Score = 0;
	//this->m_TopScore = 0;	
} 
	
bool Game2048::isClicInRectangle(int xClick,int yClick,int Rectangle_X,int Rectangle_Y,int Rectangle_WIDTH,int Rectangle_HEIGHT)//Check whether the click is in the rectangular area
{
	if(xClick>Rectangle_X&&xClick<Rectangle_X+Rectangle_WIDTH && 
       yClick>Rectangle_Y&&yClick<Rectangle_Y+Rectangle_HEIGHT)
	return true;
	return false;	
}
 
void Game2048::GameSave()//Game save
{
	FILE* fp = fopen(RECORD_FILE_NAME,"w");
	if(fp==NULL)
	{
		this->RestartDate();
		fclose(fp);
		return;
	}
	fprintf(fp,"TopSocre:%d",this->m_TopScore);
	fclose(fp);	
} 

void Game2048::LoadRecord()//File reading
{
	FILE* fp = fopen(RECORD_FILE_NAME,"r");
	if(fp==NULL)
	{
		this->RestartDate();
		fclose(fp);
		return;
	}
	fscanf(fp,"TopSocre:%d",&this->m_TopScore);
	fclose(fp);
}

Optimization is over here!!!
Thank you for seeing here.
This is already a relatively complete small project.
Next is advanced:
1. Slider effect
2. AI automatically to 2048

Topics: C++ Game Development