예제 #1
0
 private static void ParseBotInstruction(ref SpanReader parser, out int bot, out bool isLowBot, out int low, out bool isHighBot, out int high)
 {
     parser.SkipLength("bot ".Length);
     bot = parser.ReadPosIntUntil(' ');
     parser.SkipLength("gives low to ".Length);
     isLowBot = parser.Peek() == 'b';
     parser.SkipLength(isLowBot ? "bot ".Length : "output ".Length);
     low = parser.ReadPosIntUntil(' ');
     parser.SkipLength("and high to ".Length);
     isHighBot = parser.Peek() == 'b';
     parser.SkipLength(isHighBot ? "bot ".Length : "output ".Length);
     high = parser.ReadPosIntUntil('\n');
 }
예제 #2
0
    private static FieldsData ParseFields(ref SpanReader reader)
    {
        int departureFieldsIndex = 0;

        int[] departureFields = new int[6];

        var fieldList = new List <Field>();

        int maxFieldVal = int.MinValue;

        while (reader.Peek() != '\n')
        {
            ReadOnlySpan <byte> fieldName = reader.ReadUntil(':');
            reader.SkipLength(1);
            int l1 = reader.ReadPosIntUntil('-');
            int r1 = reader.ReadPosIntUntil(' ');
            reader.SkipLength("or ".Length);
            int l2 = reader.ReadPosIntUntil('-');
            int r2 = reader.ReadPosIntUntil('\n');

            fieldList.Add(new Field(l1, r1, l2, r2));
            if (fieldName.StartsWith(new byte[] { (byte)'d', (byte)'e' }))
            {
                departureFields[departureFieldsIndex++] = fieldList.Count - 1;
            }

            if (r2 > maxFieldVal)
            {
                maxFieldVal = r2;
            }
        }

        return(new FieldsData(fieldList, departureFields, maxFieldVal));
    }
예제 #3
0
    private static List <byte> ParseInput(ReadOnlySpan <byte> input)
    {
        var materialLocations = new List <byte>();
        var materials         = new Dictionary <string, int>();
        var reader            = new SpanReader(input);

        for (int floor = 1; floor < 5; floor++)
        {
            reader.SkipLength(floor is 1 or 3 ? "The first floor contains ".Length : "The second floor contains ".Length);
            if (reader.Peek() == 'n') // nothing relevant
            {
                reader.SkipLength("nothing relevant.\n".Length);
                continue;
            }

            bool isLastItem = false;
            while (!isLastItem)
            {
                ParseItem(ref reader, out string element, out bool isGenerator, out isLastItem);
                if (!materials.TryGetValue(element, out int materialIndex))
                {
                    materialIndex      = materialLocations.Count;
                    materials[element] = materialIndex;
                    materialLocations.Add(0);
                }

                materialLocations[materialIndex] |= (byte)(isGenerator ? floor << 4 : floor);
            }
        }

        return(materialLocations);
    }
예제 #4
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        int freqIndex = 0;
        int freqTotal = 0;
        var freqs     = new List <Frequency>();
        var reader    = new SpanReader(input);

        while (!reader.Done)
        {
            freqs.Add(new Frequency {
                Value = freqTotal, Index = freqIndex
            });
            freqIndex++;

            int mul = reader.Peek() == '-' ? -1 : 1;
            reader.SkipLength(1);

            freqTotal += mul * reader.ReadPosIntUntil('\n');
        }

        foreach (Frequency freq in freqs)
        {
            int mod = freq.Value % freqTotal;
            freq.ModTotal = mod < 0 ? mod + freqTotal : mod;
        }

        // sort by mods first, then by value
        freqs.Sort((a, b) => a.ModTotal != b.ModTotal
            ? a.ModTotal.CompareTo(b.ModTotal)
            : a.Value.CompareTo(b.Value));

        var prev = new Frequency {
            ModTotal = -1
        };
        int minDiff  = int.MaxValue;
        int minIndex = int.MaxValue;
        int minFreq  = 0;

        foreach (Frequency freq in freqs)
        {
            if (freq.ModTotal == prev.ModTotal)
            {
                int diff = freq.Value - prev.Value;
                if (diff < minDiff || (diff == minDiff && prev.Index < minIndex))
                {
                    minDiff  = diff;
                    minIndex = prev.Index;
                    minFreq  = freq.Value;
                }
            }

            prev = freq;
        }

        solution.SubmitPart1(freqTotal);
        solution.SubmitPart2(minFreq);
    }
