A Week In Dev – w/e 28 Feb 2021

Nothing for last week – half term with kids so stayed away from computer as much as possible.

Work

Really positive architecture meeting.
Solving issue with using Selenium to do integration testing and ‘mocking’ the database
Next week sees the first Blazor implementation that needs to be code reviewed, so have been using Pluralsight to re-visit that knowledge.

Learning

Completed the EF Core Introduction by Julie Lehman on Pluralsight.

Watched ASP.NET Community Standup – Building HTTP APIs

Very timely as we’ve just introduce Swagger across all microservices. Learnt some further best practices that will be added to the coding standards and retrospecitvely introduced to the code base.

.NET 6.0 Preview 1 Release Notes

Too early to start ‘playing’ with things, but with .NET 6 being the long term service release, really exciting to see the developments being made in this area.

.NET 6 Preview 1 Release Notes
ASP.NET Core 6 Preview 1 Release Notes
EF Core 6 Preview 1 Release Notes

Raspberry Pi Pico – UDS Sensor and Pico Explorer Base

Treated myself to a Pico Explorer Base (https://shop.pimoroni.com/products/pico-explorer-base) from Pimorino, and developed my original UDS Sensor script to use the Pico Display and Audio output. Code for this is available from Git here: https://github.com/greg-bowie/raspberrypico/blob/main/pico-explorer-parking-sensor.py

Also bought a ‘Tiny Pico 2040’ – got it working and displaying the Rainbow LEDs, but need to think of a suitable project, and probably get a soldering iron out again.

Space Station Code Submission

Also worked (did the work?) with the kids to get submissions in for the Astro PI: Mission Zero project.

Watching

Big fans of Line of Duty, so decided to go backwards and watch some of them again. Started strangely with Series 2. Now heading back to Series 1.

Fitness

Much better week this week. A PT session on Monday left me with DOMS until about Thursday!

Tuesday and Thursday saw 5k runs, and Saturday was a 5 mile run – concluding week 1 of a 20 week training plan for Race to the Stones.

By way of this running, also completed a Virtual North Downs Way 100km event, looking forward to the wooden coaster and postcard to arrive!

Need to improve diet and factor in rehab exercises for on-going groin injury, but much better than previous weeks.

A Week In Dev – w/e 14 Feb 2021

Just a summary of random events, thoughts and things that have happened over the last week. Mainly as a placeholder for myself to remember interesting articles for potential future reference.

Context

Spent 3 days in bed this week (Thu-Sat) due to a headache, sore throat and general tiredness. Not often I’m not well enough to feel like ‘not going to work’ but that’s what happened this week. Not a 100% better on Sunday as I write this, but better.

Still homeschooling and trying to juggle working full-time whilst attempting to get the kids to do their schoolwork.

Work

Purposefully vague here, but developed a new microservice that made deployment (albeit relatively straight-forward at the moment, predominately putting the architecture in place for future development). Participated in 3 code reviews, with one in-particular bringing some fundamental queries and pin-pointing points for significant improvement. Other than that, it was meetings, followed by meetings.

Also forming part of a UK wide Software Architects group where significant overlap is being identified between different programme initiatives.

Learning

The team are starting to pick up some Entity Framework Core developments now and in the coming Sprints, so refreshed myself with EF Core and particularly some of the .NET 5.0 releases with Julie Lehman’s excellent Pluralsight Course. Still got a couple of modules to go, but an excellent refresher:

https://app.pluralsight.com/library/courses/entity-framework-core-get-started

Completed the Kubernetes ‘Up and Running’ Book. Great overview of the technology, but used it as bedtime reading rather than actually following any of the examples. Feel I’ve got a good base understanding of K8S now.

Also managed to build up my Raspberry Pi cluster and format the cards. All good to go, now need to set-up the apps and the cluster – hopefully get round to this next week:

Watched an excellent overview of Code Reviews, benefits and challenges, with a particular slant on Security:

Caught up with 2 episodes of the .NET Core Podcast, particularly enjoying the Security overview of the 3rd party dependencies – a regular conversation of choice – as well as seeing an ‘outsiders’ perspective of .NET Core and its developments:

https://dotnetcore.show/episode-69-the-risks-of-third-party-code-with-niels-tanis/

https://dotnetcore.show/episode-62-what-have-i-missed-with-zac-braddy/

Alongside the theme of security for this week, also read this really interesting blog post (virtually going hand-in-hand with the .NET Core Podcast 69) about injecting packages into build pipelines. An excellent read to provide insight to some of the security issues with the various library management systems.

And then from Microsoft watched a presentation about Lighthouse API (for performance monitoring, something I’ve not used for while) and AXE (for accessibility testing) and incorporating them into the Azure DevOps pipeline.

Finally, also bought a UDS sensor from PiHut, connected it to the Pico and attempted to explain how it worked to the kids, with varying levels of success. Blog post here.

Watching

Watched the Cecil Hotel docuseries on Netflix, and then spun out to watch Morbids latest video.

Exercising

Due to illness, only had time for 1 5k run, and a home HIIT workout. Not too worried at this point, but Race to the Stones training starts w/c 22nd February – so time to buck ideas up a little.

Finally, came joint 3rd in the family Zoom quiz. Something I’d rather not talk about!

Raspberry Pi Pico and UDS

This little project should take about 20 mins to complete and the original source of inspiration came form the weekly Raspberry Pi Foundation video stream:

What we’re going to make is sample Parking Sensor alarm, much like you see on most cars today. For this demo we’ll need:

For the wiring diagram, review the YouTube video linked above. I however didn’t have a buzzer to wire up, so instead used the on-board LED to prove things worked as expected. I took the existing code and made the following changes (the changes are commented):

# pico parking sensor example
# original script from Mar Scott: https://gist.github.com/MarcScott/f9b2ebb37b32156eb5ebf2cb76bfef9d
# Video introduction from RPF: https://www.youtube.com/watch?v=XPvBm9-9jJw

from machine import Pin
from utime import sleep_us, ticks_us, sleep

trig=Pin(17, Pin.OUT)
echo=Pin(16, Pin.IN)
# Not using a Buzzer so comment out
# buzzer=Pin(27, Pin.OUT)
# To save any wiring and prove the concept, use the Pico board LED
onboardled = Pin(25, Pin.OUT)

# Test the lights work
onboardled.value(1)
sleep(2)
onboardled.value(0)

def calc_cm(echo, trig):
    ## a 10 us signal to trig starts the internal ranging program of the sensor.
    trig.low()
    sleep_us(2)
    trig.high()
    sleep_us(10)
    trig.low()
    ## echo pin is set high when the first pulses are sent out, so wait for the echo to go high
    while echo.value() == 0:
        pass
    ## Now record the time, then wait until the echo pin goes low, which means the echo has been received.
    t1 = ticks_us()
    while echo.value() == 1:
        pass
    # Record the time again, as the echo has been received
    t2 = ticks_us()
    ## Calulate time difference.
    ## Speed of sound is approx 340m/s, which is 0.034 cm/us
    ## But the sound has traveled twice the distance, so need to / 2
    ## s = t * v
    ## s = (t2-t1) * 0.034 / 2
    ## s = (t2-t1) * 0.017 which is approximately
    cm = (t2 - t1) / 58.0
    return cm

# Start the loop.
while True:
    cm = calc_cm(echo, trig)
    print(cm)
    # if less than 30 cms, then flash the on-board LED
    if cm < 30:
        onboardled.value(1)
        sleep(0.01 * cm)
        onboardled.value(0)
        sleep(0.01 * cm)

Run the program and you should see the Pico LED light up initially (for testing) and then flash when the sensor detects something within 30 cms.

Part 2 – Extending to use multiple LEDs

I then attached LEDs to Pins 14 and 15 (see the Pico Getting Starting Guide for an example, including connecting an appropriate resistor)

I then altered the code to include the new LEDs, and extended the while loop to flash them at reduced distances. The updated code is below:

# pico parking sensor example
# original script from Mar Scott: https://gist.github.com/MarcScott/f9b2ebb37b32156eb5ebf2cb76bfef9d
# Video introduction from RPF: https://www.youtube.com/watch?v=XPvBm9-9jJw

from machine import Pin
from utime import sleep_us, ticks_us, sleep

trig=Pin(17, Pin.OUT)
echo=Pin(16, Pin.IN)
# Not using a Buzzer so comment out
# buzzer=Pin(27, Pin.OUT)

# To save any wiring and prove the concept, use the Pico board LED
onboardled = Pin(25, Pin.OUT)

# After adding wiring - these are the 2 lights added to the breadboad
yellowled = Pin(15, Pin.OUT)
redled = Pin(14, Pin.OUT)

# Test the lights work
onboardled.value(1)
yellowled.value(1)
redled.value(1)
sleep(2)
yellowled.value(0)
redled.value(0)
onboardled.value(0)

# Function to calculate the distance travelled by the sensor
# and return the distance in centimetres.
def calc_cm(echo, trig):
    ## a 10 us signal to trig starts the internal ranging program of the sensor.
    trig.low()
    sleep_us(2)
    trig.high()
    sleep_us(10)
    trig.low()
    ## echo pin is set high when the first pulses are sent out, so wait for the echo to go high
    while echo.value() == 0:
        pass
    ## Now record the time, then wait until the echo pin goes low, which means the echo has been received.
    t1 = ticks_us()
    while echo.value() == 1:
        pass
    # Record the time again, as the echo has been received
    t2 = ticks_us()
    ## Calulate time difference.
    ## Speed of sound is approx 340m/s, which is 0.034 cm/us
    ## But the sound has traveled twice the distance, so need to / 2
    ## s = t * v
    ## s = (t2-t1) * 0.034 / 2
    ## s = (t2-t1) * 0.017 which is approximately
    cm = (t2 - t1) / 58.0
    return cm

# Start the loop.
while True:
    # retrieve the sensor distance calculation
    cm = calc_cm(echo, trig)
    # output to console for debugging
    print(cm)
    # if less than 30 cms, then flash the on-board LED
    if cm < 30:
        onboardled.value(1)
        sleep(0.01 * cm)
        onboardled.value(0)
        sleep(0.01 * cm)
    # if less than 15 cms, then also flash the yellow LED
    if cm < 15:
        yellowled.value(1)
        sleep(0.01 * cm)
        yellowled.value(0)
        sleep(0.01 * cm)
    # if less than 5 cms, then also flash the red LED
    if cm < 5:
        redled.value(1)
        sleep(0.01 * cm)
        redled.value(0)
        sleep(0.01 * cm)
      

The final project looks like this:

And videoing demoing the capability is here:

More Raspberry Pico Display – Buttons and Displaying Text

Continuing on from my introduction to Pico and the Pico Display, I found some more time to play with the display. This time my main focus was on how to interact with the buttons as well as starting to display text.

Turns out most of the difficult aspect of using the displays and buttons together is ensuring the appropriate pen colour is used to update the display, otherwise the text gets lost on the screen.

Below is a script that exercises a number of the functions available in the Pico display library:

import picodisplay as display
import machine
import utime

# Initial Setup - these must be done for any features to work
width = display.get_width()
height = display.get_height()
display_buffer = bytearray(width * height * 2)  # 2-bytes per pixel (RGB565)
display.init(display_buffer)

# Check the LED actually works
display.set_led(51, 153, 255) # set the LED to a blue shade
display.set_backlight(1.0)

# Define pen colours
black = display.create_pen(0, 0, 0)
white = display.create_pen(255,255,255)
green = display.create_pen(102, 255, 102)
display.set_pen(black)

# Now loop and handle button presses
while True:
    # Do buttons
    if display.is_pressed(display.BUTTON_A):
        # Display the temperature

        # Clear the display and set to black
        display.set_pen(black)
        display.set_backlight(1)
        display.set_led(0, 255, 0)
        display.clear()
        display.update()
        
        # Get the temperature
        sensor_temp = machine.ADC(4)
        conversion_factor = 3.3 / (65535)
        reading = sensor_temp.read_u16() * conversion_factor
        temperature = (27 - (reading - 0.706) / 0.001721)
        tempStr = "Temp = {:.{}f}".format(temperature, 2)
        
        # Update the display
        display.set_pen(white)
        display.text(tempStr, 0, 0, 240, 4)
        display.update()
        utime.sleep(2)
    if display.is_pressed(display.BUTTON_B):
        # Display uptime
        
        # Clear the display and set to black
        display.set_pen(black)
        display.set_backlight(1)
        display.set_led(0, 0, 255)
        display.clear()
        display.update()
        
        # Get the uptimetime
        nowtime = utime.time()
        year, month, monthday, hour, minute, second, weekday, yearday = utime.localtime(nowtime)
        current_time = "Uptime={hours}:{mins}:{seconds}".format(hours=hour, mins=minute, seconds=second)
        
        # Update the display
        display.set_pen(white)
        display.text("Pico Uptime:", 0, 0, 240, 4)
        display.text("{hours} hrs, {mins} mins, {secs} s".format(hours=hour, mins=minute, secs=second), 0, 30, 240, 2)
        display.update()
        utime.sleep(2)
    if display.is_pressed(display.BUTTON_X):
        # Pretend to close down
        display.set_led(0, 0, 0)
        display.set_backlight(0)
    if display.is_pressed(display.BUTTON_Y):
        # Some more ideas needed!
        display.set_led(255, 0, 0)

And the output is as follows:

References:

Pico Display Library Scratchpad: https://github.com/UnfinishedStuff/Pimoroni_Pico_Display_Pack_documentation

Micropython Date/Time overview:
http://docs.micropython.org/en/latest/library/utime.html

Raspberry Pi Pico–tThe £4 Computer

So after a bit of tinkering with a Raspberry Pi (or 2!) over Christmas (main project was to build a Pi Zero, attach a temperature sensor and then send an hourly reading over to Azure Cosmos Db via an Azure Function, and then after that create a small Blazor app to view and interact the data – just to explore a number of other technologies), I was buying a few more bits for experimenting with a Kubernetes cluster when I came across the Raspberry Pi Pico.

What is it?

There are probably better technical explanations of what it is, but it’s a microcontroller. So different from a ‘normal’ computer in that it will just execute programs to carry out behaviours, rather than provide (large) volume storage, or connections to keyboards etc. At £4 its amazing value, removes a number of entry barriers and supports MicroPython and C++.

You do need to solder headers on though to do anything too exciting, and it looks like some suppliers are looking to supply pre-soldered header options, much like the Raspberry Pi Zero has today.

Getting Started

So probably the 1st getting started would be to flash the on-board light (Pin 25?). This is a nice thing about the Pico is you can get immediately started with it and check things are working.

There is an excellent tutorial from Raspberry Pi themselves that goes into step-by-step details (the 1st 6 steps of the tutorial for flashing the on-board LED).

image

Reading the Temperature on the Pico

On thing I hadn’t appreciated is that the Pico has an on-board temperature sensor.

Update the blinking code from the tutorial above to this:

import machine
import utime

sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)

