VegOwOtenks

joined 7 months ago
[โ€“] VegOwOtenks 1 points 6 days ago (1 children)

Not the first year I participate but the first year I finished, 2021 was my all-time high so far with 42 stars when I was just starting oit and learning python. Knowing that there were more people in the same boat and that there was a competition kept me going, although the competiton also induced a lot of stress, not sure whether I want to keep the competitive attitude.

Thanks to everyone for uploding solutions, Ideas and program stats, this kept me optimizing away, which was a lot of fun!

[โ€“] VegOwOtenks 3 points 1 week ago (1 children)

I alwqys assumed you were Cameron Wu, who is?

[โ€“] VegOwOtenks 3 points 1 week ago* (last edited 1 week ago)

Haskell

Have a nice christmas if you're still celebrating today, otherwise hope you had a nice evening yesterday.

import Control.Arrow
import Control.Monad (join)
import Data.Bifunctor (bimap)
import qualified Data.List as List

heights = List.transpose
        >>> List.map (pred . List.length . List.takeWhile (== '#'))

parse = lines
        >>> init
        >>> List.groupBy (curry (snd >>> (/= "")))
        >>> List.map (List.filter (/= ""))
        >>> List.partition ((== "#####") . head)
        >>> second (List.map List.reverse)
        >>> join bimap (List.map heights)

cartesianProduct xs ys = [(x, y) | x <- xs, y <- ys]

part1 = uncurry cartesianProduct
        >>> List.map (uncurry (List.zipWith (+)))
        >>> List.filter (List.all (<6))
        >>> List.length
part2 = const 0

main = getContents
        >>= print
        . (part1 &&& part2)
        . parse
[โ€“] VegOwOtenks 2 points 1 week ago (1 children)

Thank you for showing this trick, I knew Haskell was lazy but this one blew my mind again.

[โ€“] VegOwOtenks 3 points 1 week ago

Haskell

Part 1 was trivial, just apply the operations and delay certain ones until you have all the inputs you need.

Code

import Control.Arrow
import Data.Bits
import Numeric

import qualified Data.Char as Char
import qualified Data.List as List
import qualified Data.Map as Map

parse s = (Map.fromList inputs, equations)
        where
                ls = lines s
                inputs = map (take 3 &&& (== "1") . drop 5) . takeWhile (/= "") $ ls
                equations = map words . filter (/= "") . tail . dropWhile (/= "") $ ls

operations = Map.fromList
        [ ("AND", (&&))
        , ("XOR", xor)
        , ("OR", (||))
        ]

solveEquations is []     = is
solveEquations is (e:es)
        | is Map.!? input1 == Nothing = solveEquations is (es ++ [e])
        | is Map.!? input2 == Nothing = solveEquations is (es ++ [e])
        | otherwise      = solveEquations (Map.insert output (opfunc value1 value2) is) es
        where
                value1 = is Map.! input1
                value2 = is Map.! input2
                opfunc = operations Map.! operation
                (input1:operation:input2:_:output:[]) = e

wireNumber prefix = List.filter ((prefix `List.isPrefixOf`) . fst)
        >>> flip zip [0..]
        >>> List.filter (snd . fst)
        >>> List.map ((2 ^ ). snd)
        >>> sum

part1 = uncurry solveEquations
        >>> Map.toList
        >>> wireNumber "z"

part2 (is, es) = List.intercalate "," . List.sort . words $ "z08 ffj dwp kfm z22 gjh jdr z31"

main = getContents
        >>= print
        . (part1 &&& part2)
        . parse

For part 2 I tried symbolic solving to detect discrepancies but I wouldn't achieve anything with it.

SymbolicEquation

data SymbolicEquation = Single { eqName :: String }
        | Combine
        { eqName :: String
        , eqOperation :: String
        , eqLeft :: SymbolicEquation
        , eqRight :: SymbolicEquation
        }
        deriving (Eq)

instance Show SymbolicEquation where
        show (Single name) = name
        show (Combine name op l r) = "(" ++ name ++ "= " ++ show l ++ " " ++ op ++ " " ++ show r ++ ")"

symbolicSolve is [] = is
symbolicSolve is (e:es)
        | is Map.!? input1 == Nothing = symbolicSolve is (es ++ [e])
        | is Map.!? input2 == Nothing = symbolicSolve is (es ++ [e])
        | otherwise = symbolicSolve (Map.insert output (Combine output operation value1 value2) is) es
        where
                value1 = is Map.! input1
                value2 = is Map.! input2
                (input1:operation:input2:_:output:[]) = e

My solution was to use the dotEngine-function to translate the operations into a digraph in graphviz-style which I simply plotted and searched through using a python script.

dotEngine

dotEngine (input1:operation:input2:_:output:[]) = [
          input1 ++ " -> " ++ output ++ " [ label=" ++ operation ++ "];"
        , input2 ++ " -> " ++ output ++ " [ label=" ++ operation ++ "];"
        ]

