Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Let Me Out - A Simple Board Game

4.75/5 (10 votes)
22 Mar 2010CPOL5 min read 1   1.6K  
A simple board game

Introduction

game

"Let Me Out" is a board game with the goal to move the magenta block, the biggest one, from the middle of the top to the middle of the bottom. You need to move other blocks and make room for the big guy. The game has 3 levels, beginner, intermediate, and expert. You can change the levels from the menu.

The amazing thing about this software is that it only has 2 main classes, Block and Board. And the code is very simple. So, I think it is good for beginners to learn Object-Oriented programming. Or, hopefully fall in love with game developing. :) Another interesting thing about it is the UI design. I saw a lot of board games in which the user needs to either drag-and-drop or use several steps to move an item. That's because to let an item move, the user needs to tell it to move and where to move. This game uses an arrow to show the direction that a block will be moved, which changes with the mouse movement. Since the user already has the direction, all she/he needs to do is just one click.

Before you continue reading, I suggest that you play the game and have fun. You may want to change the level to Beginner to get familiar with the user interface.

Background

This is a Chinese game with a thousand years of history. It's about a great battle. The guy who lost the battle tried his best to escape, which was almost impossible. But he did it. The 2x2 square, the magenta one, represents the guy who tried to escape. If you have played the game, you will understand why it is "almost impossible".

Using the Code

Block class inherits from Button class, so that it can be clicked. Its style is changed to flat, so that it looks more like a block. Since we will add blocks to board in code, we decorate the class with the attribute ToolboxItem(false), which prevents it from showing in the Visual Studio Toolbox window.

C#
[ToolboxItem(false)]
public class Block : Button {
   ...
}

Block class is responsible for remembering its size and position. When mouse is over it, an arrow will be shown. And the arrow will change direction with the mouse movement. We will talk more about this in UI Design section.

Board is divided to 4 x 5 squares as shown below:

Board

Board class is responsible for remembering the status of the squares, which can be either occupied or free. It is also responsible for moving blocks. To move a block, the Board class checks whether the Block can be moved. Board knows that because it has the record of each square. By asking Block class, it knows the position, and size of the Block, and the direct to move, so that it can decide whether the Block can be moved. If yes, the Block updates its position, and the Board updates its status. The following is the code to move a block to left. It's a method in Board class:

C#
void MoveLeft(Block block) {
    int oldX = block.X;
    if (oldX == 0) return;
    int newX = oldX - 1;

    //Check whether we can move the block
    for (int i = block.Y; i < block.Y + block.YSpan; ++i) {
        if (_occupied[newX, i]) return;
    }

    //Move it
    block.MoveBlock(newX, block.Y);

    //Change board status
    for (int i = block.Y; i < block.Y + block.YSpan; ++i) {
        _occupied[newX + block.XSpan, i] = false;
        _occupied[newX, i] = true;
    }

    //Add step
    _steps++;
}

The first 2 lines check whether block is already in the left most. If yes, it cannot be moved left further, and program returns. "newX" is the x position of the block if it has been moved. To understand the codes after, we need to make clear the properties of the Block class. Let's use an example. In the beginning of the game, the big guy is at (1, 0). In this case, block.X = 1, block.Y=0, block.YSpan=2, which is how many rows the block spans, and block.XSpan=2, which is how many columns the block spans.

In the first for loop, we check whether the squares left to the block are all free. If any of the squares is occupied, the block cannot be moved, and the program returns immediately. For the big guy example, If we want to move it left, we need to check the status of (0,0) and (1,0).

The second for loop changes the board status. If the big guy moved from (1,0) to (0,0), we need to change the status of (0,0), (1,0) to be occupied, and (2, 0), (2,1) to be free.

UI Design

In this program, we move the blocks by using an arrow to show directions and a click to trigger the movement. To achieve this, we divided the block into 4 regions, like the one below:

UI Design

When mouse is moved to the left region, the image for the block is changed to a Left arrow. Other regions all have corresponding images, and behave the same way.

To get the functions for each region needs some knowledge of geometry. Basically, you need to know the functions for 2 lines. One of them passes the point (0,0) and (w, h), so the line is y=(h/w)*x. The other one passes (w, 0) and (0, h), so the line is y=h-(h/w)*x.

The code is below, it is in Block class, for MouseMove event:

C#
    void Block_MouseMove(object sender, MouseEventArgs e) {
        int x = e.X;
        int y = e.Y;
        int h = this.Height;
        int w = this.Width;
        double k = (double)h / (double)w;
        int newIndex = -2;
        int kx = (int)(k * x + 0.5);
        if (y < h - kx) {
            if (y < kx) {
                newIndex = Direction_Up;
            } else if (y > kx) {
                newIndex = Direction_Left;
            }
        } else if (y > h - kx) {
            if (y < kx) {
                newIndex = Direction_Right;
            } else if (y > kx) {
                newIndex = Direction_Down;
            }
        }
        if (newIndex >= 0 && _direction != newIndex) {
            this.Image = Button_Images[newIndex];
            _direction = newIndex;
        }
    }
}

Button_Images is a static array of 4 images. The images come from project resource.

What's Next

This program can be improved by doing the following:

  • Save/Load Program
  • Game Solver

Save and load can be done by saving each block's position into an XML file. The total steps that the user has moved should also be saved. Board's status doesn't need to be saved because when loading, we can re-establish the board status.

Game Solver is possible because in each game state, there are not many possible moves. I'd like to write a game solver if I find that people like this game.

History

  • Version 1.0 on March 14, 2010
  • Added more documents on March 21, 2010

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)