<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Day5
{
    class Program
    {
        //Part 1
        //private const int inputParameter = 1;

        //Part 2
        private const int inputParameter = 5;

        static void Main(string[] args)
        {
            using (var sr = new StreamReader("input.txt"))
            {
                var originalOpcodes = sr.ReadToEnd().Split(',').Select(x => int.Parse(x)).ToList();
                // Part 1
                CalculateOpcodes(originalOpcodes);
            }
        }

        static void CalculateOpcodes(List<int> opCodes)
        { 
            int pointerPosition = 0;
            while(true)
            {
                var instruction = CalculateInstruction(opCodes[pointerPosition]).ToList();
                pointerPosition = ExecuteInstruction(instruction, pointerPosition, opCodes);
                if (pointerPosition == -1)
                    return;
            }
        }

        private static int ExecuteInstruction(List<int> instruction, int pointerPosition, List<int> opCodes)
        {
            switch(instruction[4])
            {
                case 1:
                    opCodes[opCodes[pointerPosition + 3]] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) + CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 2:
                    opCodes[opCodes[pointerPosition + 3]] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) * CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 3:
                    opCodes[opCodes[pointerPosition + 1]] = inputParameter;
                    return pointerPosition + 2;
                case 4:
                    Console.Write(CalculateVariable(pointerPosition + 1, instruction[2], opCodes));
                    return pointerPosition + 2;
                case 5:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) != 0)
                        return CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 6:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == 0)
                        return CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 7:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) < CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[opCodes[pointerPosition + 3]] = 1;
                    else
                        opCodes[opCodes[pointerPosition + 3]] = 0;
                    return pointerPosition + 4;
                case 8:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[opCodes[pointerPosition + 3]] = 1;
                    else                 
                        opCodes[opCodes[pointerPosition + 3]] = 0;
                    return pointerPosition + 4;
                case 9:
                    return -1;
            }
            return 0;
            int CalculateVariable(int position, int mode, List<int> opCodes) => mode == 1 ? opCodes[position] : opCodes[opCodes[position]];
        }

        private static IEnumerable<int> CalculateInstruction(int wholeOpcode)
        {
            var stringOpcode = wholeOpcode.ToString();
            for (int i = 4; i >= 0; i--)
            {
                if (stringOpcode.Length - 1 < i)
                    yield return 0;
               else
                    yield return int.Parse(stringOpcode[stringOpcode.Length - (i + 1)].ToString());
            }
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;

namespace Day4
{
    class Program
    {
        static void Main(string[] args)
        {
            const int begin = 153517, end = 630395;
            var listOfPasswords = new List<int>();
            // Part 1
            bool check = HasExactlyTwoAdjacentDigits(123444);
            for (int currentPassword = begin; currentPassword <= end; currentPassword++)
            {
                if(HasTwoAdjacentDigits(currentPassword) && !IsDecreasing(currentPassword))
                    listOfPasswords.Add(currentPassword);
            }
            Console.WriteLine($"Number of valid passwords for part 1: {listOfPasswords.Count}");

            // Part 2
            listOfPasswords = new List<int>();
            for (int currentPassword = begin; currentPassword <= end; currentPassword++)
            {
                if(HasExactlyTwoAdjacentDigits(currentPassword) && !IsDecreasing(currentPassword))
                    listOfPasswords.Add(currentPassword);
            }
            Console.WriteLine($"Number of valid passwords for part 2: {listOfPasswords.Count}");
        }

        private static bool HasTwoAdjacentDigits(int currentPassword)
        {
            string passwordInString = currentPassword.ToString();
            for (int letterNumber = 0; letterNumber < passwordInString.Length - 1; letterNumber++)
                if (passwordInString[letterNumber] == passwordInString[letterNumber + 1])
                    return true;
            return false;
        }
        private static bool IsDecreasing(int currentPassword)
        {
            string passwordInString = currentPassword.ToString();
            for (int letterNumber = 0; letterNumber < passwordInString.Length - 1; letterNumber++)
                if (passwordInString[letterNumber] > passwordInString[letterNumber + 1])
                    return true;
            return false;
        }

        private static bool HasExactlyTwoAdjacentDigits(int currentPassword)
        {
            var numberCounts = new Dictionary<char,int>();
            string passwordInString = currentPassword.ToString();
            for (int letterNumber = 0; letterNumber < passwordInString.Length - 1; letterNumber++)
            {
                if (passwordInString[letterNumber] == passwordInString[letterNumber + 1])
                {
                    if (numberCounts.ContainsKey(passwordInString[letterNumber]))
                        numberCounts[passwordInString[letterNumber]]++;
                    else
                        numberCounts.Add(passwordInString[letterNumber], 2);
                }
            }
            return numberCounts.ContainsValue(2);
        }
    }
}
<file_sep>﻿using System;
using System.IO;
using System.Linq;

namespace Day8
{
    class Program
    {
        static void Main(string[] args)
        {
            const int width = 25, height = 6, lettersInLayer = width * height;
            var text = File.ReadAllText("./input.txt");
            int numberOfLayers = text.Length / lettersInLayer;
            int[][][] layers = new int[numberOfLayers][][];

            for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++)
            {
                layers[layerNumber] = new int[height][];
                for (int currentHeight = 0; currentHeight < height; currentHeight++)
                {
                    layers[layerNumber][currentHeight] = new int[width];
                    for (int currentWidth = 0; currentWidth < width; currentWidth++)
                    {
                        layers[layerNumber][currentHeight][currentWidth] = int.Parse(text[(layerNumber * lettersInLayer) + (currentHeight * width) + currentWidth].ToString());
                    }
                }                
            }
            
            // Part 1
            int smallestNumOfZeros = int.MaxValue;
            int indexOfLayerWithSmallestNumberOfZeros = 0;
            for(int i = 0; i < numberOfLayers; i++)
            {
                int numberOfZerosInCurrentLayer = CountNumberOfGivenNumberInLayer(layers[i], 0);
                if (numberOfZerosInCurrentLayer < smallestNumOfZeros)
                {
                    smallestNumOfZeros = numberOfZerosInCurrentLayer;
                    indexOfLayerWithSmallestNumberOfZeros = i;
                }               
            }
            int result = CountNumberOfGivenNumberInLayer(layers[indexOfLayerWithSmallestNumberOfZeros], 1) * CountNumberOfGivenNumberInLayer(layers[indexOfLayerWithSmallestNumberOfZeros], 2);
            Console.WriteLine($"Part 1 solution: {result}");

            // Part 2
            int[,] renderedImage = new int[height,width];
            for (int currentHeight = 0; currentHeight < height; currentHeight++)
            {
                for (int currentWidth = 0; currentWidth < width; currentWidth++)
                {
                    int layerNumber = 0;
                    int currentPixel = 2;
                    do 
                    {
                        currentPixel = layers[layerNumber][currentHeight][currentWidth];                        
                        layerNumber++;
                    }
                    while (currentPixel == 2);
                    renderedImage[currentHeight,currentWidth] = currentPixel;
                }
            }        

            // Printing password
            for (int currentHeight = 0; currentHeight < height; currentHeight++)
            {
                for (int currentWidth = 0; currentWidth < width; currentWidth++)
                {
                    if(renderedImage[currentHeight,currentWidth] == 0) 
                        Console.Write(" ");
                    else
                        Console.Write("X");
                }
                Console.WriteLine();
            }     
        }

        private static int CountNumberOfGivenNumberInLayer(int[][] layer, int givenNumber) => layer.SelectMany(x => x).Count(x => x == givenNumber);
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Day10
{
    class Program
    {
        static void Main(string[] args)
        {
            var asteroids = new List<Asteroid>();
            var lines = File.ReadAllLines("input.txt");
            for (int i = 0; i < lines.Length; i++)
            {
                for (int j = 0; j < lines[i].Length; j++)
                {
                    if (lines[i][j].Equals('#'))
                        asteroids.Add(new Asteroid(j,i));
                }
            }

            // Part 1
            int maxSeenAsteroids = 0;
            Asteroid selectedAsteroid = null;
            foreach (var asteroid in asteroids)
            {
                var seenAsteroids = GetSeenAsteroids(asteroid, asteroids);
                if (seenAsteroids.Count > maxSeenAsteroids)
                {
                    maxSeenAsteroids = seenAsteroids.Count;
                    selectedAsteroid = asteroid;
                }                   
            }
            Console.WriteLine($"Solution part 1: {maxSeenAsteroids}");

            // Part 2
            int countOfDestroyedAsteroids = 0;
            Queue<Asteroid> asteroidsToDestroy = null;
            while (true)
            {
                var currentlySeenAsteroid = GetSeenAsteroids(selectedAsteroid, asteroids);
                var rightSide = currentlySeenAsteroid.Where(x => x.Key.side == true).OrderBy(x => x.Key.sinus).Select(x => x.Value);
                var leftSide = currentlySeenAsteroid.Where(x => x.Key.side == false).OrderByDescending(x => x.Key.sinus).Select(x => x.Value);

                asteroidsToDestroy = new Queue<Asteroid>(rightSide.Concat(leftSide));
                while (asteroidsToDestroy.Count > 0)
                {
                    if (countOfDestroyedAsteroids == 199)
                    {
                        var asteroid = asteroidsToDestroy.Dequeue();
                        Console.WriteLine($"200th destroyed asteroid will be: ({asteroid.X}, {asteroid.Y})");
                        return;
                    }
                    else
                    {
                        asteroids.Remove(asteroidsToDestroy.Dequeue());
                        countOfDestroyedAsteroids++;
                    }
                }
            }

        }

        private static Dictionary<(bool side, double sinus),  Asteroid> GetSeenAsteroids(Asteroid asteroid, List<Asteroid> asteroids)
        {
            var dictionary = new Dictionary<(bool, double), Asteroid>();
            foreach (var asteroidToCheck in asteroids)
            {
                if (asteroidToCheck == asteroid)
                    continue;

                var sideAndSinus = CalculateSideAndSinus(asteroid, asteroidToCheck);
                var distance = CalculateDistance(asteroid, asteroidToCheck);
                if (dictionary.ContainsKey(sideAndSinus) )
                {
                    if (distance < CalculateDistance(asteroid, dictionary[sideAndSinus]))
                    {
                        dictionary.Remove(sideAndSinus);
                        dictionary.Add(sideAndSinus, asteroidToCheck);
                    }
                    else
                        continue;
                }
                else
                    dictionary.Add(sideAndSinus, asteroidToCheck);
            }
            return dictionary;
                
            (bool, double) CalculateSideAndSinus(Asteroid asteroid, Asteroid asteroidToCheck)
            {
                bool side = asteroid.X <= asteroidToCheck.X;
                double sinus = CalculateSinus(asteroid, asteroidToCheck); 
                return (side, RoundSinusToFivePlaces(sinus));
            }

            double CalculateSinus(Asteroid asteroid, Asteroid asteroidToCheck) => (asteroidToCheck.Y -  asteroid.Y) / CalculateDistance(asteroid, asteroidToCheck);
            double CalculateDistance(Asteroid asteroid, Asteroid asteroidToCheck) => Math.Sqrt( Math.Pow(asteroid.X -  asteroidToCheck.X,2) + Math.Pow(asteroid.Y - asteroidToCheck.Y,2));
            double RoundSinusToFivePlaces(double exactSinus) => (double)((int)(exactSinus * 100000)) / 100000;
        }

    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;

namespace Day13
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var opcodes = File.ReadAllText("input.txt").Split(',').Select(x => BigInteger.Parse(x)).ToList();
            for (int i = 0; i < 1000; i++)
                opcodes.Add(0);
            var inputQueue = new Queue<int>();
            var outputQueue = new Queue<int>();

            var opcodesCalculator = new OpcodesCalculator(opcodes);
            await opcodesCalculator.CalculateOpcodes(inputQueue, outputQueue);
            int boardSize = 1000;
            var board = new int[boardSize][];
            for (int i = 0; i < boardSize; i++)
                board[i] = new int[boardSize];

            // Part 1
            while (outputQueue.Count > 0)
            {
                int x = outputQueue.Dequeue();
                int y = outputQueue.Dequeue();
                int tileId = outputQueue.Dequeue();

                board[y][x] = tileId;
            }
            int numberOfBlockTiles = board.SelectMany(x => x).Where(x => x == 2).Count();
            Console.WriteLine($"First part solution:  {numberOfBlockTiles}");

            // Part 2
            boardSize = 40;
            var tileBoard = new Tile[boardSize][];
            for (int i = 0; i < boardSize; i++)
            {
                tileBoard[i] = new Tile[boardSize];
                for (int j = 0; j < boardSize; j++)
                {
                    tileBoard[i][j] = new Tile(i, j, 0);
                }
            }

            opcodes[0] = 2;
            bool waitedForOutput = false;
            bool gameInitialized = false;
            opcodesCalculator = new OpcodesCalculator(opcodes);
            long score = 0;
            bool firstChekckForEndScore = true;

            var task = Task.Factory.StartNew(() => opcodesCalculator.CalculateOpcodes(inputQueue, outputQueue)).Unwrap();
            while (true)
            {
                if (!waitedForOutput)
                {
                    await Task.Delay(50);
                    waitedForOutput = true;
                }
                    
                if (outputQueue.Count < 3)
                {
                    //DrawBoard(tileBoard); // for manual playing
                    var ball = tileBoard.SelectMany(x => x).First(x => x.Type == 4);
                    var paddle = tileBoard.SelectMany(x => x).First(x => x.Type == 3);
                    int move = Math.Sign(ball.X - paddle.X);
                    //int move = ParseInput(Console.ReadKey()); // for manual playing
                    inputQueue.Enqueue(move);
                    waitedForOutput = false;
                    gameInitialized = true;
                    continue;
                }

                int x = outputQueue.Dequeue();
                int y = outputQueue.Dequeue();
                int tileId = outputQueue.Dequeue();

                if (x == -1 && y == 0)
                {
                    score = tileId;
                }
                else
                {
                    if (tileId == 3 || tileId == 4)
                    {
                        var obj = tileBoard.SelectMany(x => x).FirstOrDefault(x => x.Type == tileId);
                        if (obj != null)
                        {
                            obj.Type = 0;
                        }
                    }
                    tileBoard[x][y].Type = tileId;
                }

                if (gameInitialized && tileBoard.SelectMany(x => x).Where(x => x.Type == 2).Count() == 0)
                {
                    if (!firstChekckForEndScore) // score need some time to propagate
                    {
                        Console.WriteLine($"Second part solution: number of points = {score}");
                        break;
                    }
                    firstChekckForEndScore = false;
                }
            }

        }

        private static int ParseInput(ConsoleKeyInfo consoleKeyInfo) // for manual playing
        {
            switch(consoleKeyInfo.Key)
            {
                case ConsoleKey.LeftArrow:
                    return -1;
                case ConsoleKey.RightArrow:
                    return 1;
                default:
                    return 0;
            }
        }

        private static void DrawBoard(Tile[][] tileBoard)
        {
            Console.Clear();
            Console.WriteLine("Board state:");
            for (int i = 0; i < tileBoard.Length; i++)
            {
                for (int j = 0; j < tileBoard[i].Length; j++)
                {
                    Console.Write(ChangeIntToChar(tileBoard[j][i].Type));
                }
                Console.WriteLine();
            }

            char ChangeIntToChar(int tileId)
            {
                switch(tileId)
                {
                    case 0:
                        return ' ';
                    case 1:
                        return 'X';
                    case 2:
                        return 'B';
                    case 3:
                        return '-';
                    case 4:
                        return 'O';
                }
                return ' ';
            }
        }
    }
}
<file_sep>using System.Collections.Generic;

namespace Day11
{
    public class Floor
    {
        private int[,] floor;
        private HashSet<(int, int)> paintedPanels = new HashSet<(int, int)>();
        int currentX;
        int currentY;
        Direction currentDirectionOfRobot = Direction.Up;
        public Floor(int floorSize)
        {
            floor = new int[floorSize, floorSize];
            currentX = floorSize / 2;
            currentY = floorSize / 2;
        }

        public int GetCurrentColor() => floor[currentX, currentY];

        public void Paint(int color)
        {
            floor[currentX, currentY] = color;
            paintedPanels.Add((currentX, currentY));
        }

        public int GetNumberOfPaintedPanels() => paintedPanels.Count;
        public int[,] GetFloor() => floor;

        public void Move(int turn)
        {
            turn = turn == 0 ? -1 : 1;
            currentDirectionOfRobot = (Direction)Mod(((int)currentDirectionOfRobot + turn), 4);
            switch (currentDirectionOfRobot)
            {
                case Direction.Up:
                    currentY++;
                    break;
                case Direction.Right:
                    currentX++;
                    break;
                case Direction.Down:
                    currentY--;
                    break;
                case Direction.Left:
                    currentX--;
                    break;
            }
        }

        private int Mod(int x, int m) => (x%m + m)%m;
    }

    public enum Direction
    {
        Up = 0,
        Right = 1,
        Down = 2,
        Left = 3
    }
}<file_sep>using System.Collections.Generic;

namespace Day6
{
    internal class OrbitingObject
    {
        public string Name { get; set; }
        public OrbitingObject DirectOrbit { get; set; }
        public HashSet<OrbitingObject> AllNeighbours { get; set; } = new HashSet<OrbitingObject>();
        public OrbitingObject PreviousNode { get; set; }

        public OrbitingObject(string name, OrbitingObject directOrbit)
        {
            Name = name;
            DirectOrbit = directOrbit;
        }
    }
}<file_sep>using System;
using System.Collections.Generic;

namespace Day3
{
    internal class Field
    {
        private int[,] internalField;

        public List<(int, int)> Crosses { get; } = new List<(int, int)>();
        public Field(int xSize, int ySize)
        {
            internalField = new int[xSize, ySize];
        }

        internal (int x, int y) Apply(Command command, int startingX, int startingY)
        {
            switch(command.Direction)
            {
                case DirectionEnum.Left:
                    MarkAsVisited(command, startingX, startingY, -1, 0);
                    return (startingX  - command.Number, startingY);
                case DirectionEnum.Up:
                    MarkAsVisited(command, startingX, startingY, 0, 1);
                    return (startingX , startingY + command.Number);
                case DirectionEnum.Right:
                    MarkAsVisited(command, startingX, startingY, 1, 0);
                    return (startingX  + command.Number, startingY);
                case DirectionEnum.Down:
                    MarkAsVisited(command, startingX, startingY, 0, -1);
                    return (startingX, startingY - command.Number);
            }  
            throw new Exception("Unknown direction");         
        }

        private void MarkAsVisited(Command command, int startingX, int startingY, int moveX, int moveY)
        {
            for(int i = 0; i < command.Number; i++)
            {
                internalField[startingX + (i * moveX), startingY + (i * moveY)] += command.NumberOfPath + 1;
                if (internalField[startingX + (i * moveX), startingY + (i * moveY)] == 3)
                    Crosses.Add((startingX + (i * moveX), startingY + (i * moveY)));
            }
        }
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading;
using System.Threading.Tasks;

namespace Day11
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var opcodes = File.ReadAllText("input.txt").Split(',').Select(x => BigInteger.Parse(x)).ToList();
            for (int i = 0; i < 1000; i++)
                opcodes.Add(0);
            var inputQueue = new Queue<int>();
            var outputQueue = new Queue<int>();

            var floor = new Floor(200);
            var opcodesCalculator = new OpcodesCalculator(opcodes);
            var task = Task.Factory.StartNew(() => opcodesCalculator.CalculateOpcodes(inputQueue, outputQueue)).Unwrap();

            //Part 1
            while (!task.IsCompleted)
            {
                inputQueue.Enqueue(floor.GetCurrentColor());
                while (outputQueue.Count != 2)
                {
                    if (task.IsCompleted)
                        break;
                    else
                        await Task.Delay(5);
                }

                if (task.IsCompleted)
                    break;
                floor.Paint(outputQueue.Dequeue());
                floor.Move(outputQueue.Dequeue());
            }
            Console.WriteLine($"Part 1: Number of painted {floor.GetNumberOfPaintedPanels()}");


            //Part 2
            inputQueue.Clear();
            outputQueue.Clear();
            floor = new Floor(100);
            opcodesCalculator = new OpcodesCalculator(opcodes);
            task = Task.Factory.StartNew(() => opcodesCalculator.CalculateOpcodes(inputQueue, outputQueue)).Unwrap();            
            floor.Paint(1);
            while (!task.IsCompleted)
            {
                inputQueue.Enqueue(floor.GetCurrentColor());
                while (outputQueue.Count != 2)
                {
                    if (task.IsCompleted)
                        break;
                    else
                        await Task.Delay(5);
                }

                if (task.IsCompleted)
                    break;
                floor.Paint(outputQueue.Dequeue());
                floor.Move(outputQueue.Dequeue());
            }

            Console.WriteLine("Part 2:");
            var floorAfterPainting = floor.GetFloor();
            for(int i = 0; i < floorAfterPainting.GetLength(0); i++)
            {
                for (int j = 0; j < floorAfterPainting.GetLength(1); j++)
                {
                    Console.Write(floorAfterPainting[i, j] == 0 ? " " : "X");
                }
                Console.WriteLine();
            }
        }
    }

}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Day12
{
    class Moon
    {
        public Vector3D Position { get; private set; }
        public Vector3D BasePosition { get; }
        public Vector3D Velocity { get; private set; } = new Vector3D();

        public Moon(int x, int y, int z)
        {
            Position = new Vector3D { X = x, Y = y, Z = z };
            BasePosition = new Vector3D { X = x, Y = y, Z = z };
        }

        public void ResetToBasePosition()
        {
            Position = BasePosition;
            Velocity = new Vector3D();
        }

        public void CalculateGravity(List<Moon> moons)
        {
            foreach(var moon in moons)
            {
                if (this == moon)
                    continue;

                var velocityGain = CalculateVelocityGain(this, moon);
                Velocity = new Vector3D(Velocity.X + velocityGain.x, Velocity.Y + velocityGain.y, Velocity.Z + velocityGain.z);
            }

            (int x, int y, int z) CalculateVelocityGain(Moon moon1, Moon moon2)
            {
                int x, y, z;
                x = Math.Sign(moon2.Position.X - moon1.Position.X);
                y = Math.Sign(moon2.Position.Y - moon1.Position.Y);
                z = Math.Sign(moon2.Position.Z - moon1.Position.Z);
                return (x, y, z);
            }
        }

        public void ApplyVelocity()
        {
            Position = new Vector3D(Position.X + Velocity.X, Position.Y + Velocity.Y, Position.Z + Velocity.Z);
        }

        public int CalculateTotalEnergy()
        {
            return CalculatePotentialEnergy() * CalculateKineticEnergy();
        }

        private int CalculatePotentialEnergy() => Math.Abs(Position.X) + Math.Abs(Position.Y) + Math.Abs(Position.Z);

        private int CalculateKineticEnergy() => Math.Abs(Velocity.X) + Math.Abs(Velocity.Y) + Math.Abs(Velocity.Z);


    }
}
<file_sep>using System;

