My solutions to the Christmas calendar from KnowIT

December 2019 I found the Christmas calendar from KnowIT. I got a little bit late to the game, but that is life. My goal is to use each day as an oportunity to solve the puzzle in a programming language that I am curious about, but do not have the time to explore as much as I want. I have a Github repository where I collect my solutions and also the task description for each day. The code included below is psudo-code if nothing else is specified.

The execution time is calculated at my Macbook Pro 2015 model. 3,1 GHz Intel Core i7. 16 GB 1867 MHz DDR3
Puzzle Language Solution Submitted
Day 1: Drageproblemer Go πŸŽ… No
Day 2: Syndefloden Go πŸŽ… No
Day 3: Flate pakker er ogsΓ₯ pakker No
Day 4: Piratsneglen Sneglulf og skattekartet hans No
Day 5: Ønskerøre No
Day 6: Birte sin brilliante biletebeskyttelse No
Day 7: Hvelvets diskrete kode No
Day 8: Lotto-Lotte feirer jul i Vegas No
Day 9: Krampustall Go πŸŽ… Yes
Day 10: Peter Meter Go πŸŽ… Yes
Day 11: Sledestans Go πŸŽ… Yes
Day 12: 6174 Go πŸŽ… Yes
Day 13: The A-maze-ing Race Go No
Day 14: Alvesekvens Go πŸŽ… Yes
Day 15: 3D-pynt Go πŸŽ… Yes
Day 16: Seilkryss Go πŸŽ… Yes
Day 17: Rullande trekantar Go πŸŽ… Yes
Day 18: Use the hash, you must! Go πŸŽ… Yes
Day 19: Skjulte palindromer Go πŸŽ… Yes
Day 20: Alvene ved det runde bord
Day 21: Nissens nye superalver - πŸŽ… No
Day 22: Juleen, juleto, juletre! - πŸŽ… No
Day 23: Harshadprimtall Go πŸŽ… Yes
Day 24: Skitur Go πŸŽ… Yes

Comments on the submitted puzzles

December came fast this year, so I was not prepared from the beginning. Therefore I started a little bit late on the christmas calendar. Solving the puzzles are a lot of fun and enjoying the time I spend on each of them. Even the puzzles that I did not get the time to submit, is solved in the repository at Github.

December 9th

This was the first day that I had the oportunity to solve the puzzle at the right day and therefore also be able to submit the answer. The task this day was a varient og Kaprekar number. I wanted to solve this task using the Go programming language. I have not been doing anything else than hello world type of programming in Go before, but I am facinated about the language.

Through this task I had to read the input numbers from file and then do the check if the number was a Krampus number. The rules was easy and I ended up using recursion to solve this task.

December 10th

This was a fun task to do. Here the input file contained a set of dates and the year was 2018. Each day included three lines with how much toothpaste used, how much shampoo used and how many meters toiletpaper used. Then there was a number of calculations that needed to be done.

Jan 01:
	* 6 ml tannkrem
	* 14 ml sjampo
	* 2 meter toalettpapir
Jan 02:
	* 3 ml tannkrem
	* 19 ml sjampo
	* 3 meter toalettpapir

I starting to like Go now, so I continued on that path. The format of the input file was like the snippet above. At first I assumed that the three lines between each date was in the same order. My first attempt was then to parse the file using modulo calculation. When the first row in the file has index=0, then I could have parsed the file like.

var index = 0
while file.Read()
begin
  if index%4 == 0 { /* Line with the date */ }
  if index%4 == 1 { /* Line with toothpaste */ }
  if index%4 == 2 { /* Line with shampoo */ }
  if index%4 == 3 { /* Line with toiletpaper */ }
  index += 1
end

Then I could just use a regex to extract out the number from the read line. When I had done my first attempt and the answer was incorrect, I found out that the order of the pullet points could vary. Then I needed to have a check for what kind of information the line included. My solution can be found here.

December 11th

The task this day was a fun and easy one. The input file contained a series of chars with no spaceing. First I though how I could parse this file in Go so I got one and one char and be able to evaluate each one of them. Then I remember runes in Go and that mean that the scanner can just scan the runes.

scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanRunes)

I continued using Go for this puzzle as well. Otherwise, the solution was just to go through the input file and add or subtract a number from the initial sledge speed. Needed to keep the count on the number of ice parts next to each other and some other suff. In the end the solution was just an incremented number about how many steps needed before the sledge speed was less or equal to zero. My solution is in at Github.

December 12th

This was a fun and easy puzzle, I decided to continue using Go this day. The implementation itself is straight forward. The one thing that I missed out in the first implementation was the decision to just get out if the recursion want more than 7 times, some of the numbers stay there for ever. I only care about the numbers that use 7 steps to get the goal number anyway.

func repeatToNumber(number int, index int) (int, int) {
	if number == 6174 {
		return number, index
	}
	if index > 7 {
		return number, -1
	}
	num := calc(number)
	return repeatToNumber(num, index+1)
}

I have the code in the repository at Github. The mystery number 6174 was discovered in 1949 by the mathematician D. R. Kaprekar.

Execution time 0.035s (35ms).

December 14th

I continued using Go to solve this puzzle. The puzzle was quite simple, but I missed a little detailed from the rules. I first thought that it was the last added number to the sequence that was going to be used as a number for how many times the next number should be added. That gave off course the wrong sum in the end. When I fixed that one after reading through the rules again, it worked just fine.

