0
\$\begingroup\$

So, I have an assignment due in a couple days where I need to create a card game.

I chose to do a memory game and I'm nearly completed however I'm completely stuck and not sure how to move all of the methods or functions into classes. Shuffle, cardFlip etc I'm wanting in classes but not sure on how to move them without running into errors that I'm unsure of how to fix.

I have everything inside of the main form however I found out that I need to create classes for the card and the deck. Is there an easy way to convert my below code into classes such as a class Card, class Deck and possibly class Hand?

I currently have my cards inside of a PictureBox

using MemGame;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MemGame
{
    public partial class Form1 : Form
    {
        Timer cardTimer = new Timer();

        //Graphics myGraphics;
        //Card[] cards = new Card[16];
        int[] cards;
        List<PictureBox> cardList = new List<PictureBox>();
        Random rnd;
        List<PictureBox> cardPairs = new List<PictureBox>();


        int matchingSet = 0;
        int answer1;
        int answer2;
        bool firstAnswer = true;
        int attempts = 0;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            //myGraphics = panel1.CreateGraphics();
            //myDeck = new Deck(myGraphics);
            //myDeck.Shuffle();
            cardList.Add(pictureBox1);
            cardList.Add(pictureBox2);
            cardList.Add(pictureBox3);
            cardList.Add(pictureBox4);
            cardList.Add(pictureBox5);
            cardList.Add(pictureBox6);
            cardList.Add(pictureBox7);
            cardList.Add(pictureBox8);
            cardList.Add(pictureBox9);
            cardList.Add(pictureBox10);
            cardList.Add(pictureBox11);
            cardList.Add(pictureBox12);
            cardList.Add(pictureBox13);
            cardList.Add(pictureBox14);
            cardList.Add(pictureBox15);
            cardList.Add(pictureBox16);
            cardTimer.Interval = 1000;
            cardTimer.Tick += new System.EventHandler(hideCards);
            restartGame();
        }

        private void hideCards(object sender, EventArgs e)
        {
            cardList[answer1].BackgroundImage = Properties.Resources.facedownCard;
            cardList[answer2].BackgroundImage = Properties.Resources.facedownCard;
            CardsEnabled(true);
            disablePairs();
            cardTimer.Enabled = false;
        }

        void restartGame()
        {
            percentageLabel.Text = "Percent correct : ";
            newGame.Enabled = false;
            attempts = 0;
            matchingSet = 0;

            for (int x = 0; x < 16; x++)
            {
                cardList[x].Enabled = true;
                cardList[x].BackgroundImage = Properties.Resources.facedownCard;
                cardList[x].BackgroundImageLayout = ImageLayout.Stretch;
            }

            cards = new[] { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7 };

            cards = Shuffle(cards);

            for (int x = 0; x < 16; x++)
            {
                label1.Text = (x + " : " + cards[x]);
            }
        }

        int[] Shuffle(int[] array)
        {
            rnd = new Random();
            for (int i = array.Length; i > 0; i--)
            {
                int j = rnd.Next(i);
                int k = array[j];
                array[j] = array[i - 1];
                array[i - 1] = k;
            }
            return array;
        }
        public void CardsEnabled(bool value)
        {

            for (int x = 0; x < cardList.Count; x++)
            {
                cardList[x].Enabled = value;
            }
        }

        public void disablePairs()
        {
            for (int x = 0; x < cardPairs.Count; x++)
            {
                cardPairs[x].Enabled = false;
            }
        }

        void checkUserGuesses(int guess)
        {

            cardFlip(cards[guess], cardList[guess]);
            cardList[guess].Enabled = false;

            if (firstAnswer)
            {
                answer1 = guess;
                firstAnswer = !firstAnswer;
            }
            else
            {
                answer2 = guess;
                // If pair is a match
                if (cards[answer1] == cards[answer2])
                {
                    matchingSet++;
                    cardList[answer1].Enabled = false;
                    cardList[answer2].Enabled = false;
                    cardPairs.Add(cardList[answer1]);
                    cardPairs.Add(cardList[answer2]);
                    if (matchingSet >= 8)
                    {
                        gameWon();
                    }
                }
                // If pair is not a match
                else
                {
                    CardsEnabled(false);
                    cardTimer.Enabled = true;
                }
                firstAnswer = !firstAnswer;
                attempts++;
                percentageLabel.Text = "Tries : " + attempts;
            }
        }

        void gameWon()
        {
            percentage();
            newGame.Enabled = true;
        }

        void percentage()
        {
            int percent = (int)Math.Round((double)(100 * 8) / attempts);
            percentageLabel.Text = "Percent Correct : " + percent + "%";
        }

        public static void cardFlip(int cardValue, PictureBox cards)
        {
            switch (cardValue)
            {
                case 0:
                    cards.BackgroundImage = Properties.Resources.clubsace;
                    break;
                case 1:
                    cards.BackgroundImage = Properties.Resources.kingdiamonds;
                    break;
                case 2:
                    cards.BackgroundImage = Properties.Resources.queenspades;
                    break;
                case 3:
                    cards.BackgroundImage = Properties.Resources.jackhearts;
                    break;
                case 4:
                    cards.BackgroundImage = Properties.Resources.clubs2;
                    break;
                case 5:
                    cards.BackgroundImage = Properties.Resources.diamonds6;
                    break;
                case 6:
                    cards.BackgroundImage = Properties.Resources.hearts10;
                    break;
                case 7:
                    cards.BackgroundImage = Properties.Resources.spades7;
                    break;
                default:
                    break;
            }
            cards.BackgroundImageLayout = ImageLayout.Stretch;
        }

        private void pictureBox1_Click(object sender, EventArgs e)
        {
            checkUserGuesses(0);
        }

        private void pictureBox2_Click(object sender, EventArgs e)
        {
            checkUserGuesses(1);
        }

        private void pictureBox3_Click(object sender, EventArgs e)
        {
            checkUserGuesses(2);
        }

        private void pictureBox4_Click(object sender, EventArgs e)
        {
            checkUserGuesses(3);
        }

        private void pictureBox5_Click(object sender, EventArgs e)
        {
            checkUserGuesses(4);
        }

        private void pictureBox6_Click(object sender, EventArgs e)
        {
            checkUserGuesses(5);
        }

        private void pictureBox7_Click(object sender, EventArgs e)
        {
            checkUserGuesses(6);
        }

        private void pictureBox8_Click(object sender, EventArgs e)
        {
            checkUserGuesses(7);
        }
        private void pictureBox9_Click(object sender, EventArgs e)
        {
            checkUserGuesses(8);
        }
        private void pictureBox10_Click(object sender, EventArgs e)
        {
            checkUserGuesses(9);
        }
        private void pictureBox11_Click(object sender, EventArgs e)
        {
            checkUserGuesses(10);
        }
        private void pictureBox12_Click(object sender, EventArgs e)
        {
            checkUserGuesses(11);
        }
        private void pictureBox13_Click(object sender, EventArgs e)
        {
            checkUserGuesses(12);
        }
        private void pictureBox14_Click(object sender, EventArgs e)
        {
            checkUserGuesses(13);
        }
        private void pictureBox15_Click(object sender, EventArgs e)
        {
            checkUserGuesses(14);
        }
        private void pictureBox16_Click(object sender, EventArgs e)
        {
            checkUserGuesses(15);
        }

        private void newGame_Click(object sender, EventArgs e)
        {
            restartGame();
        }
    }
}

