Introduction
I'll present to you the way in which I created a memory game.
Background
I've found a memory game written in C# by Michael Hicks on YouTube. The game was based on font symbols and was well written, so I decided to create a memory game on my own, using pictures instead of symbols.
Using the Code
Having in mind that the game contains 16 fields (4x4), we'll have a pair of 8 different images: devil, sailors, scary, zombie, trick, wizard, bad and owl. Each image will have 2 same elements in the list, so we're sure that there will be no more or less than 2 same images per game:
List<Bitmap> pictures = new List<Bitmap>()
{
Properties.Resources.devil,Properties.Resources.devil,
Properties.Resources.sailors,Properties.Resources.sailors,
Properties.Resources.scary,Properties.Resources.scary,
Properties.Resources.zombie,Properties.Resources.zombie,
Properties.Resources.trick,Properties.Resources.trick,
Properties.Resources.wizard,Properties.Resources.wizard,
Properties.Resources.bat,Properties.Resources.bat,
Properties.Resources.owl,Properties.Resources.owl
};
We have two values of PictureBox
type called first
and second
, that will remember the images we clicked (and their tags),and a bool
value named done
which will check if the moves are made:
PictureBox first, second;
bool done = false;
Now we need to find a way to name every image in order to compare them properly. If you visited my previous project - Sliding Puzzle, you could see that I assigned a tag to each element of Bitmap
list. I'm going to do the same thing now, except it will be a little bit different. Again, I made the exact same order of the List
elements, so I can name them precisely. I will create two procedures called Tags()
and AssignImages()
. Tags go first:
private void Tags(int index)
{
switch (index)
{
case 0:
case 1:
pictures[index].Tag = "devil";
break;
case 2:
case 3:
pictures[index].Tag = "sailors";
break;
case 4:
case 5:
pictures[index].Tag = "scary";
break;
case 6:
case 7:
pictures[index].Tag = "zombie";
break;
case 8:
case 9:
pictures[index].Tag = "trick";
break;
case 10:
case 11:
pictures[index].Tag = "wizard";
break;
case 12:
case 13:
pictures[index].Tag = "bat";
break;
case 14:
case 15:
pictures[index].Tag = "owl";
break;
}
}
Now let's move to AssignImages()
procedure. As its name says, it's going to assign the images to the Form picture boxes, but before that we need to give each element its tag given by the procedure above. Once I do that, the image will contain a tag of its own and will be ready to be given to the control on the form.
But if I assign images to BackgroundImage
or Image
property, they will be shown once the form is loaded and the entire game logic will be screwed. So I'm going to use InitialImage
instead and the images won't be displayed once the program is started:
private void AssignImages()
{
Random r = new Random();
for (int i = 0; i < pictures.Count; i++)
{
Tags(i);
}
foreach (Control c in this.Controls)
{
int j = r.Next(pictures.Count);
PictureBox p = (PictureBox)c;
p.InitialImage = pictures[j];
p.Tag = pictures[j].Tag;
pictures.RemoveAt(j);
}
}
I forgot to mention that before I started writing a code, I marked all the pictureBox
controls and created a common event called picture_Click.
So once we clicked on the image, we'll check if value first
is empty knowing that we haven't clicked yet. The clicked image will set its BackgroundImage
property to the InitialImage
so it will take that reserved image and its tag and the image will be presented in pictureBox
control. The value called second
will do the same for the second clicked image, and then we're setting a boolean value done
to true
so we're not allowing the player to click on more than two images per comparison, and we're starting timer which will perform simple check if the images matched or not:
private void picture_Click(object sender, EventArgs e)
{
PictureBox cast = (PictureBox)sender;
if (cast.BackgroundImage == null && done == false)
{
if (first == null)
{
first = cast;
first.BackgroundImage = first.InitialImage;
return;
}
second = cast;
second.BackgroundImage = second.InitialImage;
done = true;
timer1.Start();
}
}
The timer will check if the images matched, and if they did, we're going to check if the player revealed all the images and won the game. There's no need to do anything else, because the images are already revealed. If they don't match, we're setting their BackgroundImage
properties to null
, and in the end, setting first
and second
to null
, while done
is set to false
in order to allow the player to click on the next pair of images. Timer interval is set to 500ms but you can change it if you wish:
private void Timer(object sender, EventArgs e)
{
timer1.Stop();
if (first.Tag.ToString() == second.Tag.ToString())
{
CheckForWinner();
}
else if (first.Tag.ToString() != second.Tag.ToString())
{
first.BackgroundImage = null;
second.BackgroundImage = null;
}
first = null;
second = null;
done = false;
}
And for the final part, we have CheckForWinner()
procedure which checks if all the images are revealed and if player won the game or not. It's very simple - if a single image is empty, the game is not over and we're exiting. Otherwise, the winner is found and the game is over:
private void CheckForWinner()
{
foreach(Control c in this.Controls)
{
PictureBox pb = (PictureBox)c;
if (pb.BackgroundImage == null) return;
}
MessageBox.Show("Congratulations!");
}
History
- 24th October, 2019: Initial version