namespace Day3
{
    internal class Command
    {
        public DirectionEnum Direction { get; set; }
        public int Number { get; set; }
        public int NumberOfPath { get; set; }

        public Command(string commandAsString, int numberOfPath)
        {
            switch(commandAsString[0])
            {
                case 'L':
                    Direction = DirectionEnum.Left;
                    break;
                case 'U':
                    Direction = DirectionEnum.Up;
                    break;
                case 'R':
                    Direction = DirectionEnum.Right;
                    break;
                case 'D':
                    Direction = DirectionEnum.Down;
                    break;
            }
            Number = int.Parse(commandAsString.Substring(1));
            NumberOfPath = numberOfPath;
        }
    }

    internal enum DirectionEnum
    {
        Left,
        Up,
        Right,
        Down
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;

namespace Day9
{
    class Program
    {
        static async Task Main(string[] args)
        {           
            var originalOpcodes = File.ReadAllText("input.txt").Split(',').Select(x => BigInteger.Parse(x)).ToList();
            for (int i = 0; i < 10000; i++)
                originalOpcodes.Add(0);
            
            // Part 1
            var inputQueue = new Queue<int>();
            inputQueue.Enqueue(1);
            var opCalc = new OpcodesCalculator(originalOpcodes);
            Console.WriteLine("Part 1 solution: ");
            await opCalc.CalculateOpcodes(inputQueue);
            
            // Part 2
            inputQueue = new Queue<int>();
            inputQueue.Enqueue(2);
            Console.WriteLine("Part 2 solution: ");
            opCalc = new OpcodesCalculator(originalOpcodes);
            await opCalc.CalculateOpcodes(inputQueue);
        }

 
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Day6
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var sr = new StreamReader("input.txt"))
            {
                var stringOrbits = sr.ReadToEnd().Split('\n').ToList();
                var dictionaryOfObjects = new Dictionary<string, OrbitingObject>();
                dictionaryOfObjects.Add("COM",new OrbitingObject("COM",null));
                foreach (var stringOrbit in stringOrbits)
                {
                    var firstObjectName = stringOrbit.Substring(0,3);
                    var secondObjectName = stringOrbit.Substring(4,3);
                    // first object
                    if (!dictionaryOfObjects.ContainsKey(firstObjectName))
                    {
                        dictionaryOfObjects.Add(firstObjectName, new OrbitingObject(firstObjectName, null));
                    }

                    // second object
                    if (!dictionaryOfObjects.ContainsKey(secondObjectName))
                    {
                        dictionaryOfObjects.Add(secondObjectName, new OrbitingObject(secondObjectName, dictionaryOfObjects[firstObjectName]));
                    }
                    else
                    {
                        dictionaryOfObjects[secondObjectName].DirectOrbit = dictionaryOfObjects[firstObjectName];
                    }                   

                    // needed for 2nd part
                    dictionaryOfObjects[firstObjectName].AllNeighbours.Add(dictionaryOfObjects[secondObjectName]);
                    dictionaryOfObjects[secondObjectName].AllNeighbours.Add(dictionaryOfObjects[firstObjectName]);

                }

                // Part 1
                int sumOfOrbits = 0;
                foreach (var orbitingObject in dictionaryOfObjects.Values)
                {
                    sumOfOrbits += CountOrbits(orbitingObject);
                }
                Console.WriteLine($"Number of orbits = {sumOfOrbits}");

                // Part 2 BFS search
                var startObject = dictionaryOfObjects["YOU"];
                var endObject = dictionaryOfObjects["SAN"];
                var visitedNodes = new List<OrbitingObject>();
                visitedNodes.Add(startObject);
                var queue = new Queue<OrbitingObject>();
                queue.Enqueue(startObject);
                
                while (queue.Count != 0)
                {
                    var currentNode = queue.Dequeue();
                    if (currentNode == endObject)
                        break;
                    foreach (var neighbour in currentNode.AllNeighbours)
                    {
                        if (visitedNodes.Contains(neighbour))
                            continue;
                        
                        neighbour.PreviousNode = currentNode;
                        queue.Enqueue(neighbour);
                        visitedNodes.Add(neighbour);
                    }
                }
                int sumOfHops = 0;
                var tempNode = endObject;
                while (tempNode.PreviousNode != startObject)
                {
                    sumOfHops++;
                    tempNode = tempNode.PreviousNode;
                }
                Console.WriteLine($"Number of hops = {sumOfHops - 1}");
            }
        }

