Just another Swedish programming sysadmin person.
Coffee is always the answer.

And beware my spaghet.

  • 59 Posts
  • 246 Comments
Joined 2 years ago
cake
Cake day: June 11th, 2023

help-circle

  • They actually did a study on it after rolling back to Windows, and it turned out to not have failed due to technical difficulties at all.
    If I recall correctly they stated that something like 80-90% of all issues reported during the period were due to badly designed processes - processes which were the same as in Windows, and the number of technical issues actually dropped.

    Certainly, the fact that Microsoft promised to build a fancy new HQ in the city if they switched back to Windows can’t have had anything to do with the choice to roll back…

















  • Ended up oversleeping somewhat, so I did the first part on the way to work using flood fills over a global visited set, and now that work’s over I’ve sat down to expand that solution to do corner counting for part two as well.

    C#
    char[] map = new char[0];
    (int X, int Y) size = (0, 0);
    
    public void Input(IEnumerable<string> lines)
    {
      map = string.Concat(lines).ToCharArray();
      size = (lines.First().Length, lines.Count());
    }
    
    Dictionary<HashSet<(int,int)>,int> areas = new Dictionary<HashSet<(int,int)>,int>();
    public void PreCalc()
    {
      HashSet<(int, int)> visited = new HashSet<(int, int)>();
      for (int y = 0; y < size.Y; ++y)
        for (int x = 0; x < size.X; ++x)
        {
          var at = (x, y);
          if (visited.Contains(at))
            continue;
    
          var area = Flood((x, y), visited);
          areas[area.Area] = area.Perim;
        }
    }
    
    public void Part1()
    {
      int sum = areas.Select(kv => kv.Key.Count * kv.Value).Sum();
    
      Console.WriteLine($"Fencing total: {sum}");
    }
    public void Part2()
    {
      int sum = areas.Select(kv => kv.Key.Count * countCorners(kv.Key)).Sum();
    
      Console.WriteLine($"Fencing total: {sum}");
    }
    
    readonly (int dX, int dY)[] links = new[] { (1, 0), (0, 1), (-1, 0), (0, -1) };
    (HashSet<(int,int)> Area, int Perim) Flood((int X, int Y) from, HashSet<(int X, int Y)> visited)
    {
      char at = map[from.Y * size.X + from.X];
    
      (HashSet<(int,int)> Area, int Perim) ret = (new HashSet<(int,int)>(), 0);
      visited.Add(from);
      ret.Area.Add(from);
    
      foreach (var link in links)
      {
        (int X, int Y) newAt = (from.X + link.dX, from.Y + link.dY);
        char offset ;
        if (newAt.X < 0 || newAt.X >= size.X || newAt.Y < 0 || newAt.Y >= size.Y)
          offset = '\0';
        else
          offset = map[newAt.Y * size.X + newAt.X];
    
        if (offset == at)
        {
          if (visited.Contains(newAt))
            continue;
    
          var nextArea = Flood(newAt, visited);
          ret.Area.UnionWith(nextArea.Area);
          ret.Perim += nextArea.Perim;
        }
        else
        {
          ret.Perim += 1;
        }
      }
    
      return ret;
    }
    
    readonly (int dX, int dY)[] cornerPoints = new[] { (0, 0), (1, 0), (1, 1), (0, 1) };
    readonly int[] diagonalValues = new[] { (2 << 0) + (2 << 2), (2 << 1) + (2 << 3) };
    int countCorners(HashSet<(int X, int Y)> points)
    {
      int corners = 0;
      var bounds = findBounds(points);
      for (int y = bounds.minY - 1; y < bounds.maxY + 1; ++y)
        for (int x = bounds.minX - 1; x < bounds.maxX + 1; ++x)
        {
          var atPoint = cornerPoints.Select(c => points.Contains((x + c.dX, y + c.dY)));
          var before = corners;
          if (atPoint.Where(c => c).Count() % 2 == 1)
            corners++;
          else if (diagonalValues.Contains(atPoint.Select((c, i) => c ? (2 << i) : 0).Sum()))
            corners += 2;
        }
    
      return corners;
    }
    
    (int minX, int minY, int maxX, int maxY) findBounds(HashSet<(int X, int Y)> points)
    {
      (int minX, int minY, int maxX, int maxY) ret = (int.MaxValue, int.MaxValue, int.MinValue, int.MinValue);
      foreach (var point in points)
      {
        ret.minX = Math.Min(ret.minX, point.X);
        ret.minY = Math.Min(ret.minY, point.Y);
        ret.maxX = Math.Max(ret.maxX, point.X);
        ret.maxY = Math.Max(ret.maxY, point.Y);
      }
    
      return ret;
    }
    


  • And now we get into the days where caching really is king. My first attempt didn’t go so well, I tried to handle the full list result as one cache step, instead of individually caching the result of calculating each stone per step.

    I think my original attempt is still calculating at home, but I finished up this much better version on the trip to work.
    All hail public transport.

    C#
    List<long> stones = new List<long>();
    public void Input(IEnumerable<string> lines)
    {
      stones = string.Concat(lines).Split(' ').Select(v => long.Parse(v)).ToList();
    }
    
    public void Part1()
    {
      var expanded = TryExpand(stones, 25);
    
      Console.WriteLine($"Stones: {expanded}");
    }
    public void Part2()
    {
      var expanded = TryExpand(stones, 75);
    
      Console.WriteLine($"Stones: {expanded}");
    }
    
    public long TryExpand(IEnumerable<long> stones, int steps)
    {
      if (steps == 0)
        return stones.Count();
      return stones.Select(s => TryExpand(s, steps)).Sum();
    }
    Dictionary<(long, int), long> cache = new Dictionary<(long, int), long>();
    public long TryExpand(long stone, int steps)
    {
      var key = (stone, steps);
      if (cache.ContainsKey(key))
        return cache[key];
    
      var result = TryExpand(Blink(stone), steps - 1);
      cache[key] = result;
      return result;
    }
    
    public IEnumerable<long> Blink(long stone)
    {
      if (stone == 0)
      {
        yield return 1;
        yield break;
      }
      var str = stone.ToString();
      if (str.Length % 2 == 0)
      {
        yield return long.Parse(str[..(str.Length / 2)]);
        yield return long.Parse(str[(str.Length / 2)..]);
        yield break;
      }
      yield return stone * 2024;
    }
    

  • Nice to have a really simple one for a change, both my day 1 and 2 solutions worked on their very first attempts.
    I rewrote the code to combine the two though, since the implementations were almost identical for both solutions, and also to replace the recursion with a search list instead.

    C#
    int[] heights = new int[0];
    (int, int) size = (0, 0);
    
    public void Input(IEnumerable<string> lines)
    {
      size = (lines.First().Length, lines.Count());
      heights = string.Concat(lines).Select(c => int.Parse(c.ToString())).ToArray();
    }
    
    int trails = 0, trailheads = 0;
    public void PreCalc()
    {
      for (int y = 0; y < size.Item2; ++y)
        for (int x = 0; x < size.Item1; ++x)
          if (heights[y * size.Item1 + x] == 0)
          {
            var unique = new HashSet<(int, int)>();
            trails += CountTrails((x, y), unique);
            trailheads += unique.Count;
          }
    }
    
    public void Part1()
    {
      Console.WriteLine($"Trailheads: {trailheads}");
    }
    public void Part2()
    {
      Console.WriteLine($"Trails: {trails}");
    }
    
    int CountTrails((int, int) from, HashSet<(int,int)> unique)
    {
      int found = 0;
    
      List<(int,int)> toSearch = new List<(int, int)>();
      toSearch.Add(from);
    
      while (toSearch.Any())
      {
        var cur = toSearch.First();
        toSearch.RemoveAt(0);
    
        int height = heights[cur.Item2 * size.Item1 + cur.Item1];
        for (int y = -1; y <= 1; ++y)
          for (int x = -1; x <= 1; ++x)
          {
            if ((y != 0 && x != 0) || (y == 0 && x == 0))
              continue;
    
            var newAt = (cur.Item1 + x, cur.Item2 + y);
            if (newAt.Item1 < 0 || newAt.Item1 >= size.Item1 || newAt.Item2 < 0 || newAt.Item2 >= size.Item2)
              continue;
    
            int newHeight = heights[newAt.Item2 * size.Item1 + newAt.Item1];
            if (newHeight - height != 1)
              continue;
    
            if (newHeight == 9)
            {
              unique.Add(newAt);
              found++;
              continue;
            }
    
            toSearch.Add(newAt);
          }
      }
    
      return found;
    }