Application of the method of introduction to C# zero foundation -- push box game -- code analysis

Keywords: C#

preface

This article belongs to the C# zero foundation introduction hundred knowledge hundred examples series. This series of articles aims to provide a systematic learning path for children's shoes learning C# language. This series of articles will be presented in the form of [knowledge points] [exercises]. If you have any questions, you can find me through comments, private letters, etc. I will answer your questions one-on-one.


Series contents:
"C# zero Basics: a hundred cases of knowledge" Directory article portal

Effect display

1, Level design

First, we need to determine the meaning of the array value and its corresponding representation:

numerical valuemeaningSymbol
0Space'' empty string
1wall ■ solid square
2case□ hollow square
3VillainHuman Chinese characters
4target☆ hollow five pointed star
5complete★ solid five pointed star

This is the basic definition. In the article using array instances: Introduction to C# zero Basics (30) array application - push box game - map initialization , I won't repeat it here.

2, Map initialization

Main logic:

  1. Define two two two-dimensional arrays to represent the level map, map with player (3), and targeMap without player (3)
  2. Traverse the map, convert the map meaning corresponding to its value into the corresponding graph, and print it
/// <summary>
///Push box
///Map value meaning: 0: open space, 1: wall, 2: box, 3: people, 4: goal, 5: complete
/// </summary>
class Sokoban
{        
	static void Main(string[] args)
    {
       // Level initialization
       InitMap();
       // Refresh map
	   UpdateMap();

	   Console.ReadLine();
	}

    // This level map
    static int[,] map;
    // Target map
    static int[,] targeMap;

    //Player's initial coordinates
    static int y = 4, x = 5;
    /// <summary>
    ///Level initialization - level array, player position
    /// </summary>
    static void InitMap()
    {
        y = 4;
        x = 5;

        map = new int[8, 9]
        {
                {0,0,0,1,1,1,0,0,0},
                {0,0,0,1,4,1,0,0,0},
                {0,0,0,1,0,1,1,1,1},
                {0,1,1,1,2,0,2,4,1},
                {0,1,4,0,2,3,1,1,1},
                {0,1,1,1,1,2,1,0,0},
                {0,0,0,0,1,4,1,0,0},
                {0,0,0,0,1,1,1,0,0}
        };

        targeMap = new int[8, 9]
        {
                {0,0,0,1,1,1,0,0,0},
                {0,0,0,1,4,1,0,0,0},
                {0,0,0,1,0,1,1,1,1},
                {0,1,1,1,2,0,2,4,1},
                {0,1,4,0,2,0,1,1,1},
                {0,1,1,1,1,2,1,0,0},
                {0,0,0,0,1,4,1,0,0},
                {0,0,0,0,1,1,1,0,0}
        };
    }

	/// <summary>
    ///Refresh map display
    /// </summary>
    static void UpdateMap()
    {
        // Clear screen
        Console.Clear();
        Console.WriteLine("-------- Sokoban --------");
        Console.WriteLine();

        // Traversal array -- print new map
        for (int i = 0; i < map.GetLength(0); i++)
        {
            for (int j = 0; j < map.GetLength(1); j++)
            {
                if (map[i, j] == 0)
                {
                    Console.Write(" ");
                }
                if (map[i, j] == 1)
                {
                    Console.Write("■");
                }
                if (map[i, j] == 2)
                {
                    Console.Write("□");
                }
                if (map[i, j] == 3)
                {
                    Console.Write("people![Please add a picture description](https://img-blog.csdnimg.cn/6a07bce3885340c6aa0b038e46858af9.gif)
");
                }
                if (map[i, j] == 4)
                {
                    Console.Write("☆");
                }
                if (map[i, j] == 5)
                {
                    Console.Write("★");
                }
            }
            Console.WriteLine();
        }

        Console.WriteLine();
        Console.WriteLine("Press ↑←↓→ Control player movement");
        Console.WriteLine("input G restart");
    }
}

3, Player movement

Implementation logic:

