2048 - third draft of specific function realization [add new number]

Keywords: C++ Game Development

2048 game - Implementation of specific functions third draft [add new number]

(updating...)
Reference blog: https://blog.csdn.net/qq_39151563/article/details/104283217
Because it is too long to put in one article, it is divided into several parts.
(there may be 10 articles =. =)
Previous articles:
2048 game series - Overview
2048 game series - first draft of function module [matrix operation]
2048 game series - second draft of function module [keyboard input]
This section describes how to generate new numbers based on conditions.

1, Determine whether to add

  1. Before we add a new number, we need to judge whether to add it. For example, when the following matrix grid slides upward, there is no change before and after the move, so it is not necessary to add a new number:

    int grid[4][4] = {
    	{0,1,2,3},
    	{0,0,0,0},
    	{0,0,0,0},
    	{0,0,0,0}
    };
    
  2. How to judge whether the movement is effective provides two ideas:
    2.1 compare each element before and after grid movement: if each element is the same, it can be judged that the movement is invalid. On the contrary, as long as one element is different, the movement is valid. (this logic is implemented in the main function)

    2.2. Judge during the movement: if the number is moved, the movement is valid; otherwise, the movement is invalid. (this logic is implemented in the Move() function)

2, Add new number logic

After knowing that the movement is valid, the logic of adding a new number is as follows:

2.1 find a vacancy

  1. If EmptyBlock = 0, there is no vacancy
  2. Using the random function of EGE
random(n);//Returns an integer from 0 to n-1

Suppose the grid is as follows:

int grid[4][4] = {
	{0,1,2,3},
	{0,0,0,0},
	{0,0,0,0},
	{0,0,0,0}
};

EmptyBlock should be 16-3 = 13, so we have 13 positions to choose from. random(EmptyBlock)+1 just returns an integer from 1 to 13 randomly

2.2 code

(little knowledge: multidimensional arrays are mapped into one dimension in computer storage)

//Add new number
//There are many debugging comments that can be deleted
void addnum(int n)
{	
	while(n--)//Add n
	{
		EmptyBlock = CalculateEmpty();
		//cout << "addnum Test" << endl;
		if(EmptyBlock<=0)	
		{
			//cout << "addnum EmptyBlock = " << EmptyBlock << endl; 
			//cout << "addnum Test1" << endl;
			return;	
		}
		int cnt = random(EmptyBlock)+1;//Randomly get a number within the number of spaces	
		//Cout < < find the first "< < CNT < < space" < < endl; 
		//cout << "cnt = " << cnt << endl;

		int *p = &grid[0][0]-1;//Record the first address of the matrix, because the following p will be returned when it is found++ 
		//cout << "n = "<< n <<endl;
		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	
				{
					//cout << "cnt = " << cnt << endl;
					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
		//Cout < < insert successfully < < endl; 
		//*p = (random(10)==0)+1;// It's OK to write like this
		EmptyBlock--;
	}
}

Test code:

Test results (2 numbers were successfully added at random):

3, Integrate into the code in the previous article

3.1 minor changes to procedures

  1. Matrix set to 0

    int grid[4][4] = {0};//Matrix set to 0
    
  2. ShowInfo() print information function

    //Display information - centralize some information to be printed
    void ShowInfo()
    {
    	cout << "dir = " << dir << endl;
        cout<< "EmptyBlock = " << CalculateEmpty() << endl;
        PrintGrid();
    } 
    
  3. Judge whether the movement is effective (the first idea used)

     			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];	
    			//cout << "tempGrid[4][4] = " << endl;	
    //			for(i=0; i<4; i++)
    //		    {
    //		        for(j=0; j<4; j++)
    //		            cout << tempGrid[i][j] << " ";
    //		        cout << endl;
    //		    }
    //		    cout << endl;	
    			Move(dir);//move
    			//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)	
                {
                    addnum(1);
                	cout << "Effective movement" << endl;
                }
    			//else 	 Cout < < invalid move < < endl;					 
    			ShowInfo(); 
    			dir = -1;//Set dir to invalid, otherwise the console will always refresh 
    

3.2 all codes

#include <iostream>
#include "graphics.h"
using namespace std;
//Test matrix 
int grid[4][4] = {0
//    {1,2,3,4},
//    {4,5,6,7},
//    {7,8,9,10},
//    {1,1,1,0}
};