예제 #5
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        var reader       = new SpanReader(input.TrimEnd((byte)'\n'));
        int earliestTime = reader.ReadPosIntUntil('\n');

        int part1Id   = -1;
        int part1Time = int.MaxValue;

        BigInteger product = 1;
        var        buses   = new List <(int n, int a)>();

        for (int i = 0; !reader.Done; i++)
        {
            if (reader.Peek() == 'x')
            {
                reader.SkipLength("x,".Length);
                continue;
            }

            int busId = reader.ReadPosIntUntil(',');

            // Part 1
            int waitTime = busId - (earliestTime % busId);
            if (waitTime < part1Time)
            {
                part1Id   = busId;
                part1Time = waitTime;
            }

            // Part 2
            product *= busId;

            int remainder = (busId - i) % busId;
            if (remainder < 0)
            {
                remainder += busId;
            }

            buses.Add((busId, remainder));
        }

        int part1 = part1Id * part1Time;

        BigInteger part2 = 0;

        foreach ((int n, int a) in buses)
        {
            BigInteger p = product / n;
            part2 += a * BigInteger.ModPow(p, n - 2, n) * p;
        }

        part2 %= product;

        solution.SubmitPart1(part1);
        solution.SubmitPart2(part2);
    }
예제 #6
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        // assume the origin is at 1 << 15, 1 << 15
        const ushort xOrigin = 1 << 15;
        const ushort yOrigin = 1 << 15;

        // pack the x and y ushorts into a uint
        uint pos = unchecked ((uint)xOrigin << 16 | yOrigin);
        uint dir = Up;

        var seenLocations = new HashSet <uint> {
            pos
        };
        int distanceToFirstRepeatedLocation = -1;
        var reader = new SpanReader(input.TrimEnd((byte)'\n'));

        while (true)
        {
            dir = MakeTurn(dir, reader.Peek() == 'L');
            reader.SkipLength(1);

            uint distance = (uint)reader.ReadPosIntUntil(',');
            if (distanceToFirstRepeatedLocation == -1)
            {
                for (uint i = 0; i < distance; i++)
                {
                    pos = unchecked (pos + dir);
                    if (!seenLocations.Add(pos))
                    {
                        distanceToFirstRepeatedLocation = ManhattanDistance(pos);
                    }
                }
            }
            else
            {
                pos = unchecked (pos + dir * distance);
            }

            if (reader.Done)
            {
                break;
            }

            reader.SkipLength(1);
        }

        int distanceToDestination = ManhattanDistance(pos);

        solution.SubmitPart1(distanceToDestination);
        solution.SubmitPart2(distanceToFirstRepeatedLocation);
예제 #7
0
    private static void ParseItem(ref SpanReader reader, out string element, out bool isGenerator, out bool isLastItem)
    {
        isLastItem = reader[1] == 'n'; // "and a <something>"
        reader.SkipLength(isLastItem ? "and a ".Length : "a ".Length);
        ReadOnlySpan <byte> elementSpan = reader.ReadUntil(' ');

        isGenerator = reader.Peek() == 'g';
        reader.SkipUntil(isLastItem ? '\n' : ' ');

        if (!isGenerator)
        {
            elementSpan = elementSpan.Slice(0, elementSpan.Length - "-compatible".Length);
        }

        element = Encoding.ASCII.GetString(elementSpan);
    }