while True:
     reading = sensor_temp.read_u16() * conversion_factor
     temperature = 27 - (reading - 0.706) / 0.001721
     print(temperature)
     utime.sleep(2)

And you’ll see the temperature being displayed. I don’t believe its highly accurate, but for home use, more than sufficient.

Using the Pico Display Pack

image

Alongside buying my Pico, I picked up a Pico Display Pack. The easiest way to get going with the Pico Display is to install the Pico SDK from Pimoroni.

Once that is done, reconnect Thonny IDE and take the example demo.py from Pimoroni and you should get the following output.

image

Next on the task would be to look into a way to display the temperature. But that’ll need to wait until later for me.

ADVENT OF CODE 2020–CSHARP – DAY 6

Day 6 Part A

Once you decode the problem, it essentially comes down to the number of unique answered questions within a group. Easiest solution (to be of my knowledge here) is to a use a HashSet, which will only allow unique values in.

Following the pattern to an earlier puzzle in the week, essentially a loop to store the answers and then add to the counter when a blank line is found. Also, note to handle the final line which probably isn’t ended with a blank line. Again, might be a nicer way to do this with LINQ.

So onto the Tests (always start with tests! – whether solving problems in spare time, or on paid time):

     [TestMethod]
     public void ExampleInput()
     {
         var inputList = new List<string>()
         {
             "abc", "", "a", "b", "c", "", "ab", "ac", "", 
              "a", "a", "a", "a", "", "b"
         };

         int sumOfCounts = new Day6PartA().GetSumOfAnswers(inputList);
         Assert.AreEqual(11, sumOfCounts);
     }

     [TestMethod]
     public void FileInput()
     {
         var filename = @"C:\_path_\Day6\input.txt";
         var inputList = File.ReadAllLines(filename).ToList();
         int sumOfCounts = new Day6PartA().GetSumOfAnswers(inputList);
         Assert.AreEqual(6443, sumOfCounts);
     }

