Poker Logic

Posted on: June 7, 2016 1:31:50 AM

Another programming challenge that I decided to do was a poker challenge The goal of this challenge is to parse 1000 random poker hands of 2 players and determine how many times the first player wins. This was actually quite a fun challenge and I suggest you try to do it on your own even though I'm posting my code.

I decided to do this in a more TDD way than I normally do. So just as I started, I'll start by posting all of my unit tests. Granted, I didn't have them all at the time of development, but I don't think it's necessary to post what I did every step of the way.

PokerTests.cs
using NUnit.Framework;
using Poker_Kata;

namespace Poker_Tests
{
    [TestFixture]
    public class PokerTests
    {
        private Player P1;
        private Player P2;

        [Test]
        public void P13OfAKind()
        {
            P1.SetHand("3D", "3H", "3C", "4D", "7D");
            P2.SetHand("2S", "2D", "2C", "AS", "KS");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.ThreeKind));
        }

        [Test]
        public void P1FourKind()
        {
            P1.SetHand("2D", "2S", "2C", "2H", "TD");
            P2.SetHand("2S", "AS", "4S", "5S", "6S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.FourKind));
        }

        [Test]
        public void P1HighCard()
        {
            P1.SetHand("2D", "3D", "4D", "5D", "8C");
            P2.SetHand("2C", "3S", "4S", "5S", "7S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
        }

        [Test]
        public void P1HighPair()
        {
            P1.SetHand("3D", "3H", "4D", "5D", "6D");
            P2.SetHand("2S", "2C", "QS", "KD", "AS");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
        }

        [Test]
        public void P1Pair()
        {
            P1.SetHand("2D", "2H", "4D", "5D", "7D");
            P2.SetHand("2C", "3S", "4S", "5S", "7S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
        }

        [Test]
        public void P1RoyalFlush()
        {
            P1.SetHand("AD", "TD", "KD", "QD", "JD");
            P2.SetHand("AS", "2S", "3S", "4S", "5S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
        }

        [Test]
        public void P1Straight()
        {
            P1.SetHand("2D", "3H", "4C", "5D", "6D");
            P2.SetHand("2S", "2D", "2C", "AS", "AS");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Straight));
        }

        [Test]
        public void P1TwoPair()
        {
            P1.SetHand("2D", "2H", "3D", "3D", "7D");
            P2.SetHand("2S", "3S", "4S", "AS", "AS");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
        }

        [Test]
        public void P1TwoPairHighCard()
        {
            P1.SetHand("4C", "4S", "2D", "AD", "2H");
            P2.SetHand("4S", "4D", "2S", "KS", "2C");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P1));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
        }

        [Test]
        public void P2AceHighCard()
        {
            P1.SetHand("2D", "3D", "4S", "5D", "7D");
            P2.SetHand("2S", "3S", "6D", "5S", "AS");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
        }

        [Test]
        public void P2Flush()
        {
            P1.SetHand("AD", "KH", "QC", "JD", "TD");
            P2.SetHand("2S", "TS", "4S", "5S", "6S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Flush));
        }

        [Test]
        public void P2FullHouse()
        {
            P1.SetHand("AD", "AH", "AC", "3D", "7D");
            P2.SetHand("2S", "2D", "2C", "3S", "3D");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.FullHouse));
        }

        [Test]
        public void P2FullHouse3CardHigher()
        {
            P1.SetHand("3D", "3H", "3C", "AD", "AD");
            P2.SetHand("4S", "4D", "4C", "2S", "2D");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.FullHouse));
        }

        [Test]
        public void P2HighCard2()
        {
            P1.SetHand("8C", "TS", "KC", "9H", "4S");
            P2.SetHand("7D", "2S", "5D", "3S", "AC");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
        }

        [Test]
        public void P2HighCard3()
        {
            P1.SetHand("4D", "5H", "TH", "QS", "KS");
            P2.SetHand("2H", "3D", "4S", "6C", "AD");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.HighCard));
        }

        [Test]
        public void P2HighFlush()
        {
            P1.SetHand("4D", "KD", "QD", "JD", "TD");
            P2.SetHand("2S", "AS", "4S", "5S", "6S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Flush));
        }

        [Test]
        public void P2LowStraight()
        {
            P1.SetHand("AD", "2H", "3C", "4D", "5C");
            P2.SetHand("2D", "3H", "4C", "5D", "6D");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Straight));
        }

        [Test]
        public void P2StraightFlush()
        {
            P1.SetHand("AD", "2H", "3C", "4D", "5C");
            P2.SetHand("AS", "2S", "3S", "4S", "5S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
        }

        [Test]
        public void P2TwoPair()
        {
            P1.SetHand("2D", "2H", "3D", "3D", "7D");
            P2.SetHand("4S", "4D", "2S", "AS", "2C");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(1));
            Assert.That(winners[0].Player, Is.EqualTo(P2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.TwoPair));
        }

        [Test]
        public void PairTie()
        {
            P1.SetHand("2D", "2H", "4D", "5D", "6D");
            P2.SetHand("2S", "2C", "4S", "5S", "6S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.Pair));
        }

        [OneTimeSetUp]
        public void TestsSetup()
        {
            P1 = new Player { Name = "P1" };
            P2 = new Player { Name = "P2" };
        }

        [Test]
        public void Tie()
        {
            P1.SetHand("2D", "3D", "4D", "5D", "6D");
            P2.SetHand("2S", "3S", "4S", "5S", "6S");

            var winners = Poker.DetermineWinner(P1, P2);

            Assert.That(winners.Length, Is.EqualTo(2));
            Assert.That(winners[0].Hand, Is.EqualTo(Hand.StraightFlush));
        }
    }
}

Now we'll need the Card, Player, and PokerHand helper classes. These are simple POCO helpers just to hold some information for whatever is utilizing the code.

Card.cs
namespace Poker_Kata
{
    public enum Faces
    {
        Club,
        Diamond,
        Spade,
        Heart
    }

    public class Card
    {
        public Card()
        {
        }

        public Card(int value, Faces face)
        {
            Value = value;
            Face = face;
        }

        public Card(string card)
        {
            switch (card[0])
            {
                case '2':
                    Value = 2;
                    break;

                case '3':
                    Value = 3;
                    break;

                case '4':
                    Value = 4;
                    break;

                case '5':
                    Value = 5;
                    break;

                case '6':
                    Value = 6;
                    break;

                case '7':
                    Value = 7;
                    break;

                case '8':
                    Value = 8;
                    break;

                case '9':
                    Value = 9;
                    break;

                case 'T':
                    Value = 10;
                    break;

                case 'J':
                    Value = 11;
                    break;

                case 'Q':
                    Value = 12;
                    break;

                case 'K':
                    Value = 13;
                    break;

                case 'A':
                    Value = 14;
                    break;
            }

            switch (card[1])
            {
                case 'H':
                    Face = Faces.Heart;
                    break;

                case 'D':
                    Face = Faces.Diamond;
                    break;

                case 'C':
                    Face = Faces.Club;
                    break;

                case 'S':
                    Face = Faces.Spade;
                    break;
            }
        }

        public Faces Face { get; set; }

        public int Value { get; set; }

        public override string ToString()
        {
            return Value + " " + Face.ToString();
        }
    }
}
Player.cs
using System.Collections.Generic;

namespace Poker_Kata
{
    public class Player
    {
        public Card[] Hand { get; set; }

        public string Name { get; set; }

        public void SetHand(params string[] cards)
        {
            List hand = new List();

            foreach (string card in cards)
            {
                hand.Add(new Card(card));
            }

            Hand = hand.ToArray();
        }
    }
}
PokerHand.cs
namespace Poker_Kata
{
    public class PokerHand
    {
        public Card[] Cards { get; set; }

        public Hand Hand { get; set; }

        public Player Player { get; set; }
    }
}

Next is the heart of the project, the Poker class. This class will hold all the of the logic and rules of poker and allow you to pass in a number of players with their hands and will determine what hands they hold along with who won the hand. I didn't make this work with just any poker game, although it wouldn't take much work to do so, I only did enough to handle each player having 5 cards.

Poker.cs
using System.Linq;

namespace Poker_Kata
{
    public enum Hand
    {
        HighCard,
        Pair,
        TwoPair,
        ThreeKind,
        FullHouse,
        Straight,
        Flush,
        FourKind,
        StraightFlush
    }

    public static class Poker
    {
        public static PokerHand[] DetermineWinner(params Player[] players)
        {
            PokerHand[] pokerHands = new PokerHand[players.Length];

            for (int i = 0; i < players.Length; i++)
            {
                var hand = GetHand(players[i].Hand);
                hand.Player = players[i];

                pokerHands[i] = hand;
            }

            pokerHands = pokerHands.Where(p => p.Hand == pokerHands.Max(p1 => p1.Hand)).ToArray();

            if (pokerHands.Length > 1)
            {
                pokerHands = FilterByHigherHand(pokerHands);

                if (pokerHands.Length > 1)
                {
                    pokerHands = FilterByHighCard(pokerHands);
                }
            }

            return pokerHands;
        }

        public static PokerHand GetHand(Card[] hand)
        {
            PokerHand pokerHand = new PokerHand();
            pokerHand.Cards = hand = hand.OrderByDescending(c => c.Value).ToArray();
            pokerHand.Hand = (hand.CountBy(c => c.Face == hand[0].Face) >= 5) ? Hand.Flush : Hand.HighCard;

            if (CheckIsStraight(hand))
            {
                pokerHand.Hand = pokerHand.Hand == Hand.Flush ? Hand.StraightFlush : Hand.Straight;
            }

            int previousCard = 0;

            foreach (var card in hand)
            {
                if (previousCard == card.Value) continue;

                int valueCount = hand.CountBy(c => c.Value == card.Value);

                if (valueCount == 2)
                {
                    if (pokerHand.Hand == Hand.Pair)
                    {
                        pokerHand.Hand = Hand.TwoPair;
                    }
                    else if (pokerHand.Hand == Hand.ThreeKind)
                    {
                        pokerHand.Hand = Hand.FullHouse;
                    }
                    else
                    {
                        pokerHand.Hand = Hand.Pair;
                    }
                }
                else if (valueCount == 3)
                {
                    if (pokerHand.Hand == Hand.Pair)
                    {
                        pokerHand.Hand = Hand.FullHouse;
                    }
                    else
                    {
                        pokerHand.Hand = Hand.ThreeKind;
                    }
                }
                else if (valueCount == 4)
                {
                    pokerHand.Hand = Hand.FourKind;
                }

                previousCard = card.Value;
            }

            return pokerHand;
        }

        private static bool CheckIsStraight(Card[] hand)
        {
            int previousValue = 0;

            foreach (var card in hand.OrderBy(c => c.Value))
            {
                if (previousValue > 0)
                {
                    if (card.Value == 14 && previousValue == 5) break;

                    if (card.Value != previousValue + 1)
                    {
                        return false;
                    }
                }

                previousValue = card.Value;
            }

            return true;
        }

        private static PokerHand[] FilterByHighCard(PokerHand[] pokerHands)
        {
            foreach (var hand in pokerHands)
            {
                hand.Cards = hand.Cards.OrderByDescending(c => c.Value).ToArray();

                if ((hand.Hand == Hand.Straight || hand.Hand == Hand.StraightFlush) &&
                    (hand.Cards.Max(c => c.Value) == 14 && hand.Cards.Min(c => c.Value) == 2))
                {
                    hand.Cards[0].Value = 1;
                    hand.Cards = hand.Cards.OrderByDescending(c => c.Value).ToArray();
                }
            }

            for (int i = 0; i < pokerHands[0].Cards.Length; i++)
            {
                int highCard = pokerHands.Select(p => p.Cards[i]).Max(c => c.Value);

                pokerHands = pokerHands.Where(p => p.Cards[i].Value == highCard).ToArray();

                if (pokerHands.Length == 1) break;
            }

            return pokerHands;
        }

        private static PokerHand[] FilterByHigherHand(PokerHand[] pokerHands)
        {
            switch (pokerHands[0].Hand)
            {
                case Hand.Pair:
                    pokerHands = FilterBySet(pokerHands, 2);
                    break;

                case Hand.TwoPair:
                    pokerHands = FilterBySet(pokerHands, 2);

                    if (pokerHands.Length > 1)
                    {
                        pokerHands = FilterBySet(pokerHands, 2, false);
                    }
                    break;

                case Hand.ThreeKind:
                case Hand.FullHouse:
                    pokerHands = FilterBySet(pokerHands, 3);
                    break;

                case Hand.FourKind:
                    pokerHands = FilterBySet(pokerHands, 4);
                    break;
            }

            return pokerHands;
        }

        private static PokerHand[] FilterBySet(PokerHand[] pokerHands, int setCount, bool checkMax = true)
        {
            var groupedCards = pokerHands.Select(p => p.Cards.GroupBy(c => c.Value).Where(g => g.Count() == setCount));
            int checkDigit;

            if (checkMax)
            {
                checkDigit = groupedCards.SelectMany(g => g).Max(g => g.Key);
            }
            else
            {
                checkDigit = groupedCards.SelectMany(g => g).Min(g => g.Key);
            }

            pokerHands = pokerHands.Where(p => p.Cards.CountBy(c => c.Value == checkDigit) == setCount).ToArray();

            return pokerHands;
        }
    }
}

In order for this code to compile, you'll need one more helper class that holds the extension CountBy.

Extensions.cs
using System;
using System.Collections.Generic;

namespace Poker_Kata
{
    public static class Extensions
    {
        public static int CountBy(this IEnumerable source, Func comparer)
        {
            int count = 0;

            foreach (var s in source)
            {
                if (comparer(s))
                {
                    count++;
                }
            }

            return count;
        }
    }
}

I believe this logic should be able to handle any 5 card poker hand, if not, please post a comment on what hand it fails on and I will happily take a look.

Now that we have all the logic and the unit tests passing, we need to read in the .txt document that the challenge provides that holds all the hands. I did this in the Program class, so when the program executes, it completes the challenge assuming you give the argument of the .txt document.

Program.cs

using System;
using System.IO;
using System.Linq;

namespace Poker_Kata
{
    internal class Program
    {
        private static string[] LoadFile(string file)
        {
            string[] contents;

            using (var fs = new FileStream(file, FileMode.Open, FileAccess.Read))
            using (var sr = new StreamReader(fs))
            {
                contents = sr.ReadToEnd().Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            }

            return contents;
        }

        private static void Main(string[] args)
        {
            if (args.Length > 0)
            {
                int p1Wins = 0;
                int p2Wins = 0;
                int ties = 0;

                var cards = LoadFile(args[0]);

                Player p1 = new Player() { Name = "P1" };
                Player p2 = new Player() { Name = "P2" };

                foreach (var hand in cards)
                {
                    var game = hand.Split(' ');

                    p1.SetHand(game.Take(5).ToArray());
                    p2.SetHand(game.Skip(5).Take(5).ToArray());

                    var winners = Poker.DetermineWinner(p1, p2);

                    p1.Hand = p1.Hand.OrderBy(c => c.Value).ToArray();
                    p2.Hand = p2.Hand.OrderBy(c => c.Value).ToArray();

                    if (winners.Length == 2)
                    {
                        ties++;
                    }
                    else if (winners[0].Player.Equals(p1))
                    {
                        p1Wins++;
                    }
                    else
                    {
                        p2Wins++;
                    }
                }

                Console.WriteLine("P1: {0}\r\nP2: {1}\r\nTies: {2}", p1Wins, p2Wins, ties);
            }
        }
    }
}

I do hope, once again, that you attempt this challenge properly without just using my code to get the answer. I know that my code could be optimized further, but I see no need to do so at this time. Thanks for reading and happy coding.

Eratasthenes Sieve

Posted on: April 30, 2016 6:58:52 PM

Another programming challenge was to determine the 5000th prime number. A common way to quickly calculate prime numbers is using a sieve. There are several that you can choose from, I chose to implement Eratasthenes sieve. The code below will allow you to get all the prime numbers up to a limit.

    public class EratosthenesSieve
    {
        public static IEnumerable PrimeNumbers(int limit)
        {
            // add 1 to the limit to determine the limit number itself as well
            BitArray bitArray = new BitArray(limit + 1, true);

            bitArray[0] = false;
            bitArray[1] = false;
            bitArray[2] = true;

            yield return 2;

            for (int i = 4; i < bitArray.Length; i += 2)
            {
                bitArray[i] = false;
            }

            for (int i = 3; i < bitArray.Length; i++)
            {
                if (bitArray[i])
                {
                    yield return i;

                    for (int x = i * 2; x < bitArray.Length; x += i)
                    {
                        bitArray[x] = false;
                    }
                }
            }
        }
    }

Luhn Algorithm

Posted on: April 25, 2016 11:52:19 PM

I recently came across some programming challenges online that I decided to try out. They are heavily math based and took me quite a bit of research. This particular challenge was to both calculate a check digit on a credit card and validate a credit card number. The algorithm I found that does this best is called Luhn Algorithm.

Luhn Algorithm is described as the following (via Wikipedia):

  1. From the right most digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9) or alternatively subtract 9 from the product (e.g., 16: 16 - 9 = 7, 18: 18 - 9 = 9).
  2. Take the sum of all the digits.
  3. If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.

Based on this description, I came up with the following code:

    public static class LuhnAlgorithm
    {
        private static readonly int[] DoubleDigitCalculation = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };

        public static int GetLuhnsCheckDigit(this string account)
        {
            int checkValue = LuhnsCalculation(account.Select(c => c - '0').ToArray(), false);

            return checkValue == 0 ? 0 : 10 - checkValue;
        }

        public static bool LuhnsPass(this string account)
        {
            return LuhnsCalculation(account.Select(c => c - '0').ToArray(), true) == 0;
        }

        private static int LuhnsCalculation(int[] digits, bool includesCheckDigit)
        {
            int index = 0;
            int modIndex = includesCheckDigit ? digits.Length % 2 : digits.Length % 2 == 1 ? 0 : 1;

            return digits.Sum(d => index++ % 2 == modIndex ? DoubleDigitCalculation[d] : d) % 10;
        }
    }

This bit of code passed all the tests and is very fast. Although it doesn't have a ton of use, it was fun to come up with.

In part 2 of the MAF example, I'm going to walk through adding an event. Events can be very tricky to move across the isolation layer and still have it act like an event. I'm going to continue on the project from the first example which is also located in GitHub. I'm going to be only showing code snippets necessary to get the events working, there is more to the project than what is being shown like the class attributes necessary for the pipeline to work. These are shown in the first example.

I'm going to start in the ContractV2 project. We're going to need to add in 2 methods in the interface and add a new interface to handle the delegate pass through.

IContractV2.cs
public interface IContractV2 : IContract
{
...
	void OnEventAdd(IEventHandlerContractV2 handler);

	void OnEventRemove(IEventHandlerContractV2 handler);
}

public interface IEventHandlerContractV2 : IContract
{
	void Handle(string message);
}

We now need to update the Views on both the adapter and the host side with the same new event.

IV2.cs
public interface IV2
{
...
	event EventHandler OnEvent;
}

Next we're going to modify the HostAdapterV2 project. We're going to modify HostAdapter.cs and add a new class to handle the conversion of the event. Starting with the new conversion class.

EventHandlerViewToContractAdapter.cs
using ContractV2.V2;
using System;
using System.AddIn.Pipeline;

namespace HostAdapterV2
{
	internal class EventHandlerViewToContractAdapter : ContractBase, IEventHandlerContractV2
	{
		private Action _event;

		public EventHandlerViewToContractAdapter(Action @event)
		{
			_event = @event;
		}

		public void Handle(string message)
		{
			_event(message);
		}
	}
}
HostAdapter.cs
public class HostAdapter : IV2
{
...
	public event EventHandler OnEvent
	{
		add
		{
			if (_OnEvent == null)
			{
				_contract.OnEventAdd(new EventHandlerViewToContractAdapter(FireOnEvent));
			}

			_OnEvent += value;
		}
		remove
		{
			_OnEvent -= value;

			if (_OnEvent == null)
			{
				_contract.OnEventRemove(new EventHandlerViewToContractAdapter(FireOnEvent));
			}
		}
	}

	private event EventHandler _OnEvent;

	private void FireOnEvent(string message)
	{
		if (_OnEvent != null)
		{
			_OnEvent(this, message);
		}
	}
}

Next we are going to play in the AddinAdapter project. This is going to be very similar to the HostAdapter with a few changes

EventHandlerContractToViewAdapter.cs
using ContractV2.V2;
using System.AddIn.Pipeline;

namespace AddinAdapterV2
{
	internal class EventHandlerContractToViewAdapter
	{
		private IEventHandlerContractV2 _contract;
		private ContractHandle _handle;

		public EventHandlerContractToViewAdapter(IEventHandlerContractV2 contract)
		{
			_contract = contract;
			_handle = new ContractHandle(_contract);
		}

		internal void Handler(object sender, string message)
		{
			_contract.Handle(message);
		}
	}
}
EventHandlerAdapter.cs
using ContractV2.V2;
using System;

namespace AddinAdapterV2
{
	internal class EventHandlerAdapter
	{
		internal static EventHandler ContractToViewAdapter(IEventHandlerContractV2 handler)
		{
			return new EventHandlerContractToViewAdapter(handler).Handler;
		}
	}
}
AddinAdapter.cs
public class AddinAdapter : ContractBase, IContractV2
{
...
	public void OnEventAdd(IEventHandlerContractV2 handler)
	{
		EventHandler adaptedEvent = EventHandlerAdapter.ContractToViewAdapter(handler);
		_eventHandlers.Add(handler, adaptedEvent);
		_view.OnEvent += adaptedEvent;
	}

	public void OnEventRemove(IEventHandlerContractV2 handler)
	{
		EventHandler adaptedEvent;
		if (_eventHandlers.TryGetValue(handler, out adaptedEvent))
		{
			_eventHandlers.Remove(handler);
			_view.OnEvent -= adaptedEvent;
		}
	}
}

Now because this is a V2, we need to modify the V1 to V2 adapter.

AddinAdapterV1ToV2.cs
public class AddinAdapterV1ToV2 : ContractBase, IContractV2
{
...
	public void OnEventAdd(IEventHandlerContractV2 handler)
	{
	}

	public void OnEventRemove(IEventHandlerContractV2 handler)
	{
	}
}

Now we can register to the event from the Views and fire the event just like we normally would. Starting with the ConsoleHost project.

Program.cs
private static void Main(string[] args)
{
...
	if (token != null)
	{
		// Activate the selected AddInToken in a new application domain
		// with the Internet trust level.
		IV2 v2 = token.Activate(AddInSecurityLevel.Internet);
		v2.OnEvent += (sender, message) => { Console.WriteLine("Event Fired: {0}", message); };
		v2.Initialize(new CallbackHandler());

		// Run the add-in.
		v2.WriteToConsole("Hello World From Host!");
		Console.WriteLine(v2.GetName());

		//var test = (Stopwatch)v2.GetSource();

		//Task.Delay(500).Wait();

		//test.Stop();
		//Console.WriteLine(test.ElapsedTicks);
	}
}

Lastly, we fire the event, we'll set it up to fire on the initialize method because it's always going to be called.

Addin.cs
public class Addin : IV2
{
...
	public event EventHandler OnEvent;
...
	public void Initialize(ICallbackV2 callback)
	{
		_callback = callback;

		if (OnEvent != null)
		{
			OnEvent(this, "Initialized called from Plugin");
		}
	}
}

There we have it, an event all the way through the pipeline that fires and handles as if it was done within the same AppDomain.

.NET AddIn Framework With Backwards Compatibility

Posted on: February 24, 2016 6:30:27 PM

At work with my teammate, Jeremy, the application we're working on currently supports plugins built by 3rd parties. The problem we started running into once we thought about making it public was that anytime we try to update the libraries, it would break any plugin that was developed that wasn't compiled using those libraries. To solve this problem, we started looking into the .NET AddIn Framework that can support backwards compatibility. Figuring out this framework took quite a bit of work, there isn't a ton of documentation nor are there many examples of what we needed. It's relatively easy to have the Host application call into a Plugin, but quite a bit more complex if you want to support the reverse. The whole process is still quite complex and takes some time to wrap your head around, I'll post some of the important code here and then a link to my test project that has the fully working source and a test project to demonstrate.

To setup the basic Host-to-AddIn structure, we followed this MSDN Example. Once you get that working, in order to get the callback to the Host setup, you need to add a new contract and modify your existing contract to support passing in a reference to your Host's handler.

ICallbackContract
using System.AddIn.Contract;

namespace ContractV2.V2
{
    public interface ICallbackContractV2 : IContract
    {
        void DoWork();
    }
}
IContractV2
using System.AddIn.Contract;
using System.AddIn.Pipeline;

namespace ContractV2.V2
{
    [AddInContract]
    public interface IContractV2 : IContract
    {
        string GetName();

        void Initialize(ICallbackContractV2 callback);

        void WriteToConsole(string output);

        object GetSource();
    }
}

You can follow the MSDN tutorial once again to propagate this contract all the way through the project, it's virtually the same with the exception of your callback interfaces and adapters don't require the same Attributes that your first contract will because it isn't technically a full AddIn contract. The next step is to create the normal AddInAdapater.

AddinAdapterV2
using AddinViewV2.V2;
using ContractV2.V2;
using System.AddIn.Pipeline;

namespace AddinAdapterV2.V2
{
    [AddInAdapter]
    public class AddinAdapter : ContractBase, IContractV2
    {
        private IV2 _view;

        public AddinAdapter(IV2 view)
        {
            _view = view;
        }

        public string GetName()
        {
            return _view.GetName();
        }

        public void Initialize(ICallbackContractV2 callback)
        {
            _view.Initialize(CallbackConverter.FromContract(callback));
        }

        public void WriteToConsole(string output)
        {
            _view.WriteToConsole(output);
        }

        public object GetSource()
        {
            return _view.GetSource();
        }
    }
}

The important thing to note in this class is the converter. This is needed to translate the contract to the callback.

CallbackConverter
using AddinViewV2.V2;
using ContractV2.V2;

namespace AddinAdapterV2.V2
{
    public class CallbackConverter
    {
        internal static ICallbackV2 FromContract(ICallbackContractV2 callback)
        {
            return new CallbackImpl(callback);
        }
    }
}

In order for this converter to work is to have a concrete implementation of the class you're converting to.

CallbackImpl
using AddinViewV2.V2;
using ContractV2.V2;

namespace AddinAdapterV2.V2
{
    public class CallbackImpl : ICallbackV2
    {
        private ICallbackContractV2 _contract;

        public CallbackImpl(ICallbackContractV2 contract)
        {
            _contract = contract;
        }

        public void DoWork()
        {
            _contract.DoWork();
        }
    }
}

You probably have noticed that my interfaces are listed as V2. This is because I also wanted to show what an upgrade scenario looked like and how to handle conversions to support backwards compatibility. To do this, you'll need to create a new Adapter project to handle the conversion.

AddinAdapterV1ToV2
using AddinView.V1;
using ContractV2.V2;
using System.AddIn.Pipeline;
using System.Diagnostics;

namespace AddinAdapterV1ToV2.V2
{
    [AddInAdapter]
    public class AddinAdapterV1ToV2 : ContractBase, IContractV2
    {
        private IV1 _view;

        public AddinAdapterV1ToV2(IV1 view)
        {
            _view = view;
        }

        public string GetName()
        {
            return _view.GetName();
        }

        public void Initialize(ICallbackContractV2 callback)
        {
            _view.Initialize(CallbackConverter.FromContractV2(callback));
        }

        public void WriteToConsole(string output)
        {
            Debug.WriteLine("Outout is ignored: ", output);
            _view.WriteToConsole();
        }

        public object GetSource()
        {
            return null;
        }
    }
}

Here you'll notice that this class implements the IContractV2 interface but the constructor requires a IV1 interface to be passed. This is what will convert a V1 Plugin to the current Host, allowing for reverse compatibility. You can also do the inverse where you convert the Host to a new Plugin; however, this is only required if you'll be releasing these libraries separate from your Host application. In my code example, I did this just to show what is necessary.

The next issue we ran into was that because it used to be tightly integrated, there were some objects that were passed around by reference. We couldn't get away from this model without a lot of restructuring and re-architecting the base code. We found that the AddIn framework Activator allowed you to specify the AppDomain. So we could use that to load the AddIn into the same AppDomain as the existing project which allows us to once again pass by reference without the need of serialization like you'd normally have to.

Program snippet
                IV2 v2 = token.Activate(AppDomain.CurrentDomain);
                v2.Initialize(new CallbackHandler());

                // Run the add-in.
                v2.WriteToConsole("Hello World From Host!");
                Console.WriteLine(v2.GetName());

                var test = (Stopwatch)v2.GetSource();

                Task.Delay(500).Wait();

                test.Stop();
                Console.WriteLine(test.ElapsedTicks);

You'll notice in the contract that the GetSource method returns an object. I've set it up to return a Stopwatch, which isn't serializable and wouldn't normally be allowed to pass through this framework. By loading the AddIn into the same AppDomain, this works.

The full code base can be found here. This is only a brief description over the trouble areas that I came across. If you have any questions, feel free to post them in the comments section below and I'll do my best to answer them for you.