예제 #8
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        int lines  = input.Count((byte)'\n');
        var instrs = new Instruction[lines];

        int i      = 0;
        var reader = new SpanReader(input);

        while (!reader.Done)
        {
            byte op = reader.Peek();
            reader.SkipLength("cpy ".Length);
            Param arg1, arg2;
            if (op is (byte)'c' or(byte) 'j')
            {
                arg1 = ParseParamUntil(ref reader, ' ');
                arg2 = ParseParamUntil(ref reader, '\n');
            }
예제 #9
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        bool[,] pixels = new bool[50, 6];

        bool[] rowBuffer = new bool[50];
        bool[] colBuffer = new bool[6];
        foreach (ReadOnlySpan <byte> line in input.SplitLines())
        {
            if (line[1] == 'e') // rect
            {
                var reader = new SpanReader(line.Slice("rect ".Length));
                int width  = reader.ReadPosIntUntil('x');
                int height = reader.ReadPosIntUntilEnd();
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {
                        pixels[x, y] = true;
                    }
                }
            }
            else if (line[7] == 'c') // rotate column
            {
                var reader = new SpanReader(line.Slice("rotate column x=".Length));
                int column = reader.ReadPosIntUntil(' ');
                reader.SkipLength("by ".Length);
                int rotateAmount = reader.ReadPosIntUntilEnd();

                for (int i = 0; i < 6; i++)
                {
                    colBuffer[i] = pixels[column, i];
                    int target = i - rotateAmount;
                    if (target < 0)
                    {
                        target += 6;
                    }

                    // if the target has already been swapped, get it from the buffer
                    pixels[column, i] = target < i ? colBuffer[target] : pixels[column, target];
                }
            }
            else // rotate row
            {
                var reader = new SpanReader(line.Slice("rotate row y=".Length));
                int row    = reader.Peek() - '0';
                reader.SkipLength("0 by ".Length);
                int rotateAmount = reader.ReadPosIntUntilEnd();

                for (int i = 0; i < 50; i++)
                {
                    rowBuffer[i] = pixels[i, row];
                    int target = i - rotateAmount;
                    if (target < 0)
                    {
                        target += 50;
                    }

                    // if the target has already been swapped, get it from the buffer
                    pixels[i, row] = target < i ? rowBuffer[target] : pixels[target, row];
                }
            }
        }

        int part1 = 0;

        foreach (bool pixel in pixels)
        {
            if (pixel)
            {
                part1++;
            }
        }

        Span <char> part2 = stackalloc char[10];

        for (int i = 0; i < 10; i++)
        {
            int letterPixels = 0;
            for (int row = 0; row < 6; row++)
            {
                for (int col = 0; col < 5; col++)
                {
                    if (pixels[i * 5 + col, row])
                    {
                        letterPixels |= 1 << (29 - (row * 5 + col));
                    }
                }
            }

            part2[i] = OCR.MaskToLetter(letterPixels);
        }

        solution.SubmitPart1(part1);
        solution.SubmitPart2(part2);
    }
예제 #10
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        // Part 1
        int x1 = 0, y1 = 0;
        int dx1 = 1, dy1 = 0;

        // Part 2
        int x2 = 0, y2 = 0;
        int dx2 = 10, dy2 = 1;

        var reader = new SpanReader(input);

        while (!reader.Done)
        {
            byte dir = reader.Peek();
            reader.SkipLength(1);
            int amount = reader.ReadPosIntUntil('\n');

            switch (dir)
            {
            case (byte)'N':
                y1  += amount;
                dy2 += amount;
                break;

            case (byte)'S':
                y1  -= amount;
                dy2 -= amount;
                break;

            case (byte)'E':
                x1  += amount;
                dx2 += amount;
                break;

            case (byte)'W':
                x1  -= amount;
                dx2 -= amount;
                break;

            case (byte)'L':
                for (int i = amount; i > 0; i -= 90)
                {
                    RotateLeft(ref dx1, ref dy1);
                    RotateLeft(ref dx2, ref dy2);
                }
                break;

            case (byte)'R':
                for (int i = amount; i > 0; i -= 90)
                {
                    RotateRight(ref dx1, ref dy1);
                    RotateRight(ref dx2, ref dy2);
                }
                break;

            case (byte)'F':
                x1 += amount * dx1;
                y1 += amount * dy1;

                x2 += amount * dx2;
                y2 += amount * dy2;
                break;
            }
        }

        int part1 = Math.Abs(x1) + Math.Abs(y1);
        int part2 = Math.Abs(x2) + Math.Abs(y2);

        solution.SubmitPart1(part1);
        solution.SubmitPart2(part2);
    }