        private static int CountOrbits(OrbitingObject orbitingObject)
        {
            if (orbitingObject.DirectOrbit == null)
                return 0;
            else
                return 1 + CountOrbits(orbitingObject.DirectOrbit);
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Day7
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var sr = new StreamReader("input.txt"))
            {
                var originalOpcodes = sr.ReadToEnd().Split(',').Select(x => int.Parse(x)).ToList();

                // Part 1
                var permutations = GenerateAllPermutations(Enumerable.Range(0, 5), 5);
                int part1MaxSignal = GetMaxThrusterSignal(originalOpcodes, permutations);
                Console.WriteLine($"Part 1 highest signal = {part1MaxSignal}");

                // Part 2
                permutations = GenerateAllPermutations(Enumerable.Range(5, 5), 5);
                int part2MaxSignal = GetMaxThrusterSignal(originalOpcodes, permutations);
                Console.WriteLine($"Part 2 highest signal = {part2MaxSignal}");
            }
        }
        private static List<List<int>> GenerateAllPermutations(IEnumerable<int> list, int length)
        {
            if (length == 1)
                return list.Select(x => new List<int> { x }).ToList();

            return GenerateAllPermutations(list, length - 1)
                .SelectMany(x => list.Where(y => !x.Contains(y)),
                (t1, t2) => t1.Concat(new List<int> { t2 }).ToList()).ToList();
        }