The implementation then follows as this:

     public int GetSumOfAnswers(List<string> input)
     {
         int sum = 0;
         HashSet<char> charactersFound = new HashSet<char>();

         foreach (var line in input)
         {
             if (line.Trim().Length == 0)
             {
                 // End of processing, so process here:
                 sum += charactersFound.Count;
                 charactersFound = new HashSet<char>();
             }
             else
             {
                 // Need to process this line, 
                 // splitting into characters and then adding to the set                 
                 // Set should handle not adding duplicates automatically.                 
                 // Could probably do this with LINQ.
                 for (int i = 0; i < line.Length; i++)
                 {
                     charactersFound.Add(line[i]);
                 }
             }
         }

         // Don't forgot the final line!
         sum += charactersFound.Count;
         return sum;
     }

Day 6 Part B

So Part B is all about finding the consistent entries from the entire group, which makes it a little more interesting. With a few other commitments I quickly churned the following solution out, but it felt a bit over worked and I thought there might be a nicer way.

     public int GetAnswersGivenByEveryoneInGroup(List<string> input)
     {
         int sum = 0;
         List<string> tempCollection = new List<string>();

         foreach (var line in input)
         {
             if (line.Trim().Length == 0)
             {
                 if (tempCollection.Count == 1)
                 {
                     sum += tempCollection[0].Length;
                 }
                 else
                 {
                     Dictionary<char, int> countOfChars = new Dictionary<char, int>();
                     foreach (var tempLine in tempCollection)
                     {
                         for (int i = 0; i < tempLine.Length; i++)
                         {
                             var currentValue = countOfChars.GetValueOrDefault(tempLine[i], 0);
                             countOfChars[tempLine[i]] = ++currentValue;
                         }
                     }

                     foreach (var entry in countOfChars)
                     {
                         if (entry.Value == tempCollection.Count)
                         {
                             sum++;
                         }
                     }
                 }
                 tempCollection = new List<string>();
             }
             else
             {
                 tempCollection.Add(line);
             }
         }

         if (tempCollection.Count == 1)
         {
             sum += tempCollection[0].Length;
         }
         else
         {
             Dictionary<char, int> countOfChars = new Dictionary<char, int>();

             foreach (var tempLine in tempCollection)
             {
                 for (int i = 0; i < tempLine.Length; i++)
                 {
                     var currentValue = countOfChars.GetValueOrDefault(tempLine[i], 0);
                     countOfChars[tempLine[i]] = ++currentValue;
                 }
             }

             foreach (var entry in countOfChars)
             {
                 if (entry.Value == tempCollection.Count)
                 {
                     sum++;
                 }
             }
         }
         return sum;
     }