  1. The user controls the player's movement through "↑←↓→", so it is necessary to detect the keyboard input in the cycle, and then process the corresponding keyboard input logic
  2. You need to pay attention to the direction of the player's movement and the calculation logic of the array. For example, moving up is y-1; it needs to be understood here
  3. Perform array out of bounds verification, and do not process unsatisfied values to avoid program error.

Simulate player movement logic:

/// <summary>
///Moving function
/// </summary>
///< param name = "direction" > moving direction < / param >
static void Move(ConsoleKey direction)
{
    // The player moves the position this time
    int ox = 0, oy = 0;

    // Offset calculation based on direction
    switch (direction)
    {
        case ConsoleKey.UpArrow:
            oy--;
            break;
        case ConsoleKey.DownArrow:
            oy++;
            break;
        case ConsoleKey.LeftArrow:
            ox--;
            break;
        case ConsoleKey.RightArrow:
            ox++;
            break;
        default:
            return;
    }

	// Player moves -- restore map
    map[y, x] = targeMap[y, x];
    y += oy;
    x += ox;
    // Make cross-border judgment
    if (y < map.GetLength(0) && y > 0 && x < map.GetLength(1) && x > 0)
    {
        map[y, x] = 3;
    }
}

Modify the Main function as follows to run the input to control the player's movement

static void Main(string[] args)
{
    // Level initialization
    InitMap();

    // Loop - > execute once after waiting for user input
    while (true)
    {
        // Refresh map
        UpdateMap();
        // Receive user input operation
        ConsoleKeyInfo keyInfo = Console.ReadKey();
        // Protagonist movement
        Move(keyInfo.Key);
    }
    
    Console.ReadLine();
}

Movement effect:

4, Players push boxes

There are two steps for players to push the box: * * 1. The player moves forward one step; 2. The box moves forward one step. * * these two steps have various conditions, such as the wall in front of the box can't move, the wall in front of the box can't move, and so on

The movement after sorting is as follows: (for convenience of description, A1 is taken as the next target point and A2 as the next two target points)

  1. A1 is an open space or target -- > can be moved
  2. A1 is a wall -- > cannot be moved
  3. A1 is the box or the box to the target point -- > can be moved -- > at this time, we need to see whether the box can be pushed, that is, A2 point
    -A2 is an open space or target point -- > can be moved
    -A2 is the wall, box or box reaching the target -- > cannot be moved

Note: when you can move, you also need to consider whether the current player's position is the target position. If it is the target position, you need to restore the target. If not, you can restore it to a space.

The mobile code is modified as follows:

/// <summary>
///Moving function
/// </summary>
///< param name = "direction" > moving direction < / param >
static void Move(ConsoleKey direction)
{
    // x, y of the next position the player wants to move
    int nx1 = x, ny1 = y;
    // Move x, y of the next two positions in the same direction
    int nx2 = x, ny2 = y;
    // The player moves the position this time
    int ox = 0, oy = 0;

    // Offset calculation based on direction
    switch (direction)
    {
        case ConsoleKey.UpArrow:
            ny1 -= 1;
            ny2 -= 2;
            oy--;
            break;
        case ConsoleKey.DownArrow:
            ny1 += 1;
            ny2 += 2;
            oy++;
            break;
        case ConsoleKey.LeftArrow:
            nx1 -= 1;
            nx2 -= 2;
            ox--;
            break;
        case ConsoleKey.RightArrow:
            nx1 += 1;
            nx2 += 2;
            ox++;
            break;
        default:
            return;
    }


    // The player's next coordinate is the open space to move
    if (map[ny1, nx1] == 0 || map[ny1, nx1] == 4)
    {
        map[ny1, nx1] = 3;
        map[y, x] = 0;
        // The player's current coordinates are the coordinates of the target point -- restore it back
        if (targeMap[y, x] == 4)
            map[y, x] = 4;
        else
            map[y, x] = 0;
        y += oy;
        x += ox;
    }
    // The next coordinate of the player is the wall and will not be processed
    else if (map[ny1, nx1] == 1)
    {
        return;
    }
    // The player's next coordinate is the box that does not reach the target or the box that reaches the target
    else if (map[ny1, nx1] == 2 || map[ny1, nx1] == 5)
    {           
        // The next target of the box is the open space
        if (map[ny2, nx2] == 0)
        {
            map[ny2, nx2] = 2;
            map[ny1, nx1] = 3;
            // The player's current coordinates are the coordinates of the target point -- restore it back
            if (targeMap[y, x] == 4)
                map[y, x] = 4;
            else
                map[y, x] = 0;
            y += oy;
            x += ox;
        }
        // The next target of the box is the target point
        else if (map[ny2, nx2] == 4)
        {
            map[ny2, nx2] = 5;
            map[ny1, nx1] = 3;
            // The player's current coordinates are the coordinates of the target point -- restore it back
            if (targeMap[y, x] == 4)
                map[y, x] = 4;
            else
                map[y, x] = 0;
            y += oy;
            x += ox;
        }
        else
        {
            // The next target of the box is a wall or an empty box or a box reaching the target -- no processing
            //if (map[ny2, nx2] == 1 || map[ny2, nx2] == 2 || map[ny2, nx2] == 5)
        }
    }
}

5, Game over

The logic of judging the end of the game is relatively simple. When there are no boxes and target points in the map, the game is considered successful. The code is implemented as follows:

/// <summary>
///End of the game - there are no boxes and target points in the map
/// </summary>
/// <returns></returns>
static bool IsGameOver()
{
    for (int i = 0; i < map.GetLength(0); i++)
    {
        for (int j = 0; j < map.GetLength(1); j++)
        {
            if (map[i, j] == 2 || map[i, j] == 4)
            {
                return false;
            }
        }
    }
    return true;
}

After the game is over, you can reopen or go to the next level according to the user's operation (this paper is not implemented, in fact, the logic can't, just set the map to the next level)