        private static int GetMaxThrusterSignal(List<int> originalOpcodes, List<List<int>> permutations)
        {
            var concurrentBag = new ConcurrentBag<int>();
            Parallel.For(0, permutations.Count, number =>
            {
                var permutation = permutations[number];

                var taskParameterQueues = new List<Queue<int>>();
                for (int i = 0; i < permutation.Count; i++)
                {
                    taskParameterQueues.Add(new Queue<int>(new int[] { permutation[i] }));
                }
                taskParameterQueues[0].Enqueue(0);

                var tasks = new List<Task>();
                tasks.Add(Task.Factory.StartNew(() => CalculateOpcodes(originalOpcodes, taskParameterQueues[0], taskParameterQueues[1])).Unwrap());
                tasks.Add(Task.Factory.StartNew(() => CalculateOpcodes(originalOpcodes, taskParameterQueues[1], taskParameterQueues[2])).Unwrap());
                tasks.Add(Task.Factory.StartNew(() => CalculateOpcodes(originalOpcodes, taskParameterQueues[2], taskParameterQueues[3])).Unwrap());
                tasks.Add(Task.Factory.StartNew(() => CalculateOpcodes(originalOpcodes, taskParameterQueues[3], taskParameterQueues[4])).Unwrap());
                tasks.Add(Task.Factory.StartNew(() => CalculateOpcodes(originalOpcodes, taskParameterQueues[4], taskParameterQueues[0])).Unwrap());

                Task.WaitAll(tasks.ToArray());
                concurrentBag.Add(taskParameterQueues[0].Dequeue());
            });
            return concurrentBag.Max();
        }