So it works, but there are loops within loops within loops, and its quite length (ok, I could refactor out to helper methods by still).

After a little break I came back to it, and came up with the following solution which only adds consistently appearing characters from each pass, and I think reads a bit better:

     public int GetAnswersGivenByEveryoneInGroupRefactor(List<string> input)
     {
         int sum = 0;
         string buildingList = string.Empty;
         var isFirstPass = true;

         foreach (var line in input)
         {
             if (line.Trim().Length == 0)
             {
                 sum += buildingList.Length;
                 buildingList = string.Empty;
                 isFirstPass = true;
             }
             else
             {
                 if (isFirstPass)
                 {
                     buildingList = line;
                     isFirstPass = false;
                 }
                 else
                 {
                     var tempNewList = new StringBuilder();

                     for (int i = 0; i < line.Length; i++)
                     {
                         if (buildingList.Contains(line[i]))
                         {
                             tempNewList.Append(line[i]); 
                         }
                     }

                     buildingList = tempNewList.ToString();
                 }
             }
         }

         sum += buildingList.Length;
         return sum;
     }

But I think this can still be improved on, using something like HashSet or Intersect. Indeed, I carried on for a bit longer and refactored to use LINQ Intersect, which now makes the code read as:

     public int GetAnswersGivenByEveryoneInGroupRefactor(List<string> input)
     {
         int sum = 0;
         string buildingList = string.Empty;
         var isFirstPass = true;

         foreach (var line in input)
         {
             if (line.Trim().Length == 0)
             {
                 sum += buildingList.Length;
                 buildingList = string.Empty;
                 isFirstPass = true;
             }
             else
             {
                 if (isFirstPass)
                 {
                     buildingList = line;
                     isFirstPass = false;
                 }
                 else
                 {
                     var buildingListArray = buildingList.ToCharArray();
                     var lineArray = line.ToCharArray();
                     var intersect = buildingListArray.Intersect(lineArray).ToArray();
                     buildingList = String.Join("", intersect);
                 }
             }
         }
         sum += buildingList.Length;
         return sum;
     }