I took a loook at the initial graph which was a vertical line with a few exception which I figured would be the misordered wires. I did try some hardware-simulations in the far past to build bit-adders which helped me recognize patterns like carry calculation. First I replaced all occurences of x__ XOR y__ -> w with x__ XOR y__ -> xor__ to recognize them more easily. The same with AND of xs and ys. Using the following script I would then use some Regex to search for the rules that corresponded to carry calculations or structures I knew. The script would break exactly four times and I would then figure out what to switch by hand through looking at the updated graphViz.

Please excuse the bad coding style in the script, I had written it on the ipython-REPL.

python script

r = open("input").read()
for i in range(2, 45):
    prevI = str(i - 1).zfill(2)
    I = str(i).zfill(2)
    forward = f"xor{I} AND carry{prevI} -> (\\w+)"
    backward = f"carry{prevI} AND xor{I} -> (\\w+)"
    m1 = re.search(forward, r)
    m2 = re.search(backward, r)
    if m1 is None and m2 is None:
        print(forward, backward)
        break
    m = m1 or m2
    r = r.replace(m.group(1), f"combinedCarry{I}")
    forward = f"and{I} OR combinedCarry{I} -> (\\w+)"
    backward = f"combinedCarry{I} OR and{I} -> (\\w+)"
    m1 = re.search(forward, r)
    m2 = re.search(backward, r)
    if m1 is None and m2 is None:
        print(forward, backward)
        break
    m = m1 or m2
    r = r.replace(m.group(1), f"carry{I}")
open("input", "w").write()

When solving such a swapped wire problem I would then use my haskell function to plot it out again and stare at it for a few minutes until I understood wich parts belonged where.

The last one looked like this
GraphViz of the last set of problem wires

In this one I needed to switch jdr and carry31 to make it work.

[โ€“] VegOwOtenks 2 points 1 week ago (1 children)

There probably are multiple ways to partition the graph. I haven't applied any optimizations and my program checks members of already detected groups again, would that yield all possible partitions because I choose all the possible starting points for a k-clique?

[โ€“] VegOwOtenks 2 points 1 week ago (3 children)

Haskell

The solution for part two could now be used for part one as well but then I would have to rewrite part 1 .-.

import Control.Arrow

import Data.Ord (comparing)

import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.Set as Set

parse = Map.fromListWith Set.union . List.map (second Set.singleton) . uncurry (++) . (id &&& List.map (uncurry (flip (,)))) . map (break (== '-') >>> second (drop 1)) . takeWhile (/= "") . lines

depthSearch connections ps
        | length ps == 4 && head ps == last ps = [ps]
        | length ps == 4 = []
        | otherwise  = head
                >>> (connections Map.!)
                >>> Set.toList
                >>> List.map (:ps)
                >>> List.concatMap (depthSearch connections)
                $ ps

interconnections (computer, connections) = depthSearch connections [computer]

part1 = (Map.assocs &&& repeat)
        >>> first (List.map (uncurry Set.insert))
        >>> first (Set.toList . Set.unions)
        >>> uncurry zip
        >>> List.concatMap interconnections
        >>> List.map (Set.fromList . take 3)
        >>> List.filter (Set.fold (List.head >>> (== 't') >>> (||)) False)
        >>> Set.fromList
        >>> Set.size

getLANParty computer connections = (connections Map.!)
        >>> findLanPartyComponent connections [computer]
        $ computer

filterCandidates connections participants candidates = List.map (connections Map.!)
        >>> List.foldl Set.intersection candidates
        >>> Set.filter ((connections Map.!) >>> \ s -> List.all (flip Set.member s) participants)
        $ participants

findLanPartyComponent connections participants candidates
        | Set.null validParticipants = participants
        | otherwise = findLanPartyComponent connections (nextParticipant : participants) (Set.delete nextParticipant candidates)
        where
                nextParticipant = Set.findMin validParticipants
                validParticipants = filterCandidates connections participants candidates

part2 = (Map.keys &&& repeat)
        >>> uncurry zip
        >>> List.map ((uncurry getLANParty) >>> List.sort)
        >>> List.nub
        >>> List.maximumBy (comparing List.length)
        >>> List.intercalate ","

main = getContents
        >>= print
        . (part1 &&& part2)
        . parse
[โ€“] VegOwOtenks 3 points 1 week ago (1 children)

Haskell

I have no Idea how to optimize this and am looking forward to the other solutions that probably run in sub-single-second times. I like my solution because it was simple to write which I hadn't managed in the previous days, runs in 17 seconds with no less than 100MB of RAM.

import Control.Arrow
import Data.Bits (xor)
import Data.Ord (comparing)

import qualified Data.List as List
import qualified Data.Map as Map

parse :: String -> [Int]
parse = map read . filter (/= "") . lines

mix = xor 
prune = flip mod 16777216
priceof = flip mod 10

nextSecret step0 = do
        let step1 = prune . mix step0 $ step0 * 64
        let step2 = prune . mix step1 $ step1 `div` 32
        let step3 = prune . mix step2 $ step2 * 2048
        step3