        static async Task CalculateOpcodes(List<int> originalOpCodes, Queue<int> input, Queue<int> output)
        {
            var opCodes = new List<int>(originalOpCodes);
            int pointerPosition = 0;
            while(true)
            {
                var instruction = CalculateInstruction(opCodes[pointerPosition]).ToList();
                pointerPosition = await ExecuteInstruction(instruction, pointerPosition, opCodes, input, output);
                if (pointerPosition == -1)
                    return;
            }
        }

        private static async Task<int> ExecuteInstruction(List<int> instruction, int pointerPosition, List<int> opCodes, Queue<int> input, Queue<int> output)
        {
            switch(instruction[4])
            {
                case 1:
                    opCodes[opCodes[pointerPosition + 3]] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) + CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 2:
                    opCodes[opCodes[pointerPosition + 3]] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) * CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 3:
                    while (input.Count == 0)
                    {
                        await Task.Delay(5);
                    }
                    opCodes[opCodes[pointerPosition + 1]] = input.Dequeue();
                    return pointerPosition + 2;
                case 4:
                    output.Enqueue(CalculateVariable(pointerPosition + 1, instruction[2], opCodes));
                    return pointerPosition + 2;
                case 5:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) != 0)
                        return CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 6:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == 0)
                        return CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 7:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) < CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[opCodes[pointerPosition + 3]] = 1;
                    else
                        opCodes[opCodes[pointerPosition + 3]] = 0;
                    return pointerPosition + 4;
                case 8:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[opCodes[pointerPosition + 3]] = 1;
                    else                 
                        opCodes[opCodes[pointerPosition + 3]] = 0;
                    return pointerPosition + 4;
                case 9:
                    return -1;
            }
            return 0;
            int CalculateVariable(int position, int mode, List<int> opCodes) => mode == 1 ? opCodes[position] : opCodes[opCodes[position]];
        }

        private static IEnumerable<int> CalculateInstruction(int wholeOpcode)
        {
            var stringOpcode = wholeOpcode.ToString();
            for (int i = 4; i >= 0; i--)
            {
                if (stringOpcode.Length - 1 < i)
                    yield return 0;
               else
                    yield return int.Parse(stringOpcode[stringOpcode.Length - (i + 1)].ToString());
            }
        }
    }
}
<file_sep>namespace Day3
{
    internal class Cell
    {
        public bool [] VisitedByPath { get; set; } = new bool[2];
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Day13
{
    class Tile
    {
        public int X { get; set; }
        public int Y { get; set; }
        public int Type { get; set; }

        public Tile(int x, int y, int type)
        {
            X = x;
            Y = y;
            Type = type;
        }
    }
}
<file_sep>namespace Day10
{
    internal class Asteroid
    {
        public int X { get; }
        public int Y { get; }
        public int NumberOfSeenAsteroids { get; set; }

        public Asteroid(int x, int y)
        {
            X = x;
            Y = y;
        }

    }
}<file_sep>using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading.Tasks;

namespace Day11
{
    public class OpcodesCalculator
    {
        private int relativeBase = 0;
        private List<BigInteger> opCodes;
        public OpcodesCalculator(List<BigInteger> originalOpCodes)
        {
            this.opCodes = new List<BigInteger>(originalOpCodes);
        }
        public async Task CalculateOpcodes(Queue<int> input, Queue<int> output = null)
        {
            int pointerPosition = 0;
            while(true)
            {
                var instruction = CalculateInstruction(opCodes[pointerPosition]).ToList();
                pointerPosition = await ExecuteInstruction(instruction, pointerPosition, opCodes, input, output);
                if (pointerPosition == -1)
                    return;
            }
        }

        private async Task<int> ExecuteInstruction(List<int> instruction, int pointerPosition, List<BigInteger> opCodes, Queue<int> input, Queue<int> output)
        {
            switch(instruction[4])
            {
                case 1:
                    opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) + CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 2:
                    opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = CalculateVariable(pointerPosition + 1, instruction[2], opCodes) * CalculateVariable(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 4;
                case 3:
                    while (input.Count == 0)
                    {
                        await Task.Delay(5);
                    }
                    opCodes[CalculatePosition(pointerPosition + 1, instruction[2], opCodes)] = input.Dequeue();
                    return pointerPosition + 2;
                case 4:
                    if (output != null)
                        output.Enqueue(CalculateVariableWithCastToInt(pointerPosition + 1, instruction[2], opCodes));
                    else
                        Console.WriteLine(CalculateVariable(pointerPosition + 1, instruction[2], opCodes));

                    return pointerPosition + 2;
                case 5:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) != 0)
                        return CalculateVariableWithCastToInt(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 6:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == 0)
                        return CalculateVariableWithCastToInt(pointerPosition + 2, instruction[1], opCodes);
                    return pointerPosition + 3;
                case 7:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) < CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = 1;
                    else
                        opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = 0;
                    return pointerPosition + 4;
                case 8:
                    if (CalculateVariable(pointerPosition + 1, instruction[2], opCodes) == CalculateVariable(pointerPosition + 2, instruction[1], opCodes))
                        opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = 1;
                    else                 
                        opCodes[CalculatePosition(pointerPosition + 3, instruction[0], opCodes)] = 0;
                    return pointerPosition + 4;
                case 9:
                    if (instruction[3] == 9)
                        return -1;
                    else
                    {
                        relativeBase += CalculateVariableWithCastToInt(pointerPosition + 1, instruction[2], opCodes);
                        return pointerPosition + 2;
                    }
            }
            return 0;
            
            // local functions
            BigInteger CalculateVariable(int position, int mode, List<BigInteger> opCodes) => opCodes[CalculatePosition(position,  mode, opCodes)];

            int CalculatePosition(int position, int mode, List<BigInteger> opCodes)
            {
                switch(mode)
                {
                    case 0:
                        return (int)opCodes[position];
                    case 1:
                        return position;
                    case 2:
                        return relativeBase + (int)opCodes[position];
                }
                return -1;
            }

            int CalculateVariableWithCastToInt(int position, int mode, List<BigInteger> opCodes) => (int)CalculateVariable(position, mode, opCodes);
        }

        private IEnumerable<int> CalculateInstruction(BigInteger wholeOpcode)
        {
            var stringOpcode = wholeOpcode.ToString();
            for (int i = 4; i >= 0; i--)
            {
                if (stringOpcode.Length - 1 < i)
                    yield return 0;
               else
                    yield return int.Parse(stringOpcode[stringOpcode.Length - (i + 1)].ToString());
            }
        }   
    }
}<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Text.RegularExpressions;

