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); }
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); }
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(); }
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()); }
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"); }
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); }
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); }
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); }
public PortalMaze(string[] lines) : this(CharMap.FromArray(lines)) { }