Modify the Main function as follows:

static void Main(string[] args)
{
    // Level initialization
    InitMap();


    // Loop - > execute once after waiting for user input
    while (true)
    {
        // Refresh map
        UpdateMap();

        // Game success
        if (IsGameOver())
        {
            Console.WriteLine("======== Congratulations ========");
            // Console.WriteLine("press any key to play again...);
            // todo... Next level
            // Console.ReadKey();
            //break;
        }

        // Receive user input operation
        ConsoleKeyInfo keyInfo = Console.ReadKey();

        // Play again
        if (keyInfo.Key == ConsoleKey.G)
        {
            // Level initialization
            InitMap();
            // Refresh map
            UpdateMap();
        }
        else
        {
            // Protagonist movement
            Move(keyInfo.Key);
        }
    }

    Console.ReadLine();
}

6, Source sharing

The above code can form a completed push box game. The combined code:

/// <summary>
///Push box
/// </summary>
class Sokoban
{       
    /// <summary>
    ///This level map
    ///Value interpretation - > 0: space; wall: 1 ■; box: 2 □; person: 3; goal: 4 ☆; completion: 5 ★
    /// </summary>
    static int[,] map;
    // Target map
    static int[,] targeMap;

    //Player's initial coordinates
    static int y = 4, x = 5;


    static void Main(string[] args)
    {
        // Level initialization
        InitMap();


        // Loop - > execute once after waiting for user input
        while (true)
        {
            // Refresh map
            UpdateMap();

            // Game success
            if (IsGameOver())
            {
                Console.WriteLine("======== Congratulations ========");
                // Console.WriteLine("press any key to play again...);
                // todo... Next level
                // Console.ReadKey();

                break;
            }

            // Receive user input operation
            ConsoleKeyInfo keyInfo = Console.ReadKey();

            // Play again
            if (keyInfo.Key == ConsoleKey.G)
            {
                // Level initialization
                InitMap();
                // Refresh map
                UpdateMap();
            }
            else
            {
                // Protagonist movement
                Move(keyInfo.Key);
            }
        }


        Console.ReadLine();
    }

    /// <summary>
    ///Level initialization - level array, player position
    /// </summary>
    static void InitMap()
    {
        y = 4;
        x = 5;

        map = new int[8, 9]
        {
                {0,0,0,1,1,1,0,0,0},
                {0,0,0,1,4,1,0,0,0},
                {0,0,0,1,0,1,1,1,1},
                {0,1,1,1,2,0,2,4,1},
                {0,1,4,0,2,3,1,1,1},
                {0,1,1,1,1,2,1,0,0},
                {0,0,0,0,1,4,1,0,0},
                {0,0,0,0,1,1,1,0,0}
        };

        targeMap = new int[8, 9]
        {
                {0,0,0,1,1,1,0,0,0},
                {0,0,0,1,4,1,0,0,0},
                {0,0,0,1,0,1,1,1,1},
                {0,1,1,1,2,0,2,4,1},
                {0,1,4,0,2,0,1,1,1},
                {0,1,1,1,1,2,1,0,0},
                {0,0,0,0,1,4,1,0,0},
                {0,0,0,0,1,1,1,0,0}
        };
    }