namespace Day12
{
    class Program
    {
        static void Main(string[] args)
        {         
            var moons = new List<Moon>();
            var lines = File.ReadAllLines("input.txt");
            var regex = new Regex(@"^<x=(?<x>-?\d+), y=(?<y>-?\d+), z=(?<z>-?\d+)>");
            foreach (var line in lines)
            {
                var match = regex.Match(line);
                moons.Add(new Moon(int.Parse(match.Groups["x"].Value), int.Parse(match.Groups["y"].Value), int.Parse(match.Groups["z"].Value)));
            }

            for (int i = 0; i < 1000; i++)
            {
                foreach (var moon in moons)
                    moon.CalculateGravity(moons);

                foreach (var moon in moons)
                    moon.ApplyVelocity();
            }

            int totalEnergy = 0;
            foreach (var moon in moons)
                totalEnergy += moon.CalculateTotalEnergy();

            Console.WriteLine($"Total energy in the system after 1000 steps: {totalEnergy}");

            // Part 2
            foreach (var moon in moons)
                moon.ResetToBasePosition();
            
            long xPeriod = 0, yPeriod = 0, zPeriod = 0;
            int numberOfSteps = 0;
            while (true)
            {
                foreach (var moon in moons)
                    moon.CalculateGravity(moons);

                foreach (var moon in moons)
                    moon.ApplyVelocity();

                numberOfSteps++;
                if (xPeriod == 0 && moons.All(x => x.Position.X == x.BasePosition.X) &&  moons.All(x => x.Velocity.X == 0))
                    xPeriod = numberOfSteps;

                if (yPeriod == 0 && moons.All(x => x.Position.Y == x.BasePosition.Y) && moons.All(x => x.Velocity.Y == 0))
                    yPeriod = numberOfSteps;

                if (zPeriod == 0 && moons.All(x => x.Position.Z == x.BasePosition.Z) && moons.All(x => x.Velocity.Z == 0))
                    zPeriod = numberOfSteps;

                if (xPeriod != 0 && yPeriod != 0 && zPeriod != 0)
                {
                    Console.WriteLine("Cycle period:");
                    Console.WriteLine(MathNet.Numerics.Euclid.LeastCommonMultiple(new[] { xPeriod, yPeriod, zPeriod }));
                    break;
                }
            }
        }

