Advent of Code 2017 in #rstats: Day 11

Once I realized that moves on a hex grid map nicely to a standard rectangular grid, this was easy.   Despite playing hours of Settlers of Catan, I’d never realized this relationship.  Maybe because nothing traverses that hex grid?

North and South move one step up or down.  The four diagonal directions move a half-step up or down and a full column laterally.  The shortest solution path will be diagonal moves to reach the desired column, then vertical moves to the right row.

It took only a minute or two to modify my part 1 function for part 2, so I present both together.

Parts 1 & 2

 

Advent of Code 2017 in #rstats: Day 9

I write less deeply-nested code now that I program in R.  When writing code poorly  in other programs, I’d often use nested IF statements (hi, Excel!).  Debugging that often looked like counting my nesting depth out loud: add one for every (,  subtract one for every ).

That strategy formed the basis for my solution today.   And I was excited to do Part 2 with arithmetic, not writing new code.

Part 1

Strategy: maintain counter of open braces, decreasing for closed braces.

So {{<a>},{<a>},{<a>},{<a>}} = 1 2 2 2 2. Sum = 9.

Part 2

The answer is the total number of characters, minus:

  • The characters canceled by !
  • The bracketing <> for each garbage string
  • The valid characters remaining after removing the garbage in Part 1

To wit:

 

Advent of Code 2017 in #rstats: Day 7

Today was hard, but a good challenge.  I haven’t written a recursive function since college CS – is that typical for data science / analysis work?  I don’t see much about recursion on #rstats Twitter.  Recursion feels like a separate way of thinking, and I had forgotten how.

Part 1

The first part was satisfying.  Data cleaning was quick and I thought joining the data.frame to itself to populate parent and child for each entry was a nice touch.

My tidy data looked like:

Then the solution was easy:

 

Part 2

This was tough going.  I got a function working for the sample input, which worked by stepping backward from the most distant children and adding their weight to their parents, then removing them and calling itself again.

But while the sample input had symmetric towers, in the bigger test data a tower could be balanced if it had two children of 4 and 2 -> 1 + 1.  In that scenario, you can’t peel off the 4 in the same step that you peel off the 1s.  (For my own satisfaction, that false solution appears far below).

I’m proud of my eventual solution.  And besides finally getting my brain to think recursively, I learned a few things: creating a named vector with structure() and using purrr::map_dbl.

I finished with a cop-out: once my function returned the weights of the subtower nodes where the problem existed and their names, it wasn’t worth programming the last bit to do the weight subtraction and get my answer.  With this output:

I just calculated it myself using get_node_weight("ycbqx") and subtracting 5.

Appendix: Solution with Symmetric Towers

Because I feel salty that this didn’t work.

 

Advent of Code 2017 in #rstats: Day 6

The solution was a simple concept but harder than I thought to implement.  I learned some lessons today:

  • Read the instructions carefully.  I built the balancing function balance_banks to return how many distributions it made of the maximum value, but removed this code when I realized that the full distribution of a bank of N values counted as one iteration, not N iterations.
  • Related: Up until now, Git has felt unnecessary for these challenges, and I’ve been too lazy to use it (though it would do wonders for my GitHub contribution graph).  Today I got nervous deleting and reworking code for Part 1 … would I need it for the not-yet-known Part 2?
  • I built my balance_banks function inside of my go_til_repeat function (because I thought it would help with passing counter information, which I did not need).  When editing and then testing, I kept failing to load both into memory; I’d update one, load the other into memory, and call the first one.  I don’t work much with nested functions of any complexity; TIL to avoid loading just one while programming and testing them.

Today was the first day that run-time performance became an issue.  My outputs for each part were correct on the first try, but each took a while (3 minutes?) to run.  Suspicious that my code was stuck in a loop, I never let it finish the first call I made: I broke the loop manually and added the counter to print every 1,000 rebalances.

But it was just slow.  To check completion, I switched to the use of double duplicated you see below instead of janitor::get_dupes(), which is easier on the brain but a relatively expensive operation.

Lastly, when I thought I had to return multiple outputs from balance_banks I had it return them in a list.  I removed the increment counter code and the function now returns just one result (the balanced vector), but I left the list syntax in place since it worked.  That’s why you see the $output in last_result <- balance_banks(last_result$output)for instance.

Adding Part 2 onto Part 1 was trivial.  I’d seen Jenny Bryan use diff(range()) in a solution to an earlier puzzle and was stoked to reuse it in Part 2.

I resorted to rbind instead of my beloved dplyr::bind_rows because my data.frames don’t have column names.  I remain tickled by situations where I work with data.frames in R while ignoring column names.

Parts 1 and 2

 

Advent of Code 2017 in #rstats: Day 5

This went smoothly.  No tricky parts and Part 2 was a simple extension of Part 1.  No packages needed to solve (I used testthat for checking against the test input).

This was a textbook case for a while loop, which I rarely use in R.

 

Advent of Code 2017 in #rstats: Day 4