    /// <summary>
    ///Refresh map display
    /// </summary>
    static void UpdateMap()
    {
        // Clear screen
        Console.Clear();
        Console.WriteLine("-------- Sokoban --------");
        Console.WriteLine();

        //Print new map
        for (int i = 0; i < map.GetLength(0); i++)
        {
            for (int j = 0; j < map.GetLength(1); j++)
            {
                if (map[i, j] == 0)
                {
                    Console.Write("  ");
                }
                if (map[i, j] == 1)
                {
                    Console.Write("■");
                }
                if (map[i, j] == 2)
                {
                    Console.Write("□");
                }
                if (map[i, j] == 3)
                {
                    Console.Write("people");
                }
                if (map[i, j] == 4)
                {
                    Console.Write("☆");
                }
                if (map[i, j] == 5)
                {
                    Console.Write("★");
                }
            }
            Console.WriteLine();
        }

        Console.WriteLine();
        Console.WriteLine("Press ↑←↓→ Control player movement");
        Console.WriteLine("input G restart");
    }


    /// <summary>
    ///Moving function
    /// </summary>
    ///< param name = "direction" > moving direction < / param >
    static void Move(ConsoleKey direction)
    {
        // x, y of the next position the player wants to move
        int nx1 = x, ny1 = y;
        // Move x, y of the next two positions in the same direction
        int nx2 = x, ny2 = y;
        // The player moves the position this time
        int ox = 0, oy = 0;

        // Offset calculation based on direction
        switch (direction)
        {
            case ConsoleKey.UpArrow:
                ny1 -= 1;
                ny2 -= 2;
                oy--;
                break;
            case ConsoleKey.DownArrow:
                ny1 += 1;
                ny2 += 2;
                oy++;
                break;
            case ConsoleKey.LeftArrow:
                nx1 -= 1;
                nx2 -= 2;
                ox--;
                break;
            case ConsoleKey.RightArrow:
                nx1 += 1;
                nx2 += 2;
                ox++;
                break;
            default:
                return;
        }


        // The player's next coordinate is the open space to move
        if (map[ny1, nx1] == 0 || map[ny1, nx1] == 4)
        {
            map[ny1, nx1] = 3;
            map[y, x] = 0;
            // The player's current coordinates are the coordinates of the target point -- restore it back
            if (targeMap[y, x] == 4)
                map[y, x] = 4;
            else
                map[y, x] = 0;
            y += oy;
            x += ox;
        }
        // The next coordinate of the player is the wall and will not be processed
        else if (map[ny1, nx1] == 1)
        {
            return;
        }
        // The player's next coordinate is the box that does not reach the target or the box that reaches the target
        else if (map[ny1, nx1] == 2 || map[ny1, nx1] == 5)
        {
            // The next target of the box is the open space
            if (map[ny2, nx2] == 0)
            {
                map[ny2, nx2] = 2;
                map[ny1, nx1] = 3;
                // The player's current coordinates are the coordinates of the target point -- restore it back
                if (targeMap[y, x] == 4)
                    map[y, x] = 4;
                else
                    map[y, x] = 0;
                y += oy;
                x += ox;
            }
            // The next target of the box is the target point
            else if (map[ny2, nx2] == 4)
            {
                map[ny2, nx2] = 5;
                map[ny1, nx1] = 3;
                // The player's current coordinates are the coordinates of the target point -- restore it back
                if (targeMap[y, x] == 4)
                    map[y, x] = 4;
                else
                    map[y, x] = 0;
                y += oy;
                x += ox;
            }
            else
            {
                // The next target of the box is a wall or an empty box or a box reaching the target -- no processing
                //if (map[ny2, nx2] == 1 || map[ny2, nx2] == 2 || map[ny2, nx2] == 5)
            }
        }
    }

    /// <summary>
    ///End of the game - there are no boxes and target points in the map
    /// </summary>
    /// <returns></returns>
    static bool IsGameOver()
    {
        for (int i = 0; i < map.GetLength(0); i++)
        {
            for (int j = 0; j < map.GetLength(1); j++)
            {
                if (map[i, j] == 2 || map[i, j] == 4)
                {
                    return false;
                }
            }
        }
        return true;
    }
}

Posted by jayant on Sun, 05 Dec 2021 13:06:28 -0800