-1
\$\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\$
  • \$\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\$ – AmateurHB Oct 21 '16 at 11:08
5
\$\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\$
  • \$\begingroup\$ But that's a memory game not poker game, both suit and rank are irrelevant. \$\endgroup\$ – Denis Oct 21 '16 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\$ – RobH Oct 21 '16 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\$ – Denis Oct 21 '16 at 19:26
  • 1
    \$\begingroup\$ @denis - Thanks for elaborating, you make a very good point which would be very worthy of an answer \$\endgroup\$ – RobH Oct 22 '16 at 13:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.