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

Sliding Puzzle in C#

5.00/5 (2 votes)
21 Oct 2019CPOL3 min read 18K   714  
An example of a simple sliding puzzle game in C#

Introduction

While I was on the job, I came up with the idea of creating a simple Sliding Puzzle game in C#. I'll present you the way in which I did it.

Using the Code

Before I started writing the code, I added already cropped images in Resources. I've also created a common MouseDown event for each PictureBox on the Form called "Fields".

The first thing I wrote was a simple Shuffle() procedure in order to assign image to controls randomly after the Form is loaded.

I've made sure that every image is in the exact same place as where the completed image would be, altogether with one null value which would present an empty square on the box. Before I assigned images to picture boxes, I wrote a tag for each image which was named by the control it should belong to in the Form. For example, the image that belongs to control called pictureBox1 would have a Tag with number "1". The image that belongs to pictureBox3 would have a Tag with number "3", etc. That way, I would get a control over the images making it easier to find a winner later in the game:

C#
private void Shuffle()
{
//Add cropped images in exact same order as they would look once the puzzle is completed 
//(from first to last) 
 List<Bitmap> pictures = new List<Bitmap>()
 {
  Properties.Resources.row_1_col_2,Properties.Resources.row_1_col_3,null,
  Properties.Resources.row_2_col_1,Properties.Resources.row_2_col_2,
                                   Properties.Resources.row_2_col_3,
  Properties.Resources.row_3_col_1,Properties.Resources.row_3_col_2,
                                   Properties.Resources.row_3_col_3
 };

 int k = 1;

 foreach(Bitmap pics in pictures)
 {
  if (pics == null) continue;
  pics.Tag = k.ToString();
  k++;
 }

 Random r = new Random();
 int pick;
             
 foreach(Control c in this.Controls)
 {
  pick = r.Next(pictures.Count);
  PictureBox p = (PictureBox)c;
  p.BackgroundImage = pictures[pick];
  pictures.RemoveAt(pick);
 }            
}

The next thing to do was to make the logic for the image movement. In Sliding Puzzle game, we can only move a figure in 4 directions - up, down, left and right, and each move has to be exactly above\below\left or right from the clicked image. If you pay attention to the Location property of the clicked control, you'll notice that the control above has exactly the same X coordinate, while the Y is higher for the height of control we clicked. The one below also has the same X coordinate while the Y is lower for the height of the square. Left and right from chosen square has the same Y coordinates as they're on the same height, but their X coordinates are different plus\minus of the width of the chosen control. Therefore, we can make 4 simple functions which will return true if there is space available to move at, and an additional one called ValidMove() which will check if there is any free move to play:

C#
private bool LookUp(PictureBox s,PictureBox f)
{
 return s.Location.X == f.Location.X && s.Location.Y == f.Location.Y - f.Height; 
}
private bool LookDown(PictureBox s,PictureBox f)
{
 return s.Location.X == f.Location.X && s.Location.Y == f.Location.Y + f.Height;
}
private bool LookLeft(PictureBox s,PictureBox f)
{
 return s.Location.X == f.Location.X - f.Width && s.Location.Y == f.Location.Y; 
}
private bool LookRight(PictureBox s,PictureBox f)
{
 return s.Location.X == f.Location.X + f.Width && s.Location.Y == f.Location.Y; 
}
private bool ValidMove(PictureBox s,PictureBox f)
{
 return LookUp(s, f) || LookDown(s, f) || LookLeft(s, f) || LookRight(s, f); 
}

Once we're done with this, we should find a PictureBox with an empty BackgroundImage:

C#
private PictureBox FindEmptyField()
{
 PictureBox pb = null, found = null; 

 foreach(Control c in this.Controls)
 {
  pb = (PictureBox)c; 
  if (pb.BackgroundImage == null)
  {
   found = pb;
   break; 
  }                
 }
 return found; 
}

After this, we're moving to our click event. First, I'll cast an object to PictureBox, and then I'll add a variable which will take an empty control so they can be compared. The code below it is under conditional check if the game is still active (not over), and if it's not, we're using a bool variable called match to check if there are any available moves in the area, and if there are, we are going to swap the images of controls and check if the player won:

C#
private void Fields(object sender, MouseEventArgs e)
{
 PictureBox field = (PictureBox)sender;

 PictureBox empty = FindEmptyField();

 if (!over)
 {
  bool match = ValidMove(empty, field);

  if (match)
  {
   Swap(empty, field);
   CheckForWinner();
  }
 }
}

Swapping images:

C#
private void Swap(PictureBox s,PictureBox f)
{
 Image save = s.BackgroundImage;
 s.BackgroundImage = f.BackgroundImage;
 f.BackgroundImage = save;
}

The last thing to do was to check if user matched all the images in the proper place. In the beginning, we've assigned a tag to each image and the tag has the number that belongs to the number of the control where the image should be. This has been a slightly tricky part as I had a logical problem with an empty square. If all the images were in place, and the empty square was between two images instead of the first control on the left of the form, it would be skipped and the winner would be found despite the fact that the original image wasn't complete. I solved it by decrementing count value so the check up would be meaningless unless all the images are in place and the empty control is in the top left corner:

C#
private void CheckForWinner()
{
 PictureBox p = null;
 int count = 8; 

 foreach (Control c in this.Controls)
 {
  p = (PictureBox)c; 

  if (p.BackgroundImage == null)
  {
   count--;
   continue; 
  }
  string tag = p.BackgroundImage.Tag.ToString();
  if (tag != count.ToString()) return;
  count--;
 }

 MessageBox.Show("Completed!");
 over = true;          
}

Points of Interest

Finding out that the elements of certain List types can have a Tag property of their own helped me in another project.

I'm an amateur programmer, writing mostly trivial projects for fun. If you find something that might be unacceptable in serious programming, you're welcome to expose it.

I've made a couple more simple WinForm games I plan to upload soon.

History

  • 21st October, 2019: Initial version
  • 22nd October, 2019: Updated

License

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