DEV Community

Cover image for Advent of Code 2020 Solution Megathread - Day 3: Toboggan Trajectory
Ryan Palo
Ryan Palo

Posted on • Edited on

Advent of Code 2020 Solution Megathread - Day 3: Toboggan Trajectory

We're on to Day 3 now! I'm seeing a bunch of cool languages getting submitted, which is really fun. Let's get to the puzzle!

The Puzzle

In today’s puzzle, we're trying to toboggan down a foresty slope to get to the coast for our trip. But trees aren't conducive to safe sledding, so we're checking our options carefully before starting. 2-D grids are a staple of Advent of Codeses past, and we've seen things dealing with rational slopes too. But it'll be interesting seeing how you decide to sled down these particular rational slopes. 😉

The Leaderboards

As always, this is the spot where I’ll plug any leaderboard codes shared from the community.

Ryan's Leaderboard: 224198-25048a19
Enter fullscreen mode Exit fullscreen mode

If you want to generate your own leaderboard and signal boost it a little bit, send it to me either in a DEV message or in a comment on one of these posts and I'll add it to the list above.

Yesterday’s Languages

Updated 03:06PM 12/12/2020 PST.

Language Count
Python 5
Ruby 3
Rust 3
JavaScript 2
Raku 1
COBOL 1
PHP 1
Elixir 1
C 1

Merry Coding!

Latest comments (39)

Collapse
 
justinh11235 profile image
Justin Hinckley

Here's my beginner's Haskell solution:

ans1 :: [String] -> Int -> Int -> Int
ans1 inp right down = length $ filter (\(ind, row) -> ind `mod` down == 0 && row !! (ind `div` down * right `mod` length row) == '#') $ zip [0..] inp

ans2 :: [String] -> [(Int, Int)] -> Int
ans2 inp = product . map (uncurry (ans1 inp))

main :: IO ()
main = do
    contents <- readFile "adventofcode3.input"

    print $ ans1 (lines contents) 3 1
    print $ ans2 (lines contents) [(1, 1), (3, 1), (5, 1), (7, 1), (1, 2)]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
adamss profile image
Adam

Vanilla JS solution in 12 lines of code:

//create arr with input as template literal

let newArr= arr.split(/\r?\n/);//split by endlines
let xCord = 0;//X coordinates
let counter = 0;//trees hit counter
for(let i = 0 ; i < newArr.length ; i++){
let temp = newArr[i].split('');//split into array of characters
if(temp[xCord]=='#'){//checking for trees
counter++;//if there is a tree increment the counter
}
xCord = xCord +3;//add +3 to the right
if(xCord>=31){//check if there is more coords than elements in array
xCord = xCord-31;//reset coords
}}
console.log(counter);//result

Collapse
 
clothierdroid profile image
David Clothier • Edited

SQL works for me ;)

0 lines of code. No loops, no boolean flag, only SQL.

Get this table naming 'day3':

table

3.1 Solution

SELECT COUNT(*) 
FROM   (SELECT id, 
               REPEAT(line, 32) toboggan 
        FROM   day3 
        HAVING MID(toboggan, ( ( id * 3 ) - 2 ), 1) = '#') found_trees
Enter fullscreen mode Exit fullscreen mode

3.2 Solution

SELECT ROUND(EXP(SUM(LN(x.a))),0) solution
FROM
(
SELECT COUNT(*) as a
FROM   (SELECT id, 
               REPEAT(line, 32) toboggan 
        FROM   day3 
        HAVING MID(toboggan, id, 1) = '#') found_trees 

UNION ALL
SELECT COUNT(*)
FROM   (SELECT id, 
               REPEAT(line, 32) toboggan 
        FROM   day3 
        HAVING MID(toboggan, ( ( id * 3 ) - 2 ), 1) = '#') found_trees
UNION ALL
SELECT COUNT(*)
FROM   (SELECT id, 
               REPEAT(line, 55) toboggan 
        FROM   day3 
        HAVING MID(toboggan, ( ( id * 5 ) - 4 ), 1) = '#') found_trees
UNION ALL
SELECT COUNT(*)
FROM   (SELECT id, 
               REPEAT(line, 75) toboggan 
        FROM   day3 
        HAVING MID(toboggan, ( ( id * 7 ) - 6), 1) = '#') found_trees
UNION ALL
SELECT COUNT(*)
FROM   (SELECT id, 
               REPEAT(line, 16) toboggan 
        FROM   day3 WHERE (id % 2) = 1
        HAVING MID(toboggan, ((id + 1) / 2), 1) = '#') found_trees
) AS x
Enter fullscreen mode Exit fullscreen mode
Collapse
 
