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:
private void Shuffle()
{
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:
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
:
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:
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:
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:
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