I did look at if it would be possible drop the ‘isFirstPass’ boolean, but removing it stopped things from passing. On reflection I suspect its because there is a chance the ‘buildingList’ parameters empties through processing.

ADVENT OF CODE 2020–CSHARP – DAY 5

Day 5 Part A

So once overcoming the initial issue of understanding the problem, this is essentially removing either the top half of a range, or the bottom half, depending on the next value in the string.

I started by keeping things simple, and building confidence, so implemented 2 tests for determining Rows and Columns. This might have ended up being overkill for the final solution, and these methods wouldn’t need to be public for the longer term, but as a starting point it seemed sensible.

     [DataTestMethod]
     [DataRow("FBFBBFF", 44)]
     [DataRow("BFFFBBF", 70)]
     [DataRow("FFFBBBF", 14)]
     [DataRow("BBFFBBF", 102)]
     public void TestForRows(string input, int expectedRowId)
     {
         var rowId = new Day5PartA().GetRow(input);
         Assert.AreEqual(expectedRowId, rowId);
     }

     [DataTestMethod]
     [DataRow("RLR", 5)]
     [DataRow("RRR", 7)]
     [DataRow("RRR", 7)]
     [DataRow("RLL", 4)]
     public void TestForColumns(string input, int expectedColumnId)
     {
         var rowId = new Day5PartA().GetColumn(input);
         Assert.AreEqual(expectedColumnId, rowId);
     }

The 2 methods are identical, and could be refactored into a single method, but I decided to leave them as-is for now (a general tendency I see in S/W development is an over eagerness to implemented generic methods, which then quickly become convoluted with if…else.. and other not required complexity). I tend to stick to the principle of 3 or 5 – once its been implemented that many times, then refactor and only the common pieces.

     public int GetRow(string input)
     {
         int lowerBound = 0;
         int upperBound = 127;

         for (int i = 0; i < input.Length; i++)
         {
             var diff = (upperBound - lowerBound + 1) / 2;

             if (input[i] == 'F')
             {
                 upperBound -= diff;
             }
             else
             {
                 lowerBound += diff;
             }
         }

         if (input[6] == 'F')
         {
             return lowerBound;
         }
         return upperBound;
     }

     public int GetColumn(string input)
     {
         int lowerBound = 0;
         int upperBound = 7;

         for (int i = 0; i < input.Length; i++)
         {
             var diff = (upperBound - lowerBound + 1) / 2;

             if (input[i] == 'L')
             {
                 upperBound -= diff;
             }
             else
             {
                 lowerBound += diff;
             }
         }

         if (input[2] == 'L')
         {
             return lowerBound;
         }

         return upperBound;
     }

