There is an advent calendar w/ code puzzles https://adventofcode.com - this only has 12 doors, so only every 2nd day there will be a new puzzle.
I’ll try to solve it in sclang and maybe also even on scsynth - lets see how it goes. Just wanted to share that b/c maybe also some others would be interested in that. Maybe it is interesting how the solutions can be sonified, would be cool if people share their sonic output here
Sadly one needs to register b/c the puzzles are personalized. I created a throwaway reddit account for that: You can just use a random mail address and skip the email verification process and use this account for authentication on advent of code.
Does someone have a tip for mod in Day 1 Puzzle 2?
I tried something like ~counter = ~counter + (~pos/100).floor.abs and I narrowed down the problem to
50-50 = 0 => +1
0-2 = -2 / 98 => +0 // this seems to be the tricky part?
1-2 = -1 / 99 => +1
I gave up and just gone brute force, but at least this also allowed me to wrap the calculation in a Tdef and sonify it Every ping is a 0-pass where each pass increases its pitch, every other rotation is also sonified.
I haven’t started with day 2, but it would be an interesting thing to only use arithmetic instead of string comparison.
Just so we all know who we’re working for (a “sponsor” of this thing):
JPMorgan Chase - Powered by 60,000 technologists and $18 billion in annual tech investment, JPMorgan Chase is revolutionizing software development to create world-class products.
:guillotine_emoji:
anyway here’s a silly audio rate solution (FOR PART ONE), because why not
Spoileur (I don't know how to do the blurry thing)
(
var rots = [-68, -30, 48, -5, 60, -55, -1, -99, 14, -82]; // or the input.txt list, regexed accordingly
b = Buffer.loadCollection(s, [0] ++ rots ++ [0]);
r = OSCFunc({ |msg| msg.postln }, '/eh');
{
// the calculation
var pl = PlayBuf.ar(1, b);
var ph = Phasor.ar(Impulse.ar(0), pl, 0, 100, 50);
var count = FOS.ar(ph < 1, 1,0,1); // summer
// the rest is for reporting the result
var dong = Done.kr(pl);
SendReply.kr(dong, '/eh', values: count);
FreeSelf.kr(TDelay.kr(dong, 1));
count;
}.plot(rots.size + 2 / s.sampleRate)
)
This works well enough for this example. In my original attempt I somehow thought I would have to distinguish zero crossing between samples from zeros on samples, but that was … confused.
I misunderstood puzzle 1 of day 2 and actually implemented part 2 the whole time…
I had problems w/ sclang b/c integers are not big enough for values such as 9292987321 - "9292987321".asInteger returns 703052729 - but floats provided a big enough range.
I found the spoiler tag in this forum, which is maybe a nice way to share solutions, so here is my awfully hacky one.
~data = File.readAllString(thisProcess.nowExecutingPath.dirname +/+ "input");
~test = "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124";
~entries = ~data.split($,);
~entries = ~test.split($,);
// part 1
(
~matches = [];
~entries.do({|e|
var x, y;
#x, y = e.split($-).asFloat;
(x..y).do({|a|
var midPoint;
~string = a.asString;
// remove .0
~string = ~string[..~string.size-3];
midPoint = (~string.size / 2).asInteger;
if(~string.size%2 == 0, {
if(~string[0..midPoint-1] == ~string[midPoint..], {~matches = ~matches.add(a.asFloat)});
});
})
});
~matches.sum;
)
// part2
(
~matches = [];
~entries.do({|e|
var x, y;
#x, y = e.split($-).asFloat;
(x..y).do({|a|
~string = a.asString;
~string = ~string[..~string.size-3];
(~string.size/2.0).floor.asInteger.do({|i, j|
var r = ~string.size/(i+1);
if(~string[0..i].dup(r).join == ~string, {~matches = ~matches.add(a)});
});
})
});
// removing duplicates - using set instead of proper coding <3
~matches.as(Set).sum
)
@girthrub have you actually checked your solution by submitting the result? The testing set does not really cover all tricky edge cases which one can encounter and I don’t really know how to obtain the result there.
(
~list = List.new;
~fun = { |n| ~list.add(n.split($-).asFloat) };
CSVFileReader.read("./puzzle2.txt".resolveRelative, func: ~fun);
~list;
)
(
var sz = ~list.size;
var i = 0, j;
~set = Set.new;
while { (i < (sz-1)) } {
j = ~list[i][0];
while { (j <= ~list[i][1]) } {
var str = j.asString.replace(".0", "");
if ((str.size % 2) == 0) {
var middle = (str.size / 2).floor.asInteger;
if (str[0..middle-1] == str[middle..]) {
~set.add(str.asFloat)
}
};
j = j + 1;
};
i = i + 1;
};
~set.sum;
"Done!".postln;
)
This one was a pain in the ass and it took me quite a few crashes. Thanks @dscheiba for the tip on using floats. I didn’t saw your code, but I did pick that up from your message. I’m not sure I have the courage needed for part two, right now. Too sleepy
Having looked a it now, they’re similar in the fundamental aspects. Trimming “.0” from the string, mod 2 to see if there’s an even number of characters, if so, divide size by half and use that to break string in half, then compare halfs.
Mékié?!.. You’re right. That’s because it’s the second part of the problem (or problem 2 - day 1). For the first part I had a single counter under the case block. Only counted if dial == 0 by the end of a rotation instruction. On the second problem you have to count not only if dial == 0 by the end of a rotation, but also if it passes through 0 during the rotation, even if the rotation doesn’t end there.
I should have been clearer, but still, yours is a much cleaner solution to the first part of the problem.
Finally figured out what I was doing wrong with part2 of day 1. At least it was a properly audio problem (if you send a phasor a new rate, that doesn’t affect its output until the next frame… oh well. )
I’ll try to refrain from doing any more of these, at least not with UGens…
i = [-68 ,-30, 48 ,-5 ,60, -55, -1, -99, 14, -82]; // or the big input
b = Buffer.loadCollection(s, i);
r = OSCFunc({ |msg| msg.postln }, '/count');
(
{
var m = 100;
var start = 50;
var turn = PlayBuf.ar(1, b,doneAction:0);
var dong = Done.kr(turn);
var xtra = turn.abs.div(m); // count extra full rotations
var pos = Phasor.ar(Impulse.ar(0), turn, 0, m, start);
var check = pos - Delay1.ar(turn.sign * turn.abs.mod(m));
var zerox = (pos > 0) * ((check < 0) + (check > m));
var onzero = (pos < 1) * (turn.abs.mod(m) > 0);
var count = FOS.ar(onzero + zerox + xtra, 1,0,1); // add it all
SendReply.kr(TDelay.kr(dong, 0.5), '/count', values: count);
FreeSelf.kr(TDelay.kr(dong, 1));
count * 0; // save yer speakers/ears
}.play
)
Wow, this is getting really tricky really fast - I hope they don’t make the puzzles harder and harder Still enjoy it very much though
Solution for day 3 - problem 2 took me one hour to implement and some paper to come up with the idea…
~data = File.readAllString(thisProcess.nowExecutingPath.dirname +/+ "input");
(
~test = "987654321111111
811111111111119
234234234234278
818181911112111";
)
~entries = ~test.split($\n).reject(_.isEmpty);
~entries = ~data.split($\n).reject(_.isEmpty);
// part 1
(
~biggest = ~entries.collect({|entry|
var biggest = 0;
entry[..entry.size-2].do({|c, i|
(i+1 .. entry.size-1).do({|j|
var number = "%%".format(c, entry[j]).asInteger;
biggest = biggest.max(number);
});
});
biggest;
});
~biggest.sum;
)
// part2
// use a recursive function which consumes a stack (our available numbers)
(
~f = {|number, stack, remaining|
var n = (number.last ? 0).asInteger;
var m = (stack.first ? 0).asInteger;
// ["calling ~f w/", number, stack, remaining, stack.size, m, n].postln;
if(stack.size==0, {
// stop executing
number.collect({|x| x.asString}).join.asFloat;
}, {
// if number is empty -> consume from stack
// if remaining stack length <= remaining slots in stack -> pull from stack
if((number.size == 0).or(stack.size <= remaining).or(m<=n), {
if(remaining > 0, {
// only push if we need values
number = number.add(m);
});
stack = stack.drop(1);
~f.(number, stack, (remaining -1).max(0));
}, {
// if stack top >= current number bottom -> remove last number value
number = number.drop(-1);
~f.(number, stack, remaining + 1);
});
});
};
)
(
~x = ~entries.collect({|e|
// pay attention - see https://github.com/supercollider/supercollider/issues/7276
var numberArray = e.size.collect({|i| (e@i).asSymbol.asInteger});
~f.([], numberArray, 12);
})
)
// too big for sclang :O
~x.sum;
// copy collected values via cs
~x.cs;
// and sum them in python using
x = ...my cs values
sum(x)
Sharing my attempt to day 3 - problem 2, since I seem to have come up with a different solution than the recursive approach posted earlier. I had issues with numbers too big for SC to handle as well, even using Float, so I did my final sum in Python.
(
var file = File(~filePath, "r");
var banks = file.readAllString.split($\n);
var joltSize = 12;
var jolts = Array.new(banks.size);
file.close;
banks.do( {
|bank|
var batteries = Array.new(joltSize);
var leftLimit = 0;
var rightLimit = bank.size - joltSize;
for (joltSize - 1, 0) {
|i|
var idx = 0;
idx = bank[leftLimit..rightLimit].maxIndex;
batteries = batteries.add(bank[idx + leftLimit]);
leftLimit = leftLimit + idx + 1;
rightLimit = min(rightLimit + idx + 1, bank.size - i);
};
jolts = jolts.add(batteries.reduce('++').asFloat);
});
jolts.sum;
)
parse :: String -> Int
parse ('L':n) = negate (read n)
parse ('R':n) = read n
solve :: String -> Int
solve = length . filter (== 0) . drop 1 . scanl step 50 . map parse. lines
where step pos d = (pos + d) `mod` 100
And I just found this different method, which works with the second part (not best performance, but interesting idea)
type CircularList a = ([a], a, [a])
dial = ([49,48..0], 50, [51..99])
moveRight :: CircularList a -> CircularList a
moveRight (ls, f, r:rs) = (f:ls, r, rs)
moveRight (ls, f, []) = moveRight ([], f, reverse (f:ls))
moveLeft :: CircularList a -> CircularList a
moveLeft (l:ls, f, rs) = (ls, l, f:rs)
moveLeft ([], f, rs) = moveLeft (reverse (f:rs), f, [])
focus :: CircularList a -> a
focus (_, f, _) = f
EDIT: Regarding the performance issue, that’s why the first list is represented in reverse. we only pay for the reverse when we “run out” on one side, but each element only gets moved across once O(1) complexity.