erikbackman profile image
Erik Bäckman

Haskell

import Control.Lens ((^?))
import Control.Lens.At (Ixed(ix))
import Data.Maybe (catMaybes, isJust)

matrixLookup :: [[a]] -> (Int, Int) -> Maybe a
matrixLookup m (x, y) = m ^? ix (abs y) . ix (abs x)

slope :: (Int, Int) -> [(Int, Int)]
slope (run, rise) = zip [0,run..] [0,rise..]

charsAlongSlope :: [String] -> (Int, Int) -> [Char]
charsAlongSlope grid (run, rise) = catMaybes
                                 $ takeWhile isJust
                                 $ matrixLookup grid <$> slope (run, rise)

parseInput :: String -> [String]
parseInput = fmap cycle . lines

solveForSlope :: String -> (Int, Int) -> Int
solveForSlope i s = length . filter (== '#') $ charsAlongSlope (parseInput i) s

solveP1 :: String -> Int
solveP1 inp = solveForSlope inp (3, -1)

solveP2 :: String -> Int
solveP2 inp = product (solveForSlope inp <$> slopes)
  where
    slopes = [ (1, -1)
             , (3, -1)
             , (5, -1)
             , (7, -1)
             , (1, -2)
             ]
Enter fullscreen mode Exit fullscreen mode
Collapse
 
thibpat profile image
Thibaut Patel

My JavaScript walkthrough:

Collapse
 
harrygibson profile image
Harry Gibson

Python implementation using numpy.

Spent ages trying to get this figured using array striding but eventually my head exploded and I resorted to using indices.

import numpy as np
from io import StringIO
import math

def count_trees(arr, steps_right, steps_down):
    n_rows, n_cols = arr.shape    
    n_down_steps = math.floor(n_rows / steps_down)
    across_positions = [(steps_right * n)  % n_cols 
                        for n in range(n_down_steps)]
    flat_array_positions = [
        (rownum * n_cols * steps_down) + across_positions[rownum] 
        for rownum in range(n_down_steps)]
    total_trees = arr.ravel()[flat_array_positions].sum()
    return total_trees

with open('input.txt', 'r') as f:
   data = f.read().replace('.', '0').replace('#', '1')
# using converters parameter to change #/. to 1/0 is a pain if 
# we don't know the number of columns. So string-replace them all first
trees_map = np.genfromtxt(StringIO(data), delimiter=1, dtype='uint8')

part_2_slopes = ((1,1), (3,1), (5,1), (7,1), (1,2))
part_1_slope = part_2_slopes[1]

print (f"Part 1: {count_trees(trees_map, *part_1_slope)} trees hit")

part_2_trees = [count_trees(trees_map, *s) for s in part_2_slopes]
part_2_res = np.prod(part_2_trees)
print (f"Part 2: Product of total trees hit is {part_2_res}")
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dirkfraanje profile image
Dirk Fraanje (the Netherlands) • Edited

My solution in C#:

