예제 #1
0
        private static (CharMap, Car[]) ReadMapAndCars(string[] input)
        {
            var map = CharMap.FromArray(input);

            var cars = map
                       .AllPoints(c => "v^<>".Contains(c))
                       .Select(p =>
            {
                var direction = map[p] switch
                {
                    '>' => Direction.Right,
                    'v' => Direction.Down,
                    '<' => Direction.Left,
                    '^' => Direction.Up,
                    _ => throw new Exception("Unknown direction")
                };
                var pose = Pose.From(p, direction);
                return(new Car(pose));
            })
                       .ToArray();

            // Use the map as-is for driving the cars, so wipe the actual
            // car-symbols away from it.
            foreach (var c in cars)
            {
                map[c.Pose.Point] = c.Pose.Direction == Direction.Up || c.Pose.Direction == Direction.Down ? '|' : '-';
            }

            return(map, cars);
        }
예제 #2
0
        private static SparseMap <bool> GetLights(string[] input)
        {
            var map    = CharMap.FromArray(input);
            var lights = new SparseMap <bool>();

            foreach (var p in map.AllPoints())
            {
                lights[p] = map[p] == '#';
            }
            return(lights);
        }
예제 #3
0
            public Combat(string[] input, int elfAttack)
            {
                _map = CharMap.FromArray(input);

                // Find all units and the size of the map
                _units = _map
                         .AllPoints(c => c == 'E' || c == 'G')
                         .Select(p => new Unit(p, _map[p], _map[p] == 'E' ? elfAttack : 3))
                         .ToArray();
                var(_, max) = _map.MinMax();
                _width      = max.X + 1;
                _height     = max.Y + 1;

                // For debugging, print out the initial position
                ConsoleWrite();
            }
예제 #4
0
        private int CountActivePoints(string[] input, int dimensions, int cycles)
        {
            var points = CharMap
                         .FromArray(input, '.')
                         .AllPoints(ch => ch == '#');

            var space = new Space(dimensions);

            foreach (var p in points)
            {
                space.Set(new[] { (sbyte)p.X, (sbyte)p.Y, (sbyte)0, (sbyte)0 });
            }

            for (var cycle = 0; cycle < cycles; cycle++)
            {
                // We need only investigate the active points and their neighbours
                var pointsToInvestigate = new Space(dimensions);
                foreach (var p in space.Active)
                {
                    var neighbours = space.NeighboursOf(p);
                    pointsToInvestigate.MergeWith(neighbours);
                }

                // Build the next cycle's space
                var nextSpace = new Space(dimensions);
                foreach (var p in pointsToInvestigate.Active)
                {
                    var neighbours       = space.NeighboursOf(p);
                    var activeNeighbours = neighbours.Active.Count(x => space.IsSet(x));

                    var isCellActive = space.IsSet(p);
                    if (isCellActive)
                    {
                        activeNeighbours--;                         // don't count the active cell itself
                    }
                    if (isCellActive && (activeNeighbours == 2 || activeNeighbours == 3) || !isCellActive && activeNeighbours == 3)
                    {
                        nextSpace.Set(p);
                    }
                }

                // Update space after the cycle
                space = nextSpace;
            }

            return(space.Active.Count());
        }
예제 #5
0
        private static int ShortestPath(string[] input, bool returnToZero)
        {
            var map  = CharMap.FromArray(input);
            var maze = new Maze(map)
                       .WithEntry(map.FirstOrDefault(c => c == '0'));
            var allNumbers = map
                             .AllPoints(c => char.IsDigit(c)).ToArray()
                             .Aggregate(0U, (mask, p) => mask |= (uint)(1U << (map[p] - '0')));

            var graph = DuctGraph.BuildUnitGraphFromMaze(maze);

            foreach (var v in graph.Vertices.Values)
            {
                v.Value = new HashSet <uint>();
            }

            var queue = new Queue <(DuctGraph.Vertex, uint, int)>();

            queue.Enqueue((graph.Root, 0U, 0));
            while (queue.Any())
            {
                var(node, found, steps) = queue.Dequeue();

                if (node.Value.Contains(found))
                {
                    continue;
                }
                node.Value.Add(found);

                var ch = map[node.Pos];
                if (char.IsDigit(ch))
                {
                    found |= 1U << (ch - '0');
                    if (found == allNumbers && (!returnToZero || ch == '0'))
                    {
                        return(steps);
                    }
                }

                foreach (var n in node.Edges.Keys.Where(n => !n.Value.Contains(found)))
                {
                    queue.Enqueue((n, found, steps + 1));
                }
            }

            throw new Exception("No path found");
        }
