Heads and Tails Game - Part 2 of 2

I'll now show you a mathematical trick to always win the heads and tails game (for more information about the game itself, please read "heads and tails game - part 1").

Suppose that your opponent plays something like this: TTTHTHTHTHHHTTT. Here is the simplest and weirdest trick to achieve a 75% chance of winning:

O = TTTHTHTHTHHHTTT.
Define C = F(O) as follows:

  • Take the second to last element of O. In this case, "T" (TTTHTHTHTHHHTTT)
  • Move that element to the front. Hence so far C = F(O) = TTTTHTHTHTHHHTT
  • Flip the first element. Hence, C = F(O) = HTTTHTHTHTHHHTT
That's all. This will give you a 75% chance of winning - pure magic!!!

Well, I win!!! :)
You win 269175 times (or 25.3797893244125% of the total)
And I win 791413 times (or 74.6202106755875% of the total)

Wait, what if we decide to follow the same approach but, instead of using the second to last element, we use, say, the fourth element? Wouldn't that give us an edge as well? Well, let's see:

My guess: TTTTTHTHTHHHTTT
You win!!! :)
You win 828727 times (or 50.0364378018496% of the total)
And I win 827520 times (or 49.9635621981504% of the total)

:) Pretty even, hence clearly that element won't work (and you can try with other elements too, it won't work nearly as well as the second to last element).

I find this fascinating. The algorithm for F(O) seems as random to me as if I was playing any other random guess. And yet, it pushes your odds of winning from ~50% to ~75%. Unfortunately I don't know the mathematics of it, why and how this works :( If anyone does, please let me know. Code is below if you want to play with it.
Finally, there is one extra modification to F(O) that is worth noticing: if after the transformation you end up with an F(O) that only contains heads or only contains tails, flip the first element. It boosts your chances by at least 10%. Just don't ask me why. Cheers, Marcelo.

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

namespace HeadTailsGameNS
{
    class Program
    {
        static void Main(string[] args)
        {
            string opponentGuess = args[0];
            HeadTailsGame headTailsGame = new HeadTailsGame(opponentGuess);

            Console.WriteLine("My guess: {0}", headTailsGame.ComputerGuess);
            Console.WriteLine();

            headTailsGame.Play();
        }
    }

    class HeadTailsGame
    {
        private string opponentGuess = "";
        private string computerGuess = "";
        private int lenGuess = 0;
        public string ComputerGuess
        {
            get { return computerGuess; }
        }

        private HeadTailsGame() { }

        public HeadTailsGame(string opponentGuess)
        {
            this.opponentGuess = opponentGuess;
            this.lenGuess = this.opponentGuess.Length;

            CreateMagicalComputerGuess();
            //CreateRandomComputerGuess();
            //CreateComputerGuess();
        }

        public void Play()
        {
            int nRound = 1000;
            Random rd = new Random((int)(DateTime.Now.Ticks % Int32.MaxValue));

            int victoriesOpponent = 0;
            int victoriesComputer = 0;

            for (int i = 0; i < nRound; i++)
            {
                int indexOpponent = 0;
                int indexComputer = 0;
                while (indexOpponent < this.lenGuess - 1 && indexComputer < this.lenGuess - 1)
                {
                    char toss = (rd.Next(0, 2) == 0) ? 'H' : 'T';
                    if (this.opponentGuess[indexOpponent] == toss)
                    {
                        indexOpponent++;
                    }
                    else
                    {
                        indexOpponent = 0;
                    }

                    if (this.computerGuess[indexComputer] == toss)
                    {
                        indexComputer++;
                    }
                    else
                    {
                        indexComputer = 0;
                    }
                }

                if (indexOpponent == this.lenGuess - 1)
                {
                    victoriesOpponent++;
                }
                if (indexComputer == this.lenGuess - 1)
                {
                    victoriesComputer++;
                }
            }

            int totalVictories = victoriesOpponent + victoriesComputer;

            if (victoriesOpponent == victoriesComputer)
            {
                Console.WriteLine("It is a tie!!! :)");
            }
            else if (victoriesOpponent > victoriesComputer)
            {
                Console.WriteLine("You win!!! :)");
            }
            else
            {
                Console.WriteLine("Well, I win!!! :)");
            }
            Console.WriteLine("You win {0} times (or {1}% of the total)", victoriesOpponent, 100.0 * victoriesOpponent / totalVictories);
            Console.WriteLine("And I win {0} times (or {1}% of the total)", victoriesComputer, 100.0 * victoriesComputer / totalVictories);
        }

        private void CreateMagicalComputerGuess()
        {
            //{Here is the trick!!!
            int indexToChange = this.lenGuess - 2;
            this.computerGuess = (this.opponentGuess[indexToChange] == 'H') ? "T" : "H";
            for (int i = 0; i < this.lenGuess; i++)
            {
                if (i != indexToChange)
                {
                    this.computerGuess += this.opponentGuess[i].ToString();
                }
            }

            bool allSame = true;
            for (int i = 1; i < this.lenGuess; i++)
            {
                if (this.computerGuess[i] != this.computerGuess[0])
                {
                    allSame = false;
                    break;
                }
            }
            if (allSame)
            {
                string temp = "";
                temp = (this.computerGuess[0] == 'H') ? "T" : "H";
                temp += this.computerGuess.Substring(1);
                this.computerGuess = temp;
            }
            //}Here is the trick!!!
        }

        private void CreateRandomComputerGuess()
        {
            computerGuess = "";
            Random rd = new Random((int)(DateTime.Now.Ticks % Int32.MaxValue));

            for (int i = 0; i < lenGuess; i++)
            {
                computerGuess += (rd.Next(0, 2) == 0) ? "H" : "T";
            }
        }

        private void CreateComputerGuess()
        {
            computerGuess = (opponentGuess[0] == 'H') ? "T" : "H";
            computerGuess += opponentGuess.Substring(1);
        }
    }
}

Comments

Popular posts from this blog

Changing the root of a binary tree

ProjectEuler Problem 719 (some hints, but no spoilers)

The Power Sum, a recursive problem by HackerRank