With in place, the GetSeatId method is a simple call of to these 2 methods and then doing the calculation:

     public int GetSeatId(string input)
     {
         var row = GetRow(input.Substring(0, 7));
         var column = GetColumn(input.Substring(7));

         return (row * 8) + column;
     }

Day 5 Part B

The hardest part of this question was working out what the question actually was. Finally I figured out it was essentially saying the range of Seat IDs is sequential, and there is a Seat ID missing – find it. NB. The range does not start at zero, so you need to handle that.

There are a few ways to implement this, but personally I find the following implementation using LINQ simplest to read (which is the important aspect of code, IMHO).

     public int MissingSeatId(List<int> seatInput)
     {
         var lowestSeatId = seatInput.Min();
         var highestSeatId = seatInput.Max();
         var missingSeatIds = Enumerable.Range(lowestSeatId, highestSeatId).Except(seatInput);

         return missingSeatIds.ElementAt(0);
     }

ADVENT OF CODE 2020–CSHARP – DAY 4

So onto Day 4, and a little bit quicker today, just because it’s a Friday.

Day 4 Part A

I’m sure there must be a nicer way to do this, but went for quick and dirty and implemented something with this high level thinking:

  • Process each line and store parameters into an object. Object relatively simple as a Dictionary, but only allow addition of ‘Valid’ keys – and cid is not a ‘valid’ key in the current implementation.
  • Need to handle the splitting of parameters on each line with a space initially (but being wary of lines with just a single entry, i.e. no space). And then split those parameters on the ‘:’.
  • On a blank line being reached, check validity of the object just being processed.
  • Also need to handle the last entry in the file, which probably won’t have a blank line to declare the end of its processing.
  • Finally, by only allowing ‘valid’ values into the Dictionary, a simple check of Length == 7 should suffice for whether the overall passport is valid.

As such the ‘supporting’ object looks like this:

public class PassportInfo {
     private Dictionary<string, string> PassportDictionary;
     private readonly List<string> ValidKeys = new List<string>()
     {
         "byr",
         "iyr",
         "eyr",
         "hgt",
         "hcl",
         "ecl",
         "pid"
     };

     public PassportInfo()
     {
         PassportDictionary = new Dictionary<string, string>();
     }

     public void UpdateInfo(string key, string value)
     {
         if (value.Length > 0 && ValidKeys.Contains(key))
         {
             PassportDictionary.Add(key, value);
         }
     }

     public bool IsValid()
     {
         if (PassportDictionary.Count == 7)
         {
             return true;
         }
         return false;
     }
}

And then the main method to parse the file looks like this:

     public int GetNumberOfValidPassports(List<string> passportFile)
     {
         var validPassportsCount = 0;
         PassportInfo passportInfo = new PassportInfo();
         foreach (var line in passportFile)
         {
             if (line.Trim().Length == 0)
             {
                 if (passportInfo.IsValid())
                 {
                     validPassportsCount++;
                 }
                 // Reset passportInfo.
                 passportInfo = new PassportInfo();
             }
             else
             {
                 string[] comps = new string[1];
                 comps[0] = line;
                 if (line.Contains(' '))
                 {
                     comps = line.Split(' ');
                 }

                 for (int i = 0; i < comps.Length; i++)
                 {
                     string[] keyValuePair = comps[i].Split(':');
                     passportInfo.UpdateInfo(keyValuePair[0], keyValuePair[1]);
                 }
             }
         }

         if (passportInfo.IsValid())
         {
             validPassportsCount++;
         }

         return validPassportsCount;
     }

I’ll skip over posting the supporting tests as they’re very similar to previous days.

Day 4 Part B

Truly horrible implementation this one, but its getting a little late. Got lucky by introducing the PassportInfo class in Part A, so it was really fleshing out the Adding to the Dictionary in the case of valid values being determined.

A couple of things I’d consider to improve:

  • Extracting each of the methods and Unit Testing to ensure they meet the requirements.
  • Look at C# Pattern Matching Syntax (in fact, decided to be less lazy and did use this implementation).
  • Other thing considered but not implemented was short-circuiting the processing of the passport on discovery of the 1st invalid piece of data.

