private static int CountLightsOnAfter(SparseMap <bool> lights, int rounds, bool cornersAreStuck) { var corners = lights.Corners().ToList(); // A light which is on stays on when 2 or 3 neighbors are on, and turns off otherwise. // A light which is off turns on if exactly 3 neighbors are on, and stays off otherwise. for (var i = 0; i < rounds; i++) { var nextlights = new SparseMap <bool>(); foreach (var(p, on) in lights.All()) { if (cornersAreStuck && corners.Any(corner => p == corner)) { nextlights[p] = true; } else { var n = p.LookDiagonallyAround().Count(x => lights[x]); nextlights[p] = on && (n == 2 || n == 3) || !on && (n == 3); } } lights = nextlights; } var lightsOn = lights.AllPoints(c => c).Count(); return(lightsOn); }
private SparseMap <char> GenerateMap(IntCodeMachine icm) { var map = new SparseMap <char>(true); (int x, int y)cur = (0, 0); while (icm.Step()) { while (icm.Output.CanRead) { var tile = icm.Output.Read(); //Console.Write((char)tile); if (tile == 10) { cur.x = 0; cur.y++; } else { map.Set(cur, (char)tile); cur.x++; } } if (icm.WantInput) { } } return(map); }
private int FindIntersections(SparseMap <char> map) { var alignment = 0; for (int y = map.Ymin; y < map.Ymax; y++) { for (int x = map.Xmin; x < map.Xmax; x++) { var c = map.Get((x, y)); var neigh = 0; if (c == '#') { neigh += map.Get((x - 1, y)) == c ? 1 : 0; neigh += map.Get((x + 1, y)) == c ? 1 : 0; neigh += map.Get((x, y - 1)) == c ? 1 : 0; neigh += map.Get((x, y + 1)) == c ? 1 : 0; } if (neigh > 2) { map.Set((x, y), 'O'); alignment += x * y; } } } return(alignment); }
protected override int Part2(string[] input) { var map = new SparseMap <int>(); var overlap = 0; foreach (var s in input) { var(x1, y1, x2, y2) = s.RxMatch("%d,%d -> %d,%d").Get <int, int, int, int>(); var dx = x2 == x1 ? 0 : x2 > x1 ? 1 : -1; var dy = y2 == y1 ? 0 : y2 > y1 ? 1 : -1; var D = Math.Max(Math.Abs(x2 - x1), Math.Abs(y2 - y1)); for (var d = 0; d <= D; d++) { var x = x1 + d * dx; var y = y1 + d * dy; if (map[x][y] == 1) { //Console.WriteLine($"{x}, {y1}"); overlap++; } map[x][y]++; } } return(overlap); }
static Cluster MoveLeft(SparseMap <int> distField, Cluster c) { // Keep track of the paths examined in a stack, starting with // the Goal-point. MoveTo the Left will find the next cluster, // ie the one that took the fewest moves. var path = new Stack <Point>(); path.Push(c.Goal); Cluster nextCluster = null; MoveTo(path, c.Goal.Left); return(nextCluster); void MoveTo(Stack <Point> path, Point moveto) { // The path is all the discs that will have to be moved var movefrom = path.Peek(); var moves = path.Count(); // If can't do any better than the moves found for the best next // cluster then don't examine this path any further if (moves + (distField?[movefrom] ?? 0) >= nextCluster?.Moves) { return; } // If we can move the content then we know it will be the sortest // path found so far (see above) so move the entire centipede of // disc contents all the way back to the original goal+destination. if (c.CanMoveContentTo(movefrom, moveto)) { // yes we can nextCluster = c.Copy(); foreach (var p in path) { nextCluster.MoveContent(p, moveto); moveto = p; } return; } // No room on this disc. Examine all neighbors that has a disc that // would fit the data if it was empty. If there's a distance-field // (only for the first move) then visit the closest ones first; if // this optimization isn't done then it'll take forever. var sizeNeeded = c.Disks[movefrom].Used; var neighborsWithEnoughSize = moveto .LookAround() .Where(p => !path.Contains(p) && c.Disks?[p]?.Size >= sizeNeeded) .ToArray(); path.Push(moveto); foreach (var n in neighborsWithEnoughSize.OrderBy(n => distField?[n] ?? 0)) { MoveTo(path, n); } path.Pop(); } }
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 override void First() { var icm = new IntCodeMachine(); icm.Init("Day15/repairdroid.ic"); map = CreateMap(icm); int val = ShortestPath(map); Echo($"Shortest path from (0,0) to oxygen tank in {val} steps"); ValidateAnswer(val, 218); }
public override void Second() { var icm = new IntCodeMachine(); icm.Init("Day17/cameras.ic"); icm.mem[0] = 2; // Override movement control var map = new SparseMap <char>(true); var y0 = Console.CursorTop; (int x, int y)cur = (0, 0); int cmd = 0, cIdx = 0; var dust = 0L; while (icm.Step()) { while (icm.Output.CanRead) { var tile = icm.Output.Read(); if (tile == 10) { cur.x = 0; cur.y++; } else if (tile < 256) { map.Set(cur, (char)tile); cur.x++; } else { dust = tile; } } if (icm.WantInput) { if (cmd < movements.Length && cIdx < movements[cmd].Length) { icm.Input.Write((long)movements[cmd][cIdx]); cIdx++; } else { cmd++; cIdx = 0; } } } map.Render(); Echo($"Dust collected: {dust}"); ValidateAnswer(dust, 742673); }
protected override int Part2(string[] input) { var key = input[0]; // Geenrate al hashes and transform them into a sparsemap, which is // really easy (and fast) to walk around var hashes = Enumerable.Range(0, 128) .Select(i => Common.KnotHash.Hash($"{key}-{i}")) .ToArray(); var map = new SparseMap <bool>(); for (var y = 0; y < hashes.Length; y++) { var hash = hashes[y]; for (var x = 0; x < hash.Length; x++) { for (var b = 0; b < 8; b++) { if ((hash[x] & 1U << (7 - b)) != 0) { map[x * 8 + b][y] = true; } } } } // Find the next set bit, clear the entire region, and count it // until all set bits has been cleared var n = 0; while (true) { var set = map.FirstOrDefault(b => b); if (set == null) { break; } ClearRegion(set); n++; } return(n); void ClearRegion(Point pos) { map[pos] = false; foreach (var p in pos.LookAround().Where(p => map[p])) { ClearRegion(p); } } }
protected override int Part2(string[] input) { var cluster = new Cluster(input); // For moving the first point (and only for that, it seems) we need to // know the preferred direction for moving data around. There is 1 empty // disc and it's the only one we can move anything onto, and there are // a number of big blocks that can't be moved at all. With that in mind // we create a distance-field with shortest distance from (== to) the // empty spot, working around the big blocks. When examining the routes // this will tell us which discs are closest to the empty disc. var distField = new SparseMap <int>(); var distq = new Queue <Point>(); var bigblocks = new HashSet <Point>(cluster.Disks.All(d => d.Used > 100).Select(x => x.Item1)); var empty = cluster.Disks.All(d => d.Used == 0).Single().Item1; distq.Enqueue(empty); while (distq.Any()) { var p = distq.Dequeue(); var dist = distField[p]; var neighbors = p.LookAround().Where(n => distField[n] == 0 && cluster.Disks[n] != null && !bigblocks.Contains(n)); foreach (var n in neighbors) { distField[n] = dist + 1; distq.Enqueue(n); } } // The stragegy: // Move the Goal to the left, one spot at a time. (That seems to produce the // shortest path overall). Do so by examining which of the neighbors has room // to spare can can move their data away, either directly or by pushing data // data away from their neighbors, recursively. // We only need the distance-field for the first move. It's costly to calculate // for the remaining moves and doesn't seem to make a difference so just set // it to null after the first move. var moves = 0; while (cluster.Goal != Point.Origin) { cluster = MoveLeft(distField, cluster); moves += cluster.Moves; distField = null; //cluster.WriteToConsole(); } return(moves);
protected override int Part1(string[] input) { var raw = input[0]; var map = new SparseMap <int>(); var pos = Point.From(0, 0); map[pos]++; foreach (var ch in raw) { pos = pos.Move(ch); map[pos]++; } var visited = map.AllPoints().Count(); return(visited); }
protected override int Part2(string[] input) { var n = int.Parse(input[0]); var map = new SparseMap <int>(0); // Seed the map at (0,0) with 1 // Then spiral out (skip 0,0) while building up map-values map[Point.Origin] = 1; foreach (var pos in Point.Origin.SpiralFrom().Skip(1)) { var v = map[pos] = pos.LookDiagonallyAround().Sum(p => map[p]); if (v > n) { return(v); } } throw new Exception("Not found"); }
public Cluster(string[] input) { Disks = new SparseMap <Disk>(); foreach (var line in input.SkipWhile(x => !x.StartsWith("/dev"))) { // root@ebhq-gridcenter# df -h // Filesystem Size Used Avail Use% // /dev/grid/node-x0-y2 90T 70T 20T 77% var(x, y, size, used) = line .RxMatch("/dev/grid/node-x%d-y%d %DT %DT") .Get <int, int, int, int>(); Disks[x][y] = new Disk { Size = size, Used = used }; } Goal = Point.From(Disks.MinMax().Item2.X, 0); }
protected override int Part1(string[] input) { var map = new SparseMap <int>(); var overlap = 0; foreach (var s in input) { var(x1, y1, x2, y2) = s.RxMatch("%d,%d -> %d,%d").Get <int, int, int, int>(); if (x1 == x2) { var yy1 = Math.Min(y1, y2); var yy2 = Math.Max(y1, y2); for (var y = yy1; y <= yy2; y++) { if (map[x1][y] == 1) { //Console.WriteLine($"{x1}, {y}"); overlap++; } map[x1][y]++; } } if (y1 == y2) { var xx1 = Math.Min(x1, x2); var xx2 = Math.Max(x1, x2); for (var x = xx1; x <= xx2; x++) { if (map[x][y1] == 1) { //Console.WriteLine($"{x}, {y1}"); overlap++; } map[x][y1]++; } } } return(overlap); }
protected override int Part2(string[] input) { var raw = input[0]; var map = new SparseMap <int>(); var deliveries = new Point[] { Point.From(0, 0), Point.From(0, 0) }; foreach (var d in deliveries) { map[d]++; } for (var i = 0; i < raw.Length; i++) { var turn = i % deliveries.Length; deliveries[turn] = deliveries[turn].Move(raw[i]); map[deliveries[turn]]++; } var visited = map.AllPoints().Count(); return(visited); }
private static PortalGraph BuildSimpleGraph(PortalMaze maze) { var walked = new SparseMap <PortalGraph.Vertex>(); var graph = new PortalGraph(); graph.AddVertex(maze.Entry); BuildSimpleGraph(graph.Root); return(graph); void BuildSimpleGraph(PortalGraph.Vertex node) { while (walked[node.Pos] == null) { walked[node.Pos] = node; var positions = node.Pos .LookAround() .Select(p => new { Pos = p, Dest = maze.Transform(p) }) .Where(x => maze.Map[x.Dest] == '.') .Where(x => walked[x.Dest] == null || !walked[x.Dest].Edges.ContainsKey(node)) .ToList(); foreach (var p in positions.Where(x => walked[x.Dest] != null).ToList()) { var existing = walked[p.Dest]; var portal = maze.Portals[p.Pos]; var portalValue = portal == null ? 0 : portal.IsDownward ? -1 : 1; var portalName = portal == null ? null : portal.Name; node.Value = portal; existing.Value = portal; node.Edges[existing] = portalValue; existing.Edges[node] = -portalValue; positions.Remove(p); } switch (positions.Count()) { case 0: return; case 1: var p = positions.First(); var next = graph.AddVertex(p.Dest); //graph.Vertices.Add(next); var portal = maze.Portals[p.Pos]; var portalValue = portal == null ? 0 : portal.IsDownward ? -1 : 1; node.Value = portal; next.Value = portal; node.Edges[next] = portalValue; next.Edges[node] = -portalValue; node = next; break; default: var forks = positions.Select(x => graph.AddVertex(x.Dest)).ToList(); foreach (var fork in forks) { BuildSimpleGraph(fork); } return; } } } }
/// <summary>Sample invocation of path-finding code in <see cref="EMonk.Pathfinding.PathMap"/>.</summary> static void PathfinderSample() { Console.Clear(); Console.WriteLine("Pathfinding Sample"); Console.WriteLine("=================="); // create a simple set of map nodes Console.Write("Generating sample map tiles"); SparseMap <MapPosition, MapNode> map = new SparseMap <MapPosition, MapNode>(); // add strip of high-cost tiles for (int i = 20; i <= 30; ++i) { map[new MapPosition(i, 15)] = new MapNode(true, 15); } // create a lower cost hole through strip map[new MapPosition(22, 15)] = new MapNode(true, 2); // add a medium-cost block in the way of the normal path map[new MapPosition(23, 14)] = new MapNode(true, 4); map[new MapPosition(22, 14)] = new MapNode(true, 4); Console.WriteLine(); // generate path map for this MapPosition origin = new MapPosition(25, 25); double maxCost = 50; Console.Write("Generating PathMap for Origin={0}, MaxCost={1}", origin, maxCost); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); PathMap <MapNode> m = PathMap <MapNode> .Generate(map, origin, maxCost); sw.Stop(); if (m == null) { Console.WriteLine(" failed!"); Console.WriteLine(); ShowError("Error occurred while calculating PathMap."); return; } Console.WriteLine(" [{0:0.00}ms]", sw.Elapsed.TotalMilliseconds); Console.WriteLine("Statistics:"); Console.WriteLine("\tInput Nodes \t{0}", map.Count); Console.WriteLine("\tOutput Positions\t{0}", m.Count); Console.WriteLine("\tScan Iterations \t{0}", m.Iterations); Console.WriteLine(); // find path to end-point MapPosition target = new MapPosition(25, 14); Console.Write("Finding path to target position {0}", target); if (null == m[target]) { Console.WriteLine(" failed."); Console.WriteLine(); ShowError("Target was not present in PathMap's potential move set."); return; } double pcost; sw.Restart(); MapPosition[] path = m.GetPath(new MapPosition(25, 14), out pcost); sw.Stop(); if (path == null) { Console.WriteLine(" failed!"); Console.WriteLine(); ShowError("Error occurred while calculating path."); return; } Console.WriteLine(" [{0:0.00}ms]", sw.Elapsed.TotalMilliseconds); Console.WriteLine(); // Display generated path Console.WriteLine("Generated path from {0}:", path.First()); Console.WriteLine("\tDirection\tNew Position\tCost\tTotal Cost"); MapPosition prev = null; foreach (var curr in path) { if (prev != null) { Console.WriteLine("\t{0,-10}\t{1,-9}\t{2}\t{3}", prev.DirectionTo(curr).ToString(), curr, (m[curr].TotalCost - m[prev].TotalCost), m[curr].TotalCost); } prev = curr; } Console.WriteLine(); Console.WriteLine("Press any key to continue"); Console.ReadKey(); }
public PortalMaze(CharMap map) : base(map) { var portalinfo = Map.AllPoints(char.IsUpper).OrderBy(p => p.Y).ThenBy(p => p.X); var portalsByName = new Dictionary <string, List <Tuple <Point, Point> > >(); // Map all entry-portals foreach (var p in portalinfo) { if (Map[p.Down] == '.') { // X // p -> Y <- exit // . <- arrival AddPortal(p.Up, p, p, p.Down); } else if (Map[p.Up] == '.') { // . <- arrival // p -> X <- exit // Y AddPortal(p, p.Down, p, p.Up); } //}; //// Next find the portal exits and match with their entry //foreach (var p in portalinfo) //{ else if (Map[p.Right] == '.') { // exit vv arrival // XY. // ^ p AddPortal(p.Left, p, p, p.Right); } else if (Map[p.Left] == '.') { // arrival vv exit // .XY // ^ p AddPortal(p, p.Right, p, p.Left); } } ; // Link up portals Entry = portalsByName["AA"].First().Item2; Exit = portalsByName["ZZ"].First().Item2; Map[portalsByName["AA"].First().Item1] = '#'; Map[portalsByName["ZZ"].First().Item1] = '#'; ExternalMapPoints = new Point[] { Exit }; Portals = new SparseMap <Portal>(); var area = Map.MinMax(); foreach (var pair in portalsByName.Where(p => p.Key != "AA" && p.Key != "ZZ")) { var p1 = pair.Value[0]; var p2 = pair.Value[1]; Portals[p1.Item1] = new Portal { Name = pair.Key, Pos = p2.Item2, IsDownward = !IsOuterPortal(p1.Item1) }; Portals[p2.Item1] = new Portal { Name = pair.Key, Pos = p1.Item2, IsDownward = !IsOuterPortal(p2.Item1) }; } bool IsOuterPortal(Point p) => p.X <4 || p.X> area.Item2.X - 4 || p.Y <4 || p.Y> area.Item2.Y - 4; void AddPortal(Point pos1, Point pos2, Point departure, Point arrival) { var name = new string(new char[] { Map[pos1], Map[pos2] }); if (!portalsByName.TryGetValue(name, out var pn)) { portalsByName[name] = new List <Tuple <Point, Point> >(); } portalsByName[name].Add(new Tuple <Point, Point>(departure, arrival)); } }