An example of the class Card

class Card
{
    int rank;
    string suit;
    Graphics cardTable;
    private int xCoordinate = 10;
    private int yCoordinate = 10;
    private int width = 60;
    private int height = 80;
    private bool faceUp = true;

    public Card(Graphics cardTable, int rank, string suit)
    {
        this.rank = rank;
        this.suit = suit;
        this.cardTable = cardTable;
    }

    public int XCoordinate
    {
        set
        {
            xCoordinate = value;
        }
    }

    public Rectangle MySpace
    {
        get
        {
            return new Rectangle(xCoordinate, yCoordinate, width, height);
        }
    }

    public void display()
    {
        if (faceUp)
        {
            cardTable.DrawImage((Image)Properties.Resources.ResourceManager.GetObject(suit + rank), xCoordinate, yCoordinate, width, height);
        }
        else
        {
            cardTable.FillRectangle(Brushes.HotPink, xCoordinate, yCoordinate, width, height);
        }
    }

    public void flip()
    {
        faceUp = !faceUp;
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Sorry, thank you RubberDuck and Janos. I am new here. But yes, I'm stuck trying to move all of the methods functions into classes. \$\endgroup\$ Commented Oct 21, 2016 at 11:08

1 Answer 1

6
\$\begingroup\$

I'm reluctant to give you too many pieces of the puzzle because it's clearly an assignment (well done for disclosing that btw). I think I can start you off in the right direction though.

Firstly, as you guessed, you need to encapsulate what you think a card is. It has a rank:

public enum Rank
{
   Joker = 0,
   Ace = 1,
   // ...
}

And the card also has a suit:

public enum Suit
{
    Clubs = 0,
    // ...
}

We know that a card is immutable - it can't change rank or suit in its lifetime. So let's make a class to put all that knowledge together:

public class Card
{
    public Rank Rank { get; }

    public Suit Suit { get; }

    public string ResourceKey => $"{Suit}{Rank}".ToLowerInvariant();

    public Card(Rank rank, Suit suit)
    {
        Rank = rank;
        Suit = suit;
    }
}

That's all your card needs to know! It definitely shouldn't be depending on any of your UI elements. Your UI then needs a list of available cards which can be your Deck class if you want. On to a review...


All methods should be PascalCase see capitalization conventions.

Methods should contain a verb. percentage is one example of a bad name, that's a noun! A better name might be CalculateScore as that's what you're really doing.

Your Shuffle method shuffles the array in place - you don't need to return the array. In fact, it's misleading to because it makes it appear that the input array isn't changed.

I haven't done Windows Forms in years so I won't attempt to review that part.

\$\endgroup\$
4
  • \$\begingroup\$ But that's a memory game not poker game, both suit and rank are irrelevant. \$\endgroup\$ Commented Oct 21, 2016 at 16:39
  • \$\begingroup\$ @denis what do you mean? 2 cards are the same if they have the same suit and rank. The name of the corresponding resource is based on the suit and rank. They still seem to be central concepts to me. \$\endgroup\$ Commented Oct 21, 2016 at 18:51
  • \$\begingroup\$ I see your point @RobH and it is correct for the exact OP's question however the game might not play with a suit and a rank there might be some monsters under the card, maybe some city buildings, etc. so in general for that type of game a single enum would be better in my opinion. Because if someone tells you to switch the card images to something that doesn't involves the usual card deck you might have to rewrite most of the game logic as if you were using 2 enums instead of 1. \$\endgroup\$ Commented Oct 21, 2016 at 19:26
  • 1
    \$\begingroup\$ @denis - Thanks for elaborating, you make a very good point which would be very worthy of an answer \$\endgroup\$ Commented Oct 22, 2016 at 13:43

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.