        static bool SaveCurrentStates(HashSet<((Vector3D, Vector3D), (Vector3D, Vector3D), (Vector3D, Vector3D), (Vector3D, Vector3D))> set, List<Moon> moons)
        {
            return set.Add(((moons[0].Position, moons[0].Velocity), (moons[1].Position, moons[1].Velocity), (moons[2].Position, moons[2].Velocity), (moons[3].Position, moons[3].Velocity)));
        }
    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Day3
{
    class Program
    {
        const int fieldSize = 50000;
        const int startingCoordinate = fieldSize / 2;

        static void Main(string[] args)
        {
            using (var sr = new StreamReader("input.txt"))
            {
                var lines = sr.ReadToEnd().Split('\n');
                var firstPathCommands = lines[0].Split(',')
                    .Select(x => new Command(x, 0)).ToList();
                var secondPathCommands = lines[1].Split(',')
                    .Select(x => new Command(x, 1)).ToList();

                // Part 1
                var field = new Field(fieldSize, fieldSize);
                int currentX = startingCoordinate;
                int currentY = startingCoordinate;
                foreach(var command in firstPathCommands)
                {
                    (currentX, currentY) = field.Apply(command, currentX, currentY);
                }
                currentX = startingCoordinate;
                currentY = startingCoordinate;
                foreach(var command in secondPathCommands)
                {
                    (currentX, currentY) = field.Apply(command, currentX, currentY);
                } 

                var crosses = field.Crosses;
                crosses.Remove((startingCoordinate,startingCoordinate));
                int minDistance = int.MaxValue;
                foreach (var cross in crosses)
                {
                    int distance = CalculateManhattanDistance((startingCoordinate, startingCoordinate), cross);
                    if (distance < minDistance)
                        minDistance = distance;
                }
                Console.WriteLine($"Distance to closest intersection = {minDistance}");

                // Part 2              
                var firstPathSteps = CalculateNumberOfStepsToAllCrosses(firstPathCommands, crosses);
                var secondPathSteps = CalculateNumberOfStepsToAllCrosses(secondPathCommands, crosses);
                var zippedList = firstPathSteps.Zip(secondPathSteps, (x, y) => (x + y));
                Console.WriteLine($"Number of steps  = {zippedList.Min()}");
            }
        }

        private static int CalculateManhattanDistance((int x, int y) startingPoint, (int x, int y) cross)
        {
            return Math.Abs(startingPoint.x - cross.x) + Math.Abs(startingPoint.y - cross.y);
        }

        private static List<int> CalculateNumberOfStepsToAllCrosses(List<Command> commands, List<(int,int)> crosses)
        {
            var numbersOfStepsInPath = new List<int>();
            var field = new Field(fieldSize, fieldSize);
            int crossNumber = 0;
            foreach (var cross in crosses)
            {
                
                numbersOfStepsInPath.Add(0);
                var startingPos = (startingCoordinate, startingCoordinate);
                foreach(var command in commands)
                {
                    var currentPos = field.Apply(command, startingPos.Item1, startingPos.Item2);
                    numbersOfStepsInPath[crossNumber] += command.Number;
                    if (IsBetween(startingPos, currentPos, cross))
                    {
                        numbersOfStepsInPath[crossNumber] -= Math.Abs((currentPos.x - cross.Item1) + (currentPos.y - cross.Item2));
                        break;
                    }
                    startingPos = currentPos;            
                }
                crossNumber++;
            }
            return numbersOfStepsInPath;

            static bool IsBetween((int x, int y) startingPos, (int x, int y) currentPos, (int x, int y) cross)
            {
                if (startingPos.y == cross.y)
                {
                    if (Math.Abs(currentPos.x - startingPos.x) > Math.Abs(currentPos.x - cross.x))
                        return true;
                }
                else if (startingPos.x == cross.x)
                {
                    if (Math.Abs(currentPos.y - startingPos.y) > Math.Abs(currentPos.y - cross.y))
                        return true;
                }
                return false;
            }
        }


    }
}
<file_sep>﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace Day2
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var sr = new StreamReader("input.txt"))
            {
                var originalOpcodes = sr.ReadToEnd().Split(',').Select(x => int.Parse(x)).ToList();
                // Part 1
                int result = CalculateOpcodes(originalOpcodes, 12, 2);
                Console.WriteLine($"Solution for part 1: {result}");

                // Part 2
                int numberToSearch = 19690720;
                for (int noun = 0; noun < 100; noun++)
                {
                    for (int verb = 0; verb < 100; verb++)
                    {
                        if (CalculateOpcodes(originalOpcodes, noun, verb) == numberToSearch)
                        {
                            Console.WriteLine($"Solution for part 2: {100 * noun + verb}");
                            break;
                        }                            
                    }
                }
            }
        }

        static int CalculateOpcodes(List<int> originalOpcodes, int noun, int verb)
        {
            var opCodes = new List<int>(originalOpcodes);
            opCodes[1] = noun;
            opCodes[2] = verb;          
            int currentOpcodePosition = 0;
            while(true)
            {
                int currentOpcode = opCodes[currentOpcodePosition];
                switch(currentOpcode)
                {
                    case 1:
                        opCodes[opCodes[currentOpcodePosition + 3]] = opCodes[opCodes[currentOpcodePosition + 1]] + opCodes[opCodes[currentOpcodePosition + 2]];
                        currentOpcodePosition += 4;
                        break;
                    case 2:
                        opCodes[opCodes[currentOpcodePosition + 3]] = opCodes[opCodes[currentOpcodePosition + 1]] * opCodes[opCodes[currentOpcodePosition + 2]];
                        currentOpcodePosition += 4;
                    break;
                    case 99:
                        return opCodes[0];
                }
            }
        }
    }
}
<file_sep>﻿using System;
using System.IO;
using System.Linq;

namespace Day1
{
    class Program
    {
        static void Main(string[] args)
        {           
            using (var sr = new StreamReader("input.txt"))
            {
                var moduleMasses = sr.ReadToEnd().Split('\n').Select(x => int.Parse(x)).ToList();
                // Part 1
                int fuelSum = 0;
                foreach (var mass in moduleMasses)
                {
                    fuelSum += CalculateFuelForModule(mass);
                }
                Console.WriteLine($"Solution for part 1: {fuelSum}");

                // Part 2
                fuelSum = 0;
                foreach (var mass in moduleMasses)
                {
                    var moduleFuel = CalculateFuelForModule(mass);
                    while (moduleFuel >= 0)
                    {
                        fuelSum += moduleFuel;
                        moduleFuel = CalculateFuelForModule(moduleFuel);                       
                    }
                }           
                Console.WriteLine($"Solution for part 2: {fuelSum}");
            }
        }

        public static int CalculateFuelForModule(int moduleMass) => moduleMass / 3 - 2;
    }
}