But still, its got me past today’s challenge, and wraps up the working week.

     public void UpdateInfo(string key, string value)
     {
         if (ValidKeys.Contains(key))
         {
             switch (key)
             {
                 case "byr":
                     int tempBirthYear = Int16.Parse(value);
                     if (tempBirthYear >= 1920 && tempBirthYear <= 2002)
                     {
                         PassportDictionary.Add(key, value);
                     }
                     break;

                 case "iyr":
                     int tempIssueYear = Int16.Parse(value);
                     if (tempIssueYear >= 2010 && tempIssueYear <= 2020)
                     {
                         PassportDictionary.Add(key, value);
                     }
                     break;

                 case "eyr":
                     int tempExpiryYear = Int16.Parse(value);
                     if (tempExpiryYear >= 2020 && tempExpiryYear <= 2030)
                     {
                         PassportDictionary.Add(key, value);
                     }
                     break;

                 case "hgt":
                     if (value.ToLower().EndsWith("cm"))
                     {
                         var tempValue = value.Replace("cm", "");
                         int height = Int16.Parse(tempValue);
                         if (height >= 150 && height <= 193)
                         {
                             PassportDictionary.Add(key, value);
                         }
                     }
                     else if (value.ToLower().EndsWith("in"))
                     {
                         var tempValue = value.Replace("in", "");
                         int height = Int16.Parse(tempValue);
                         if (height >= 59 && height <= 76)
                         {
                             PassportDictionary.Add(key, value);
                         }
                     }
                     break;

                 case "hcl":
                     if (value.StartsWith("#") && value.Length == 7)
                     {
                         var tempValue = value.Substring(1);
                         if (tempValue.All(c => "0123456789abcdef".Contains(c)))
                         {
                             PassportDictionary.Add(key, value);
                         }
                     }
                     break;

                 case "ecl":
                     if (ValidEyeColourList.Contains(value))
                     {
                         PassportDictionary.Add(key, value);
                     }
                     break;

                 case "pid":
                     if (value.Length == 9 && Int32.TryParse(value, out _))
                     {
                         PassportDictionary.Add(key, value);
                     }
                     break;
             }
         }
     }

ADVENT OF CODE 2020–CSHARP – DAY 3

At first this problem concerned me. I started immediately thinking that I’d need to start duplicating the map, but then it dawned on me that I could actually just reset the line point back to the start, retaining the offset that it had gone pass the end of line.

Day 3 Part A

With that in mind, I created the test with the sample input:

     [TestMethod]
     public void SampleInputTest()
     {
         // Example Input
         var inputMap = new List<string>()
         {
             "..##.......",
             "#...#...#..",
             ".#....#..#.",
             "..#.#...#.#",
             ".#...##..#.",
             "..#.##.....",
             ".#.#.#....#",
             ".#........#",
             "#.##...#...",
             "#...##....#",
             ".#..#...#.#"
         };

         var numberOfTreesHit = new Day3PartA().GetNumberOfHits(inputMap, 3, 1);         
         Assert.AreEqual(7, numberOfTreesHit);
     }

(NB. I got lucky with declaring constants for the moves for X and Y in the initial implementation, so ended up altering the signature for Part B for the method – so this code isn’t exactly the same as it was when solving the problem).

Turning to the code itself, I felt it needed to do a couple of things:

  • Declare constants for right (X) and down (Y) moves.
  • Due to the 1st line not needing to be considered, the last entry could be ignored as this is when the <whatever travelling device> had got through the map.
  • Needed to consider when X when passed the end of the line, and do a reset back to the start (applying the offset)
  • NB. Subtle change to the for loop coming up, which was originally just incrementing by 1, but subsequently changed to handle the varying down (Y) moves, as part of Part B.
  • I was considering if there was an algorithm to just calculate the series of co-ordinates and then retrieve those entries from the line entries – that may be an more efficient approach.

So the code looked very similar to this:

     public int GetNumberOfHits(List<string> inputMap, int moveX, int moveY)
     {
         var numberOfHits = 0;
         var currentX = 0;
         var currentY = 0;
         var numberOfLines = inputMap.Count;

         for (int i = 0; i < numberOfLines - 1; i = i + moveY)
         {
             currentX += moveX;
             currentY += moveY;
             var mapLine = inputMap[currentY];

             if (currentX >= mapLine.Length)
             {
                 currentX -= mapLine.Length;
             }

             if (mapLine[currentX] == '#')
             {
                 numberOfHits++;
             }
         }

         return numberOfHits;
     }

Finally, as ever, another test case to use the file as input, and find out the actual number:

     public void FileInputTest()
     {
         var filename = @"C:\_path_\Day1\Day3\input.txt";
         var inputMap = File.ReadAllLines(filename).ToList();
         var numberOfTreesHit = new Day3PartA().GetNumberOfHits(inputMap, 3, 1);
         Assert.AreEqual(242, numberOfTreesHit);
     }

Day 3 Part B

And then I got lucky with the next step. I was able to alter the signature method to take in the varying right (X) and down (Y) parameters and remove my previously declared constants. The only I change I needed to handle was the for loop index incrementing by the value for down (Y).

In order to test though, I used the DataRow method on Test Suites in MSTest, so the next test looked like:

     [DataTestMethod]
     [DataRow(1, 1, 2)]
     [DataRow(3, 1, 7)]
     [DataRow(5, 1, 3)]
     [DataRow(7, 1, 4)]
     [DataRow(1, 2, 2)]
     public void ExampleMultipleRuns(int moveX, int moveY, int expectedHits)
     {
         // Example Input
         var inputMap = new List<string>()
         {
             "..##.......",
             "#...#...#..",
             ".#....#..#.",
             "..#.#...#.#",
             ".#...##..#.",
             "..#.##.....",
             ".#.#.#....#",
             ".#........#",
             "#.##...#...",
             "#...##....#",
             ".#..#...#.#" 
         };

         var numberOfTreesHit = new Day3PartA().GetNumberOfHits(inputMap, moveX, moveY);
         Assert.AreEqual(expectedHits, numberOfTreesHit);
     }