December 15th

This was a fun puzzle. I solved it using Go and this time, I could use mathematical concepts that I have not used in years. I wrote a function to calculate the cross product and dot product from two vectors.

func (v Coordinate) crossProduct(ov Coordinate) Coordinate {
	return Coordinate{
		X: v.Y*ov.Z - v.Z*ov.Y,
		Y: v.Z*ov.X - v.X*ov.Z,
		Z: v.X*ov.Y - v.Y*ov.X,
	}
}

func (coor1 Coordinate) dotProduct(coor2 Coordinate) float64 {
	return coor1.X*coor2.X + coor1.Y*coor2.Y + coor1.Z*coor2.Z
}

Then I went through all the coordinates from the triangles to calculate the volum.

func massCalulation(decors map[int]Decor) float64 {
	var sum float64
	for _, value := range decors {
		sum += (value.Edge1.crossProduct(value.Edge2).dotProduct(value.Edge3) / 6) / 1000
	}
	return sum
}

December 16th

This puzzle I have to say I had some trouble with. In the morning, some part of the description was a bit unclear. I then decided to continue when I got back from work, but then I got the correct answer for the example, but the wrong one for the input file (121). Then when I changed that when the boat turned, I did not move in the Y-axis I got the correct answer. The code I have for this one is Go, but it is not at all pretty to look at. Some part of me want to re-write, but I leave it and continue on tomorrow with the next one.

December 17th

Nice and easy puzzle this day. It was nice to not need to parse any input file this time. Just pure calculations. For this puzzle, I defined functions to calculate triangle number and a check for square number.

func calcTriangleNumber(n int) int {
	return (n * (n + 1)) / 2
}

func isSquareNumber(n float64) bool {
	sr := math.Sqrt(n)
	return ((sr - math.Floor(sr)) == 0)
}

Then I also needed to have a function to rotate a number. My first attempt to rotate a number was pure mathematical and only dealing with int and returning each number. This works as long as I do not use this recursivly with the result of as the input next time. That will be a problem when there is a leading zero. My final left rotate function of an integer takes an integer as an argument and then return back an integer slice of that numbered rotated.

func leftRotate(num int) []int {
	digits := numberOfDigits(num)
	var iSlice []int = make([]int, digits)
	pow10 := int(math.Pow10(digits - 1))
	for i := 0; i < digits; i++ {
		firstDigit := num / pow10
		left := ((num * 10) + firstDigit) - (firstDigit * pow10 * 10)
		num = left
		iSlice[i] = left
	}
	return iSlice
}

December 18th

This puzzle was to convert names (first- and lastname) into a Star Wars name. There are a set of rules that should be followed to pick a Star Wars name and those where easy enough to follow. Again, I used Go to solve this task.

The code for this day is super ugly, but I was in no mood after work to do anything else than get the right answer.

Execution time: 21ms

December 19th

Another day without any need to parse input files. Just pure calculations of numbers. Solution this day is written in Go before I went to work. The code is not fast and probably a lot of things I could do to make it faster. The correct answer is calculated so for now, it should be good.

December 23th

Todays puzzle was an easy one where I just needed to loop through a set of numbers and do some math on each number. I did solve the puzzle using Go today, the solution is not optimized, but it works.

December 24th

Nice little task where I could check out some plotting package in Go. First time I also used go get <xxx> to get a package that was not included into the Go language as standard. I saved each trip as it's own image and went through the images in the end. Then I recognized the letters and that they spelled out God jul.

Running the solution using go test -bench . -count 100 I can performance benchmark the solution by running the main function 100 times. Go testing package has some easy to use testing and benchmarking capabilities.

goos: darwin
goarch: amd64
BenchmarkSkitur-4   	       2	 805454191 ns/op
BenchmarkSkitur-4   	       2	 808459700 ns/op
...
...
BenchmarkSkitur-4   	       2	 755276290 ns/op
PASS
ok  	_/Users/teis/Projects/KnowItJulekalender2019/Day24	237.687s

Running the solution benchmark using go test -bench . -benchtime 100x it is running enough iterations to take time t.

Run enough iterations of each benchmark to take t, specified Β  Β  as a time.Duration (for example, -benchtime 1h30s). Β  Β  The default is 1 second (1s). Β  Β  The special syntax Nx means to run the benchmark N times Β  Β  (for example, -benchtime 100x). Testing flags
goos: darwin
goarch: amd64
BenchmarkSkitur-4   	     100	 831860792 ns/op
PASS
ok  	_/Users/teis/Projects/KnowItJulekalender2019/Day24	83.971s

What did I learn?

One essential thing that I learned several times though these puzzles, it helps a lot to read the description and make sure to understand it. A little bit related is also to think "easy". There is no need to "overthink" any of the tasks. Even that I have the execution time written down, getting the most performant code has not been a priority for me during these exercises. I have been focusing on getting to know the basics and getting started to use Go programming language.

Summary

Looking back at this month, it has been fun to have a programming exercise to solve every day. 2019 was a special year with a lot to do at work when Christmas got closer and closer. I had to work long days and over-time a lot and the energy to solve the puzzles some of the days in the end was really hard to find. I am looking forward to the Christmas calendar in December 2020, then I am not working at the place I am working in 2019 and hopefully December will be more joyful and the energy level will be back to get solve the puzzles every day.

Hopefully, there will be another chance next year. Then I will do the task every day.

Teis Lindemark

Read more posts by this author.