Whew, much easier than day 3.  Probably would be a great day to learn the tidytext package but I’ll be faster just muddling through with my current tools.

I wrote a function that compares the length of the input tokens to length of unique() input tokens.  Then for part 2, I added an argument as to whether anagrams should be permitted; if not, the tokens are each sorted alphabetically first into alphagrams (a word borrowed from competitive Scrabble).

Parts 1 & 2 together

I borrowed the string sorting algorithm from StackOverflow – I cited it below and upvoted as well 🙂

Now it’s just a matter of testing & running:

Advent of Code 2017 in #rstats: Day 2

After reading this puzzle, I was excited and concerned to see “spreadsheet”:

  1. Excited: R is good at rectangular numeric data, and I work with a lot of spreadsheets
  2. Concerned: Will I be starting with a single long string?  How do I get that into rectangular form?

After considering trying to chop up one long string, I copy-pasted my input to a text file and read.delim() worked on the first try.

Part 1 was simple, Part 2 was tricky.  The idea of row-wise iteration got in my head and I spent a lot of time with apply and lapply… I still hold a grudge against them from battles as a new R user, but having (mostly) bent them to my will I went to them here.  Maybe I should spend more time with the purrr package.

Part 1

Straightforward:

Part 2

Got the idea quickly, but executing took a bit longer.  I decided right away that I would:

  1. Work on a function that tackled a row (as a vector) at a time
  2. The function would divide every value in the vector by every other value in the vector
  3. It would then pick out the integer that wasn’t 1, as well as its reciprocal, and divide them
  4. Then run this function row-wise on the input

Actually building up that function took ~20 minutes.  I felt confident the approach would work so didn’t stop to consider anything else… I’m excited to see other solutions after I publish this, as maybe there’s something elegant I missed.

Along the way I learned: is.integer doesn’t do what I thought it would.  Come on, base R.

I don’t work with matrices much (not a mathematician, don’t have big data) and was a somewhat vexed by the matrix -> data.frame conversion.

 

Advent of Code 2017 in #rstats: Day 1

Today’s puzzle is a good example of thinking “R-ishly”.   In R, I find it easier to compare vectors than to compare strings, and easier to work with vectorized functions than to loop.  So my code starts by splitting the input into a vector.

Part 1:

Split the input into a vector, add the first value to the end, and use dplyr’s lead() function to compare each value to the following value:

Part 2:

This challenge is simpler, in my opinion.  You can halve the vector, then compare the first half to the second half, summing the matches (don’t forget to double that result).

AABG Knob Creek Barrel Project

In December 2014, a friend of a friend acquired a 53 gallon barrel that had previously held Knob Creek Single Barrel Reserve whiskey for 9 years.  It made its way from Kentucky to Ann Arbor and now lives in my basement.

Staining = spilled beer

Since then we’ve rotated beers through it.  In order of fill date:

  • Imperial Stout
  • Scotch Ale (this beer naturally soured)
  • Oud Bruin (we pitched 8 packs of Blackman Flemish Sour Mix)
  • Tart of Darkness Stout (here we introduced Brett C., actually a strain of Brett Anomalous)
  • Dark Saison
  • Belgian Golden Strong
  • IPA

We empty + refill every six months or so.  The brewers are a rotating cast, with people dropping in and out.  We typically aim to bring 11 shares of 5 gallons each, filling the barrel to the top and leaving some extra to top up the angel’s share.

This barrel has produced consistently good beers, and at this point the sour character is locked in.  Beers from this barrel have won silver and gold medals in the American Wild Ale category at the 2016 & 2017 Michigan Beer Cup.  Brewers often perform a tertiary fermentation on fruit – tart cherries are a favorite, this being Michigan – and sometimes blend with young or clean beers to cut sourness to taste.

How much sorbate and sulfite should you add to stabilize cider?

I re-researched this every fall when it came time to backsweeten the previous year’s cider, so I wrote this guide to future me.

If you add sugar to hard cider and don’t want that addition to restart fermentation (which would increase alcohol and leave the cider even drier), you’ll need to stabilize it.  The most common method in home cidermaking is to add both potassium sorbate and potassium sulfite.  This guidance from BYO magazine on backsweetening provides background on the approach.

Tl;dr

For each gallon of cider, use 1/2 tsp of potassium sorbate and 1/2 tsp of 10% sulfite solution (an extra step, but worth making – the solution is easier to work with than dry potassium sulfite).

Longer version

How much potassium sorbate?

Winemakers say…

Winemakers talk more about sorbic acid, the relevant chemical; potassium sorbate is 74% sorbic acid.  There are legal limits of 0.2 g/L (Europe) and 0.3 g/L (America); the sensory level for perceiving this chemical’s flavor is reported at 0.135 g/L.

Adjusting these from sorbic acid -> potassium sorbate (what a homebrewer weighs) gives legal limits of 0.26 g/L and 0.4 g/L, respectively, with a taste threshold of 0.18 g/L.

Continue reading How much sorbate and sulfite should you add to stabilize cider?