int EmptyBlock = 4 ;	//Number of spaces 
int dir = -1;				// 0-left, 1-up, 2-right, 3-down 
//Print function 
void PrintGrid()
{
    for(int i=0; i<4; i++)
    {
        for(int j=0; j<4; j++)
            cout << grid[i][j] << " ";
        cout << endl;
    }
    cout << endl;
}
//Calculate space function 
int CalculateEmpty()
{
    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;
}


//display information
void ShowInfo()
{
	cout << "dir = " << dir << endl;
    cout<< "EmptyBlock = " << CalculateEmpty() << endl;
	cout << "grid[4][4] = " << endl;
	PrintGrid();
} 

//Moving 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}};
void Move(int dir)
{
	//bool moved = false; 
	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];
       //cout << "(" << tx << ", " << ty << ")" << endl;
       
        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;
                   // moved = true;
                }
                else if(grid[t1y][t1x]==grid[t2y][t2x])
                {
                    grid[t1y][t1x]++;
                    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;
}

//Add new number
void addnum(int n)
{	
	while(n--)//Add n
	{
		EmptyBlock = CalculateEmpty();
		if(EmptyBlock<=0)	
		{
			//cout << "addnum EmptyBlock = " << EmptyBlock << endl; 
			//cout << "addnum Test1" << endl;
			return;	
		}
		int cnt = random(EmptyBlock)+1;//Randomly get a number within the number of spaces	
		//Cout < < find the first "< < CNT < < space" < < endl; 
		//cout << "cnt = " << cnt << endl;

		int *p = &grid[0][0]-1;//Record the first address of the matrix, because the following p will be returned when it is found++ 
		//cout << "n = "<< n <<endl;
		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	
				{
					//cout << "cnt = " << cnt << endl;
					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
		//Cout < < insert successfully < < endl; 
		//*p = (random(10)==0)+1;// It's OK to write like this
		EmptyBlock--;
	}
}
  
int main()
{
	initgraph(200, 200);
	addnum(2); //Add a new number at 2 random locations 
	ShowInfo();   
    for ( ; is_run(); delay_fps(60) )
	{
		cleardevice();
		
		// todo: logical update (data update)
		//Key detection	
		while(kbmsg())
		{
		    key_msg keyMsg = getkey();
		    if(keyMsg.msg == key_msg_down)
		    {
		        switch(keyMsg.key)
		        {
		            case 'A':case key_left  : dir = 0; break;//Left 
		            case 'W':case key_up  	: dir = 1; break;//upper 
		            case 'D':case key_right : dir = 2; break;//right 
		            case 'S':case key_down	: dir = 3; break;//lower 
		        }
		    }
		}   
		// todo: drawing updates
		if(dir!=-1)
		{
			system("cls");
			switch(dir)
		        {
		            case  0: cout << "Pressed A/Left key" << endl; break;//Left 
		            case  1: cout << "Pressed W/Up key" << endl; break;//upper 
		            case  2: cout << "Pressed D/Right click" << endl; break;//right 
		            case  3: cout << "Pressed S/Down key" << endl; break;//lower 
		        }
		        
		        
		    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];
			{ 
			//cout << "tempGrid[4][4] = " << endl;	
//			for(i=0; i<4; i++)
//		    {
//		        for(j=0; j<4; j++)
//		            cout << tempGrid[i][j] << " ";
//		        cout << endl;
//		    }
//		    cout << endl;
			}	
		
			Move(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) cout < < move:<< 	 flag << endl;
			if(flag)
			{
				cout << "Effective movement" << endl;
				addnum(1);
			}	
			else	cout << "Invalid move" << endl;						 
			ShowInfo(); 
			dir = -1;//Set dir to invalid, otherwise the console will always refresh 
		}		
	} 
	getch();
    closegraph();  
    return 0;
}

4, Operation effect display

Conclusion:

Here, the "kernel" part of the game is almost over. It looks like a 2048, but it's too ugly=
The function implementation is summarized below:

  1. Mobile logic
  2. Keyboard input control
  3. Add new number

The next draft is to write the optimization part (the following tentative points):

  1. Replace numbers with graphics
  2. Add scoring
  3. Class encapsulation

Posted by jmut on Fri, 12 Nov 2021 03:28:24 -0800