rdmueller

1856308?v=4

rdmueller
Ralf D. Müller
Github: rdmueller, Twitter: RalfDMueller
Blog : https://docs-as-co.de

About me

More will follow shortly.

Day 00: ruby

This year, I wil luse Advent of Code to learn ruby. The reason is simple. I don’t know ruby yet and Asciidoctor, the converter for AsciiDoc, is written in ruby. So this knowledge should help me to make better use of Asciidoctor.

What I’ve found to get started with ruby are the ruby koans. Programming koans are simply put just a bunch of unit tests which you have to fix to get them to pass. This way, you also learn the language they are written in. You also learn how to write tests in the given language which gives you a good start because you can do test driven development right from the start!

A second goal for this year it to use documentation driven development/design (DDD/D) 🤣. With this approach, I first write my thoughts down as documentation and then start to code.

While I write the docs, I will already decided about classes and methods I need. I will already add them to the docs as references to the code.

Next I will turn my thoughts into test driven development (TDD). As soon as I know what I want to code, I can already write some acceptance criteria and then some tests. These tests can also be referenced from the docs.

Now let’s start with the simple hello world:

solution.rb
#!/usr/bin/env ruby
puts "Hello World"

Acceptance criteria is that the code returns the text "Hello World" when executed. Let’s write the tests as bash script :-)

tests.bash
#!/bin/bash
# run tests with
# ./test.bash &> results.txt

OUTPUT=$(./solution.rb)
if [ "$OUTPUT" == "Hello World" ]
then
   echo "success"
else
   echo "fail"
   echo "$OUTPUT"
fi
results.txt
success

Day 00: python

This solution is written in Python. It is the soölution for Day1 taken from AoC-2018-

First Star

So, the solution seems to be simple - one line of code in Groovy :-)

Read a file line by line and add all numbers. It seems that Python comes with great file ahndling methods, but I have to tell python in wich mode I open the file - read, write etc. I also have to cast the line read to in via int(). Here is my solution for the first star:

startFrequency = 0
currentFrequency = startFrequency
file = open("input.txt", "r")
for line in file:
    frequencyChange = int(line)
    currentFrequency += frequencyChange
print("Solution Star One: ", currentFrequency)
file.close()

Second Star

Now for the second part.

I started the live stream ión twitch by lizthegry. She just finished the stream - it seems I am slow :-) - 15 minutes into advent of code…​

Now, for the second part I need a list to collect all the frequencies I’ve already encountered.

It already starts: I had to re-read the specs twice. The second time I also had to go carefully through one of the example to understand that you might have to read the input several times. That reminds me of last year where I’ve coded the solution as function and did test driven development. Maybe I start this tomorrow…​.

However, I collected my second star and already learned a lot about Python!

startFrequency = 0
currentFrequency = startFrequency
alreadySeen = [currentFrequency]
found = False
for times in range(1000):
    file = open("input.txt", "r")
    for line in file:
        frequencyChange = int(line)
        currentFrequency += frequencyChange
        if currentFrequency in alreadySeen:
            print("Solution Star two: ", currentFrequency)
            found = True
            break
        alreadySeen.append(currentFrequency)
    if found:
        break
file.close()

Day 01: ruby

The Tyranny of the Rocket Equation

Since I still don’t know too much about Ruby, I will have to google most of the solution.

The original puzzle can be found at https://adventofcode.com/2019/day/1 .

Star 1

First, let’s set up the right equation as method. It seems that my knowledge about Groovy helps me a lot with Ruby. A method will return the result of the last statement, so no explicit return necessary. And the Integer DIV also rounds by default.

rocket equation
# calculate the fuel needs for one module
def rocket_equation(mass)
  mass/3-2
end

Let’s write some tests for this. I found a good introductionn to unit test with ruby. That helped to get the tests qickly up and running.

rocket equations tests
    def test_acceptance_criteria1
        assert rocket_equation(    12) ==     2
        assert rocket_equation(    14) ==     2
        assert rocket_equation(  1969) ==   654
        assert rocket_equation(100756) == 33583
    end

Now I need a method which adds the required fuel up. I already now about ruby arrays from the koans (day00), but I still have to figure out how to iterate over an array.

sum up the fuel
# sum up the requirements for a list of modules
def sum_up(mass_list)
    sum = 0
    mass_list.each { |mass|
        sum += rocket_equation(mass)
    }
    return sum
end

And again some tests

sum up tests
    def test_acceptance_criteria2
        assert sum_up( [12, 14, 1969, 100756] ) == 2 + 2 + 654 + 33583
    end

Now I need to read in the file with my input for day01.

read file
def read_input()
    input = File.open('input.txt').read
    masses = []
    input.each_line { |line|
        masses << Integer(line)
    }
    return masses
end

This gives me the solution

3226488

Here is also the result of the tests:

3226488
Loaded suite ./tests/day1s1
Started
..
Finished in 0.000542482 seconds.
-------------------------------------------------------------------------------
2 tests, 5 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-------------------------------------------------------------------------------
3686.76 tests/s, 9216.90 assertions/s
Star 2

For star 2, we have to change the rocket equation:

new rocket equation
# calculate the fuel needs for one module
def rocket_equation(mass)
    neededFuel = 0
    additionalFuel = mass/3-2
    while (additionalFuel>=0) do
        neededFuel += additionalFuel
        additionalFuel = additionalFuel/3-2
    end
    return neededFuel
end

As you can see, I decided to use an iterative and not a recursive approach.

additional tests
    def test_acceptance_criteria1
        assert_equal     2, rocket_equation(    14)
        assert_equal   966, rocket_equation(  1969)
        assert_equal 50346, rocket_equation(100756)
    end

Works quite nice. The result is

4836845

and the result of the additional tests is:

4836845
Loaded suite ./tests/day1s2
Started
.
Finished in 0.000514866 seconds.
-------------------------------------------------------------------------------
1 tests, 3 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
-------------------------------------------------------------------------------
1942.25 tests/s, 5826.76 assertions/s

Day 02: ruby

1202 Program Alarm

The original puzzle can be found at https://adventofcode.com/2019/day/2

Today, my quality criteria is just to do it quick. I only have time to spend while I commute, so apox 15 minutes in each direction. Let’s go!

First Star

I need a little interpreter for the code isntructions. First time, I didn’t get the reference right, but then I noticed that I had to use the instruction of the instruction (ìnt[int[x]]) and everything worked out.

First Star
def run(noun, verb)
    inst = File.read("./input.txt").split(",").map(&:to_i)
    inst[1]=noun
    inst[2]=verb
    pointer = 0
    while inst[pointer]!=99 do
        if inst[pointer]==1 then
            inst[inst[pointer+3]]=inst[inst[pointer+1]]+inst[inst[pointer+2]]
        end
        if inst[pointer]==2 then
            inst[inst[pointer+3]]=inst[inst[pointer+1]]*inst[inst[pointer+2]]
        end
        if inst[pointer]!=1 && inst[pointer]!=2 then
            puts "error"
            puts pointer
            puts isntructions
            puts "/error"
        end
        pointer += 4
    end
    return inst[0]
end
puts "star 1:"
puts run(12,2)
Second Star

The code for the first star started out as a simple script. For the second star, I wrapped it as method and iterated brute force over it until it matched the result.

Second Star
puts "star 2:"
(0..99).each { |noun|
    (0..99).each { |verb|
        if (run(noun,verb)==19690720) then
            puts 100*noun+verb
        end
   }
}

Day 03: ruby

I start to recognize that I use a special coding style when I code in a new language. Let’s call it the smallest common subset style (scs-style). I hate to look up language features because it is time consuming. So instead to use the best language constructs, I use the features which are most likely to work, because they are common to all languages.

As an example, I started to use if-then-else instead of a switch statement. That is horrible and doesn’t teach me anything. So I wonder if it makes sense to first code in a language which I know well (Groovy) and then translate it to Ruby?

Day 4: Secure Container

This sounds like an easy puzzle today. Let’s take the brute force approach for star one.

Star One

Let’s iterate over all possible passwords and count how many of them are valid. The processing power should be enough.

#!/usr/bin/env ruby

def checkPass(pass)
    valid = false
    # It is a six-digit number
    # => yes, all are six-digit

    # The value is within the range given in your puzzle input.
    # yes, I iterate over the range

    # Two adjacent digits are the same (like 22 in 122345).
    pass_s = pass.to_s
    if (  (pass_s[0]==pass_s[1]) ||
          (pass_s[1]==pass_s[2]) ||
          (pass_s[2]==pass_s[3]) ||
          (pass_s[3]==pass_s[4]) ||
          (pass_s[4]==pass_s[5])) then

        # Going from left to right, the digits never decrease
        if (  (pass_s[0]<=pass_s[1]) &&
              (pass_s[1]<=pass_s[2]) &&
              (pass_s[2]<=pass_s[3]) &&
              (pass_s[3]<=pass_s[4]) &&
              (pass_s[4]<=pass_s[5])) then

            valid = true
        end
    end
    return valid
end

validPass = 0
(130254..678275).each { |pass|
    if (checkPass(pass)==true) then
        validPass += 1
    end
}

puts checkPass(111111)==true
puts checkPass(223450)==false
puts checkPass(123789)==false


puts validPass

Star Two

I think I can stay with the brute force approach, but I have to update my checks.

#!/usr/bin/env ruby

def checkPass(pass)
    valid = false
    # It is a six-digit number
    # => yes, all are six-digit

    # The value is within the range given in your puzzle input.
    # yes, I iterate over the range

    # Two adjacent digits are the same (like 22 in 122345).
    pass_s = pass.to_s
    if (  (pass_s[0]==pass_s[1]) ||
          (pass_s[1]==pass_s[2]) ||
          (pass_s[2]==pass_s[3]) ||
          (pass_s[3]==pass_s[4]) ||
          (pass_s[4]==pass_s[5])) then

        # Going from left to right, the digits never decrease
        if (  (pass_s[0]<=pass_s[1]) &&
              (pass_s[1]<=pass_s[2]) &&
              (pass_s[2]<=pass_s[3]) &&
              (pass_s[3]<=pass_s[4]) &&
              (pass_s[4]<=pass_s[5])) then

            # the two adjacent matching digits are not part of a larger group of matching digits
            # let's count how many times each digit is in the pass
            # and it is valid if one digit is two times in the pass
            count = [0,0,0,0,0,0,0,0,0,0]
            (0..9).each { |digit|
                (0..5).each { |pos|
                    if (pass_s[pos]==digit.to_s) then
                        count[digit] += 1
                    end
                }
            }
            # now check that one count is equal to 2
            (0..9).each { |digit|
                if (count[digit]==2) then
                    valid = true
                end
            }
        end
    end
    return valid
end

validPass = 0
(130254..678275).each { |pass|
    if (checkPass(pass)==true) then
        validPass += 1
    end
}

puts checkPass(111111)==true
puts checkPass(223450)==false
puts checkPass(123789)==false


puts validPass