UA-5095748-1

Wednesday, December 12, 2012

a fun math card game and a computer program to solve it.

My wife has been playing this fun card game ever since she was a kid, and I just learn about it recently. The game can be played with 2 or more people.

1) Start with a normal deck of card and remove all the face cards, so only cards 1 to 10 remains
2) Shuffle the cards
3) Set 4 cards face-down and apart in a square fashion so all 4 cards can be see at once
4) Everyone turn over the cards at the same time
5) Use all 4 cards, and any combination of +, -, *, and /,
6) When a player to find a solution that results as 24, the player must slaps the table and calls out the solution.
7) First person slaps and provides the correct answer wins the round and keeps the 4 cards.
6) Play until all the cards gone and the winner is the person with the most number of cards

Here is another variation for younger kids.
5) Allow only +, and - for math operations and you don't have to use all 4 cards
6) find a solution that results as 5
7) the 1st person that slaps, provides the correct answer, and uses the most cards, wins the round and keeps number of cards used in the solutions. The unused cards goes into a junk pile.

Needless to say, I didn't win any games. So I decided to write this little C# program to find all possible solutions to sooth my ego a bit. :)

This recursive program can handle any number of cards (not just 4) and solve for any value (not just 24).
It can find all possible solutions or just the 1st one. This is brute force algorithm to checks for all combination, I imagine there must be a more elegant solution.

If you need a refresher on recursion, here is a link with good description. I referenced this model when I wrote this code.

Here is the code.
----------------------------


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class PlayCards
    {
        enum Operator
        {
            Plus,
            Minus,
            Multiple,
            Divide
        };

        static void Main(string[] args)
        {
            int[] cards = new int[] {1, 2, 3, 4};
            //Operator[] permOperators = new Operator[] { Operator.Plus, Operator.Minus, Operator.Multiple};
            PlayCards game = new PlayCards();

            string line;
            System.Console.WriteLine("Enter a cards shown separated by space. Example: 1 2 3 4"); 
            line = System.Console.ReadLine();
            string[] words = line.Split(' ');

            cards = new int[words.Count()];
            int i = 0; 
            foreach (string word in words)
            {
                int card = 0;
                if (word.Length > 0 && int.TryParse(word, out card) == true)
                {
                    cards[i] = card;
                    i++; 
                }
            }
            int[] newCards = new int[i];
            Array.Copy(cards, newCards, i); 

            game.recursiveCards(new int[0], newCards, new Operator[0], 24, true);
            System.Console.WriteLine("1st solutions");

            int found = game.recursiveCards(new int[0], newCards, new Operator[0], 24, false);
            System.Console.WriteLine("found " + found + " solutions");
        }

        private int recursiveCards(int[] permCards, int[] remainCards, Operator[] permOperators, int target, bool firstOnly)
        {
            int result = 0; 

            if (remainCards.Count() == 0)
            {
                // evaluate result
                int evaluateRes = 0;
                evaluateRes = permCards[0]; 

                for (int i = 1; i < permCards.Count(); i++)
                {
                    if (i <= permCards.Count() - 1)
                    {
                        switch(permOperators[i - 1])
                        {
                            case Operator.Plus:
                                evaluateRes = evaluateRes + permCards[i]; 
                                break; 
                            case Operator.Minus:
                                evaluateRes = evaluateRes - permCards[i]; 
                                break;
                            case Operator.Multiple:
                                evaluateRes = evaluateRes * permCards[i]; 
                                break;
                            case Operator.Divide:
                                evaluateRes = evaluateRes % permCards[i]; 
                                break;
                        }
                    }
                }

                if (evaluateRes == target)
                {
                    // print result
                    for (int i = 0; i < permCards.Count() - 1; i++)
                    {
                        System.Console.Write(permCards[i] + " ");
                        System.Console.Write(permOperators[i].ToString() + " " );
                    }
                    System.Console.WriteLine(permCards[permCards.Count() - 1] + " = " + evaluateRes);
                    result = 1; 
                }
            }
            else
            {
                // set the newPermCards
                int[] newPermCards = new int[permCards.Count() + 1];
                int[] newRemainCards = new int[remainCards.Count() - 1]; 
                permCards.CopyTo(newPermCards, 0);

                for (int i = 0; i < remainCards.Count(); i++)
                {
                    // generate the remaining cards permutations
                    newPermCards[permCards.Count()] = remainCards[i]; 
                    int r = 0; 
                    for (int j = 0; j < remainCards.Count(); j++)
                    {
                        if (j != i)
                        {
                            newRemainCards[r] = remainCards[j];
                            r++; 
                        }
                    }

                    // generate the new operators permutations
                    Operator[] newPermOperators = new Operator[permOperators.Count() + 1];                        
                    permOperators.CopyTo(newPermOperators, 0);
                    foreach (Operator newOp in Enum.GetValues(typeof(Operator)))
                    {
                        newPermOperators[permOperators.Count()] = newOp;

                        // recusively compute results
                        result += recursiveCards(newPermCards, newRemainCards, newPermOperators, target, firstOnly);

                        // check if we need to terminate on 1st or keep on looking
                        if (firstOnly && result > 0)
                        {
                            return result; 
                        }
                    }
                }
            }

            return result; 
        }
    }
}







No comments: