Software developer in Halifax, Nova Scotia, Canada. Dad to 2 kids.

Find me on the Fediverse in other places:

  • 1 Post
  • 36 Comments
Joined 1 year ago
cake
Cake day: June 6th, 2023

help-circle





  • If using open source projects and sharing my experience by helping others on forums and logging detailed bugs when I find them counts as contribution, then everyday.

    I’m a software dev myself, but I have enough on my plate with my day job and two kids that have to be taken to all manner of activities. I don’t know how all these people find the time to work on free software, probably for little to no compensation, but my hat is off to all of you, wherever you are.








  • Man this one frustrated me because of a subtle difference in the wording of part 1 vs part 2. I had the correct logic from the start, but with an off-by-one error because of my interpretation of the wording. Part 1 says, “any rows or columns that contain no galaxies should all actually be twice as big” while part 2 says, “each empty column should be replaced with 1000000 empty columns”.

    I added 1 column/row in part 1, and 1_000_000 in part 2. But if you’re replacing an empty column with 1_000_000, you’re actually adding 999_999 columns. It took me a good hour to discover where that off-by-one error was coming from.



  • Snippet:

            static void Part1(string data)
            {
                var result = ParseInput(data)
                    .Select(history => ProcessHistory(history, g => g.Length - 1, (a, b) => a + b))
                    .Sum();
    
                Console.WriteLine(result);
            }
    
            static void Part2(string data)
            {
                var result = ParseInput(data)
                    .Select(history => ProcessHistory(history, g => 0, (a, b) => a - b))
                    .Sum();
    
                Console.WriteLine(result);
            }
    
            static int ProcessHistory(
                int[] history,
                Func guessIndex,
                Func collateGuess)
            {
                bool allZeros = true;
    
                var diffs = new int[history.Length - 1];
                for (int i = 0; i < diffs.Length; i++)
                {
                    var diff = history[i + 1] - history[i];
                    diffs[i] = diff;
                    allZeros = allZeros && (diff == 0);
                }
    
                var guess = history[guessIndex(history)];
    
                if (!allZeros)
                {
                    guess = collateGuess(
                        guess,
                        ProcessHistory(diffs, guessIndex, collateGuess));
                }
    
                return guess;
            }
    



  • I barely registered a difference between part 1 and part 2.

    Part 1: 00:00:00.0018302
    Part 2: 00:00:00.0073136
    

    I suppose it took about 3.5 times as long, but I didn’t notice :P

    Edit: I realize that I made the implicit assumption in my solution that it doesn’t make sense to have multiple jokers be interpreted as different values. i.e., The hand with the maximum value will have all Jokers interpreted as the same other card. I think that is true though. It worked out for me anyway.


  • Wow, this is exactly what I did, but in C#. That’s cool.

        public class Hand
        {
            public string Cards;
            public int Rank;
            public int Bid;
        }
    
        public static HandType IdentifyHandType(string hand)
        {
            var cardCounts = hand
                .Aggregate(
                    new Dictionary(),
                    (counts, card) => 
                    {
                        counts[card] = counts.TryGetValue(card, out var count) ? (count + 1) : 1;
                        return counts;
                    })
                .OrderByDescending(kvp => kvp.Value);
    
            using (var cardCount = cardCounts.GetEnumerator())
            {
                cardCount.MoveNext();
                switch (cardCount.Current.Value)
                {
                    case 5: return HandType.FiveOfAKind;
                    case 4: return HandType.FourOfAKind;
                    case 3: { cardCount.MoveNext(); return (cardCount.Current.Value == 2) ? HandType.FullHouse : HandType.ThreeOfAKind; }
                    case 2: { cardCount.MoveNext(); return (cardCount.Current.Value == 2) ? HandType.TwoPairs : HandType.OnePair; }
                }
            }
    
            return HandType.HighCard;
        }
    
        public static Hand SetHandRank(Hand hand, Dictionary cardValues)
        {
            int rank = 0;
            int offset = 0;
    
            var cardValueHand = hand.Cards;
            for (int i = cardValueHand.Length - 1; i >= 0; i--)
            {
                var card = cardValueHand[i];
                var cardValue = cardValues[card];
                var offsetCardValue = cardValue << offset;
                rank |= offsetCardValue;
                offset += 4; // To store values up to 13 we need 4 bits.
            }
    
            // Put the hand type at the high end because it is the most
            // important factor in the rank.
            var handType = (int)IdentifyHandType(hand.Cards);
            var offsetHandType = handType << offset;
            rank |= offsetHandType;
    
            hand.Rank = rank;
            return hand;
        }