lainchan / λ
Advent of Code thread
Terzibashjian · 2021-12-01 14:59
Anyone doing AoC this year? I'll be using Guile Scheme this year, what about you guys?
https://adventofcode.com/
Naomi Nascimento · 2021-12-01 15:00
And the solution for today
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1))
(define (get-lines port trans)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))
(define (count-increases nums)
(let [(diffs (map - nums (cdr nums)))]
(fold (lambda (el acc) (if (negative? el) (1+ acc) acc))
0
diffs)))
(define (count-increases-windowed nums)
(let* [(windowed (map + nums (cdr nums) (cddr nums)))
(diffs (map - windowed (cdr windowed)))]
(fold (lambda (el acc) (if (negative? el) (1+ acc) acc))
0
diffs)))
(count-increases (call-with-input-file "input" (lambda (port) (get-lines port string->number))))
(count-increases-windowed (call-with-input-file "input" (lambda (port) (get-lines port string->number))))
Liet Kynes · 2021-12-01 16:11
This is my first time participating, used C++ for today. Thinking to try redoing today's code in C and keep using C for every day from now on.
Ender · 2021-12-01 17:37
Did today's first part in pure sh, then had to focus on other stuff. Not really interested in competing, so maybe I could try to catch up when I have more time.
Stallman · 2021-12-01 19:38
I'll try to participate, hopefully it works without logging in using one of those cancer services. Probably not going to stick around to the end, but it would be fun. Solved the first one in Perl and Python, but I'm planning to do Lua too to get a good feeling for their differences.
Trinity · 2021-12-01 20:16
>>28722
>>28724
>>28729
post your solutions, it's always interesting to see how other people do it
if · 2021-12-01 20:27
was there not an alt website that offered data and solution without the need to login?
Yamaoto Toshi · 2021-12-01 20:34
my solution in haskell
incr :: [Int] -> Int
incr xs = length . filter (< 0) $ zipWith (-) xs (tail xs)
threeWindow :: [Int] -> [Int]
threeWindow xs = zipWith3 (\x y z -> x + y + z) xs (tail xs) (drop 2 xs)
main :: IO()
main = do
filecontent <- readFile "input"
let m = map (\x -> read x :: Int) (lines filecontent)
print $ incr m
print . incr . threeWindow $ m
Dr. X · 2021-12-01 22:56
I made a leaderboard, for anyone interested: 388839-baef610d
Day 1 in Common Lisp:
;; Omitting parse-lines from my utils file from last year. It does what you probably think it does
(defparameter +input+ (parse-lines #'parse-integer))
(defun solve-part-1 ()
;; CL loop's `:for ... :on` is a perfect contender for this problem!
(loop :for (a b) :on +input+
:while b
:count (> b a)))
;; I wanted to iterate over 3/4 items at a time as a "better" solution, but I've found making a throwaway list to be plainly easier for myself.
(defun sliding-sums ()
(loop :for (a b c) :on +input+
:while c
:collect (+ a b c)))
(defun solve-part-2 ()
(loop :for (a b) :on (sliding-sums)
:while b
:count (> b a)))
SHODAN · 2021-12-01 23:47
Finished day 1
https://github.com/williamblair/adventofcode2021
Last year I did up to day 18, we'll see how far I do this year.
>>28743
Joined!
hash table · 2021-12-02 01:36
I'm taking this as an opportunity to learn APL; here's my solution for today:
⍝ our dataset
ex←⍎⎕FIO[49]'input-1.txt'
⍝ first problem
sl←{+/∊</¯1 1↓¨2/⊂⍵}
an←sl ex
⍝ second problem
e∆←{∊+/(-⍺-1)↓¨(1-⍨⍳⍺)⌽¨⍺/⊂⍵}
a∆←sl 3e∆ ex Would appreciate comments, it's my first time writing APL outside textbook exercises; it is quite fun.
McCarthy · 2021-12-02 08:14
>>28729
okay, here they are for day 1:
#!/usr/bin/python3
import sys
increases = 0
old_value = None
for line in sys.stdin:
value = int(line.rstrip('\n'))
if old_value:
increases += 1 if value > old_value else 0
print(value, '({})'.format('increased' if value > old_value else 'decreased' if value < old_value else 'same'))
else:
print(value, '(N/A no previous value)')
old_value = value
print('total of {} increases'.format(increases))
#!/usr/bin/perl
use strict;
use warnings;
my $increased = 0;
my $old = undef;
sub compare {
my $arg = $_[0];
if (!defined $old) {
printf "%d (N/A no previous value)\n", $arg;
return $arg;
}
printf "%d (%s)\n", $arg, $arg > $old ? 'increased' : $arg < $old ? 'decreased' : 'same';
$increased += $arg > $old;
return $arg;
}
foreach (<STDIN>) {
chomp($_);
$old = compare($_ + 0);
}
printf "total of %d increases\n", $increased;
Keep in mind I don't really know perl, I'm trying to learn. Here is day 2:
#!/usr/bin/python3
import sys
class Submarine:
def __init__(self):
self.position = 0
self.depth = 0
self.direction_lookup = {
'forward': (0, 1, 1),
'up': (1, 0, -1),
'down': (1, 0, 1)
}
print('position', self.position, self.depth)
def encode_position(self):
return self.position * self.depth
def move(self, direction, units):
try:
vertical, horizontal, multiplier = self.direction_lookup[direction]
self.position += horizontal * multiplier * units
self.depth += vertical * multiplier * units
print('new position', self.position, self.depth)
except KeyError:
print('invalid command')
sub = Submarine()
for line in sys.stdin:
assert ' ' in line
direction, units = line.rstrip('\n').split()
print(direction, units)
sub.move(direction, int(units))
print('distance {}'.format(sub.encode_position()))
Nameless · 2021-12-02 09:06
here is my day 2 solution:
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1))
(define (get-lines port trans)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))
(define in (call-with-input-file "input"
(lambda (p)
(get-lines p (lambda (l)
(match-let [((cmd val) (string-split l #\space))]
(cons (string->symbol cmd) (string->number val))))))))
(define (problem-1 cmds)
(apply * (fold (lambda (curr-cmd state)
(match-let [((cmd . val) curr-cmd)
((pos depth) state)]
(cond
[(eq? 'forward cmd) `(,(+ pos val) ,depth)]
[(eq? 'down cmd) `(,pos ,(+ depth val))]
[(eq? 'up cmd) `(,pos ,(- depth val))])))
'(0 0)
cmds)))
(define (problem-2 cmds)
(apply * (list-head (fold (lambda (curr-cmd state)
(match-let [((cmd . val) curr-cmd)
((pos depth aim) state)]
(cond
[(eq? 'forward cmd) `(,(+ pos val) ,(+ depth (* aim val)) ,aim)]
[(eq? 'down cmd) `(,pos ,depth ,(+ aim val))]
[(eq? 'up cmd) `(,pos ,depth ,(- aim val))])))
'(0 0 0)
cmds) 2)))
(format #t "~a~%~a~%" (problem-1 in) (problem-2 in))
t. your friendly schemer
setq · 2021-12-02 09:12
>>28750
I've also done lua this time, it's an interesting language
#!/usr/bin/lua
--https://www.lua.org/pil/16.html
Submarine = {
position = 0,
depth = 0,
direction_lookup = {
['forward'] = {0, 1, 1},
['up'] = {1, 0, -1},
['down'] = {1, 0, 1}
}
}
function Submarine:new (o)
o = o or {}
setmetatable(o, self)
self.__index = self
print('position', self.position, self.depth)
return o
end
function Submarine:move (direction, units)
a = self.direction_lookup[direction]
self.position = self.position + a[2] * a[3] * units
self.depth = self.depth + a[1] * a[3] * units
print('new position', self.position, self.depth)
end
function Submarine:encode_position ()
return self.position * self.depth
end
sub = Submarine:new()
while true do
local line = io.read()
if line == nil then break end
_, _, direction, units = string.find(line, "(%a+)%s(%d+)")
print(direction, units)
if not pcall(sub.move, sub, direction, tonumber(units)) then
print('invalid command')
end
end
print('distance', sub:encode_position())
The Phantom Phreak · 2021-12-02 09:40
Interestingly, the format used is compatible with Tcl lists, so no parsing needed.
First problem:
#!/usr/bin/env tclsh
set data [read [open [lindex $argv 0]]]
foreach cur $data next [lrange $data 1 end] {
if {$next > $cur} {
incr count
}
}
puts "Increase count: $count"
Second:
#!/usr/bin/env tclsh
namespace path {::tcl::mathop ::tcl::mathfunc}
foreach {dir dist} [read [open [lindex $argv 0]]] {
switch $dir {
forward {incr hor $dist}
down {incr depth $dist}
up {incr depth -$dist}
}
}
puts "Horizontal position: $hor, depth: $depth, total: [* $hor $depth]"
Armitage · 2021-12-02 09:44
>>28756
did you just repost the same post after deleting it with no changes?
still, your solution is pretty genius and makes want to learn tcl
nil · 2021-12-02 09:47
>>28757
I did, fuarrrked up my post by trying to use the code tag.
J.C. Denton · 2021-12-02 10:09
>>28732
>>28724
View discretion advised
#!/bin/sh
while IFS= read -r num; do
count=$((num > ${prev-num} ? count+1 : count))
prev=$num
done < "input.txt"
echo $count
John Titor · 2021-12-02 10:18
>>28756
I actually failed the first one, here's the fixed version:
#!/usr/bin/env tclsh
set data [read [open [lindex $argv 0]]]
foreach cur [lrange $data 0 end-1] next [lrange $data 1 end] {
if {$next > $cur} {
incr count
}
}
puts "Increase count: $count"
but it's inefficient and ugly, this one is better:
#!/usr/bin/env tclsh
foreach cur [lassign [read [open [lindex $argv 0]]] prev] {
if {$cur > $prev} {
incr count
}
set prev $cur
}
puts "Increase count: $count"
nibble · 2021-12-02 11:37
I'm using this AoC as an excuse to learn Ocaml which I started looking at a few months ago before quickly becoming too busy with school.
I'd appreciate if someone more experienced could point out if these questions could be better written.
(* eliding some helper functions to read input into a list of strings *)
(* q1 *)
let rec diffs = function
| a::b::c -> b - a :: (diffs (b :: c))
| _ -> [];;
let rec window_diffs = function
| a1::a2::a3::b1::xs ->
b1 - a1 :: window_diffs (a2::a3::b1::xs)
| _ -> [];;
let q1 l =
diffs l
|> List.filter (fun x -> x > 0)
|> List.length
let q2 l =
window_diffs l
|> List.filter (fun x -> x > 0)
|> List.length
(* q2 *)
type command =
| Forward of int
| Down of int
| Up of int;;
type position = int * int;;
let command_of_str str =
try Scanf.sscanf str "%s %d"
(fun c i ->
match c.[0] with
| 'f' -> Some(Forward i)
| 'd' -> Some(Down i)
| 'u' -> Some(Up i)
| _ -> None)
with End_of_file -> None;;
let process_com_1 (h, d) = function
| Forward i -> (h +i, d)
| Down i -> (h, d+i)
| Up i -> (h, d-i)
type bearing = int * int * int;;
let process_com_2 (h, d, a) = function
| Forward i -> (h +i, d + (a * i), a)
| Down i -> (h, d, a+i)
| Up i -> (h, d, a-i)
let q1 l = let (h, d) = List.fold_left process_com_1 (0, 0) l in h * d;;
let q2 l = let (h, d, _) = List.fold_left process_com_2 (0, 0, 0) l in h * d;;
Naomi Nascimento · 2021-12-02 14:47
hope you will pull through my fellow lains
swap · 2021-12-02 14:56
>>28759
The (()) forms are a bashism, your /bin/sh is probably symlinked to /bin/bash if this code runs.
Might wanna change that if you want to write "pure sh".
overriding · 2021-12-02 15:05
>>28768
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/utilities/V3_chap02.html#tag_18_06_04
Now you can drown in shame.
Iverson · 2021-12-02 15:19
>>28768
panik
>>28769
kalm
Thank you anon <3
Demeere · 2021-12-02 15:30
day 2
position [] h v = (h,v)
position (x:xs) h v
| fst x == "up" = position xs h (v-snd x)
| fst x == "down" = position xs h (v+snd x)
| otherwise = position xs (h+snd x) v
aim [] _ h v = (h,v)
aim (x:xs) a h v
| fst x == "up" = aim xs (a-snd x) h v
| fst x == "down" = aim xs (a+snd x) h v
| otherwise = aim xs a (h+snd x) (v+(snd x * a))
main :: IO()
main = do
filecontent <- readFile "input"
let movement = map ((\[x,y] -> (x,read y :: Int)) . words) (lines filecontent)
print $ uncurry (*) $ position movement 0 0
print $ uncurry (*) $ aim movement 0 0 0
Unsigned · 2021-12-02 15:31
I'm just using plain C++. A little programming keeps me from losing my mind at my IT monkey job.
>>28756
>>28761
Neat! The only time I've heard about anyone using Tcl is on the sqlite website because the Tcl bindings seem to be the author's favorite.
Incognito · 2021-12-02 15:40
>>28769
Oh, interesting! Seems I was wrong '^^ Is it a new feature in the 2018 standard?
BloodCat · 2021-12-02 16:13
2nd day, in APL:
⍝ x y
forward←{⍵ 0}◊down←{0 ⍵}◊up←{0 (-⍵)}
⍝ first problem
in←⍎⎕FIO[49]'input-2.txt'
an←×/∊+/in
⍝ second problem
h∆←∊1↑¨in ⍝ horz. variation
i∆←∊+\1↓¨in ⍝ aim variation
a∆←×/+/¨h∆ (h∆×i∆) I was having trouble with the second part since I thought of it in terms of a left fold over the input list, accumulating the x, y and aim; which is quite natural, but turns out to be rather awkward to express in APL (at least from what I've tried). Eventually I realized I could "scan" (like fold, but returns the intermediate values) over the vertical variation to get the aim values, then just multiply with the horizontal variation and fold as expected. What functional programming does to your brain...
Splicer · 2021-12-02 16:27
>>28773
It has always been here (cf 2008 edition: https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/) . You may confuse it with [[]] which is indeed not portable.
A bit unrelated, here's a nice sh portability enhancer:
if ! sh -c 'fun() { local a=1; }; fun'
then
if sh -c 'fun() { typeset a=1; }; fun'
then
alias local=typeset
else
echo "local/typeset not supported by sh, aborting"
exit 1
fi
fi
I also have a pure POSIX sh readlink -f implementation, which is a massive pain to have in every script (cause you can't even source another file without it).
array · 2021-12-02 22:48
I'm seeing how far I can get with C.
Day1_Task1
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int prev_num = 0;
int increase_count = 0;
read = getline(&line, &len, fp);
prev_num = atoi(line);
while ((read = getline(&line, &len, fp)) != -1) {
if (atoi(line) > prev_num)
increase_count++;
prev_num = atoi(line);
}
printf("%d\n", increase_count);
fclose(fp);
return 0;
}
Day1_Task2
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 3
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int increase_count = 0;
int buffer[BUFFER_SIZE] = {0, 0, 0};
for (int i = 0; i < BUFFER_SIZE; i++) {
read = getline(&line, &len, fp);
buffer[i] = atoi(line);
}
/* 199 A
* 200 A B
* 208 A B C
* 210 B C D
* 200 E C D
* 207 E F D
* 240 E F G
* 269 F G H
* 260 I G H
* 263 I J H
*
* Sum the As, Bs, Cs etc. then compare sumA and sumB,
* then sumB and sumC, sumC and sumD, etc.
*
* Because they're sums, only need to compare the numbers
* that aren't shared. So in the first example, instead of
* comparing 199+200+208 against 200+208+210, we can compare
* 199 against 210.
*
* buffer has 3 spaces. As file is read, buffer will store
* the numbers to compare.
* buffer[0]: First value of current sum
* buffer[1]: First value of next sum
* buffer[2]: First value of the sum after next
*
* Each iteration, get last value of next sum, compare
* first value of current sum against last value of
* next sum, then move everthing down in the buffer by 1.
* */
while ((read = getline(&line, &len, fp)) != -1) {
if (atoi(line) > buffer[0])
increase_count++;
buffer[0] = buffer[1];
buffer[1] = buffer[2];
buffer[2] = atoi(line);
}
printf("%d\n", increase_count);
fclose(fp);
return 0;
}
Day2_Task1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
/* for spltting string */
char *action;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int h_pos = 0;
int depth = 0;
while ((read = getline(&line, &len, fp)) != -1) {
action = strsep(&line, " ");
if (strcmp("forward", action) == 0)
h_pos += atoi(strsep(&line, " "));
else if (strcmp("down", action) == 0)
depth += atoi(strsep(&line, " "));
else if (strcmp("up", action) == 0)
depth -= atoi(strsep(&line, " "));
}
printf("%d\n", h_pos*depth);
fclose(fp);
return 0;
}
Day2_Task2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
/* for spltting string */
char *action;
int amount;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int h_pos = 0;
int depth = 0;
int aim = 0;
while ((read = getline(&line, &len, fp)) != -1) {
action = strsep(&line, " ");
if (strcmp("forward", action) == 0) {
amount = atoi(strsep(&line, " "));
h_pos += amount;
depth += aim*amount;
}
else if (strcmp("down", action) == 0)
aim += atoi(strsep(&line, " "));
else if (strcmp("up", action) == 0)
aim -= atoi(strsep(&line, " "));
}
printf("%d\n", h_pos*depth);
fclose(fp);
return 0;
}
Just another Random Lainon · 2021-12-02 22:49
>>28778
Sorry about the formatting, lain seems to have eaten my tabs.
Case · 2021-12-02 22:52
Day 2 is still straightforward:
(defparameter +input+
(parse-lines
(lambda (line)
(destructuring-bind (cmd arg) (split-sequence #\Space line)
(cons (intern (string-upcase cmd) :keyword)
(parse-integer arg))))))
(defun solve-part-1 ()
(loop :with breadth := 0
:with depth := 0
:for (cmd . mag) :in +input+
:do (ecase cmd
(:down (incf depth mag))
(:up (decf depth mag))
(:forward (incf breadth mag)))
:finally (return (* depth breadth))))
(defun solve-part-2 ()
(loop :with aim := 0
:with breadth := 0
:with depth := 0
:for (cmd . mag) :in +input+
:do (ecase cmd
(:down (incf aim mag))
(:up (decf aim mag))
(:forward
(incf breadth mag)
(incf depth (* aim mag))))
:finally (return (* depth breadth))))
All hail Moloch, eater of tab indentation
Tequila · 2021-12-03 08:51
Day 3 starting to become a little more convoluted. What happens if there is no dominant 1 or 0 at some position? I hope the elfs don't use this in production.
#!/usr/bin/python3
import sys
class Decoder:
def __init__(self, width):
self.width = width
self.ones = [0] * width
self.zeroes = [0] * width
self.gamma_rate = 0
self.epsilon_rate = 0
def decode_line(self, line):
assert len(line) == self.width
for i in range(len(line)):
self.zeroes[i] += 1 - int(line[i])
self.ones[i] += int(line[i])
def finalize(self):
assert len(self.ones) == len(self.zeroes)
length = len(self.ones)
for i in range(length):
self.gamma_rate += (1 if self.ones[i] > self.zeroes[i] else 0) * 2 ** (length - i - 1)
self.epsilon_rate += (1 if self.zeroes[i] > self.ones[i] else 0) * 2 ** (length - i - 1)
# annoying first pass to init list
line = input()
dec = Decoder(len(line))
dec.decode_line(line)
for line in sys.stdin:
line = line.rstrip('\n')
dec.decode_line(line)
dec.finalize()
print(dec.gamma_rate, dec.epsilon_rate, dec.gamma_rate * dec.epsilon_rate)
Unsigned · 2021-12-03 09:31
Is it me or you can't get the second part without logging into their gay soykafe?
Anyway, 3rd in Tcl, I noticed that Tcl is retarded on multiple fronts:
* binary format pads with 0 AT THE fuarrrkING END, completely retarded.
tclsh <<< 'chan configure stdout -translation binary; puts -nonewline [binary format B5 "10110"]' | xxd -b
00000000: 10110000 * No support for unsigned integers, so I can't use bitwise negation, and no bit vectors like CL, so I use map like a peasant.
#!/usr/bin/env tclsh
namespace path ::tcl::mathop
proc bin2num {binstr} {
binary scan [binary format B64 [format %064s $binstr]] W dec
return $dec
}
set data [read [open [lindex $argv 0]]]
set sum [lmap i {0 1 2 3 4} {
+ {*}[lmap s $data {string index $s $i}]
}]
set Y_bin_digits [lmap x $sum {> $x [/ [llength $data] 2]}]
set E_bin_digits [lmap b $Y_bin_digits {! $b}]
set Y_bin [join $Y_bin_digits ""]
set E_bin [join $E_bin_digits ""]
set Y [bin2num $Y_bin]
set E [bin2num $E_bin]
puts "γ: $Y, ε: $E, power consumption: [* $Y $E]"
PS: who the fuarrrk on Lainchan's admin team thought discarding tabulations in code tags was a good idea?
Hopper · 2021-12-03 10:19
So here is mine for Day 3. It took me longer than I expected.
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in (get-lines "input" (lambda (s)
(map (lambda (c) (- (char->integer c) 48))
(string->list s)))))
(define (count-bits bin)
(fold (lambda (num cnt) (map + num cnt)) (iota (length (car bin)) 0 0) bin))
(define (problem-1 bin)
(let* [(len-half (/ (length bin) 2))
(counts (count-bits bin))
(counts->number (lambda (compare)
(string->number
(list->string
(map (lambda (cnt) (if (compare cnt len-half) #\1 #\0))
counts))
2)))
(gamma (counts->number >=))
(epsilon (counts->number <))]
(* gamma epsilon)))
(define (num-search bin compare bit)
(if (= 1 (length bin))
(string->number (list->string (map (lambda (b) (if (= b 1) #\1 #\0)) (car bin))) 2)
(let* [(len-half (/ (length bin) 2))
(bit-count (list-ref (count-bits bin) bit))
(keep-1s (compare bit-count len-half))
(candidates (filter (lambda (num)
(eq? (= 1 (list-ref num bit)) keep-1s))
bin))]
(num-search candidates compare (1+ bit)))))
(define (problem-2 bin)
(let [(oxygen (num-search bin >= 0))
(co2 (num-search bin < 0))]
(* oxygen co2)))
(format #t "~a~%~a~%" (problem-1 in) (problem-2 in))
Nguoi La · 2021-12-03 12:54
>>28785
It's not you, I'm not logged in either and before >>28778, I didn't even notice there was supposed to be a part 2. Don't care either way, not going to log in, less work per day I suppose.
Anyways, it's surprising to see tcl lack unsigned integers and bit vectors.
>discarding tabulations
just use spaces lmao
hash table · 2021-12-03 14:27
day 3 in crystal, I feel so dirty for not managing to compress it into 30 lines max like other people can :(
Zuse · 2021-12-03 16:23
>>28789
I would say lines of code aren't the most important metric in the world. There are other things such as algorithmic design decisions. For example, iterating a list multiple times or requiring all input data to be loaded into memory.
Thank you for your crystal contribution.
McCarthy · 2021-12-03 16:52
>>28790
Sure. During work, that's much more important. But when it comes to pieces like this where speed is not that important and maintainability is moot, I can't help but admire those densely packed solutions the guys who mastered the language put together. I like to watch what the crystal core team puts out, it makes me feel like a failure but I also learn a bunch every time
Technician · 2021-12-04 02:23
Hello, friend. We are tied right now. Let's be rivals!
vector · 2021-12-04 12:29
It got harder suddenly. With all this soykaf to mutate, I realize pure functional scripting (without creating a data type) may get hard:
#!/usr/bin/env tclsh
namespace path ::tcl::mathop
package require textutil::split
set BW 5; # Board width
set BH 5; # Board height
proc lincr {_list index {increment 1}} {
upvar $_list list
lset list $index [+ [lindex $list $index] $increment]
}
# Boards are of the form {nums row_tally col_tally unmarked_sum winner}
proc parse_data {path} {
global BW BH
set chan [open $path]
set draws [split [gets $chan] ,]
foreach board [textutil::split::splitx [read $chan] {\n\n}] {
lappend boards [list $board [lrepeat $BH 0] [lrepeat $BW 0] [+ {*}$board] 0]
}
close $chan
return [list $draws $boards]
}
proc update_board {draw board} {
global BW BH
lassign $board nums rowtally coltally remsum
set i [lsearch -exact $nums $draw]
if {$i == -1} {
return $board
}
set row [/ $i $BW]
set col [% $i $BW]
lincr rowtally $row
lincr coltally $col
incr remsum -[lindex $nums $i]
set win [expr {[lindex $rowtally $row] == $BW || [lindex $coltally $col] == $BH}]
return [list $nums $rowtally $coltally $remsum $win]
}
proc play_turn {_boards draw} {
upvar 1 $_boards boards
set winner {}
for {set i 0} {$i < [llength $boards]} {incr i} {
if {[lindex $boards $i 4] == 1} {
continue
}
lset boards $i [update_board $draw [lindex $boards $i]]
if {[lindex $boards $i 4] == 1} {
set winner $i
}
}
return $winner
}
lassign [parse_data [lindex $argv 0]] draws boards
foreach draw $draws {
set winner [play_turn boards $draw]
if {$winner ne ""} {
incr wincount
set score [* [lindex $boards $winner 3] $draw]
set lastwinner $winner
if {$wincount == 1} {
puts "First winner: $winner, score: $score"
} elseif {$wincount == [llength $boards]} {
break
}
}
}
puts "Last winner: $lastwinner, score: $score"
H.R. Giger · 2021-12-04 14:15
pub fn solve(input: &str) -> (u32, u32) {
let mut iter = input.split("\n\n");
let numbers = iter.next().unwrap().split(',').map(|s| s.parse().unwrap());
let mut boards: Vec<Vec<_>> = iter
.map(|s| {
s.lines()
.flat_map(|s| s.split_whitespace().map(|s| (s.parse::<u32>().unwrap() << 1) | 1))
.collect()
})
.collect();
let size = (boards[0].len() as f64).sqrt() as usize;
let check = |board: &mut [u32], drawn| {
board.iter_mut().for_each(|n| *n = if *n >> 1 == drawn { 0 } else { *n });
let drawn = |y, x| board[y * size + x] & 1 == 0;
(0..size).any(|y| (0..size).all(|x| drawn(y, x)) || (0..size).all(|x| drawn(x, y)))
};
let mut first = 0;
let mut last = 0;
for drawn in numbers {
let mut i = 0;
while i < boards.len() {
if check(&mut boards[i], drawn) {
let score = boards.swap_remove(i).iter().map(|&n| n >> 1).sum::<u32>() * drawn;
first += u32::from(first == 0) * score;
last = score;
} else {
i += 1;
}
}
}
(first, last)
}
Roger Pollack · 2021-12-04 14:22
Not much time today, but I'm not stopping. Here is my poorly crafted and rushed solution for today:
#!/usr/bin/python3
import sys
class Board:
def __init__(self):
self.matrix = []
self.marks = []
self.columns = 0
self.winner = False
self.last_draw = 0
def __str__(self):
return '\n'.join([ '\t'.join(str(e) for e in row) for row in self.matrix ])
def decode(self, line):
elements = ' '.join(line.split()).split(' ')
self.columns = len(elements)
self.matrix.append([ [int(e), False] for e in elements ])
def mark(self, draw):
self.last_draw = draw
for row in self.matrix:
for element in row:
if draw == element[0]:
element[1] = True
def row_check(self, i):
for j in range(self.columns):
if not self.matrix[j][i][1]:
return False
return True
def column_check(self, i):
for j in range(self.columns):
if not self.matrix[i][j][1]:
return False
return True
def check_win(self):
possible_wins = [ True for e in range(self.columns) ]
for i in range(self.columns):
horizontal = self.row_check(i)
vertical = self.column_check(i)
if (not horizontal) and (not vertical):
possible_wins[i] = False
continue
for i in range(self.columns):
if possible_wins[i]:
self.winner = True
def final_score(self):
score = 0
for row in self.matrix:
for element in row:
if not element[1]:
score += element[0]
return score * self.last_draw
def someone_wins(draws, boards):
for draw in draws:
print('drawing', draw)
# three passes, because there can be multiple winners
for board in boards:
board.mark(draw)
for board in boards:
board.check_win()
for board in boards:
if board.winner:
return True
return False
draws = [ int(e) for e in input().split(',') ]
boards = []
for line in sys.stdin:
if line == '\n':
boards.append(Board())
continue
boards[-1].decode(line.rstrip('\n'))
if someone_wins(draws, boards):
for board in boards:
if board.winner:
print('board has won:\n', board)
print('final score:', board.final_score())
Judge Fang · 2021-12-04 17:45
Here is my todays stuff. Difficulty is slowly but surely increasing.
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1) (srfi srfi-8))
(define (get-input filename)
(call-with-input-file filename
(lambda (port)
(let [(bingo-nums (map string->number
(string-split (read-line port) #\,)))]
(read-line port)
(let loop [(line (read-line port))
(board '())
(boards '())]
;;(when (string? line) (format #t "len ~a, ~a~%" (string-length line) line))
(cond
[(eof-object? line)
(cons bingo-nums (reverse (cons (reverse board) boards)))]
[(= 0 (string-length line))
(loop (read-line port)
'()
(cons (reverse board) boards))]
[else
(loop (read-line port)
(cons (map string->number
(remove string-null?
(string-split line #\space)))
board)
boards)]))))))
(define in (get-input "input"))
(define (and-f a . b)
(fold (lambda (a b) (and a b)) a b))
(define (or-f a b)
(or a b))
(define (is-winner? marks)
(or
;;rows
(fold (lambda (row prev)
(or prev (fold and-f #t row)))
#f marks)
;;cols
(fold or-f #f (apply map and-f marks))))
(define (get-winner num boards boards-marks)
(let loop [(boards boards)
(boards-marks boards-marks)]
(cond
[(null? boards-marks) #f]
[(is-winner? (car boards-marks)) (list num (car boards) (car boards-marks))]
[else (loop (cdr boards) (cdr boards-marks))])))
(define (play-board num board marks)
(map (lambda (b-line m-line)
(map (lambda (b-spot m-spot)
(or (= b-spot num) m-spot))
b-line m-line))
board marks))
(define (play-number num boards boards-marks)
(map (lambda (board marks)
(play-board num board marks))
boards boards-marks))
(define (compute-result num board marks)
(* num
(apply + (map (lambda (marks-row board-row)
(apply + (map (lambda (marked? val) (if marked? 0 val)) marks-row board-row)))
marks board))))
(define (problem-1 nums boards)
(match-let* [(board-n (length boards))
(boards-marks (make-list board-n (make-list 5 (make-list 5 #f))))
((num board marks)
(let loop [(num-prev -1)
(nums nums)
(boards-marks boards-marks)]
(cond
[(get-winner num-prev boards boards-marks)]
[(null? nums) #f]
[else (loop
(car nums)
(cdr nums)
(play-number (car nums) boards boards-marks))])))]
(compute-result num board marks)))
(define (problem-2 nums boards)
(match-let* [(board-n (length boards))
(boards-marks (make-list board-n (make-list 5 (make-list 5 #f))))]
(let loop-1 [(nums nums)
(boards boards)
(boards-marks boards-marks)]
(let loop-2 [(boards-next '())
(marks-next '())
(boards boards)
(boards-marks boards-marks)]
(match-let* [((board . boards-remain) boards)
((marks . marks-remain) boards-marks)
(num (car nums))
(played-marks (play-board num board marks))
(winner? (is-winner? played-marks)) ;;has current board just won?
(boards-next (if winner? boards-next (cons board boards-next))) ;;it doesn't go to next round
(marks-next (if winner? marks-next (cons played-marks marks-next)))]
(cond
[(and (null? boards-next) (null? boards-remain) winner?)
(compute-result num board played-marks)] ;;only board left and won
[(null? boards-remain) ;;no board remain for this number
(loop-1 (cdr nums) boards-next marks-next)]
[else ;;test next remaining boards for this number
(loop-2 boards-next marks-next boards-remain marks-remain)]))))))
(format #t "~a~%~a~%" (problem-1 (car in) (cdr in)) (problem-2 (car in) (cdr in)))
Motoko Kusanagi · 2021-12-05 00:16
I ended up skipping yesterday yesterday, have it today instead:
⍝ binary → decimal
dec←{+/⍵×⌽2*(⍳⍴⍵)-1}
⍝ first problem
in←⎕FIO[49]'input-3.txt'
i∆←⊃⍎¨¨in
cb←{0≤×+⌿(¯1 1)[⍵+1]}
an←{(dec ⍵)×(dec ~⍵)}cb i∆
⍝ second problem
⍝ c∆←⊃(↑⍴i∆)/⊂cb i∆
⍝ fn←{({⍵=cb ⍵}⍵[;⍺])⌿⍵}
⍝ the initial plan was \ with something like {({⍵=cb ⍵}⍵[;⍺])⌿⍵} and picking the
⍝ 1st with length 1, but it refuses (i'm not sure i properly understand scan yet)
⍝ also, gnu apl doesn't support guards, and i would also need a way to
⍝ specify the most or least common, so expanding on that strategy
⍝ wouldn't end up much different from this.
⍝ still can't think of a much better solution than a recursive function
⍝ with explicit control flow...
∇ z←n solve[x] m; ⍝ axis is a gnu extension
→(1<↑⍴m)/3
z←m◊→end
z←∊(1+n) solve[x] ((~⍣x) {⍵=cb ⍵}m[;n])⌿m
end:
∇
a∆←{(dec 1 solve[0] ⍵)×(dec 1 solve[1] ⍵)}i∆ The solution to the 2nd problem doesn't feel to me like something I should be writing in APL, but I'm also not sure if I have much of a framework to judge what I should and should not be writing yet. Probably should find somebody else doing APL to peek at their answers later or something, could be enlightening.
I've came to realize that I had somehow read and conveniently forgotten that APL, a language about manipulating arrays, happens to have multidimensional arrays that behave helpfully; who would have thought! This makes my solution to day 2 slightly more concise:
⍝ x y
forward←{⍵ 0}◊down←{0 ⍵}◊up←{0 (-⍵)}
⍝ first problem
in←⊃⍎⎕FIO[49]'input-2.txt'
an←×/+⌿in
⍝ second problem
h∆←in[;1]◊i∆←+\in[;2]
a∆←×/+/⊃h∆ (h∆×i∆) Makes me wonder what else I could be missing.
It isn't early now so I'm likely to do today tomorrow, hope with that I'm not dooming myself to being ever a day behind. Might do some of the harder problems in a language I can think about better if that becomes the case; kind of defeats the purpose, though.
Frank Garza · 2021-12-05 10:29
Day 5 ended up being pretty fun
#!/usr/bin/env python3
import sys
import re
class Plot:
def __init__(self):
self.width = 0
self.height = 0
self.pixels = []
def __str__(self):
return '\n'.join([ str(row) for row in self.pixels ])
def higher(self, y1, y2):
return y1 > self.height or y2 > self.height
def wider(self, x1, x2):
return x1 > self.width or x2 > self.width
def resize(self, x1, y1, x2, y2):
if self.higher(y1, y2):
m = max(y1, y2) + 1
for i in range(m - self.height):
self.pixels.append([0] * self.width)
self.height = m
if self.wider(x1, x2):
m = max(x1, x2) + 1
for row in self.pixels:
for i in range(m - self.width):
row.append(0)
self.width = m
def line(self, x1, y1, x2, y2):
print('({}, {}) -> ({}, {})'.format(x1, y1, x2, y2))
if (x1 != x2) and (y1 != y2):
print('not a straight line')
return
# if necessary add to grid
self.resize(x1, y1, x2, y2)
# vertical line
if x1 == x2:
for i in range(min(y1, y2), max(y1, y2) + 1):
self.pixels[i][x1] += 1
# horizontal line
elif y1 == y2:
for i in range(min(x1, x2), max(x1, x2) + 1):
self.pixels[y1][i] += 1
print(self)
def get_danger_points(self):
return sum([ sum(1 if pixel > 1 else 0 for pixel in row) for row in self.pixels])
plot = Plot()
pattern = re.compile(r"^([0-9]+),([0-9]+) -> ([0-9]+),([0-9]+)$")
for line in sys.stdin:
line = line.rstrip('\n')
match = re.search(pattern, line)
if match:
plot.line(int(match.group(1)), int(match.group(2)), int(match.group(3)), int(match.group(4)))
print(plot.get_danger_points())
protected · 2021-12-05 11:18
Strangely easy compared to yesterday.
#!/usr/bin/env tclsh
namespace path {::tcl::mathop ::tcl::mathfunc}
proc lincr {_list args} {
upvar $_list list
lset list {*}$args [+ [lindex $list {*}$args] 1]
}
set grid [lrepeat 10 [lrepeat 10 0]]
set chan [open [lindex $argv 0]]
while {[gets $chan line] >= 0} {
regexp {^(\d+),(\d+) -> (\d+),(\d+)$} $line -> sx sy ex ey
if {$sx == $ex} {
for {set i [min $sy $ey]} {$i <= [max $sy $ey]} {incr i} {
lincr grid $i $sx
}
} elseif {$sy == $ey} {
for {set i [min $sx $ex]} {$i <= [max $sx $ex]} {incr i} {
lincr grid $sy $i
}
}
}
puts [+ {*}[lmap x [concat {*}$grid] {> $x 1}]]
Trinity · 2021-12-05 12:04
Today was a fast one.
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1) (srfi srfi-43))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in (get-lines
"input"
(lambda (line)
(match-let* [((x1s mid y2s) (string-split line #\,))
(x1 (string->number x1s))
(y2 (string->number y2s))
((y1s _ x2s) (string-split mid #\space))
(y1 (string->number y1s))
(x2 (string->number x2s))]
`((,x1 ,y1) (,x2 ,y2))))))
(define vert-horizont (filter (match-lambda
[((x1 y1) (x2 y2))
(or (= x1 x2) (= y1 y2))])
in))
(define boundaries (fold (lambda (line p-max)
(match-let* [(((x1 y1) (x2 y2)) line)
((xm ym) p-max)]
`(,(max xm x1 x2) ,(max ym y1 y2))))
'(0 0)
in))
(define (d a2 a1)
(if (= a2 a1)
0
(/ (- a2 a1) (abs (- a2 a1)))))
(define (solution lines boundaries)
(match-let* [((xmax ymax) boundaries)
(diagram (make-vector (* (1+ xmax) (1+ ymax)) 0))]
(for-each (match-lambda
[((x1 y1) (x2 y2))
(let [(dx (d x2 x1))
(dy (d y2 y1))]
(do [(xi x1 (+ xi dx))
(yi y1 (+ yi dy))]
((and (= xi (+ x2 dx)) (= yi (+ y2 dy))))
;;(format #t "(~a ~a)->(~a ~a)->(~a ~a)~%" x1 y1 xi yi x2 y2)
(let* [(i (+ xi (* yi xmax)))
(val (vector-ref diagram i))]
(vector-set! diagram i (1+ val)))))])
lines)
(vector-count (lambda (i el) (>= el 2)) diagram)))
(format #t "~a~%~a~%" (solution vert-horizont boundaries) (solution in boundaries))
Trinity · 2021-12-05 12:06
>>28808
Only fast because I didn't resize... man, Tcl lacking matrix (and the module in Tcllib is garbage) doesn't make it fun.
#!/usr/bin/env tclsh
namespace path {::tcl::mathop ::tcl::mathfunc}
proc lincr {_list args} {
upvar $_list list
lset list {*}$args [+ [lindex $list {*}$args] 1]
}
set grid [lrepeat 10 [lrepeat 10 0]]
lassign {0 0} w h
set chan [open [lindex $argv 0]]
while {[gets $chan line] >= 0} {
regexp {^(\d+),(\d+) -> (\d+),(\d+)$} $line -> sx sy ex ey
# Resize if needed
set nw [+ [max $sx $ex] 1]
set nh [+ [max $sy $ey] 1]
if {$nw > $w} {
for {set i 0} {$i < $h} {incr i} {
lset grid $i [concat [lindex $grid $i] [lrepeat [- $nw $w] 0]]
}
set w $nw
}
if {$nh > $h} {
lappend grid [lrepeat [- $nh $h] [lrepeat $w 0]]
set h $nh
}
if {$sx == $ex} {
for {set i [min $sy $ey]} {$i <= [max $sy $ey]} {incr i} {
lincr grid $i $sx
}
} elseif {$sy == $ey} {
for {set i [min $sx $ex]} {$i <= [max $sx $ex]} {incr i} {
lincr grid $sy $i
}
}
}
puts [+ {*}[lmap x [concat {*}$grid] {> $x 1}]]
↑ · 2021-12-05 15:00
day2, just started
@AllArgsConstructor
public class SubmarineStrategy {
private Submarine submarine = new Submarine();
public void followStrategy(FileReaderAOC fileReaderAOC) throws IOException {
fileReaderAOC.eatUpAllLines();
List<String> directions = fileReaderAOC.getAllElements();
for(String directive : directions) {
String direction = directive.split(" ")[0];
int howMuch = Integer.parseInt(directive.split(" ")[1]);
if("up".equals(direction)) {
submarine.up(howMuch);
}
if("down".equals(direction)) {
submarine.down(howMuch);
}
if("forward".equals(direction)) {
submarine.forward(howMuch);
}
}
}
public void followStrategyPart2(FileReaderAOC fileReaderAOC) throws IOException {
fileReaderAOC.eatUpAllLines();
List<String> directions = fileReaderAOC.getAllElements();
for(String directive : directions) {
String direction = directive.split(" ")[0];
int howMuch = Integer.parseInt(directive.split(" ")[1]);
if("up".equals(direction)) {
submarine.up2(howMuch);
}
if("down".equals(direction)) {
submarine.down2(howMuch);
}
if("forward".equals(direction)) {
submarine.forward2(howMuch);
}
}
}
public int getWay() {
return submarine.getDepth() * submarine.getHorizontal();
}
}
@NoArgsConstructor
@Getter
public class Submarine {
private int horizontal;
private int depth;
private int aim;
public void up(int howMuch) {
depth-=howMuch;
}
public void up2(int howMuch) {
aim-=howMuch;
}
public void down(int howMuch) {
depth+=howMuch;
}
public void down2(int howMuch) {
aim+=howMuch;
}
public void forward(int howMuch) {
horizontal += howMuch;
}
public void forward2(int howMuch) {
horizontal += howMuch;
depth += (aim*howMuch);
}
}
@NoArgsConstructor
public class FileReaderAOC {
private String relFileName;
private File testFile;
@Getter
private List<String> allElements = new ArrayList<>();
@Setter
private BufferedReader bufferedReader;
public FileReaderAOC(String relFileName) {
this.relFileName = relFileName;
}
public static FileReaderAOC getFromFileName(String relFileName) throws FileNotFoundException {
FileReaderAOC fileReaderAOC = new FileReaderAOC(relFileName);
File file = new File(relFileName);
FileInputStream fstream = new FileInputStream(file);
fileReaderAOC.setBufferedReader(new BufferedReader(new InputStreamReader(fstream)));
return fileReaderAOC;
}
public static FileReaderAOC getFromFileName(InputStream relFileName) throws FileNotFoundException {
FileReaderAOC fileReaderAOC = new FileReaderAOC();
fileReaderAOC.setBufferedReader(new BufferedReader(new InputStreamReader(relFileName)));
return fileReaderAOC;
}
public String getNextLine() throws IOException {
return bufferedReader.readLine();
}
public List<String> eatUpAllLines() throws IOException {
String line;
while ((line = bufferedReader.readLine()) != null) {
allElements.add(line);
}
return allElements;
}
}
Cat Mother · 2021-12-05 18:09
>>28762
OCaml lain, come back. I liked your code and am in dire need to sharpen up my ML-fu.
Trinity · 2021-12-05 19:44
I really struggled with the second part of day 3 in C. I've fallen behind quite a bit but either way here's day 3:
Part 1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#define BITSIZE 12
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
/* Keep a "card counting" count of bits.
* positive means more 1s
* negative means more 0s
* */
int bitcounts[BITSIZE];
for (int i = 0; i < BITSIZE; i++)
bitcounts[i] = 0;
while ((read = getline(&line, &len, fp)) != -1) {
for (int i = 0; i < BITSIZE; i++) {
if (line[i] == '1')
bitcounts[i]++;
else
bitcounts[i]--;
}
}
/* set each bit of gamma and epsilon */
unsigned long gamma = 0;
unsigned long epsilon = 0;
for (int i = 0; i < BITSIZE; i++) {
if (bitcounts[i] > 0)
gamma |= 1 << (BITSIZE-1-i);
else
epsilon |= 1 << (BITSIZE-1-i);
}
printf("%ld\n", gamma*epsilon);
fclose(fp);
return 0;
}
Part 2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BITSIZE 12
#define FILESIZE 1000
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
/* Read in the input file and count the bits in the first column*/
long inputs[FILESIZE];
int i = 0;
int bitcount = 0; /* +ve: more 1s, -ve: more 0s */
while ((read = getline(&line, &len, fp)) != -1) {
inputs[i] = strtol(line, NULL, 2);
i++;
if (line[0] == '1')
bitcount++;
else
bitcount--;
}
fclose(fp);
/* Move the inputs with the least and most common bits
* as per the puzzle*/
int least_count = 0;
int most_count = 0;
long least[FILESIZE];
long most[FILESIZE];
int most_common_bit;
if (bitcount >= 0)
most_common_bit = 1;
else
most_common_bit = 0;
/* Current bit position */
int bitpos = BITSIZE-1;
int bit = 1 << bitpos;
for (int i = 0; i < FILESIZE; i++) {
/* Compares the current bit with the most common bit */
if (((inputs[i] & bit) >> bitpos) == most_common_bit) {
most[most_count] = inputs[i];
most_count++;
}
else {
least[least_count] = inputs[i];
least_count++;
}
}
/* O2 rating */
int most_size = most_count;
while (most_size > 1) {
bitpos--;
bit = 1 << bitpos;
/* copy to inputs
* count most common bit*/
bitcount = 0;
for (int i = 0; i < most_size; i++) {
inputs[i] = most[i];
if (((inputs[i] & bit) >> bitpos) == 1)
bitcount++;
else
bitcount--;
}
if (bitcount >= 0)
most_common_bit = 1;
else
most_common_bit = 0;
/* filter inputs to most*/
most_count = 0;
for (int i = 0; i < most_size; i++) {
if (((inputs[i] & bit) >> bitpos) == most_common_bit) {
most[most_count] = inputs[i];
most_count++;
}
}
most_size = most_count;
}
/* CO2 rating */
int least_size = least_count;
bitpos = BITSIZE-1;
int least_common_bit;
while (least_size > 1) {
bitpos--;
bit = 1 << bitpos;
/* copy to inputs
* count least common bit*/
bitcount = 0;
for (int i = 0; i < least_size; i++) {
inputs[i] = least[i];
if (((inputs[i] & bit) >> bitpos) == 1)
bitcount++;
else
bitcount--;
}
if (bitcount >= 0)
least_common_bit = 0;
else
least_common_bit = 1;
/* filter inputs to most*/
least_count = 0;
for (int i = 0; i < least_size; i++) {
if (((inputs[i] & bit) >> bitpos) == least_common_bit) {
least[least_count] = inputs[i];
least_count++;
}
}
least_size = least_count;
}
printf("%ld\n", most[0]*least[0]);
return 0;
}
Eldon Tyrell · 2021-12-05 20:32
>>28812
I want to see more of ocaml as well
Me, myself and I · 2021-12-05 21:18
>>28719
Another solution in crystal. I didn't have much time today, but I still thinks it turned out okay.
Paste: http://ix.io/3GQu
Me, myself and I · 2021-12-05 21:19
>>28817
For some reason the site refused to attach my image
Sabo Engle · 2021-12-05 21:22
>>28818
maybe if i shrink it a bit?
Unidentified · 2021-12-06 09:06
Day 6 was a nice one as well.
#!/usr/bin/env python3
import sys
import time
def consume(countdown):
value = countdown[0]
countdown[0] = 0
return value
countdown = [0] * 9
for element in input().split(','): countdown[int(element)] += 1
for day in range(128 if len(sys.argv) < 2 else int(sys.argv[1])):
print(countdown)
print(sum(countdown), 'fishies after', day, 'days')
# extract respawns
spawns = consume(countdown)
# shift
for i in range(8):
countdown[i] += countdown[i + 1]
countdown[i + 1] = 0
# apply previously extracted respawns
countdown[6] += spawns
countdown[8] += spawns
#time.sleep(.01)
Jane Doe · 2021-12-06 09:44
yeah, day six was nice. It even did the bigboy in okay time. I might solve this with actual matrices if I feel like it later today
Mitnick · 2021-12-06 11:30
And easy one.
#!/usr/bin/env tclsh
namespace path ::tcl::mathop
lassign $argv path days
set fishes [split [read [open $path]] ,]
for {set d 0} {$d < $days} {incr d} {
set newcount [+ {*}[lmap f $fishes {== $f 0}]]
set fishes [lmap f $fishes {expr {$f ? $f - 1 : 6}}]
lappend fishes {*}[lrepeat $newcount 8]
}
puts [llength $fishes]
Hitomi · 2021-12-06 17:59
I'm still behind on days 4 and 5 but at least day 6 was easier.
Part 1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DAYS 80
#define MAX_INTERNAL_TIME 8
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read = getline(&line, &len, fp);
fclose(fp);
/* The array keeps track of the internal timers of the fish.
* The index is the timer and the value is the number of fish
* */
int lanternfish[MAX_INTERNAL_TIME+1] = {0};
char *token;
while ((token = strsep(&line, ",")) != NULL) {
lanternfish[atoi(token)]++;
}
int day = DAYS;
int created = 0;
while(day) {
created = lanternfish[0];
for (int i = 0; i < MAX_INTERNAL_TIME; i++)
lanternfish[i] = lanternfish[i+1];
lanternfish[6] += created;
lanternfish[8] = created;
day--;
}
int total_fish = 0;
for (int i = 0; i < MAX_INTERNAL_TIME+1; i++)
total_fish += lanternfish[i];
printf("%ld\n", total_fish);
return 0;
}
Part 2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DAYS 256
#define MAX_INTERNAL_TIME 8
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read = getline(&line, &len, fp);
fclose(fp);
/* The array keeps track of the internal timers of the fish.
* The index is the timer and the value is the number of fish
* */
long lanternfish[MAX_INTERNAL_TIME+1] = {0};
char *token;
while ((token = strsep(&line, ",")) != NULL) {
lanternfish[strtol(token, NULL, 10)]++;
}
int day = DAYS;
long created = 0;
while(day) {
created = lanternfish[0];
for (int i = 0; i < MAX_INTERNAL_TIME; i++)
lanternfish[i] = lanternfish[i+1];
lanternfish[6] += created;
lanternfish[8] = created;
day--;
}
long total_fish = 0;
for (int i = 0; i < MAX_INTERNAL_TIME+1; i++)
total_fish += lanternfish[i];
printf("%ld\n", total_fish);
return 0;
}
setq · 2021-12-06 22:20
that was an easy one. I was worried I would have to look for some patterns in part 2.
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1) (srfi srfi-43))
(define (get-init-state)
(call-with-input-file "input"
(lambda (port)
(let [(fishes (map string->number
(string-split (read-line port) #\,)))
(state (make-vector 9 0))]
(for-each (lambda (fish)
(let [(val (vector-ref state fish))]
(vector-set! state fish (1+ val))))
fishes)
state))))
(define (simulate-day! state)
(let [(d6 (vector-ref state 6))
(d8 (vector-ref state 8))]
(vector-for-each (lambda (day fishes)
(let* [(new-day (modulo (1- day) 9))
(fishes-new-old (vector-ref state new-day))]
(cond
[(= day 0)
(vector-set! state 6 (+ fishes d6))
(vector-set! state 8 (+ fishes d8))
(vector-set! state day 0)]
[(= day 6)
(vector-set! state new-day (+ fishes-new-old d6))
(vector-set! state day (- fishes d6))]
[(= day 8)
(vector-set! state new-day (+ fishes-new-old d8))
(vector-set! state day (- fishes d8))]
[else
(vector-set! state new-day (+ fishes-new-old fishes))
(vector-set! state day 0)])))
state)))
(define (solution state days)
(do [(day 0 (1+ day))]
((= days day))
(simulate-day! state))
(vector-fold (lambda (_ sum fishes) (+ sum fishes)) 0 state))
(let* [(init-state (get-init-state))
(init-state2 (vector-copy init-state))]
(format #t "~a~%~a~%" (solution init-state 80) (solution init-state 256)))
Splicer · 2021-12-06 23:59
Sadly I have been doing everything in Python for time reasons, I'll have more time to try other languages after day 14 or so.
with open('input.txt', 'r') as in_f:
start = [int(x) for x in in_f.readline().split(',')]
lanternfish = [0] * 9
for s in start:
lanternfish[s] += 1
for _ in range(256):
spawners = lanternfish[0]
for j in range(len(lanternfish) - 1):
lanternfish[j] = lanternfish[j + 1]
lanternfish[6] += spawners
lanternfish[8] = spawners
print(sum(lanternfish))
overriding · 2021-12-07 09:01
Not sure why today was so easy, the naive approach works good enough too.
#!/usr/bin/env python3
import sys
positions = list(map(int, input().split(',')))
print(positions)
min_dist = sys.maxsize
index = -1
# quadratic is good enough
for i, crab in enumerate(positions):
dist = sum([ abs(crab - other_crab) for other_crab in positions ])
if dist < min_dist:
min_dist = dist
index = i
print('for crab', crab, 'at', positions[i], 'dist of others is', dist)
print('optimal position is that of crab', index, 'at', positions[index], 'with an expenditure of', min_dist)
Lain Iwakura · 2021-12-07 11:38
It's actually getting easier. I was thinking about getting correct answer for part 2 analytically but then I just brute forced it with reasonable bounds.
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1) (srfi srfi-43))
(define (get-input)
(call-with-input-file "input"
(lambda (port)
(let [(positions (map string->number
(string-split (read-line port) #\,)))]
positions))))
(define (compute-cost positions point)
(fold + 0 (map (lambda (p) (abs (- p point))) positions)))
(define (compute-cost-2 positions point)
(fold + 0 (map (lambda (p)
(let [(dist (abs (- p point)))]
(/ (* dist (1+ dist)) 2)))
positions)))
(define (find-median positions)
(list-ref (sort positions <) (/ (length positions) 2)))
(define (find-mean positions)
(round (/ (apply + positions) (length positions))))
(define (problem-1 positions)
(compute-cost positions (find-median positions)))
(define (problem-2 positions)
(let [(median (find-median positions))
(mean (find-mean positions))
(cost (lambda (p) (compute-cost-2 positions p)))]
(apply min (unfold (lambda (p) (> p mean))
cost
1+
median))))
(let [(input (get-input))]
(format #t "~a~%~a~%" (problem-1 input) (problem-2 input)))
Technician · 2021-12-07 14:34
one-liner (after file I/O and imports) in Python for part 1:
import numpy as np
with open('inputs.txt', 'r') as in_f:
crabs = [int(x) for x in in_f.readline().split(',')]
print(sum([abs(x - np.median(crabs)) for x in crabs]))
Terzibashjian · 2021-12-07 23:04
Still haven't caught up on days 4 and 5 but day 7 was straight forward.
Part 1:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_H_POS 10000
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("exampleinput.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read = getline(&line, &len, fp);
fclose(fp);
int h_positions[MAX_H_POS] = {0};
char *token;
int max_value = 0;
while ((token = strsep(&line, ",")) != NULL) {
h_positions[atoi(token)]++;
if (atoi(token) > max_value)
max_value = atoi(token);
}
int least_fuel = 0;
for (int i = 0; i < max_value+1; i++) {
int cost = 0;
for (int j = 0; j < max_value+1; j++) {
cost += abs(i-j)*h_positions[j];
}
if (i == 0)
least_fuel = cost;
else
if (cost < least_fuel)
least_fuel = cost;
}
printf("%d\n", least_fuel);
return 0;
}
Part 2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_H_POS 10000
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("input.txt", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
read = getline(&line, &len, fp);
fclose(fp);
int h_positions[MAX_H_POS] = {0};
char *token;
int max_value = 0;
while ((token = strsep(&line, ",")) != NULL) {
h_positions[atoi(token)]++;
if (atoi(token) > max_value)
max_value = atoi(token);
}
int least_fuel = 0;
for (int i = 0; i < max_value+1; i++) {
int cost = 0;
for (int j = 0; j < max_value+1; j++) {
/* sum(n) = n(n+1)/2 */
cost += (abs(i-j)*(abs(i-j)+1)/2)*h_positions[j];
}
if (i == 0)
least_fuel = cost;
else
if (cost < least_fuel)
least_fuel = cost;
}
printf("%d\n", least_fuel);
return 0;
}
Lukasiewicz · 2021-12-08 09:49
Day 8. Anyone know if there is a mirror that includes the part 2 prompts that doesn't need logging in?
#!/usr/bin/env python3
import sys
lengths = {
2: 1,
4: 4,
3: 7,
7: 8
}
unique = 0
for line in sys.stdin:
if '|' not in line: continue
for token in line.split('|')[1].split():
if len(token) in lengths.keys():
unique += 1
print(f'token {token}, length {len(token)}, segment {lengths[len(token)]}')
print(f'{unique} unique digits')
Koos van der Merwe · 2021-12-08 10:10
Day 7, day 8 will come soon
Meddler · 2021-12-08 11:36
>>28719
Here's day 8 in crystal:
crystal
record Input, flicker : Set(Set(Char)), data : Array(Set(Char))
module Aoc08
abstract class Solver
def initialize(@data : Array(Input))
end
abstract def run
end
class Part1 < Solver
def run
@data.map { |l| l.data }
.flatten
.select { |f| [2, 3, 4, 7].includes? f.size }
.size
end
end
class Part2 < Solver
def self.prime_digits(input)
[{2, 1}, {3, 7}, {4, 4}, {7, 8}].reduce(Hash(Int8, (Set(Char))).new) do |hash, hardcode|
if (n = input.select { |e| e.size == hardcode[0] }.shift?)
hash[hardcode[1].to_i8] = n
end
hash
end
end
def self.solve_digits(input, wanted_codes, solved)
return solved if wanted_codes.empty?
code = wanted_codes.first
case code.size
when 5
case
when solved[7].subset_of?(code) then solved[3] = code
when (solved[4] - solved[1]).subset_of?(code) then solved[5] = code
else solved[2] = code
end
when 6
case
when (solved[7] + solved[4]).subset_of?(code) then solved[9] = code
when solved[7].subset_of? code then solved[0] = code
else solved[6] = code
end
end
wanted_codes.delete(code)
return solve_digits(input, wanted_codes, solved)
end
def run
@data.map do |input|
to_solve = input.flicker + input.data.to_set
digit_to_code_dict = Part2.solve_digits(to_solve,
input.data.dup,
Part2.prime_digits(to_solve))
input.data
.map { |coded_digit| digit_to_code_dict.key_for(coded_digit).to_s }
.join("")
.to_i
end
.sum
end
end
end
data = File.read_lines("./../08.txt")
.compact
.map do |l|
l = l.split(" | ")
Input.new(
l.shift.split.map(&.chars.to_set).to_set,
l.shift.split.map(&.chars.to_set))
end
puts "Part one: #{Aoc08::Part1.new(data.dup).run}"
puts "Part two: #{Aoc08::Part2.new(data.dup).run}"
I'm not sure if theres a trick to this one that goes beyond figuring out what digits have to be decoded in order to get the lines data number, but I doubt it makes much difference. I'll try to get it to run on multiple fibers and throw it at a bigboy next
Dr. X · 2021-12-08 14:50
Ugly ay 8 part 2, ended up sorting all strings
right = []
left = []
with open('inputs/15.txt', 'r') as in_f:
for line in in_f:
split = line.split('|')
left.append([''.join(sorted(x)) for x in split[0].split()])
right.append([''.join(sorted(x)) for x in split[1].split()])
class Display:
def __init__(self, left, right):
self.decoder = self.create_decoder(left)
self.num = self.decode(right)
def intersect(self, s0, s1):
return len(set(s0).intersection(set(s1)))
def create_decoder(self, l):
decoder = [''] * 10
# Easy numbers first
for s in l:
if len(s) == 2:
decoder[1] = s
elif len(s) == 4:
decoder[4] = s
elif len(s) == 3:
decoder[7] = s
elif len(s) == 7:
decoder[8] = s
# Harder numbers now
for s in l:
shared = self.intersect(s, decoder[1])
# 2, 3, or 5
if len(s) == 5:
if shared == 2:
decoder[3] = s
else:
shared = self.intersect(s, decoder[4])
if shared == 2:
decoder[2] = s
else:
decoder[5] = s
# 0, 5, or 9
elif len(s) == 6:
if shared == 1:
decoder[6] = s
else:
shared = self.intersect(s, decoder[4])
if shared == 4:
decoder[9] = s
else:
decoder[0] = s
return decoder
def decode(self, r):
tens = 1000
ret = 0
for s in r:
ret += tens * self.decoder.index(s)
tens //= 10
return ret
def get_num(self):
return self.num
final_sum = 0
for l, r in zip(left, right):
final_sum += Display(l, r).get_num()
print(final_sum)
Hagbard Celine · 2021-12-09 00:54
took me way too much time, but alas....
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in (get-lines "input"
(lambda (line)
(let* [(parts (string-split line #\space))
(input (list-head parts 10))
(output (list-tail parts 11))]
(list input output)))))
;;1,4,7,8 appear?
;;2,4,3,7 lengths
(define (problem-1 input)
(fold + 0
(map (lambda (entry)
(count (lambda (out) (member out '(2 3 4 7)))
(map string-length (list-ref entry 1))))
input)))
(define (construct-decoder entry-input)
(define decoded (make-hash-table))
(define (has? num)
(hash-ref decoded num))
(define (insert! num segment)
(hash-set! decoded num segment))
(define (in? num segment)
(lset<= eq? (hash-ref decoded num '(#\x)) segment))
(let loop [(segments (map string->list entry-input))]
(if (null? segments) decoded
(match-let* [((segment . remain) segments)
(len (length segment))]
;;(format #t "~a ~a~%" segment (hash-map->list cons decoded))
(cond
[(= len 2) (insert! 1 segment) (loop remain)]
[(= len 3) (insert! 7 segment) (loop remain)]
[(= len 4) (insert! 4 segment) (loop remain)]
[(= len 7) (insert! 8 segment) (loop remain)]
[(and (= len 6) (in? 4 segment))
(insert! 9 segment) (loop remain)]
[(and (= len 6) (in? 1 segment) (has? 4) (not (in? 4 segment)))
(insert! 0 segment) (loop remain)]
[(and (= len 6) (has? 1) (has? 4))
(insert! 6 segment) (loop remain)]
[(and (= len 5) (in? 1 segment))
(insert! 3 segment) (loop remain)]
[(and (= len 5) (has? 1) (not (in? 1 segment))
(= 3 (length (lset-intersection eq?
(hash-ref decoded 4 '(#\x))
segment))))
(insert! 5 segment) (loop remain)]
[(and (= len 5) (has? 3) (has? 5))
(insert! 2 segment) (loop remain)]
[else (loop (append remain (list segment)))])))))
(define (decode decoder out-segments)
(let* [(key-vals (hash-map->list cons decoder))
(inv-decoder (lambda (code-in)
(number->string
(car
(find (match-lambda
[(num . code)
;;(format #t "~a ~a ~a~%" code-in num code)
(lset= eq? code code-in)])
key-vals)))))
(decoded (map inv-decoder (map string->list out-segments)))]
(string->number (string-join decoded ""))))
(define (problem-2 input)
(fold + 0
(map (match-lambda
[(in-coded out-coded)
(let* [(decoder (construct-decoder in-coded))
(result-num (decode decoder out-coded))]
;;(format #t "~a ~a ~a~%" in-coded out-coded result-num)
result-num
)])
input)))
(format #t "~a~%~a~%" (problem-1 in) (problem-2 in))
Netrunner · 2021-12-09 06:14
This is the first day that is geared towards recursivity right?
import bisect
with open('test.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
map = []
for line in lines:
map.append([int(x) for x in line])
def color_basin(map, x, y):
if map[x][y] == 9:
return 0
map[x][y] = 9
left = color_basin(map, x - 1, y) if x != 0 else 0
right = color_basin(map, x + 1, y) if x != len(map) - 1 else 0
up = color_basin(map, x, y - 1) if y != 0 else 0
down = color_basin(map, x, y + 1) if y != len(map[0]) - 1 else 0
return 1 + left + right + up + down
sizes = []
for x in range(len(map)):
for y in range(len(map[0])):
if map[x][y] != 9:
bisect.insort(sizes, color_basin(map, x, y))
prod = 1
for x in sizes[len(sizes) - 3:]:
prod *= x
print(prod)
Koos van der Merwe · 2021-12-09 09:28
Day 9. Wanted to try a sliding window approach, but it ended up quite ugly.
#!/usr/bin/env python3
import sys
# sliding window of input line elements
class Window:
def __init__(self):
self.window = [ None, None, None ]
def slide(self):
for i in range(len(self.window) - 1):
self.window[i] = self.window[i + 1]
print(self.window)
def analyze(self):
if not self.window[1]: return
for i in range(len(self.window[1])):
up = self.window[0][i] if self.window[0] else sys.maxsize
left = sys.maxsize if i == 0 else self.window[1][i - 1]
middle = self.window[1][i]
right = sys.maxsize if i == len(self.window[1]) - 1 else self.window[1][i + 1]
down = self.window[2][i]
if smaller(middle, [up, left, right, down]):
yield middle + 1
def process(self, elements):
self.window[len(self.window) - 1] = elements
def smaller(a, elements):
for b in elements:
if b <= a:
return False
return True
def collect(generator):
return sum([ e for e in generator ])
total = 0
window = Window()
for line in sys.stdin:
window.slide()
window.process(list(map(int, line.rstrip('\n'))))
#for e in window.analyze(): print(e)
total += collect(window.analyze())
# very ugly to have to repeat loop body
window.slide()
window.process([ 1 + e for e in window.window[2]])
total += collect(window.analyze())
print(f'sum of risk levels of all low points is {total}')
Ein · 2021-12-09 10:50
I'm quiet happy with my code for todays challenge
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in
(let* [(data (get-lines "input"
(lambda (line)
(map (lambda (chr-num) (- (char->integer chr-num) 48))
(string->list line)))))
(rows (length data))
(cols (length (car data)))
(matrix (make-vector (* rows cols) 0))]
(for-each (lambda (row i)
(for-each (lambda (n j) (vector-set! matrix (+ j (* i cols)) n))
row
(iota cols)))
data
(iota rows))
(list rows cols matrix (lambda (row col . dflt)
(let [(fail (if (null? dflt) #f (car dflt)))]
(cond
[(< row 0) fail]
[(>= row rows) fail]
[(< col 0) fail]
[(>= col cols) fail]
[else (vector-ref matrix (+ col (* row cols)))]))))))
(define (get-basins rows cols matrix-raw mat)
(do [(i 0 (1+ i))
(basins '())]
[(= i rows) basins]
(do [(j 0 (1+ j))]
[(= j cols)]
(when (and (< (mat i j) (mat (1+ i) j (inf)))
(< (mat i j) (mat (1- i) j (inf)))
(< (mat i j) (mat i (1+ j) (inf)))
(< (mat i j) (mat i (1- j) (inf))))
(set! basins (cons (list i j) basins))))))
(define (problem-1 rows cols matrix-raw mat)
(fold + 0 (map (lambda (basin) (1+ (apply mat basin)))
(get-basins rows cols matrix-raw mat))))
(define (basin-surface mat row col)
(define visited (make-hash-table))
(let dfs [(i row)
(j col)]
(cond
[(= 9 (mat i j 9)) 0]
[(hash-ref visited (cons i j)) 0]
[else (hash-set! visited (cons i j) #t)
(+ 1
(dfs (1+ i) j)
(dfs (1- i) j)
(dfs i (1+ j))
(dfs i (1- j)))])))
(define (problem-2 rows cols matrix-raw mat)
(apply *
(list-head
(sort (map (lambda (basin) (apply basin-surface mat basin))
(get-basins rows cols matrix-raw mat))
>)
3)))
(format #t "~a~%~a~%" (apply problem-1 in) (apply problem-2 in))
Unidentified · 2021-12-09 14:08
>>28874
I did it using a queue, but yes, it's essentially recursive
require "bit_array"
record Point, x : Int32, y : Int32 do
def ==(other)
return x == other.x &&
y == other.y
end
end
module Aoc09
extend self
abstract class Solver
def initialize(@data : Array(Array(UInt8)))
end
abstract def run
def find_lowest
lows = [] of Array(Int32)
@data.each_with_index do |line, y|
line.map_with_index do |n, x|
if [-1, 1].map do |mod|
res = [] of UInt8
if 0 <= (l = y + mod) < @data.size
res << @data[l][x]
end
if 0 <= (r = x + mod) < @data[0].size
res << line[r]
end
res
end
.flatten
.compact
.min >(n)
lows << [x, y]
end
end
end
return lows
end
end
class Part1 < Solver
def run
lows = find_lowest
return lows.map{|pp| @data[pp[1]][pp[0]]}.sum(lows.size)
end
end
class Part2 < Solver
def run
visited = (Array(BitArray).new(@data.size) { BitArray.new(@data[0].size) })
queue = [] of Point
result = find_lowest.map do |root|
queue << Point.new(root[0], root[1])
basin_size = 0
while queue.any?
point = queue.shift
visited[point.y].toggle(point.x)
basin_size += 1
queue += [{0, 1}, {0, -1}, {1, 0}, {-1, 0}]
.map do |mod|
pending = Point.new(point.x + mod[0], point.y + mod[1])
if 0<= pending.x < @data.[0].size &&
0 <= pending.y < @data.size &&
@data[pending.y][pending.x] < 9 &&
!visited[pending.y][pending.x]
pending
else
nil
end
end
.compact
queue.uniq!
end
basin_size
end
.sort
.last(3)
.product
return result
end
end
end
include Aoc09
input = File.read_lines("./../09.txt")
.map(&.chars.map(&.to_u8))
puts "Silver: #{Part1.new(input.clone).run}"
puts "Gold: #{Part2.new(input.clone).run}" ...technically 100loc, please no bully
BloodCat · 2021-12-10 05:30
I feel weird using Python's list as a stack
import bisect
with open('test.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
closers = {'(': ')', '[': ']', '{': '}', '<': '>'}
points = {')': 3, ']': 57, '}': 1197, '>': 25137}
points2 = {')': 1, ']': 2, '}': 3, '>': 4}
illegals = []
scores = []
for line in lines:
next_closers = []
illegal_flag = False
# PART 1
for c in line:
if c in ['(', '[', '{', '<']:
next_closers.append(closers[c])
elif c != next_closers.pop():
illegals.append(c)
illegal_flag = True
break
if illegal_flag:
continue
# PART 2
score = 0
while next_closers:
curr = next_closers.pop()
score *= 5
score += points2[curr]
bisect.insort(scores, score)
print(sum([points[x] for x in illegals]))
print(scores[len(scores) // 2])
Ein · 2021-12-10 08:44
Day 10. Very fun example today.
#!/usr/bin/env python3
import sys
lookup = {
'(': 1, '[': 1, '{': 1, '<': 1,
')': -1, ']': -1, '}': -1, '>': -1,
}
expected = {
')': '(', ']': '[', '}': '{', '>': '<',
'(': ')', '[': ']', '{': '}', '<': '>'
}
score = {
')': 3,
']': 57,
'}': 1197,
'>': 25137
}
balance = 0
for line in sys.stdin:
line = line.rstrip('\n')
print(line)
# find balance and score of line
brackets = 0
stack = []
for c in line:
closing = lookup[c] < 0
brackets += lookup[c]
# print with indentation
print(' ' * (brackets + (1 if closing else 0)), c, brackets, lookup[c])
# manipulate stack to check for correctness and score
if not closing:
stack.append(c)
else:
bracket = stack.pop()
if bracket != expected[c]:
print(f'closure incorrect: {bracket} == {expected[c]} = {bracket == expected[c]}')
balance += score[c]
print(f'final bracket balance {brackets}')
# find closure to line
print('a closure look like')
for _ in range(len(stack)):
b = expected[stack.pop()]
closing = lookup[b] < 0
brackets += lookup[b]
print(' ' * (brackets + (1 if closing else 0)), b, brackets, lookup[b])
print(f'file gets a score of {balance}')
Turing · 2021-12-10 10:05
here is mine
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 q) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in (get-lines "input" string->list))
(define score-table (alist->hash-table '((#\) . 3)
(#\] . 57)
(#\} . 1197)
(#\> . 25137))))
(define complete-score-table (alist->hash-table '((#\) . 1)
(#\] . 2)
(#\} . 3)
(#\> . 4))))
(define closing '(#\) #\] #\} #\>))
(define (match? open close)
(or
(and (eq? #\( open) (eq? #\) close))
(and (eq? #\[ open) (eq? #\] close))
(and (eq? #\{ open) (eq? #\} close))
(and (eq? #\< open) (eq? #\> close))))
(define (find-closing open)
(find (lambda (close) (match? open close)) closing))
(define (corruption-score line)
(define stack (make-q))
(let loop [(els line)]
(if (null? els) 0
(let* [(el (car els))
(close? (member el closing eq?))
(els (cdr els))]
(cond
[(and close? (match? (q-front stack) el))
(q-pop! stack) (loop els)]
[close?
;;(format #t "~a x ~a : ~a~%" (q-front stack) el els)
(hash-ref score-table el)]
[else
(q-push! stack el) (loop els)])))))
(define (complete stack)
(let loop [(open (q-pop! stack))
(score 0)]
(let [(new-score (+ (* 5 score)
(hash-ref complete-score-table (find-closing open))))]
(if (q-empty? stack)
new-score
(loop (q-pop! stack) new-score)))))
(define (autocomplete-score line)
(define stack (make-q))
(let loop [(els line)]
(if (null? els)
(complete stack)
(let* [(el (car els))
(close? (member el closing eq?))
(els (cdr els))]
(cond
[(and close? (match? (q-front stack) el))
(q-pop! stack) (loop els)]
[close? 0];;corrupted
[else
(q-push! stack el) (loop els)])))))
(define (problem-1 input)
(fold + 0 (map corruption-score input)))
(define (problem-2 input)
(let [(scores (filter positive? (map autocomplete-score input)))]
(list-ref (sort scores <)
(floor (/ (length scores) 2)))))
(format #t "~a~%~a~%" (problem-1 in) (problem-2 in))
push · 2021-12-10 13:00
cr
module Aoc10
extend Aoc10
abstract class Solver
def initialize(@data : Array(Array(Char)))
end
def matching(c)
c.to_s.tr("([{<>}])", ")]}><{[{")[0]
end
abstract def run
def check_lines
stack = [] of Char
@data.map do |line|
stack.clear
res = line.find do |c|
if "([{<".includes? c
stack.push(c) && false
else
matching(stack.pop?) != c
end
end
res || stack.dup
end
end
end
class Part1 < Solver
Scores = {')' => 3, ']' => 57, '}' => 1197, '>' => 25137}
def run
check_lines
.sum { |e| e.is_a?(Char) ? Scores[e] : 0 }
end
end
class Part2 < Solver
def run
check_lines
.map do |line|
line.is_a?(Char) ? nil : line.map { |c| matching(c).not_nil! }
end
.compact
.map(&.reverse)
.map do |line|
line.reduce(0_u64) do |m, c|
m *= 5
m += ".)]}>".index(c).not_nil!
end
end
.sort
.mid
end
end
end
class Array
def mid
return self[(self.size / 2).to_i]
end
end
input = File.read_lines("./../10.txt").map(&.chars)
puts "Silver: #{Aoc10::Part1.new(input).run}"
puts "Gold: #{Aoc10::Part2.new(input).run}"
for some reason much cleaner than the previous days.
swap · 2021-12-10 13:44
>>28888
Any special reason why you're reversing the input lines?
Delilah · 2021-12-10 14:05
>>28891
that is a general function that I copy paste across most AoC challenges. I need to reverse because I am appending to the begging of result list so you need to reverse it in the end to get the original ordering....maybe I should just string-split the input on \n now that I think about it.
Project 2501 · 2021-12-10 16:25
>>28889
what is this language?
let · 2021-12-10 18:36
>>28896
There are only a handful of lains posting solutions, and the languages they're using all look different from each other and have been named several times. You really can crack this one on your own, I think.
Actually, it's pretty much down to three right now, as even Tcl lain hasn't posted these past few days it seems. Consider yourselves cheered on, everyone!
Cereal Killer · 2021-12-10 23:32
>>28896
The other lain has a point, but I'll still gladly tell you that it's crystal-lang.org. The people at manas basically wondered what would happen if you where to recreate ruby as frontend for clang.
Tooling is still kind of sparse, I use two vim plugins along with the scry lsp client. I find unit tests to be very helpful for the aoc, i always define the parts as tests and work the to completion.
Junichi Fujiwara · 2021-12-11 08:26
numpy MVP as usual
import numpy as np
def invalid(o, x, y):
return x < 0 or x >= len(o) or y < 0 or y >= len(o[0])
def flash(o, x, y, f):
for i in range(-1, 2):
for j in range(-1, 2):
if i == j == 0 or invalid(o, x + i, y + j) or o[x + i, y + j] == 10:
continue
o[x + i, y + j] += 1
if o[x + i, y + j] == 10:
f.append([x + i, y + j])
flashes = 0
octopi = np.genfromtxt('input.txt', delimiter=1, dtype=int)
gold = 0
while True:
octopi += 1
gold += 1
flashers = np.argwhere(octopi == 10).tolist()
delta_flashes = 0
while flashers:
curr = flashers.pop()
flash(octopi, curr[0], curr[1], flashers)
delta_flashes += 1
octopi %= 10
if gold <= 100:
flashes += delta_flashes
if delta_flashes == 100:
break
print(flashes)
print(gold)
Linda Lee · 2021-12-11 12:08
here is mine
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define (make-objects rows cols matrix)
(list
(lambda (row col . dflt)
;;getter
(let [(fail (if (null? dflt) #f (car dflt)))]
(cond
[(< row 0) fail]
[(>= row rows) fail]
[(< col 0) fail]
[(>= col cols) fail]
[else (vector-ref matrix (+ col (* row cols)))])))
(lambda (row col val)
;;setter
(when (and (>= row 0) (< row rows) (>= col 0) (< col cols))
(let [(index (+ col (* row cols)))]
(if (procedure? val)
(vector-set! matrix index (val (vector-ref matrix index)))
(vector-set! matrix index val)))))))
(define (in)
(let* [(data (get-lines "input"
(lambda (line)
(map (lambda (chr-num) (- (char->integer chr-num) 48))
(string->list line)))))
(rows (length data))
(cols (length (car data)))
(matrix (make-vector (* rows cols) 0))]
(for-each (lambda (row i)
(for-each (lambda (n j) (vector-set! matrix (+ j (* i cols)) n))
row
(iota cols)))
data
(iota rows))
(append (list rows cols matrix) (make-objects rows cols matrix))))
(define (step rows cols m-get m-set!)
(match-let [((flash-get flash-set!) (make-objects rows cols (make-vector (* rows cols) #f)))]
(letrec [(propagate
(lambda (i j)
(when (and (< 9 (m-get i j -1)) (not (flash-get i j)))
(flash-set! i j #t);;mark as already flashed in this step
(m-set! (1+ i) j 1+)
(propagate (1+ i) j)
(m-set! (1- i) j 1+)
(propagate (1- i) j)
(m-set! (1+ i) (1+ j) 1+)
(propagate (1+ i) (1+ j))
(m-set! (1- i) (1- j) 1+)
(propagate (1- i) (1- j))
(m-set! (1+ i) (1- j) 1+)
(propagate (1+ i) (1- j))
(m-set! (1- i) (1+ j) 1+)
(propagate (1- i) (1+ j))
(m-set! i (1+ j) 1+)
(propagate i (1+ j))
(m-set! i (1- j) 1+)
(propagate i (1- j)))))]
(do [(i 0 (1+ i))]
[(= i rows)]
(do [(j 0 (1+ j))]
[(= j cols)]
(m-set! i j 1+)
(when (< 9 (m-get i j))
(propagate i j))))
(do [(i 0 (1+ i))
(flashed 0)]
[(= i rows) flashed]
(do [(j 0 (1+ j))]
[(= j cols)]
(when (< 9 (m-get i j))
(set! flashed (1+ flashed))
(m-set! i j 0)))))))
(define (problem-1 rows cols matrix m-get m-set!)
(fold + 0 (map (lambda (_) (step rows cols m-get m-set!)) (iota 100))))
(define (problem-2 rows cols matrix m-get m-set!)
(do [(counter 1 (1+ counter))
(flashed (step rows cols m-get m-set!) (step rows cols m-get m-set!))]
[(= flashed (* rows cols)) counter]))
(format #t "~a~%~a~%" (apply problem-1 (in)) (apply problem-2 (in)))
Arisu Mizuki · 2021-12-12 00:41
Didn't have time today, but I hope to publish my crystal tomorrow
Pulse · 2021-12-12 07:34
Had some extra time today so I wrote it out in cpp, I just brute-forced the gold star so I'm curious to see what you guys came up with
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
using std::cout; using std::ifstream;
using std::set; using std::map;
using std::string; using std::vector;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
// Just store the Node's id, neighbord, and if it's big or not
class node
{
public:
string id;
bool is_big;
vector<string> neighbors;
node(const string &id) : id(id)
{
is_big = std::all_of(id.begin(), id.end(),
[](unsigned char c){ return std::isupper(c); });
}
node() : node("") {}
void add_neighbor(const string &s) { neighbors.push_back(s); }
bool can_repeat() const { return !is_big and id != "start" and id != "end"; }
bool operator <(const node &rhs) const { return id < rhs.id; }
};
void find_paths(const node &curr, string path, map<string, node> nodes,
set<string> &paths, map<string, size_t> &visited, const string& repeat)
{
auto updated_path = path + curr.id + ",";
if(curr.id == "end")
{
paths.emplace(updated_path);
return;
}
++visited[curr.id];
for(const auto &s : curr.neighbors)
{
auto n = nodes[s];
if((!n.is_big && visited[s] > 0 && s != repeat) ||
(s == repeat && visited[s] > 1))
continue;
find_paths(n, updated_path, nodes, paths, visited, repeat);
}
--visited[curr.id];
}
int main()
{
vector<string> input = read_input("input.txt");
// Build "graph"
map<string, node> nodes;
map<string, size_t> visited;
for(const auto &s : input)
{
auto dash = s.find("-");
auto s0 = s.substr(0, dash);
auto s1 = s.substr(dash + 1, s.length());
if(nodes.find(s0) == nodes.end())
nodes[s0] = node(s0);
if(nodes.find(s1) == nodes.end())
nodes[s1] = node(s1);
nodes[s0].add_neighbor(s1);
nodes[s1].add_neighbor(s0);
visited[s0] = 0;
visited[s1] = 0;
}
// Output silver
set<string> paths;
find_paths(nodes["start"], "", nodes, paths, visited, "");
cout << paths.size() << "\n";
// Output gold
set<string> gold_paths;
for(const auto &p : nodes)
{
if(!p.second.can_repeat())
continue;
find_paths(nodes["start"], "", nodes, gold_paths, visited, p.first);
}
cout << gold_paths.size() << "\n";
return 0;
}
Dujour · 2021-12-12 10:43
DFS worked out nicely for me today.
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define paths
(let [(path-table (make-hash-table))]
(get-lines "input"
(lambda (line)
(match-let* [((src dest) (string-split line #\-))
(src-paths (hash-ref path-table src '()))
(dest-paths (hash-ref path-table dest '()))]
(hash-set! path-table src (cons dest src-paths))
(unless (or (string= src "start") (string= dest "end"))
(hash-set! path-table dest (cons src dest-paths))))))
path-table))
(define (dfs-count node path)
(cond [(string= node "end")
1]
[(and (char-lower-case? (string-ref node 0))
(find (lambda (p) (string= p node)) path))
0]
[else (fold + 0 (map (lambda (new-node) (dfs-count new-node (cons node path)))
(hash-ref paths node)))]))
(define (dfs-count2 node path small-2nd?)
(cond [(string= node "end")
1]
[(and small-2nd?
(char-lower-case? (string-ref node 0))
(find (lambda (p) (string= p node)) path))
0]
[(and (not small-2nd?)
(char-lower-case? (string-ref node 0))
(find (lambda (p) (string= p node)) path))
(fold + 0 (map (lambda (new-node) (dfs-count2 new-node (cons node path) #t))
(hash-ref paths node)))]
[else (fold + 0 (map (lambda (new-node) (dfs-count2 new-node (cons node path) small-2nd?))
(hash-ref paths node)))]))
(format #t "~a~%~a~%" (dfs-count "start" '()) (dfs-count2 "start" '() #f))
if · 2021-12-13 06:50
This one was fun, I had forgotten about the story prompt so the gold star was a nice surprise.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <set>
using std::cout; using std::ifstream;
using std::stoi; using std::set;
using std::string; using std::vector;
using std::pair; using std::array;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
pair<int,int> fold(pair<int,int> const &p, int line, bool along_x)
{
pair<int,int> ret;
if(along_x && p.first > line)
{
ret.first = line - (p.first - line);
ret.second = p.second;
} else if(!along_x && p.second > line) {
ret.second = line - (p.second - line);
ret.first = p.first;
} else {
ret.first = p.first;
ret.second = p.second;
}
return ret;
}
int main()
{
vector<string> input = read_input("input.txt");
// Parse input
set<pair<int,int>> dots;
vector<string> instructions;
for(auto const &s : input)
{
auto comma = s.find(",");
if(comma != string::npos)
dots.emplace(pair(stoi(s.substr(0, comma)),
stoi(s.substr(comma + 1, s.length()))));
else if(s.length())
instructions.push_back(s);
}
// Do the folds
size_t inst_count = 0;
for(auto const &inst : instructions)
{
bool along_x = inst.find("x") != string::npos;
int val = stoi(inst.substr(inst.find("=") + 1, inst.length()));
set<pair<int,int>> new_dots;
for(auto const &d : dots)
new_dots.emplace(fold(d, val, along_x));
dots = new_dots;
// Silver star
if(!inst_count)
cout << dots.size() << "\n";
++inst_count;
}
// Gold star
array<array<int, 40>, 6> arr{};
for(auto const &d : dots)
arr[d.second][d.first] = 1;
for(auto const &r : arr)
{
for(auto const &c : r)
cout << c;
cout << "\n";
}
return 0;
}
Lunch · 2021-12-13 11:32
I had some fold problems that didn't show up in first star so debugging was fun.
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43) (ice-9 receive))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define in
(let loop [(lines (get-lines "input" identity))
(dots '()) (instructions '())]
(cond
[(null? lines)
(cons (reverse instructions) (reverse dots))]
[(= 0 (string-length (car lines)))
(loop (cdr lines) dots instructions)]
[(eq? #\f (string-ref (car lines) 0))
(match-let* [((fold over cmd) (string-split (car lines) #\space))
((axis value) (string-split cmd #\=))
(axis-sym (string->symbol axis))
(value-n (string->number value))]
(loop (cdr lines)
dots
(cons (list axis-sym value-n) instructions)))]
[else (loop (cdr lines)
(cons (map string->number (string-split (car lines) #\,)) dots)
instructions)])))
(define (bounds dots) (apply map max dots))
(define (split dots cmd)
(match-let [((axis value) cmd)]
(receive (smaller bigger)
(partition (match-lambda
[(x-val y-val)
(if (eq? axis 'x)
(< x-val value)
(< y-val value))])
dots)
(cons smaller bigger))))
(define (dot-eq? dot1 dot2)
(and (= (list-ref dot1 0) (list-ref dot2 0))
(= (list-ref dot1 1) (list-ref dot2 1))))
(define (fold-once instruction dots)
(match-let* [((x-max y-max) (bounds dots))
((axis value) instruction)
((keep-dots . fold-dots) (split dots instruction))
(folded-dots (map (match-lambda
[(x-val y-val)
(if (eq? axis 'x)
`(,(- (* 2 value) x-val) ,y-val)
`(,x-val ,(- (* 2 value) y-val)))])
fold-dots))]
(lset-union dot-eq? keep-dots folded-dots)))
(define (dot-print dots)
(match-let* [((x-max y-max) (bounds dots))
(x-size (1+ x-max))
(y-size (1+ y-max))
(vec (make-vector (* x-size y-size) " "))]
(for-each (match-lambda
[(x y)
(vector-set! vec (+ x (* y x-size)) "#")])
dots)
(vector-for-each (lambda (i v)
(when (zero? (modulo i x-size))
(newline))
(display v))
vec)
(newline)))
(define (problem-1 instructions dots)
(length (fold-once (car instructions) dots)))
(define (problem-2 instructions dots)
(dot-print (fold fold-once dots instructions)))
(format #t "~a~%" (problem-1 (car in) (cdr in)))
(problem-2 (car in) (cdr in))
Neo · 2021-12-13 13:15
>get behind in AoC
>want to do it because I have a lot of free time at work
>spend 2 hours trying to figure out why my program isn't working
>it was, I just entered the solution incorrectly 2 times
AHHHHHHHHHHHHHHHHHHHHHHHHHHH!!!!!!!!!!!! I'm just using C++ with the STL and it makes me realize why boost bloat is so popular. it's kinda soykaf for text input, somehow even more of a pain to use than plain C.
The Oracle · 2021-12-13 14:39
Day 13. I've missed the previous 2 because of real life, tried to rush them after the fact but only got incomplete solutions. Solution doesn't pay attention to resulting matrix sizes after splitting the matrix by the folding line.
#!/usr/bin/env python3
import sys
import re
import collections
def print_bool_matrix(matrix):
print(f'{width} by {height} matrix')
print('\n'.join([ ' '.join([ '#' if e else '.' for e in row ]) for row in matrix ]))
matrix = []
width = 0
height = 0
folding = False
for line in sys.stdin:
if line == '\n':
folding = True
continue
# first part
if not folding:
# get points
x, y = [ int(group) for group in re.match(r'(\d+),(\d+)', line).groups() ]
# resize if needed
if y + 1 > height:
for i in range(y + 1 - height):
matrix.append([False] * width)
height = y + 1
if x + 1 > width:
for row in matrix:
for i in range(x + 1 - width):
row.append(False)
width = x + 1
matrix[y][x] = True
# second part
else:
axis, stop = [ group for group in re.match(r'fold along ([xy])=(\d)', line).groups() ]
horizontal = 'y' in axis
stop = int(stop)
print(f'fold by {axis}={stop}')
if horizontal:
# make horizontal fold
up = matrix[0:stop]
bottom = matrix[stop + 1:]
bottom = bottom[::-1]
# save fold
matrix = [ [ u or b for u, b in zip(up_rows, bottom_rows) ] for up_rows, bottom_rows in zip(up, bottom) ]
height = stop
else:
# make vertical fold
left = [ row[0:stop] for row in matrix ]
right = [ row[stop + 1:] for row in matrix ]
right = [ row[::-1] for row in right ]
# save fold
matrix = [ [ l or r for l, r in zip(left_rows, right_rows) ] for left_rows, right_rows in zip(left, right) ]
width = stop
visible = sum([ len([ e for e in row if e ]) for row in matrix ])
print(f'there are {visible} visible dots')
print_bool_matrix(matrix)
cond · 2021-12-13 14:50
>>28903
thank you lain! consider yourself heartily thanked
bit · 2021-12-14 06:08
Maps go brrr
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
using std::cout; using std::ifstream;
using std::stoi; using std::string;
using std::vector; using std::map;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
void apply_rules(map<string,size_t> &b, const map<string,string> &r)
{
map<string,size_t> delta_map;
for(auto const& p : r)
{
if(b.find(p.first) == b.end())
continue;
delta_map[p.first[0] + p.second] += b[p.first];
delta_map[p.second + p.first[1]] += b[p.first];
b[p.first] = 0;
}
for(auto const &p : delta_map)
b[p.first] += p.second;
}
int main()
{
vector<string> input = read_input("input.txt");
// Parse input
auto formula = input[0];
map<string,size_t> bigrams;
map<string,string> rules;
for(auto const &s : input)
{
if(s.find("-") == string::npos)
continue;
rules[s.substr(0, 2)] = s.substr(6, 7);
}
for(size_t i = 0; i != formula.length() - 1; ++i)
++bigrams[formula.substr(i, 2)];
++bigrams[formula.substr(formula.length() - 1, 1)];
// Do 40 steps of the formula
for(size_t i = 0; i != 40; ++i)
apply_rules(bigrams, rules);
// Get counts
map<char,size_t> counts;
for(auto const &p : bigrams)
counts[p.first[0]] += p.second;
size_t max = 0;
size_t min = (size_t) - 1;
for(auto const &p : counts)
{
max = p.second > max ? p.second : max;
min = p.second < min ? p.second : min;
}
cout << max - min << "\n";
return 0;
}
swap · 2021-12-14 06:11
>>28943
I agree that parsing text with C++ feels awful, making it not feel fragile takes way too much work, but at least I don't have to use strtok.
setq · 2021-12-14 06:34
(anonymous user #388839) reporting in, maps are fun:
(defpackage #:advent2021.day14
(:use #:cl #:alexandria #:cl-ppcre #:advent2021.util)
(:export #:solve-part-1 #:solve-part-2))
(in-package #:advent2021.day14)
(with-puzzle-file (stream)
(defparameter +template+
(read-line stream))
(read-line stream)
(defparameter +rules+
(loop :with table := (make-hash-table :test 'equal)
:for line := (read-line stream nil)
:while line
:for (pair result) := (split " -> " line)
:do (setf (gethash (cons (char pair 0) (char pair 1))
table)
(char result 0))
:finally (return table))))
(defun template-table (template)
"Encode the template as a table of the count of element pairs."
(loop :with pair-table := (make-hash-table :test 'equal)
;; Iterate across each pair of characters in the template (e.g. for the
;; template "NNCB", (a b) is bound to (#\N #\N), (#\N #\C), then (#\C
;; #\B).
;; By encoding the table as a count of all pairs, subsequent
;; generations only need to apply a rule twice for each unique pair,
;; instead of applying them once for each pair that exists.
:for (a b) :on (coerce template 'list)
:while b
:do (incf (gethash (cons a b) pair-table 0))
:finally (return pair-table)))
(defun apply-rules (pair-table)
"Return a fresh pair table generated by applying the problem-set rules on
each pair."
(loop :with next-generation := (make-hash-table :test 'equal)
:for (a . b) :being :the :hash-keys :of pair-table
:using (hash-value count)
:for c := (gethash (cons a b) +rules+)
:do (incf (gethash (cons a c) next-generation 0) count)
(incf (gethash (cons c b) next-generation 0) count)
:finally (return next-generation)))
(defun count-elements (pair-table)
"Return a table of the count of all elements."
(loop :with freq-table := (make-hash-table)
:with template := (coerce +template+ 'list)
:for (a . b) :being :the :hash-keys :of pair-table
:using (hash-value count)
;; "Count" each element in each pair only half as often, as they appear
;; encoded as pairs twice as much than encoded as a string.
:do (incf (gethash a freq-table 0) (/ count 2))
(incf (gethash b freq-table 0) (/ count 2))
:finally (progn
;; Bump up the first and last elements, as they're only
;; counted once.
(incf (gethash (first template) freq-table) 1/2)
(incf (gethash (car (last template)) freq-table) 1/2)
(return freq-table))))
(defun element-range (freq-table)
"Return the difference of the frequencies of the most common and least common
elements."
(loop :for elem :being :the :hash-keys :of freq-table
:using (hash-value count)
:maximize count :into highest
:minimize count :into lowest
:finally (return (- highest lowest))))
(defun solve (n-generations)
(loop :with polymer := (template-table +template+)
:repeat n-generations
:do (setf polymer (apply-rules polymer))
:finally (return (element-range (count-elements polymer)))))
(defun solve-part-1 ()
(solve 10))
(defun solve-part-2 ()
(solve 40))
Lainchan leaderboard 388839-baef610d
The Phantom Phreak · 2021-12-14 09:31
Day 14. (map max part 2)
#!/usr/bin/env python3
import sys
import re
def inc(dictionary, key):
try:
dictionary[key] += 1
except KeyError:
dictionary[key] = 1
polymer = {}
template = input()
for p in template:
inc(polymer, p)
rules = {}
for line in sys.stdin:
if line == '\n': continue
source, target = [ group for group in re.match(r'([a-zA-Z]{2}) -> ([a-zA-Z])', line).groups() ]
rules[source] = target
for i in range(int(sys.argv[1]) if len(sys.argv) > 1 else 16):
template_new = []
prev = template[0]
template_new.append(prev)
for c in template[1:]:
insertion = rules[prev + c]
inc(polymer, insertion)
template_new.append(insertion)
template_new.append(c)
prev = c
print(f'at step {i}: {template} -> {"".join(template_new)} {polymer}')
template = ''.join(template_new)
l = min([polymer[p] for p in polymer.keys()])
m = max([polymer[p] for p in polymer.keys()])
print(f'the most common element ({m}) minus the least common element ({l}) yields a difference of {m - l}')
J.C. Denton · 2021-12-14 09:36
I'm digging the lispers, the code always looks so clean. Thanks >>28961 in particular for keeping it so readable.
Koos van der Merwe · 2021-12-14 14:58
>>28961
Guile can into maps as well...but without your fancy loops, recursion serves well enough :) Now I see how should I have done the counting, I didn't really think that trough it seems.
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43) (ice-9 receive))
(define (get-input filename)
(call-with-input-file filename
(lambda (port)
(let [(init (string->list (read-line port)))
(trans (lambda (line)
(match-let* [((pair _ mid) (string-split line #\space))
(key (string->list pair))
(val (string-ref mid 0))]
(cons key (list (list (first key) val)
(list val (last key)))))))]
(read-line port) ;;skip empty line
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(cons init (reverse lines))
(loop (read-line port) (cons (trans line) lines))))))))
(define (in)
(let* [(input (get-input "input"))
(polymer-lst (car input))
(extensions (alist->hash-table (cdr input)))
(polymer (make-hash-table))
(increment (lambda (el) (hash-set! polymer el
(1+ (hash-ref polymer el 0)))))]
(for-each (lambda (a b) (increment (list a b)))
polymer-lst (cdr polymer-lst))
(list polymer extensions)))
(define (extend-polymer polymer extensions)
(let* [(new-polymer (alist->hash-table (hash-map->list cons polymer)))
(increment (lambda (val el)
(hash-set! new-polymer el
(max 0 (+ val (hash-ref new-polymer el 0))))))]
(hash-for-each-handle (match-lambda
[(pair . count)
(when (hash-ref extensions pair)
(increment (* -1 count) pair))
(for-each (lambda (key) (increment count key))
(hash-ref extensions pair))])
polymer)
new-polymer))
(define (score polymer)
(let* [(counted (make-hash-table))
(increment (lambda (el val) (hash-set! counted el
(+ val (hash-ref counted el 0)))))]
(hash-for-each-handle (lambda (el) (increment (caar el) (cdr el)))
polymer)
(let [(sorted (sort (hash-map->list cons counted)
(lambda (a b) (> (cdr a) (cdr b)))))]
(- (cdr (car sorted)) (cdr (last sorted))))))
(define (strength step-limit polymer extensions)
(do [(step 0 (1+ step))
(polymer polymer (extend-polymer polymer extensions))]
[(= step-limit step)
(1+ (score polymer))])) ;; +1 because the last character is the most common one and it gets ignored during count
(format #t "~a~%~a~%" (apply strength 10 (in)) (apply strength 40 (in)))
Jet Black · 2021-12-15 07:24
Gotta admit it took me a while to realize I didn't have to update priorities in an already full queue, and could just start with an empty one.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <limits>
#include <queue>
using std::cout; using std::ifstream;
using std::priority_queue; using std::string;
using std::vector; using std::pair;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
class node
{
public:
size_t x, y;
int d;
node(size_t x, size_t y, int d) : x(x), y(y), d(d) {}
node() : node(0, 0, 0) {}
bool operator <(const node& rhs) const { return d > rhs.d; }
};
void extend_map(vector<vector<int>> &d)
{
vector<vector<int>> big;
for(size_t i = 0; i != d.size() * 5; ++i)
big.push_back(vector<int>(d[0].size() * 5,
std::numeric_limits<int>::max()));
d = big;
}
int smart_get(const vector<vector<int>> &r, size_t x, size_t y)
{
auto ret = r[x % (r.size())][y % (r[0].size())];
ret += x / r.size() + y / r[0].size();
if(ret > 9)
ret = (ret % 10) + 1;
return ret;
}
vector<pair<size_t,size_t>> get_neighbors(size_t x, size_t y,
size_t lim_x, size_t lim_y)
{
vector<pair<size_t,size_t>> ret;
if(x != 0) { ret.push_back(pair(x - 1, y)); }
if(x != lim_x - 1) { ret.push_back(pair(x + 1, y)); }
if(y != 0) { ret.push_back(pair(x, y - 1)); }
if(y != lim_y - 1) { ret.push_back(pair(x, y + 1)); }
return ret;
}
int dijkstra(vector<vector<int>> &d, const vector<vector<int>> &r)
{
priority_queue<node> q;
q.push(node(0, 0, 0));
d[0][0] = 0;
while(!q.empty())
{
auto n = q.top();
auto curr_d = d[n.x][n.y];
q.pop();
for(auto const &p : get_neighbors(n.x, n.y, d.size(), d[0].size()))
{
auto risk = smart_get(r, p.first, p.second);
auto new_d = curr_d + risk;
if(new_d < d[p.first][p.second])
{
d[p.first][p.second] = new_d;
q.push(node(p.first, p.second, new_d));
}
}
}
return d[d.size() - 1][d[0].size() - 1];
}
int main()
{
vector<string> input = read_input("input.txt");
// Parse input
vector<vector<int>> dist;
vector<vector<int>> risk;
for(auto const &s : input)
{
vector<int> new_r;
for(size_t j = 0; j != s.length(); ++j)
new_r.push_back(s[j] - '0');
risk.push_back(new_r);
dist.push_back(vector<int>(s.length(), std::numeric_limits<int>::max()));
}
// Silver
cout << dijkstra(dist, risk) << "\n";
// Gold
extend_map(dist);
cout << dijkstra(dist, risk) << "\n";
return 0;
}
Bob Arctor · 2021-12-15 10:30
Day 15. very classic problem today
#!/usr/bin/env python3
import sys
from queue import PriorityQueue
from dataclasses import dataclass
class Bounds:
width = 0
height = 0
@dataclass(order=True)
class Pos:
x: int
y: int
def bound(pos):
return pos.x >= 0 and pos.y >= 0 and pos.x < Bounds.width and pos.y < Bounds.height
def adjacent(pos):
adjacents = []
for i in range(-1, 2):
for j in range(-1, 2):
if i == j or i == -j: continue
if bound(Pos(pos.x + j, pos.y + i)):
adjacents.append(Pos(pos.x + j, pos.y + i))
return adjacents
risks = []
costs = []
visited = []
for line in sys.stdin:
risks.append([int(r) for r in line.rstrip('\n')])
costs.append([sys.maxsize for r in line.rstrip('\n')])
visited.append([False for r in line.rstrip('\n')])
l = len(line.rstrip('\n'))
if l > Bounds.width: Bounds.width = l
h = len(risks)
if h > Bounds.height: Bounds.height = h
q = PriorityQueue()
costs[0][0] = 0
risks[0][0] = 0
q.put((risks[0][0], Pos(0, 0)))
while not q.empty():
cost, pos = q.get()
visited[pos.y][pos.x] = True
for a in adjacent(pos):
new_cost = cost + risks[a.y][a.x]
if new_cost < costs[a.y][a.x]: costs[a.y][a.x] = new_cost
if not visited[a.y][a.x]:
q.put((new_cost, a))
# results
print(f'risk (cost so far)')
print('\n'.join(['\t'.join([f'{c}({risks[y][x]})' for x, c in enumerate(row)]) for y, row in enumerate(costs)]))
print(f'{costs[Bounds.height - 1][Bounds.width - 1]} is the lowest total risk of any path from top left (0, 0) to bottom right ({Bounds.height - 1}, {Bounds.width - 1})')
Fred Nurk · 2021-12-15 10:59
A* was fun, matrix wrangling not so much.
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 hash-table) (srfi srfi-1) (srfi srfi-43) (ice-9 receive))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define (make-objects rows cols matrix)
(list
(lambda (row col . dflt)
;;getter
(let [(fail (if (null? dflt) #f (car dflt)))]
(cond
[(< row 0) fail]
[(>= row rows) fail]
[(< col 0) fail]
[(>= col cols) fail]
[else (vector-ref matrix (+ col (* row cols)))])))
(lambda (row col val)
;;setter
(when (and (>= row 0) (< row rows) (>= col 0) (< col cols))
(let [(index (+ col (* row cols)))]
(if (procedure? val)
(vector-set! matrix index (val (vector-ref matrix index)))
(vector-set! matrix index val)))))))
(define (in)
(match-let* [(data (get-lines "input"
(lambda (line)
(map (lambda (chr-num) (- (char->integer chr-num) 48))
(string->list line)))))
(rows (length data))
(cols (length (car data)))
(matrix (make-vector (* rows cols) 0))
((matrix-get matrix-set!) (make-objects rows cols matrix))
(not-visited (make-vector (* rows cols) #t))
((not-visited-get not-visited-set!) (make-objects rows cols not-visited))]
;;set matrix values
(for-each (lambda (row i)
(for-each (lambda (n j) (matrix-set! i j n)) row (iota cols)))
data
(iota rows))
(list rows cols matrix-get not-visited-set! not-visited-get)))
(define (for-each-matrix rows cols proc)
(for-each (lambda (row)
(for-each (lambda (col) (proc row col))
(iota cols)))
(iota rows)))
(define (new-val old add)
(fold (lambda (add old) (if (= 10 (+ add old)) 1 (+ add old)))
old (make-list add 1)))
(define (in2 rows cols oldmatrix-get)
(define (compute-new row col add)
(new-val (oldmatrix-get row col) add))
(match-let* [(new-rows (* 5 rows))
(new-cols (* 5 cols))
(matrix (make-vector (* new-rows new-cols) 0))
(not-visited (make-vector (* new-rows new-cols) #t))
((matrix-get matrix-set!) (make-objects new-rows new-cols matrix))
((not-visited-get not-visited-set!) (make-objects new-rows new-cols not-visited))]
(for-each-matrix
rows cols
(lambda (row col)
(for-each-matrix
5 5
(lambda (i j) (matrix-set! (+ row (* i rows)) (+ col (* j cols)) (compute-new row col (+ i j)))))))
(list new-rows new-cols matrix-get not-visited-set! not-visited-get)))
(define (A*-search rows cols matrix visit! not-visited?)
(define goal `(,(1- rows) ,(1- cols)))
(define (heuristic pos)
;;Manhattan distance as heuristic
(+ (- (first goal) (first pos))
(- (second goal) (second pos))))
(define (insert-front pos path cost front)
(let* [(h (heuristic pos))
(f (+ cost h))
;;(new-path (cons pos path)) ;;remeber path
(new-path (list pos)) ;;don't track path
(new-node (list f cost new-path))]
(let loop [(front front)]
(match front
['() (cons new-node '())]
[((fi costi pathi) . remain)
(if (<= fi f)
(cons (car front) (loop remain))
(cons new-node front))]))))
(let A* [(front (insert-front '(0 0) '() 0 '()))]
(match-let* [(((f cost path) . remain) front)
(pos (car path))
((row col) pos)]
(if (zero? (heuristic pos))
(cons cost path)
(A* (fold (lambda (pos front)
(if (and (apply matrix pos) (apply not-visited? pos))
(begin
(visit! (first pos) (second pos) #f)
(insert-front pos path (+ cost (apply matrix pos)) front))
front))
remain
(list `(,(1+ row) ,col) `(,(1- row) ,col) `(,row ,(1+ col)) `(,row ,(1- col)))))))))
(let* [(input (in))
(input2 (apply in2 (list-head input 3)))]
(format #t "~a~%~a~%" (car (apply A*-search input)) (car (apply A*-search input2))))
Drifter · 2021-12-15 11:53
>>28981
Now I see I should have just wrapped the calls to matrix instead of generating the huge one. It still wasn't that slow despite this blunder.
Jane Doe · 2021-12-15 13:41
>>28983
I've redid my code (>>28981) to use smart_get like from >>28976 expecting speed up but it actually made things slower for 1s.
(define (in2 rows cols oldmatrix-get)
(match-let* [(new-rows (* 5 rows))
(new-cols (* 5 cols))
((not-visited-get not-visited-set!)
(make-objects new-rows new-cols (make-vector (* new-rows new-cols) #t)))
(new-matrix-get (lambda (row col)
(if (or (>= row new-rows) (>= col new-cols)
(negative? row) (negative? col))
#f
(let* [(val (oldmatrix-get (modulo row rows)
(modulo col cols)))
(new-val (+ val (floor (/ row rows))
(floor (/ col cols))))]
(if (> new-val 9) (1+ (modulo new-val 10)) new-val)))))]
(list new-rows new-cols new-matrix-get not-visited-set! not-visited-get)))
Dozer · 2021-12-16 06:46
Hello lains, parsing is probably my favorite type of puzzple. Here's my Day 16:
(defpackage #:advent2021.day16
(:use #:cl #:alexandria #:cl-ppcre #:queues #:advent2021.util)
(:export #:solve-part-1 #:solve-part-2))
(in-package #:advent2021.day16)
(defun hex2val (c)
(if (<= (char-code #\0) (char-code c) (char-code #\9))
(- (char-code c) (char-code #\0)) ;; 0..9
(- (char-code (char-upcase c)) (char-code #\A) -10))) ;; A..F
(defparameter +input+
;; Flatten the hex puzzle input into a bit-array.
(loop :with line := (with-puzzle-file (stream)
(read-line stream))
:with array := (make-array (* 4 (length line))
:element-type 'bit)
:for c :across line
:for nybble := (hex2val c)
:for i :upfrom 0 :by 4
:do (setf (aref array i) (logand 1 (ash nybble -3)))
:do (setf (aref array (+ 1 i)) (logand 1 (ash nybble -2)))
:do (setf (aref array (+ 2 i)) (logand 1 (ash nybble -1)))
:do (setf (aref array (+ 3 i)) (logand 1 (ash nybble -0)))
:finally (return array)))
(defconstant +literal-type+ 4)
(defmacro defgetf-accessor (name indicator)
"Define an inline-desirable wrapper function around getf."
`(progn
(declaim (inline ,name))
;; XXX this macro leaks symbols NEW-VALUE and PLACE to the INDICATOR
;; param. I'd rather not change it to use gensyms though, because I want
;; those symbol names to be present in the function documentation, and the
;; indicators are only keywords in practice anyways.
(defun ,name (place)
(getf place ,indicator))
(defun (setf ,name) (new-value place)
(setf (getf place ,indicator) new-value))))
(defun make-reader ()
(list :pos 0))
(defgetf-accessor pos :pos)
(defun make-packet (version type payload)
(list :version version :type type
(if (= type +literal-type+)
:value :subpackets) payload))
(defgetf-accessor ptype :type)
(defgetf-accessor version :version)
(defgetf-accessor value :value)
(defgetf-accessor subpackets :subpackets)
(defun read-bits (reader size)
(loop :with start := (pos reader)
:with result := 0
:for i :from start :below (+ start size)
:do (setf result (logior (ash result 1)
(aref +input+ i)))
:finally (incf (pos reader) size)
(return result)))
(defun read-literal (reader)
(loop :with result := 0
:for group := (read-bits reader 5)
:do (setf result (logior (ash result 4)
(logand group #b1111)))
:while (= (logand #b10000 group) #b10000)
:finally (return result)))
(defun read-packet (reader)
(let ((version (read-bits reader 3))
(type (read-bits reader 3)))
(cond
;; simple (literal-value) packet:
((= type 4)
(make-packet version type (read-literal reader)))
;; sized compound packet:
((= 0 (read-bits reader 1))
(make-packet version type
(loop :with end := (+ (read-bits reader 15)
(pos reader))
:collect (read-packet reader)
:until (= (pos reader) end)
:do (assert (not (> (pos reader) end))))))
;; sizeless compound packet:
(t (make-packet version type
(loop :repeat (read-bits reader 11)
:collect (read-packet reader)))))))
(defun sum-versions (packet)
(+ (version packet)
(reduce #'+ (mapcar #'sum-versions (subpackets packet)))))
(defun solve-part-1 ()
(sum-versions (read-packet (make-reader))))
(defun eval-packet (packet)
(etypecase (ptype packet)
;; n-ary arithmetic compound packet:
((integer 0 3)
(reduce (nth (ptype packet)
(list #'+ #'* #'min #'max))
(mapcar #'eval-packet (subpackets packet))))
;; simple (literal-value) packet:
((eql 4)
(value packet))
;; binary test compound packet:
((integer 5 7)
(if (funcall (nth (- (ptype packet) 5)
(list #'> #'< #'=))
(eval-packet (first (subpackets packet)))
(eval-packet (second (subpackets packet))))
1 0))))
(defun solve-part-2 ()
(eval-packet (read-packet (make-reader))))
Unknown · 2021-12-16 07:45
>>28984
Interesting, I wonder if inlining the smart_get in C++ would make it as fast as constructing the big table.
>>29019
For me it was the opposite, get ready for some real soykaf code lads:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <sstream>
using std::cout; using std::ifstream;
using std::vector; using std::pair;
using std::string;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
struct packet
{
unsigned long version, id, lit, l_id, l_sub;
vector<packet> subs;
};
unsigned long bit_to_int(const vector<bool> &b, size_t i, size_t l)
{
unsigned long ret = 0;
for(size_t j = 0, s = l - 1; j != l; ++j, --s)
ret += b[i + j] << s;
return ret;
}
// Returns how many bits were parsed for the packet at b[i],
// packet gets pushed into r, v is running version sum
size_t parse_packet(const vector<bool>& b, size_t i, vector<packet> &r,
unsigned long &v)
{
size_t parsed = 0;
packet p;
p.version = bit_to_int(b, i, 3);
p.id = bit_to_int(b, i + 3, 3);
parsed += 6;
switch(p.id)
{
// Literal
case 4:
{
size_t counter = i + 6;
unsigned long val = 0;
while(b[counter])
{
val += bit_to_int(b, counter + 1, 4);
val <<= 4;
counter += 5;
parsed += 5;
}
val += bit_to_int(b, counter + 1, 4);
parsed += 5;
p.lit = val;
break;
}
// Operator
default:
p.l_id = bit_to_int(b, i + 6, 1);
parsed += 1;
// Change how many bits we read based on length id
if(p.l_id)
{
p.l_sub = bit_to_int(b, i + 7, 11);
parsed += 11;
} else {
p.l_sub = bit_to_int(b, i + 7, 15);
parsed += 15;
}
// Keep reading until we have read n packages or n bits,
// insert into current packages subs vector
size_t sub_parsed = 0;
if(p.l_id)
{
for(size_t c = 0; c != p.l_sub; ++c)
sub_parsed += parse_packet(b, i + 18 + sub_parsed, p.subs, v);
} else {
auto idx = r.size() - 1;
while(sub_parsed < p.l_sub)
sub_parsed += parse_packet(b, i + 22 + sub_parsed, p.subs, v);
}
parsed += sub_parsed;
}
r.push_back(p);
v += p.version;
return parsed;
}
packet parse_bitstring(const vector<bool> &b, unsigned long &v)
{
vector<packet> ret;
parse_packet(b, 0, ret, v);
return ret[0];
}
unsigned long calculate(const packet &packet)
{
unsigned long ret = 0;
switch(packet.id)
{
// Lit
case 4:
ret = packet.lit;
break;
// Sum
case 0:
for(auto const &p : packet.subs)
ret += calculate(p);
break;
// Prod
case 1:
ret = 1;
for(auto const &p : packet.subs)
ret *= calculate(p);
break;
// Min
case 2:
ret -= 1;
for(auto const &p : packet.subs)
{
auto val = calculate(p);
if(val < ret)
ret = val;
}
break;
// Max
case 3:
for(auto const &p : packet.subs)
{
auto val = calculate(p);
if(val > ret)
ret = val;
}
break;
// GT
case 5:
ret = calculate(packet.subs[0]) > calculate(packet.subs[1]);
break;
// LT
case 6:
ret = calculate(packet.subs[0]) < calculate(packet.subs[1]);
break;
// EQ
case 7:
ret = calculate(packet.subs[0]) == calculate(packet.subs[1]);
}
return ret;
}
int main()
{
vector<string> input = read_input("input.txt");
// Parse input into bitstring
vector<bool> bitstring;
for(auto it = input[0].crbegin(); it != input[0].crend(); ++it)
{
string hexed = "0x";
hexed.push_back(*it);
std::stringstream ss;
ss << std::hex << hexed;
unsigned long num;
ss >> num;
for(size_t i = 0; i != 4; ++i)
{
bitstring.push_back(num & 1);
num >>= 1;
}
}
std::reverse(bitstring.begin(), bitstring.end());
// Parse the bitstring
unsigned long v_sum = 0;
auto packet = parse_bitstring(bitstring, v_sum);
// Silver
cout << v_sum << "\n";
// Gold
cout << calculate(packet) << "\n";
return 0;
}
Knuth · 2021-12-16 08:28
I hate this type of puzzles, I'll force myself to do it in the evening
⍕ · 2021-12-16 10:43
>>29019
Thas a nise pazzle in a nise lang
Judge Fang · 2021-12-16 21:55
That was much more fun than I expected
(use-modules (ice-9 rdelim) (ice-9 match) (srfi srfi-1))
(define (hex->bin hex)
(let* [(bin (number->string
(string->number (list->string `(,hex)) 16)
2))
(len (string-length bin))]
(if (= 4 len)
bin
(string-join (append (make-list (- 4 len) "0") `(,bin)) ""))))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define (convert line)
(string->list (string-join (map hex->bin (string->list line)) "")))
(define in (convert (car (get-lines "input" identity))))
;;helpers
(define (bits->number bits) (string->number (list->string bits) 2))
(define (1? bit) (char=? #\1 bit))
(define (0? bit) (not (1? bit)))
(define (packet->V-T-B packet)
(let* [(V-bits (list-head packet 3))
(T-bits (list-head (list-tail packet 3) 3))
(B-bits (list-tail packet 6))
(read-bits 6)]
(cons* read-bits (bits->number V-bits) (bits->number T-bits) B-bits)))
(define (type-4->num payload)
"Parse type 4 number and return it with remain and read bits"
(let loop [(payload payload)
(bin-num '())
(read-bits 0)]
(let* [(mark (car payload))
(part (list-head (cdr payload) 4))
(num (append bin-num part))
(remain (list-tail payload 5))]
(if (0? mark)
(cons* (+ 5 read-bits)
(string->number (list->string num) 2)
remain)
(loop remain num (+ 5 read-bits))))))
(define (sub-type payload)
"Return number of bits/packets, type and remain"
(let* [(I-bit (car payload))
(offset (if (1? I-bit) 11 15))
(type (if (1? I-bit) 'packet-count 'bit-count))
(read-bits (1+ offset))]
(cons* read-bits
type
(bits->number (list-head (cdr payload) offset))
(list-tail (cdr payload) offset))))
(define (parse-packet bits)
"Format (bits-read version type decoded-content remaining-bits)"
(match-let* [((r0 ver typ . payload) (packet->V-T-B bits))
((r1 num . payload) (if (= 4 typ)
(type-4->num payload)
(cons* 0 -1 payload)))
((r2 pck-typ pck-len . payload) (if (not (= 4 typ))
(sub-type payload)
(cons* 0 'none -1 payload)))]
(define (sub-parser stop?)
(let loop [(count 0)
(read-bits 0)
(sub-packets '())
(payload payload)]
(if (stop? count read-bits)
(cons* (+ r0 r2 read-bits) ver typ (reverse sub-packets) payload)
(let* [(sub-packet (parse-packet payload))
(sub-pck-strip (list-head sub-packet 4))
(r3 (car sub-packet))
(payload (list-tail sub-packet 4))]
(loop (1+ count) (+ read-bits r3) (cons sub-pck-strip sub-packets) payload)))))
(cond
[(= 4 typ)
(cons* (+ r0 r1) ver typ num payload)]
[(eq? pck-typ 'packet-count)
(sub-parser (lambda (pck-count bit-count) (= pck-count pck-len)))]
[(eq? pck-typ 'bit-count)
(sub-parser (lambda (pck-count bit-count) (= bit-count pck-len)))]
[else (format #t "Error decoding!~%") #f])))
(define (problem-1 bits)
(match-let dfs [((_ ver _ content . _) (parse-packet bits))]
(if (list? content)
(apply + ver (map dfs content))
ver)))
(define (problem-2 bits)
(match-let dfs [((_ _ typ content . _) (parse-packet bits))]
(let [(unwrapped (if (number? content) `(,content) (map dfs content)))
(b2n (lambda (bool?) (if bool? 1 0)))]
(case typ
[(4) content]
[(0) (apply + 0 unwrapped)]
[(1) (apply * 1 unwrapped)]
[(2) (apply min unwrapped)]
[(3) (apply max unwrapped)]
[(5) (b2n (apply > unwrapped))]
[(6) (b2n (apply < unwrapped))]
[(7) (b2n (apply = unwrapped))]))))
(format #t "~a~%~a~%" (problem-1 in) (problem-2 in))
word · 2021-12-17 08:35
I tried to be smart about day 17 but fuarrrk it, loops and maps it is:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <map>
using std::cout; using std::ifstream;
using std::map; using std::string;
using std::vector; using std::pair;
vector<string> read_input(const char* filepath)
{
ifstream in_f(filepath);
string line;
vector<string> ret;
while(std::getline(in_f, line))
ret.push_back(line);
return ret;
}
vector<int> valid_x_at_time(int t, const pair<int,int> &x)
{
vector<int> ret;
if(t == 1)
{
for(int i = x.first; i != x.second + 1; ++i)
ret.push_back(i);
return ret;
}
for(int start_x = 0; start_x != x.first; ++start_x)
{
int pos = t >= start_x ? start_x * (start_x + 1) / 2 :
t * ((start_x - t + 1) + start_x) / 2;
if(pos >= x.first && pos <= x.second)
ret.push_back(start_x);
}
return ret;
}
int y_pos(int start, int t)
{
int ret = 0;
for(size_t i = 0; i != t; ++i)
{
ret += start;
--start;
}
return ret;
}
pair<int,int> solve(const pair<int,int> &x, const pair<int,int> &y)
{
int high = 0;
map<pair<int,int>,int> valid;
for(int i = -200; i != 200; ++i)
{
for(int t = 0; true; ++t)
{
int pos = y_pos(i, t);
if(pos >= y.first && pos <= y.second)
{
for(const auto &v : valid_x_at_time(t, x))
++valid[pair<int,int>(v, i)];
high = i > high ? i : high;
} else if(pos < y.first) {
break;
}
}
}
return pair<int,int>(high * (high + 1) / 2, valid.size());
}
int main()
{
vector<string> input = read_input("input.txt");
// Parse input
pair<int,int> x_lims, y_lims;
auto s = input[0];
auto comma_pos = s.find(",");
auto x_string = s.substr(s.find("=") + 1, comma_pos);
auto y_string = s.substr(s.find("=", comma_pos) + 1, s.length() - 1);
x_lims.first = std::stoi(x_string.substr(0, x_string.find("..")));
x_lims.second = std::stoi(x_string.substr(x_string.find("..") + 2,
x_string.length() - 1));
y_lims.first = std::stoi(y_string.substr(0, y_string.find("..")));
y_lims.second = std::stoi(y_string.substr(y_string.find("..") + 2,
y_string.length() - 1));
// Get results
auto p = solve(x_lims, y_lims);
// Silver
cout << p.first << "\n";
// Gold
cout << p.second << "\n";
return 0;
}
Linda Lee · 2021-12-17 11:42
Shitcode for today...I worked with so many wrong assumptions but still came out with correct answer. How did you limit the search space? For me the overshoot? function does it and a limit of 10,000 steps beyond last found max.
(use-modules (ice-9 rdelim) (ice-9 regex) (ice-9 q) (ice-9 match) (srfi srfi-1))
(define in
(let [(line (call-with-input-file "input" read-line))]
(map (lambda (match) (string->number (match:substring match)))
(list-matches "-?[0-9]+" line))))
(define (simulate vel-x vel-y box)
(match-let* [((x-min x-max y-min y-max) box)
(overshoot?
(lambda (x2 y2 x1 y1)
(cond
[(= x2 x1) ;;vertical falling
(not (and (>= x1 x-min) (<= x1 x-max)))]
[(= y2 y1) ;;horizontal falling
(not (and (>= y1 y-min) (<= y1 y-max)))]
[else
(let* [(k (/ (- y2 y1) (- x2 x1)))
(n (- y2 (* k x2)))
(fy (lambda (x) (+ n (* k x))))
(fx (lambda (y) (/ (- y n) k)))
(yi (fy x-max))
(xi (fx y-min))]
;;(format #t "over xi:~a yi:~a~%" (exact->inexact xi) (exact->inexact yi))
(not (or (and (>= yi y-min) (<= yi y-max))
(and (>= xi x-min) (<= xi x-max)))))])))
(hit?
(lambda (x y xp yp)
(cond
[(and (or (> x x-max) (< y y-min))
(and (not (zero? xp)) (not (zero? yp)))
(overshoot? x y xp yp))
'miss]
[(or (> x x-max) (< y y-min))
'flyby]
[(and (>= x x-min) (<= x x-max)
(>= y y-min) (<= y y-max))
'hit]
[else
'early])))]
(let step [(x 0) (y 0)
(xp 0) (yp 0)
(max-h 0)
(vel-x vel-x) (vel-y vel-y)]
;;(format #t "(~a ~a)~%" x y)
(let [(hit (hit? x y xp yp))]
(cond
[(eq? hit 'miss) (cons 'miss -1)]
[(eq? hit 'flyby) (cons 'flyby 0)]
[(eq? hit 'hit) (cons 'hit max-h)]
[(eq? 'early)
(step (+ x vel-x) (+ y vel-y)
x y
(max y max-h)
(max 0 (1- vel-x)) (1- vel-y))])))))
(define (greedy-search max-limit box)
"Start velocity at (0,0) go only up or right"
(define visited (make-hash-table))
(define (visit! vel-x vel-y) (hash-set! visited (cons vel-x vel-y) #t))
(define (visited? vel-x vel-y) (hash-ref visited (cons vel-x vel-y) #f))
(let greed-bfs [(limit max-limit)
(front (enq! (make-q) '(0 0)))
(best 0)
(hits 0)]
(if (or (zero? limit) (q-empty? front))
(cons hits best)
(match-let* [((x y) (deq! front))
((status . res) (simulate x y box))]
(cond
[(and (negative? res)
(positive? best)) (greed-bfs (1- limit) front best hits)]
[else
(for-each (lambda (pos)
(unless (apply visited? pos)
(apply visit! pos)
(enq! front pos)))
(list `(,x ,(1+ y)) `(,(1+ x) ,(1+ y)) `(,(1+ x) ,y)
`(,x ,(1- y)) `(,(1+ x) ,(1- y))))
(greed-bfs (if (> res best) max-limit (1- limit))
front
(max res best)
(if (eq? 'hit status) (1+ hits) hits))])))))
(let [(res (greedy-search 10000 in))]
(format #t "~a~%~a~%" (cdr res) (car res)))
Ender · 2021-12-17 11:47
Day 17. Lot of room for optimization, but I don't have infinite time, none of the mechanisms I've tried (or tried coming up with) really usefully restricted the search space, so I just try within some silly bounds.
#!/usr/bin/env python3
import sys
import re
from dataclasses import dataclass
@dataclass(order=True)
class Vec:
x: int
y: int
def __str__(self):
return f'({self.x}, {self.y})'
x1, x2, y1, y2 = [int(group) for group in re.match(r'^target area: x=(-?[0-9]+)..(-?[0-9]+), y=(-?[0-9]+)..(-?[0-9]+)$', input()).groups()]
print(x1, x2, y1, y2)
def within(p, a, b):
return p >= min(a, b) and p <= max(a, b)
n = -1
y_global_max = -sys.maxsize
v0_global_max = None
for k in range(-x2 if x2 >= 0 else x2, x2 if x2 >= 0 else -x2):
for l in range(-500, 500):
n += 1
p0 = Vec(0, 0)
p = Vec(p0.x, p0.y)
v0 = Vec(k, l)
v = Vec(v0.x, v0.y)
y_local_max = -sys.maxsize
if p0.y + v0.y < p0.y and y_global_max > 0:
continue
#print(f'attempt #{n} (v0 = {v0})')
while not (within(p.x, x1, x2) and within(p.y, y1, y2)) and p.y > y2:
if p.y > y_local_max: y_local_max = p.y
p.x = p.x + v.x
v.x += 1 if v.x < 0 else -1 if v.x > 0 else 0
p.y = p.y + v.y
v.y -= 1
if p.x > x2 and v.x >= 0:
break
if p.x < x1 and v.x <= 0:
break
#print(f'throw #{n} p = {p} (v = {v})')
if within(p.x, x1, x2) and within(p.y, y1, y2):
print(f'success @ attempt #{n} with v0 = {v0}, probe at p = {p} ([{x1}, {x2}], [{y1}, {y2}], local y max = {y_local_max})')
if y_local_max > y_global_max:
print(f'new local best {y_local_max} with v0 = {v0} (previous {y_global_max})')
y_global_max = y_local_max
v0_global_max = Vec(v0.x, v0.y)
print(f'global max of {y_global_max} with initial velocity v0 = {v0_global_max}')
Tried to do day 16 too now, but it was too horrible, dealing with binary data in python. Didn't bother with string manipulation afterwards.
Miho Iwakura · 2021-12-18 10:08
Didn't have much time today so I went back to python instead of cpp. Please ignore the brute force gold star:
import math
import copy
import itertools
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
class Snail:
# Constructor
def __init__(self, val, left=None, right=None):
if val is not None:
self.is_lit = True
self.lit = val
else:
self.is_lit = False
self.lit = val
self.l = left
self.r = right
self.p = None
if self.l:
self.l.p = self
if self.r:
self.r.p = self
# Sum definition
def __add_neighbor(snail, val, left):
curr_s = snail
curr_p = curr_s.p
while curr_p and (curr_s == curr_p.l) == left:
curr_s = curr_p
curr_p = curr_p.p
if not curr_p:
return
curr_s = curr_p.l if left else curr_p.r
while not curr_s.is_lit:
curr_s = curr_s.r if left else curr_s.l
curr_s.lit += val
def __explode_aux(snail):
left_v = snail.l.lit
right_v = snail.r.lit
Snail.__add_neighbor(snail, left_v, True)
Snail.__add_neighbor(snail, right_v, False)
snail.is_lit = True
snail.lit = 0
snail.l = None
snail.r = None
def explode(self, l):
if self.is_lit:
return False
if l == 4:
Snail.__explode_aux(self)
return True
return self.l.explode(l + 1) or self.r.explode(l + 1)
def split(self):
if self.is_lit and self.lit > 9:
new_l = math.floor(self.lit / 2)
new_r = math.ceil(self.lit / 2)
self.is_lit = False
self.lit = None
self.l = Snail(new_l)
self.r = Snail(new_r)
self.l.p = self
self.r.p = self
return True
elif self.is_lit:
return False
return self.l.split() or self.r.split()
def __add__(self, o):
new_snail = Snail(None, self, o)
check = True
while check:
check = new_snail.explode(0)
check = check or new_snail.split()
return new_snail
# Magnitude
def __lt__(self, o):
return self.magnitude() < o.magnitude()
def magnitude(self):
if self.is_lit:
return self.lit
return 3 * self.l.magnitude() + 2 * self.r.magnitude()
# I/O
def from_string(line, i):
curr = line[i]
i += 1
if(curr == '['):
left, i = Snail.from_string(line, i)
i += 1
right, i = Snail.from_string(line, i)
i += 1
return (Snail(None, left, right), i)
else:
return (Snail(int(curr)), i)
def __str__(self):
if self.is_lit:
return str(self.lit)
return '[' + str(self.l) + ', ' + str(self.r) + ']'
snails = [Snail.from_string(line, 0)[0] for line in lines]
c_snails = copy.deepcopy(snails)
sum_snail = snails[0]
for snail in snails[1:]:
sum_snail += snail
# Silver
print(sum_snail.magnitude())
# Gold
max_sum = 0
for x, y in itertools.product(c_snails, repeat=2):
x0 = copy.deepcopy(x)
y0 = copy.deepcopy(y)
x1 = copy.deepcopy(x)
y1 = copy.deepcopy(y)
s0 = (x0 + y0).magnitude()
s1 = (x1 + y1).magnitude()
max_sum = s0 if s0 > max_sum else max_sum
max_sum = s1 if s1 > max_sum else max_sum
print(max_sum)
Nobody important · 2021-12-18 22:08
worst day for me by far....I was stuck on some simple condition edge case :(
(use-modules (ice-9 rdelim) (ice-9 match) (ice-9 eval-string) (ice-9 string-fun) (srfi srfi-1))
(define (get-lines filename trans)
(call-with-input-file filename
(lambda (port)
(let loop [(line (read-line port))
(lines '())]
(if (eof-object? line)
(reverse lines)
(loop (read-line port) (cons (trans line) lines)))))))
(define (line->tree line)
(eval-string
(string-concatenate `("'" ,(string-replace-substring line "," " . ")))))
(define in (get-lines "input" line->tree))
(define (explode snailfish)
(define (propagate snailfish direction value)
(match-let [((sub-l . sub-r) snailfish)]
(cond
[(and (eq? direction 'left) (number? sub-l))
(cons (+ value sub-l) sub-r)]
[(and (eq? direction 'right) (number? sub-r))
(cons sub-l (+ value sub-r))]
[(eq? direction 'left)
(cons (propagate sub-l direction value) sub-r)]
[(eq? direction 'right)
(cons sub-l (propagate sub-r direction value))])))
(let dfs [(snailfish snailfish)
(lvl 0)]
"Returns (node . (exploded-l exploded-r exploded?)"
(cond
[(and (number? (car snailfish))
(number? (cdr snailfish))
(< lvl 4))
`(,snailfish . (-1 -1 #f))] ;;end nothing to explode
[(and (number? (car snailfish))
(number? (cdr snailfish))
(= lvl 4))
(cons 0 (list (car snailfish) (cdr snailfish) #t))] ;;exploded
[(number? (car snailfish)) ;;(num (...))
(match-let* [((num . sub) snailfish)
((sub . exploded) (dfs sub (1+ lvl)))
((l-explode r-explode exploded?) exploded)]
(if (= -1 l-explode)
(cons (cons num sub)
exploded)
(cons (cons (+ num l-explode) sub)
(list -1 r-explode #t))))]
[(number? (cdr snailfish)) ;;((...) num)
(match-let* [((sub-snailfish . num) snailfish)
((sub-snailfish . exploded) (dfs sub-snailfish (1+ lvl)))
((l-explode r-explode exploded?) exploded)]
(if (= -1 r-explode)
(cons (cons sub-snailfish num)
exploded)
(cons (cons sub-snailfish (+ num r-explode))
(list l-explode -1 #t))))]
[else
(match-let* [((sub-l . sub-r) snailfish)
((sub-l . exploded-l) (dfs sub-l (1+ lvl)))
((l-explode r-explode exploded?) exploded-l)]
(cond
[(and exploded? (< -1 r-explode))
(cons (cons sub-l (propagate sub-r 'left r-explode))
(list l-explode -1 #t))]
[exploded?
(cons (cons sub-l sub-r)
exploded-l)]
[else
(match-let* [((sub-r . exploded-r) (dfs sub-r (1+ lvl)))
((l-explode r-explode exploded?) exploded-r)]
(if (and exploded? (< -1 l-explode))
(cons (cons (propagate sub-l 'right l-explode) sub-r)
(list -1 r-explode #t))
(cons (cons sub-l sub-r)
exploded-r)))]))])))
(define (split snailfish)
(define (split-num num)
(cons (floor (/ num 2)) (ceiling (/ num 2))))
(match-let [((sub-l . sub-r) snailfish)]
;;(format #t "~a | ~a~%" sub-l sub-r)
(cond
[(and (number? sub-l) (<= 10 sub-l))
(cons (cons (split-num sub-l) sub-r)
#t)]
[(and (number? sub-r) (<= 10 sub-r))
(if (pair? sub-l)
(match-let [((sub-l . splitted?) (split sub-l))]
(if splitted?
(cons (cons sub-l sub-r) #t)
(cons (cons sub-l (split-num sub-r)) #t)))
(cons (cons sub-l (split-num sub-r)) #t))]
[(and (pair? sub-l) (pair? sub-r))
(match-let [((sub-l . splitted?) (split sub-l))]
(if splitted?
(cons (cons sub-l sub-r) #t)
(match-let [((sub-r . splitted?) (split sub-r))]
(cons (cons sub-l sub-r) splitted?))))]
[(pair? sub-l)
(match-let [((sub-l . splitted?) (split sub-l))]
(cons (cons sub-l sub-r) splitted?))]
[(pair? sub-r)
(match-let [((sub-r . splitted?) (split sub-r))]
(cons (cons sub-l sub-r) splitted?))]
[else
(cons snailfish #f)])))
(define (printer snailfish)
(match-let dfs [((sub-l . sub-r) snailfish)]
(display "[")
(if (pair? sub-l) (dfs sub-l) (display sub-l))
(display ",")
(if (pair? sub-r) (dfs sub-r) (display sub-r))
(display "]"))
(newline))
(define (magnitude snailfish)
(match-let dfs [((sub-l . sub-r) snailfish)]
(+
(* 3 (if (pair? sub-l) (dfs sub-l) sub-l))
(* 2 (if (pair? sub-r) (dfs sub-r) sub-r)))))
(define (add snailfish2 snailfish1)
(define (exploding snailfish)
;;(printer snailfish)
(match-let [((snailfish . (l r exploded?)) (explode snailfish))]
;;(when exploded? (display "ex "))
(if exploded?
(exploding snailfish)
(match-let [((snailfish . splitted?) (split snailfish))]
;;(when splitted? (display "sp "))
(if splitted?
(exploding snailfish)
snailfish)))))
(exploding (cons snailfish1 snailfish2)))
(define (find-max fishes)
(let loop [(fishes fishes)
(max-mag 0)]
(if (null? fishes)
max-mag
(let loop2 [(fishes2 (cdr fishes))
(max-mag2 max-mag)]
(if (null? fishes2)
(loop (cdr fishes) max-mag2)
(loop2 (cdr fishes2)
(max max-mag2
(magnitude (add (car fishes) (car fishes2)))
(magnitude (add (car fishes2) (car fishes))))))))))
(when (string= "d18.scm" (car (command-line)))
(let [(fishes (get-lines (second (command-line)) line->tree))]
(format #t "~a~%~a~%" (magnitude (reduce add '() fishes))
(find-max fishes))))
Nobody special · 2021-12-19 23:25
I guess that's it from me boys. My soykaf doesn't work.
Turing · 2021-12-20 04:51
Once again python and the naive solution (20 min on real input lol) for time reasons. Hopefully after the 25th I can come back to this one to implement solving for the correct rotation between two scanners in a single step
import itertools
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
class Scanner:
class Point:
def __init__(self, x, y, z, s=None):
if s:
toks = [int(t) for t in s.split(',')]
self.x = toks[0]
self.y = toks[1]
self.z = toks[2]
else:
self.x = x
self.y = y
self.z = z
def distance(self, o):
return abs(self.x - o.x) + abs(self.y - o.y) + abs(self.z - o.z)
def __str__(self):
return f'({self.x}, {self.y}, {self.z})'
def __eq__(self, o):
return self.x == o.x and self.y == o.y and self.z == o.z
def __hash__(self):
return hash(self.x) ^ hash(self.y) ^ hash(self.z)
def __init__(self, idx):
self.id = idx
self.loc = self.Point(0, 0, 0)
self.points = []
self.aligned = True if idx == 0 else False
self.no_align = []
def add_point(self, s):
self.points.append(self.Point(None, None, None, s))
def get_rotations(self, x, y, z):
return [
self.Point(x,y,z),self.Point(x,z,-y),self.Point(x,-y,-z),self.Point(x,-z,y),
self.Point(-x,-y,z),self.Point(-x,z,y),self.Point(-x,y,-z),self.Point(-x,-z,-y),
self.Point(y,-x,z),self.Point(y,z,x),self.Point(y,x,-z),self.Point(y,-z,-x),
self.Point(-y,x,z),self.Point(-y,z,-x),self.Point(-y,-x,-z),self.Point(-y,-z,x),
self.Point(z,y,-x),self.Point(z,-x,-y),self.Point(z,-y,x),self.Point(z,x,y),
self.Point(-z,-y,-x),self.Point(-z,-x,y),self.Point(-z,y,x),self.Point(-z,x,-y)
]
def build_rotations(self):
self.rotations = [[] for _ in range(24)]
for p in self.points:
rots = self.get_rotations(p.x, p.y, p.z)
for i, r in enumerate(rots):
self.rotations[i].append(r)
def get_absolute_points(self):
ret = []
for p in self.points:
ret.append(self.Point(p.x + self.loc.x, p.y + self.loc.y,
p.z + self.loc.z))
return ret
def align(self, o):
if self.id in o.no_align or o.id in self.no_align or o.aligned:
return False
found_align = False
for rot in o.rotations:
for p_ref, p_o in itertools.product(self.points, rot):
shift = [p_ref.x - p_o.x, p_ref.y - p_o.y, p_ref.z - p_o.z]
hits = 0
for p in rot:
shifted = self.Point(p.x + shift[0], p.y + shift[1], p.z + shift[2])
if shifted in self.points:
hits += 1
if hits == 12:
found_align = True
correct_shift = shift
break
if found_align:
break
if found_align:
break
if found_align:
o.loc.x = self.loc.x + correct_shift[0]
o.loc.y = self.loc.y + correct_shift[1]
o.loc.z = self.loc.z + correct_shift[2]
o.points = rot
o.aligned = True
return found_align
# Parse input
scanners = []
for line in lines:
if not line:
continue
elif 'scanner' in line:
scanners.append(Scanner(int(line.split()[2])))
else:
scanners[-1].add_point(line)
for s in scanners:
s.build_rotations()
aligned = [scanners[0]]
unaligned = scanners[1:]
while len(aligned) != len(scanners):
new_aligned = aligned
for s0, s1 in itertools.product(aligned, unaligned):
if s0.align(s1):
new_aligned.append(s1)
aligned = new_aligned
# Puzzle outputs
silver = set()
for s in aligned:
for p in s.get_absolute_points():
silver.add(p)
gold = 0
for s0, s1 in itertools.product(scanners, scanners):
dist = s0.loc.distance(s1.loc)
if dist > gold:
gold = dist
print(len(silver))
print(gold)
if · 2021-12-20 05:44
The damn trick got me, I might've made top 100 if I had caught it. Once again with Python:
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
class Image:
mapper = {'#': '1', '.': '0'}
def __init__(self, lines):
self.im = []
for i, l in enumerate(lines):
if not l:
continue
elif i == 0:
self.algo = ''.join(l)
else:
row = [x for x in l]
self.im.append(row)
def smart_get(self, x, y, flip):
if x < 0 or x >= len(self.im[0]) or y < 0 or y >= len(self.im):
if flip and self.algo[0] == '#':
return '#'
else:
return '.'
return self.im[y][x]
def get_surrounding(self, x, y, flip):
ret = []
for i in range(-1, 2):
for j in range(-1, 2):
ret.append(self.smart_get(x + j, y + i, flip))
return int(''.join([Image.mapper[x] for x in ret]), 2)
def apply(self, n):
for a in range(n):
new_image = []
for i in range(-1, len(self.im) + 1):
new_row = []
for j in range(-1, len(self.im[0]) + 1):
algo_coord = self.get_surrounding(j, i, a % 2 == 1)
algo_char = self.algo[algo_coord]
new_row.append(algo_char)
new_image.append(new_row)
self.im = new_image
def count(self):
ret = 0
for l in self.im:
ret += l.count('#')
return ret
def print_image(self):
for l in self.im:
print(''.join(l))
print('')
image = Image(lines)
# Silver
image.apply(2)
print(image.count())
# Gold
image.apply(48)
print(image.count())
Agent Jones · 2021-12-21 06:14
@cache is straight up magic
import itertools
from functools import cache
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
in_0 = int(lines[0].split()[-1])
in_1 = int(lines[1].split()[-1])
p0_p = in_0
p1_p = in_1
p0_s = 0
p1_s = 0
winner = -1
rolls = 0
for i in range(1, 1000, 6):
p0_p += i + (i + 1) + (i + 2)
rolls += 3
if p0_p > 10:
if p0_p % 10 == 0:
p0_p = 10
else:
p0_p %= 10
p0_s += p0_p
if p0_s >= 1000:
winner = 0
break
p1_p += (i + 3) + (i + 4) + (i + 5)
rolls += 3
if p1_p > 10:
if p1_p % 10 == 0:
p1_p = 10
else:
p1_p %= 10
p1_s += p1_p
if p1_s >= 1000:
winner = 1
break
# Silver
if winner == 0:
print(p1_s * rolls)
elif winner == 1:
print(p0_s * rolls)
# Gold
@cache
def dirac(turn, p0_score, p1_score, p0_pos, p1_pos):
if p0_score >= 21:
return 1
if p1_score >= 21:
return 0
ret = 0
for x, y, z in itertools.product([1, 2, 3], repeat=3):
new_pos = p0_pos + x + y + z if turn == 0 else p1_pos + x + y + z
if new_pos > 10:
new_pos = 10 if new_pos % 10 == 0 else new_pos % 10
new_score = p0_score + new_pos if turn == 0 else p1_score + new_pos
if turn == 0:
ret += dirac(1, new_score, p1_score, new_pos, p1_pos)
else:
ret += dirac(0, p0_score, new_score, p0_pos, new_pos)
return ret
print(dirac(0, 0, 0, in_0, in_1))
Tank · 2021-12-21 12:39
Life has been too busy, but I managed to find some time today to do some of the past exercises. Doing day 20 made me want to visualize the procedure, so I present to you lains what the image enhancer does in vid related, I think it's pretty neat.
Hitomi · 2021-12-21 14:56
>>29098
That looks great, it was a nice problem after day 19
Spike Spiegel · 2021-12-22 07:48
Day 22 once again in Python, I wanna get off Eric's geometry ride:
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
class Cube:
def __init__(self, on, x_min, x_max, y_min, y_max, z_min, z_max, l=None):
if l is not None:
self.on = l.split()[0] == 'on'
toks = l.split()[1].split(',')
self.x_min = int(toks[0].split('=')[1].split('..')[0])
self.x_max = int(toks[0].split('=')[1].split('..')[1])
self.y_min = int(toks[1].split('=')[1].split('..')[0])
self.y_max = int(toks[1].split('=')[1].split('..')[1])
self.z_min = int(toks[2].split('=')[1].split('..')[0])
self.z_max = int(toks[2].split('=')[1].split('..')[1])
else:
self.on = on
self.x_min = x_min
self.x_max = x_max
self.y_min = y_min
self.y_max = y_max
self.z_min = z_min
self.z_max = z_max
self.e_x = (self.x_max - self.x_min) / 2
self.e_y = (self.y_max - self.y_min) / 2
self.e_z = (self.z_max - self.z_min) / 2
self.c_x = self.x_min + self.e_x
self.c_y = self.y_min + self.e_y
self.c_z = self.z_min + self.e_z
def is_big(self):
return (self.x_min < -50 or self.x_max > 50 or
self.y_min < -50 or self.y_max > 50 or
self.z_min < -50 or self.z_max > 50)
def volume(self):
x_len = self.x_max - self.x_min + 1
y_len = self.y_max - self.y_min + 1
z_len = self.z_max - self.z_min + 1
return x_len * y_len * z_len
def intersects(self, o):
if (abs(self.c_x - o.c_x)-(self.e_x+o.e_x) <= 0 and
abs(self.c_y - o.c_y)-(self.e_y+o.e_y) <= 0 and
abs(self.c_z - o.c_z)-(self.e_z+o.e_z) <= 0):
return True
return False
def contains(self, c1):
if (self.x_min >= c1.x_min and self.x_max <= c1.x_max and
self.y_min >= c1.y_min and self.y_max <= c1.y_max and
self.z_min >= c1.z_min and self.z_max <= c1.z_max):
return self
elif (c1.x_min >= self.x_min and c1.x_max <= self.x_max and
c1.y_min >= self.y_min and c1.y_max <= self.y_max and
c1.z_min >= self.z_min and c1.z_max <= self.z_max):
return c1
return None
def split(self, o):
splits = []
i_x_min = max(self.x_min, o.x_min)
i_x_max = min(self.x_max, o.x_max)
i_y_min = max(self.y_min, o.y_min)
i_y_max = min(self.y_max, o.y_max)
i_z_min = max(self.z_min, o.z_min)
i_z_max = min(self.z_max, o.z_max)
# X from left
if self.x_min < i_x_min:
splits.append(Cube(self.on, self.x_min, i_x_min - 1,
self.y_min, self.y_max, self.z_min, self.z_max))
self.x_min = i_x_min
# And right
if i_x_max < self.x_max:
splits.append(Cube(self.on, i_x_max + 1, self.x_max,
self.y_min, self.y_max, self.z_min, self.z_max))
self.x_max = i_x_max
# Y from below
if self.y_min < i_y_min:
splits.append(Cube(self.on, self.x_min, self.x_max,
self.y_min, i_y_min - 1, self.z_min, self.z_max))
self.y_min = i_y_min
# And above
if i_y_max < self.y_max:
splits.append(Cube(self.on, self.x_min, self.x_max,
i_y_max + 1, self.y_max, self.z_min, self.z_max))
self.y_max = i_y_max
# Z from behind
if self.z_min < i_z_min:
splits.append(Cube(self.on, self.x_min, self.x_max,
self.y_min, self.y_max, self.z_min, i_z_min - 1))
self.z_min = i_z_min
# And front
if i_z_max < self.z_max:
splits.append(Cube(self.on, self.x_min, self.x_max,
self.y_min, self.y_max, i_z_max + 1, self.z_max))
self.z_max = i_z_max
return splits
# Solve as we parse
silver_flag = True
cubes = [Cube(None, None, None, None, None, None, None, lines[0])]
for line in lines[1:]:
new_cube = Cube(None, None, None, None, None, None, None, line)
# Output silver when we start reading bigger cubes
if silver_flag and new_cube.is_big():
print(sum([x.volume() for x in cubes]))
silver_flag = False
new_cubes = []
add_flag = new_cube.on
for i, cube in enumerate(cubes):
# No intersection
if not cube.intersects(new_cube):
new_cubes.append(cube)
continue
contained = cube.contains(new_cube)
# Old cube in list is contained, do not add to new cubes
if contained and contained == cube:
continue
# New cube is contained, add the container and rest of list unchanged
if contained and contained == new_cube:
if new_cube.on:
new_cubes += cubes[i:]
add_flag = False
break
# Else, we have to split (also if contained and off)
splits = cube.split(new_cube)
new_cubes += splits
if add_flag:
new_cubes.append(new_cube)
cubes = new_cubes
# Gold
print(sum([x.volume() for x in cubes]))
Yu-ichi · 2021-12-22 08:26
Day 22 (only part1):
#!/usr/bin/env python3
import sys
import re
def process_command(command, cube):
print(f'processing {command}')
for z in range(command[1][4], command[1][5] + 1):
for y in range(command[1][2], command[1][3] + 1):
for x in range(command[1][0], command[1][1] + 1):
cube.add((x, y, z)) if command[0] else cube.discard((x, y, z))
initialization_size = 50
commands = []
cube = set()
for line in sys.stdin:
match = re.match(r'(on|off) x=(-?[0-9]+)..(-?[0-9]+),y=(-?[0-9]+)..(-?[0-9]+),z=(-?[0-9]+)..(-?[0-9]+)', line.rstrip('\n'))
if not match or len([group for group in match.groups()[1:] if abs(int(group)) > 50]) > 0:
continue
commands.append((match.groups()[0] == 'on', [int(group) for group in match.groups()[1:]]))
while len(commands) > 0:
command = commands.pop(0)
process_command(command, cube)
print(f'{len(cube)} cubes are turned on')
# TODO: try calculating intersections instead
>>29110
Not going to lie, that seems slightly convoluted. Like all 3D geometry code typically ends up I imagine.
⍕ · 2021-12-23 18:00
No code from me today, solved it by hand
Naomi Nascimento · 2021-12-24 08:56
Code for today is only a formality really, again solved by hand and checked with the script:
class ALU:
def __init__(self, n):
self.digits = [int(x) for x in n]
def smart_z(self):
s_z = self.digits[0] + 15
s_z = s_z * 26 + self.digits[1] + 8
s_z = s_z * 26 + self.digits[2] + 2
# d2 - 7 = d3
if (s_z % 26) - 9 == self.digits[3]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[3] + 6
s_z = s_z * 26 + self.digits[4] + 13
s_z = s_z * 26 + self.digits[5] + 4
s_z = s_z * 26 + self.digits[6] + 1
# d6 - 4 = d7
if (s_z % 26) - 5 == self.digits[7]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[7] + 9
s_z = s_z * 26 + self.digits[8] + 5
# d8 - 2 = d9
if (s_z % 26) - 7 == self.digits[9]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[9] + 13
# d5 - 8 = d10
if (s_z % 26) - 12 == self.digits[10]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[10] + 9
# d4 + 3 = d11
if (s_z % 26) - 10 == self.digits[11]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[11] + 6
# d1 + 7 = d12
if (s_z % 26) - 1 == self.digits[12]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[12] + 2
# d0 + 4 = d13
if (s_z % 26) - 11 == self.digits[13]:
s_z = s_z // 26
else:
s_z = s_z // 26
s_z = s_z * 26 + self.digits[13] + 2
return s_z
print(ALU(str(52926995971999)).smart_z())
print(ALU(str(11811951311485)).smart_z())
Unnamed · 2021-12-24 09:23
That's it for me lains, I don't even see what's to be done probably because I'm not logged in.
But I came here to wish every participant a nice and merry christmas. It was a nice ride!
No matter what you managed and didn't manage, at least something was learned by trying!
Trinity · 2021-12-24 19:51
>>29151
In case you come back to this one, the assembly that is the puzzle input can be broken up into 14 blocks, one for each digit. There are two kinds of blocks, the ones that increase z by a factor of about 26, and the ones that conditionally decrease it by a factor of 26.
You have 7 increasing blocks and 7 decreasing blocks, so you have to hit all conditions for z to be 0 at the end. The conditions are the digit constraints.
In any case, merry christmas everyone, it was fun seeing people's solutions.
Unnamed · 2021-12-25 05:58
And just posting today's solution for the sake of completeness, hope everyone had a nice dinner.
import numpy as np
with open('input.txt', 'r') as in_f:
lines = [x.strip() for x in in_f.readlines()]
mapper = {'.': 0, '>': 1, 'v': 2}
width = len(lines[0])
height = len(lines)
cucumbers = np.zeros((height, width), dtype=int)
right_coords = []
down_coords = []
for i, line in enumerate(lines):
for j, c in enumerate(line):
to_add = mapper[c]
if to_add == 1:
right_coords.append([i, j])
elif to_add == 2:
down_coords.append([i, j])
cucumbers[i, j] = to_add
moved_flag = True
iter_counter = 0
while moved_flag:
moved_flag = False
# Right cucumbers
right_neighbors_coords = [[x[0], (x[1] + 1) % width] for x in right_coords]
right_neighbors = np.array([cucumbers[x[0], x[1]] for x in right_neighbors_coords])
zero_neighbors = np.nonzero(right_neighbors == 0)[0]
for idx in zero_neighbors:
cucumbers[right_coords[idx][0], right_coords[idx][1]] = 0
cucumbers[right_neighbors_coords[idx][0], right_neighbors_coords[idx][1]] = 1
right_coords[idx] = right_neighbors_coords[idx]
if len(zero_neighbors) > 0:
moved_flag = True
# Down cucumbers
down_neighbors_coords = [[(x[0] + 1) % height, x[1]] for x in down_coords]
down_neighbors = np.array([cucumbers[x[0], x[1]] for x in down_neighbors_coords])
zero_neighbors = np.nonzero(down_neighbors == 0)[0]
for idx in zero_neighbors:
cucumbers[down_coords[idx][0], down_coords[idx][1]] = 0
cucumbers[down_neighbors_coords[idx][0], down_neighbors_coords[idx][1]] = 2
down_coords[idx] = down_neighbors_coords[idx]
if len(zero_neighbors) > 0:
moved_flag = True
iter_counter += 1
print(iter_counter)