part1 = sum . map (head . drop 2000 . iterate nextSecret)
part2 = map (iterate nextSecret
                >>> take 2001
                >>> map priceof
                >>> (id &&& tail)
                >>> uncurry (zipWith (curry (uncurry (flip (-)) &&& snd)))
                >>> map (take 4) . List.tails
                >>> filter ((==4) . length)
                >>> map (List.map fst &&& snd . List.last)
                >>> List.foldl (\ m (s, p) -> Map.insertWith (flip const) s p m) Map.empty
                )
        >>> Map.unionsWith (+)
        >>> Map.assocs
        >>> List.maximumBy (comparing snd)

main = getContents
        >>= print
        . (part1 &&& part2)
        . parse
[โ€“] VegOwOtenks 3 points 1 week ago

Thanks a lot for the recommendation, I did enjoy the read!

[โ€“] VegOwOtenks 1 points 1 week ago

Some others have answered already, but yes, there was a well-hidden line in the problem description about the map having only a single path from start to end..

[โ€“] VegOwOtenks 2 points 1 week ago (2 children)

I was wondering about encryption (is this what you're talking about?) because these algorithms change so frequently I'd be surprised if they had anything back then considered 'secure' by now.

73
submitted 1 week ago* (last edited 1 week ago) by VegOwOtenks to c/nostupidquestions
 

I mean the Voyager 1 probe which is currently the human-made object the farthest away from earth. The space program people operating the mission seem to have great control options, they even "moved software from one chip to another" (link) Apart from the probably gigantic and expensive installation needed to receive and/or send messages from/to that far away from home (23 hours of delay?), are there any safety measures to prevent a potentially malicous actor from sending commands to the probe?

[โ€“] VegOwOtenks 2 points 1 week ago* (last edited 1 week ago) (6 children)

Haskell

First parse and floodfill from start, each position then holds the distance from the start

For part 1, I check all neighbor tiles of neighbor tiles that are walls and calculate the distance that would've been in-between.

In part 2 I check all tiles within a manhattan distance <= 20 and calculate the distance in-between on the path. Then filter out all cheats <100 and count

Takes 1.4s sadly, I believe there is still potential for optimization.

Edit: coding style

import Control.Arrow

import qualified Data.List as List
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.Maybe as Maybe

parse s = Map.fromList [ ((y, x), c) | (l, y) <- zip ls [0..], (c, x) <- zip l [0..]]
        where
        ls = lines s

floodFill m = floodFill' m startPosition (Map.singleton startPosition 0)
        where
                startPosition = Map.assocs
                        >>> filter ((== 'S') . snd)
                        >>> head
                        >>> fst
                        $ m

neighbors (p1, p2) = [(p1-1, p2), (p1, p2-1), (p1, p2+1), (p1+1, p2)]

floodFill' m p f
        | m Map.! p == 'E' = f
        | otherwise = floodFill' m n f'
        where
                seconds = f Map.! p
                ns = neighbors p
                n = List.filter ((m Map.!) >>> (`Set.member` (Set.fromList ".E")))
                        >>> List.filter ((f Map.!?) >>> Maybe.isNothing)
                        >>> head
                        $ ns
                f' = Map.insert n (succ seconds) f

taxiCabDistance (a1, a2) (b1, b2) = abs (a1 - b1) + abs (a2 - b2)

calculateCheatAdvantage f (p1, p2) = c2 - c1 - taxiCabDistance p1 p2
        where
                c1 = f Map.! p1
                c2 = f Map.! p2

cheatDeltas :: Int -> Int -> [(Int, Int)]
cheatDeltas l h = [(y, x) | x <- [-h..h], y <- [-h..h], let d = abs x + abs y, d <= h, d >= l]

(a1, a2) .+. (b1, b2) = (a1 + b1, a2 + b2)

solve l h (f, ps) = Set.toList
        >>> List.map ( repeat
                >>> zip (cheatDeltas l h)
                >>> List.map (snd &&& uncurry (.+.))
                >>> List.filter (snd >>> (`Set.member` ps))
                >>> List.map (calculateCheatAdvantage f)
                >>> List.filter (>= 100)
                >>> List.length
                )
        >>> List.sum
        $ ps
part1 = solve 2 2
part2 = solve 1 20

main = getContents
        >>= print
        . (part1 &&& part2)
        . (id &&& Map.keysSet)
        . floodFill
        . parse
 

Up until now I simply used Element, it just works and it doesn't look too bad. Unfortunately, I now have two Matrix accounts, my personal account and the account my university automatically created on their own matrix instance.
I need to communicate using both my accounts now, but Element couldn't handle two accounts at the same time, so I went on to install a second client, Fractal, which also supports multiple accounts. However, I am somewhat unhappy with Fractal because I cannot select text in messages.

Please share your experiences and recommendations with or on matrix clients.

 
-29
submitted 3 months ago by VegOwOtenks to c/memes
 
 
396
Regex flavors (lemmy.world)
 
 
223
getsockname() (lemmy.world)
submitted 7 months ago by VegOwOtenks to c/linuxmemes
 
view more: next โ€บ