예제 #11
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        var ingredientCount    = new Dictionary <string, int>();
        var allgerenCandidates = new Dictionary <string, HashSet <string> >();

        var ingredientSet = new HashSet <string>();
        var reader        = new SpanReader(input);

        int totalIngredients = 0;

        while (!reader.Done)
        {
            while (reader.Peek() != '(')
            {
                string ingredient = Encoding.ASCII.GetString(reader.ReadUntil(' '));
                ingredientCount[ingredient] = ingredientCount.GetValueOrDefault(ingredient) + 1;
                ingredientSet.Add(ingredient);
                totalIngredients++;
            }

            reader.SkipLength("(contains ".Length);
            foreach (ReadOnlySpan <byte> allergen in reader.ReadUntil(')').Split(new[] { (byte)',', (byte)' ' }))
            {
                string allergenStr = Encoding.ASCII.GetString(allergen);
                if (allgerenCandidates.TryGetValue(allergenStr, out HashSet <string>?curSet))
                {
                    curSet.IntersectWith(ingredientSet);
                }
                else
                {
                    allgerenCandidates[allergenStr] = new HashSet <string>(ingredientSet);
                }
            }

            reader.SkipLength(1);
            ingredientSet.Clear();
        }

        int allergenIndex = 0;

        string[] allergens = new string[allgerenCandidates.Count];
        foreach (string allergen in allgerenCandidates.Keys)
        {
            allergens[allergenIndex++] = allergen;
        }
        Array.Sort(allergens);

        string?[] ingredients = new string?[allergens.Length];
        int       part1       = totalIngredients;

        for (int allergensLeft = 0; allergensLeft < allergens.Length; allergensLeft++)
        {
            string foundIngredient = string.Empty;
            for (int i = 0; i < allergens.Length; i++)
            {
                if (ingredients[i] != null)
                {
                    continue;
                }

                string allergen = allergens[i];

                HashSet <string> candidates = allgerenCandidates[allergen];
                if (candidates.Count == 1)
                {
                    foundIngredient = candidates.Single();
                    ingredients[i]  = foundIngredient;
                    part1          -= ingredientCount[foundIngredient];
                    break;
                }
            }

            foreach (HashSet <string> candidates in allgerenCandidates.Values)
            {
                candidates.Remove(foundIngredient);
            }
        }

        string part2 = string.Join(',', ingredients);

        solution.SubmitPart1(part1);
        solution.SubmitPart2(part2);
    }
예제 #12
0
    public void Solve(ReadOnlySpan <byte> input, Solution solution)
    {
        // negative means output, positive means bot
        var botOutputs = new Dictionary <int, (bool IsLowBot, int Low, bool IsHighBot, int High)>();
        var valueQueue = new Queue <(bool IsBot, int Destination, int Value)>();

        int maxBotId    = 0;
        int maxOutputId = 0;

        // first pass we just keep track of where the bot sends its chips
        var parser = new SpanReader(input);

        while (!parser.Done)
        {
            if (parser.Peek() == 'b')
            {
                ParseBotInstruction(ref parser, out int bot, out bool isLowBot, out int low, out bool isHighBot, out int high);
                botOutputs[bot] = (isLowBot, low, isHighBot, high);

                maxBotId = Math.Max(bot, maxBotId);
                if (!isLowBot)
                {
                    maxOutputId = Math.Max(maxOutputId, low);
                }

                if (!isHighBot)
                {
                    maxOutputId = Math.Max(maxOutputId, high);
                }
            }
            else
            {
                ParseBotStartingValue(ref parser, out int bot, out int value);
                valueQueue.Enqueue((true, bot, value));
            }
        }

        int[]? botValues    = new int[maxBotId + 1];
        int[]? outputValues = new int[maxOutputId + 1];

        int part1 = 0;

        while (valueQueue.TryDequeue(out (bool IsBot, int Destination, int Value)x))
        {
            (bool isBot, int destination, int value) = x;

            if (!isBot)
            {
                outputValues[destination] = value;
                continue;
            }

            int curValue = botValues[destination];
            if (curValue == 0)
            {
                botValues[destination] = value;
            }
            else
            {
                (bool IsLowBot, int Low, bool IsHighBot, int High) = botOutputs[destination];

                int low  = Math.Min(curValue, value);
                int high = Math.Max(curValue, value);

                if (low == 17 && high == 61)
                {
                    part1 = destination;
                }

                valueQueue.Enqueue((IsLowBot, Low, low));
                valueQueue.Enqueue((IsHighBot, High, high));
            }
        }

        int part2 = outputValues[0] * outputValues[1] * outputValues[2];

        solution.SubmitPart1(part1);
        solution.SubmitPart2(part2);
    }