And the finally was able to do the same test, but using the file for input:

     [DataTestMethod]
     [DataRow(1, 1, 82)]
     [DataRow(3, 1, 242)]
     [DataRow(5, 1, 71)]
     [DataRow(7, 1, 67)]
     [DataRow(1, 2, 24)]
     public void FileInputMultipleRuns(int moveX, int moveY, int expectedHits)
     {
         var filename = @"C:\_path_\Day3\input.txt";
         var inputMap = File.ReadAllLines(filename).ToList();
         var numberOfTreesHit = new Day3PartA().GetNumberOfHits(inputMap, moveX, moveY);
         Assert.AreEqual(expectedHits, numberOfTreesHit);
     }

ADVENT OF CODE 2020–CSHARP – DAY 2

So Day 2 was a ‘password’ issue. Again it really boiled down to parsing a list of strings, separating the parts and checking the ‘validity of the password’.

Day 2 Part A

So again, starting with a test method to prove the example, the test code looks like this:

[TestMethod]
public void DetectedInvalidPasswords_List()
{
         List<string> inputList = new List<string>()
         {
             "1-3 a: abcde",
             "1-3 b: cdefg",
             "2-9 c: ccccccccc"
         };

         var invalidPasswordEntries = new Day2PartA().GetCountOfInvalidPasswords(inputList);            
         Assert.AreEqual(1, invalidPasswordEntries);
}

The processor for Invalid Password code was a simple splitting of the components, initially using <space> and that allowed me to get the constitute parts (I’d probably introduce some kind of object at this point to store the parts, but was lazy here).

Finally it was using linq to check how often the character appeared in the string, before then checking it was within the tolerances of the min and max values.

public int GetCountOfInvalidPasswords(List<string> inputList)
{
    var invalidCounter = 0;
     foreach (var entry in inputList)
     {
         var comps = entry.Split(' ');
         var occurances = comps[0].Split('-');

         int minOccurances = Int16.Parse(occurances[0]);
         int maxOccurances = Int16.Parse(occurances[1]);
         char requiredChar = comps[1][0];
         var password = comps[2];

         int charAppears = password.Count(c => c == requiredChar);

         if (!((charAppears >= minOccurances) && (charAppears <= maxOccurances)))
         {
             invalidCounter++;
         }
     }
     return invalidCounter;
}

Finally, introduced a test to process the file input as opposed to the list provided as an example:

[TestMethod]
public void DetectedInvalidPasswords_File()
{
    var filename = @"C:\_path_to_\AdventOfCode2020CS\Day1\Day2\input.txt";
    var lines = File.ReadAllLines(filename).ToList();

    var invalidPasswordEntries = new Day2PartA().GetCountOfInvalidPasswords(lines);

    Assert.AreEqual(500, invalidPasswordEntries);
}

Day 2 Part B

The next part changed the rule slightly, looking for characters in the ‘correct’ positions, helpfully highlighting that things were not zero-index.

So introducing a next test case for the example provided:

     [TestMethod]
     public void DetectedValidPasswords_List()
     {
         List<string> inputList = new List<string>()
         {
             "1-3 a: abcde",
             "1-3 b: cdefg",
             "2-9 c: ccccccccc"
         };

         var validPasswordEntries = new Day2PartB().GetCountOfValidPasswords(inputList);

         Assert.AreEqual(1, validPasswordEntries);
     }

The method to process the entries altered slightly – still using the same extractor, string splitting methods with minor renaming of variables. It was then use of the logical OR in C# to check that only one of the positions were set to the character provided, remembering to handle the non-zero index.

     public int GetCountOfValidPasswords(List<string> inputList)
     {
         var validCounter = 0;
         foreach (var entry in inputList)
         {
             var comps = entry.Split(' ');
             var occurances = comps[0].Split('-');

             int position1 = Int16.Parse(occurances[0]);
             int position2 = Int16.Parse(occurances[1]);
             char requiredChar = comps[1][0];
             var password = comps[2];

             if ((password[position1 - 1] == requiredChar) ^ (password[position2 - 1] == requiredChar))
             {
                 validCounter++;
             }
         }

         return validCounter;
     }

And then finally the test case to pass in the file:

     [TestMethod]
     public void DetectedValidPasswords_File()
     {
         var filename = @"C:\_path_to_\AdventOfCode2020CS\Day1\Day2\input.txt";
         var lines = File.ReadAllLines(filename).ToList();

         var validPasswordEntries = new Day2PartB().GetCountOfValidPasswords(lines);

         Assert.AreEqual(313, validPasswordEntries);
     }