예제 #6
0
        protected override int Part1(string[] input)
        {
            var seats    = CharMap.FromArray(input);
            var occupied = 0;

            while (true)
            {
                seats = seats.Transform((p, ch) =>
                                        ch == 'L' && p.LookDiagonallyAround().All(c => seats[c] != '#') ? '#' :
                                        ch == '#' && p.LookDiagonallyAround().Count(c => seats[c] == '#') >= 4 ? 'L' :
                                        ch
                                        );
                var n = seats.Count('#');
                if (occupied == n)
                {
                    break;
                }
                occupied = n;
            }
            return(occupied);
        }
예제 #7
0
        protected override int Part1(string[] input)
        {
            var map = CharMap.FromArray(input, '.');

            var(_, p2) = map.MinMax();
            var p = Pose.From(p2.X / 2, p2.Y / 2, Direction.Up);

            var infections = 0;

            for (var i = 0; i < 10000; i++)
            {
                // If the current node is infected, it turns to its right. Otherwise, it turns to its left. (Turning is done in-place; the current node does not change.)
                // If the current node is clean, it becomes infected. Otherwise, it becomes cleaned. (This is done after the node is considered for the purposes of changing direction.)
                // The virus carrier moves forward one node in the direction it is facing.
                if (map[p.Point] == '#')
                {
                    p.TurnRight();
                }
                else
                {
                    p.TurnLeft();
                }
                if (map[p.Point] == '.')
                {
                    map[p.Point] = '#';
                    infections++;
                }
                else
                {
                    map[p.Point] = '.';
                }
                p.Move(1);
            }

            return(infections);
        }
예제 #8
0
        protected override int Part2(string[] input)
        {
            var inputMap = CharMap.FromArray(input);

            // Use a 2D array for fastest storage (faster than CharMap/sparse array
            // or Dictionary). Trials show that x,y stray <300 steps from origin so
            // that'll do with a bit of margin. Shift all x,y coordinates into positive
            // numbers by xyOffset so x,y can index the char[,]-map directly.
            var xyOffset = 500;
            var map      = new char[xyOffset * 2, xyOffset *2];

            // Populate the map with all known infections
            foreach (var(p, ch) in inputMap.All(c => c == '#'))
            {
                map[p.X + xyOffset, p.Y + xyOffset] = '#';
            }

            // Initial x,y is at the centre of the map
            var(_, size) = inputMap.MinMax();
            var x = (uint)size.X / 2 + xyOffset;
            var y = (uint)size.Y / 2 + xyOffset;

            // Setup fast lookups for determining dx/dy-movements and turns,
            // using the current direction as index.
            // This is faster than a switch or PointWithDirection.
            var(up, right, down, left) = (0, 1, 2, 3);
            var dx         = new [] { 0, 1, 0, -1 };
            var dy         = new [] { -1, 0, 1, 0 };
            var turnleft   = new [] { left, up, right, down };
            var turnright  = new [] { right, down, left, up };
            var turnaround = new [] { down, left, up, right };

            int dir        = up;
            var infections = 0;

            for (var i = 0; i < 10_000_000; i++)
            {
                // Clean nodes become weakened.
                // Weakened nodes become infected.
                // Infected nodes become flagged.
                // Flagged nodes become clean.
                // Decide which way to turn based on the current node:
                //   If it is clean, it turns left.
                //   If it is weakened, it does not turn, and will continue moving in the same direction.
                //   If it is infected, it turns right.
                //   If it is flagged, it reverses direction, and will go back the way it came.
                // Modify the state of the current node, as described above.
                // The virus carrier moves forward one node in the direction it is facing.
                switch (map[x, y])
                {
                case '\0': map[x, y] = 'w'; dir = turnleft[dir]; break;

                case 'w': map[x, y] = '#'; infections++; break;

                case '#': map[x, y] = 'f'; dir = turnright[dir]; break;

                case 'f': map[x, y] = '\0'; dir = turnaround[dir]; break;
                }
                x += dx[dir];
                y += dy[dir];
            }

            return(infections);
        }
예제 #9
0
 public PortalMaze(string[] lines)
     : this(CharMap.FromArray(lines))
 {
 }