Beispiel #1
0
        public override string Solve(string input, bool part2)
        {
            foreach (var image in GetGroupedLines(input))
            {
                images.Add(new CameraImage(GetLines(image)));
            }
            dimension = (int)Math.Floor(Math.Sqrt(images.Count));

            Render(false);
            Console.WriteLine();

            List <(int, int, char)> borders = new List <(int, int, char)>();

            foreach (CameraImage image in images)
            {
                for (int i = 0; i < 2; ++i)
                {   //get all borders for every image. since Right and bottom basically read reversed, we don't need to flip and only do a 180.
                    //since the value for example for a border at the left would be the same for the same border at the top we can skip this rotation step
                    image.RotateRight();
                    image.RotateRight();
                    borders.Add((image.ID, image.TopBorderNr, 'U'));
                    borders.Add((image.ID, image.BottomBorderNr, 'D'));
                    borders.Add((image.ID, image.LeftBorderNr, 'L'));
                    borders.Add((image.ID, image.RightBorderNr, 'R'));
                }
            }

            //Group the images according to how they share a border
            Lookup <int, (int, int, char)> tileGroups = (Lookup <int, (int, int, char)>)borders.ToLookup(k => k.Item2, v => v);
            //Separate these groups for outer and inner connections
            //(outer connection being the image border that connects to no other image and is therefore part of the outer border of the final image)
            var borderConnections = tileGroups.Where(x => x.Count() == 1);
            var innerConnections  = tileGroups.Where(x => x.Count() == 2);
            //all borders must be pairs. there can't be a border value, that matches more than 2 images. at least i would panic a bit in that case.
            var undetermined = tileGroups.Where(x => x.Count() > 2);

            if (undetermined.Count() > 0)
            {
                throw new InvalidOperationException("Some borders couldn't be assigned !!");
            }


            //reorganize the inner connections, so that we don't have duplicates (bidirectional) anymore
            List <(int, int, int, string, int, string)> uniqueInnerConnections = new List <(int, int, int, string, int, string)>();

            foreach (var connection in innerConnections)
            {
                (int id1, int border1, char side1) = connection.ElementAt(0);
                (int id2, int border2, char side2) = connection.ElementAt(1);

                var detected = uniqueInnerConnections.Where(x =>
                                                            (id1 == x.Item1 || id1 == x.Item2) &&
                                                            (id2 == x.Item1 || id2 == x.Item2)
                                                            ).ToList();

                string sideName = side1.ToString() + side2.ToString();

                if (detected.Count() == 0)
                {
                    uniqueInnerConnections.Add((id1, id2, connection.Key, sideName, 0, ""));
                }
                else
                {
                    (int dId1, int dId2, int dBorder1, string dSide1, int dBorder2, string dSide2) = detected[0];
                    uniqueInnerConnections[uniqueInnerConnections.IndexOf(detected[0])]            =
                        (dId1, dId2, dBorder1, dSide1, connection.Key, sideName);
                }
            }

            //reorganize the outer borders, so that all possible border values are assigned to th same image
            Dictionary <int, List <(int, char)> > uniqueBorderConnections = new Dictionary <int, List <(int, char)> >();

            foreach (var connection in borderConnections)
            {
                (int id1, int border1, char side) = connection.ElementAt(0);

                if (!uniqueBorderConnections.ContainsKey(id1))
                {
                    uniqueBorderConnections.Add(id1, new List <(int, char)>()
                    {
                        (connection.Key, side)
                    });
                }
                else
                {
                    List <(int, char)> dBorders = uniqueBorderConnections[id1];
                    if (dBorders.Count >= 1 && dBorders.Count <= 4)
                    {
                        dBorders.Add((connection.Key, side));
                    }
                }
            }



            if (part2)
            {
                ArrangeImage(uniqueBorderConnections, uniqueInnerConnections);
                //Create an image from the rendered Map
                CameraImage map = new CameraImage(GetLines("Tile 2020:\r\n" + Render(true)));
                Console.WriteLine();
                //Create an image from the monster string
                CameraImage monster       = new CameraImage(GetLines("Tile 2020:\r\n..................#.\r\n#....##....##....###\r\n.#..#..#..#..#..#..."));
                int         monsterWidth  = monster.TopBorder.Count;
                int         monsterHeight = monster.Image.Count;

                //Set up trying to find the monsters
                int  currMonsterCnt = 0;
                int  rotationCount  = 0;
                bool flipped        = false;
                Console.SetCursorPosition(0, 0);
                while (currMonsterCnt == 0)
                {
                    currMonsterCnt = 0;
                    //Going over every line in the map except the last three, because our monster is 3 rows tall
                    for (int y = 0; y <= map.Image.Count - monsterHeight; ++y)
                    {   //Moving along this line leaving enough space for the width of our monster.
                        for (int x = 0; x <= map.Image[0].Count - monsterWidth; ++x)
                        {
                            //go over the lines of the monster
                            bool isMonster = true;
                            for (int monsterY = 0; monsterY < monsterHeight; ++monsterY)
                            {
                                //Get the area of the current line of the map and the monster as number (bitmask)
                                long mappedValue = Bitwise.GetBitMaskLong(map.Image[y + monsterY].GetRange(x, monsterWidth));
                                long monsterRow  = Bitwise.GetBitMaskLong(monster.Image[monsterY]);
                                //if the map contains the monster, the and join should output a perfect replica of the monster
                                if ((mappedValue & monsterRow) != monsterRow)
                                {//otherwise we don't have a complete monster
                                    isMonster = false;
                                    break;
                                }
                            }
                            if (isMonster)
                            {   //When we got a full monster increase the counter and censor the area, the monster is located in
                                //I was to lazy, to work out the pixels, that are part of the monster so we just draw  in the area the monster takes up (monsterWidth x monsterHeight)
                                for (int monsterY = 0; monsterY < monsterHeight; ++monsterY)
                                {
                                    Console.SetCursorPosition(x, y + monsterY);
                                    //MArk the area with a letter to differentiate monsters. especially those that overlap
                                    Console.WriteLine("".PadLeft(monsterWidth, (char)(0x41 + currMonsterCnt)));
                                }
                                ++currMonsterCnt;
                            }
                        }
                    }


                    if (currMonsterCnt == 0)
                    {//if we didn't find a single monster, rotate the image by 90 degrees
                        map.RotateRight();
                        Render(true);
                        if (++rotationCount >= 4)
                        {//we did a full 360. Perhaps we need to flip
                            if (flipped)
                            {
                                if (rotationCount >= 5)//we flipped one more time to be sure. this image couldn't be properly aligned. that shouldn't happen :(
                                {
                                    throw new InvalidOperationException("LÖÖP");
                                }
                                else
                                {
                                    continue;
                                }
                            }
                            map.Flip();
                            rotationCount = 0;
                            flipped       = true;
                        }
                    }
                }

                Console.CursorTop = map.Image.Count + 1;
                int roughness = map.ToString().Where(x => x == '#').Count();
                roughness -= monster.ToString().Where(x => x == '#').Count() * currMonsterCnt;
                return("Monsters Found: " + currMonsterCnt + "\r\nRoughness: " + roughness);
            }
            else
            {//part 1 only needs the product of the ids of the corner tiles
                long product = 1;
                foreach (var connection in uniqueBorderConnections.Where(x => x.Value.Count == 4))
                {
                    product *= connection.Key;
                }
                return("Product of corner tile-IDs: " + product);
            }
        }