public static class Day3
{
    static int r;
    static int d;
    static string[] input = new string[] { //input };
    static Stopwatch timer = new Stopwatch();

    static int trees = 0;

    public static void Execute()
    {
        timer.Start();
        findNextLocationAndUpdateTreeCount();
    }

    private static void findNextLocationAndUpdateTreeCount()
    {
        var line = input[d];
        var positionOnLine = line.ToCharArray()[r];
        if (positionOnLine == '#')
            trees++;
        //For part 2 change r and d to the values you have to check
        r += 3;
        d += 1;
        if (input.Length <= d) {
            timer.Stop();
            Console.WriteLine($"Answer: {trees} trees");
            Console.WriteLine($"Executed in: {timer.ElapsedMilliseconds} milliseconds ({timer.ElapsedTicks}  Ticks)");
            Console.ReadLine();
        }
        if(line.Length <= r)
        {
            //By using r - line.Length you don't have to copy the whole input when you reached the end on the right (also possible to use Mod)
            r = r - line.Length;
        }
        findNextLocationAndUpdateTreeCount();
    }


}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
artyomovs profile image
artyomovs • Edited

My ugly python version

from pprint import pprint

lines = list()

with open("input.txt", "r") as f:
    lines = [line.strip() for line in f.readlines()]

pprint(lines)

width = len(lines[0])
height = len(lines)

routes = [(1,1), (3,1), (5,1), (7,1), (1,2)]

def get_next_spot(x, y, route):
    y = y + route[1]
    x = x + route[0]
    if x > width:
        x = x % width
    return x, y

x = 1 
y = 1

all_trees = 1


# print(lines[0])
for route in routes:
    trees = 0
    print('-------------')
    print(f"dx = {route[0]} dy = {route[1]}\n")
    y = route[1]
    x = 1
    j = 0
    lines_new = list(lines)
    for line in lines[1:]:
        s = line        
        if (j + 1) % (route[1]) == 0:    
            x, y = get_next_spot(x, y, route)
            s = line[:x - 1] + "O" + line[x:] 
            if line[x - 1] == "#":
                trees += 1
                s = line[:x - 1] + "X" + line[x:]                
        lines_new[j + 1] = s
        j = j + 1
    all_trees = all_trees * trees
    pprint(lines_new)    
    print(f"dx = {route[0]} dy = {route[1]} trees = {trees} all_trees = {all_trees}")


print(all_trees)

Enter fullscreen mode Exit fullscreen mode
Collapse
 
rpalo profile image
Ryan Palo • Edited

Hi! Thanks for sharing!

FYI, if you want to get some syntax highlighting in your comments, you can use three backtics followed by the language name to open and three backticks by themselves to close. Here's the Markdown cheat sheet for reference.

Collapse
 
anvsh profile image
Anvesh Saxena

Completed my day 3 with a little help from comments here

import sys

def slope_calc(data, right, down):
    tree = 0
    open_space = 0
    first = True
    position = right
    line_count = 0

    for line in data:
        if line != "":
            line_read = line.rstrip("\n")
            if first:
                first = False
            else:
                if line_count % down == 0:
                    if line_read[position] == ".":
                        open_space += 1
                    else:
                        if line_read[position] == "#":
                            tree += 1
                    position += right
                    if position > len(line_read)-1:
                        position = position-len(line_read)
        line_count += 1

    print("Tree = {}, Open = {}".format(tree, open_space))
    return tree

data = []
for line in sys.stdin:
    data.append(line.rstrip("\n"))

print(slope_calc(data,1,1) * slope_calc(data,3,1) * slope_calc(data,5,1) * slope_calc(data,7,1) * slope_calc(data,1,2))
Enter fullscreen mode Exit fullscreen mode
Collapse
 
maartenbetman profile image
Maarten Betman

Short Python solution

import math

with open("./data/Map.txt", 'r') as f:
    lines = f.read().splitlines() 

routes = ((1, 1), (3, 1), (5, 1), (7, 1), (1, 2))

total = []
for route in routes:
    c = 0
    for v in range(0, len(lines), route[1]):
        h = int(divmod((v / route[1]) * route[0], len(lines[v]))[1])
        value = lines[v][h]
        if value == "#":
            c += 1
    total.append(c)

print("Solution 1 =>", total[1])
print("Solution 2 =>", math.prod(total))
Enter fullscreen mode Exit fullscreen mode