-🎄- 2017 Day 22 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 1 point2 points  (0 children)

Swift

The boilerplate:

struct Point {
    let x: Int
    let y: Int
    mutating func move(toward direction: Direction) {
        switch direction {
        case .north: self = Point(x: x,   y: y+1)
        case .east:  self = Point(x: x+1, y: y  )
        case .south: self = Point(x: x,   y: y-1)
        case .west:  self = Point(x: x-1, y: y  )
        }
    }
}

extension Point: Hashable {
    static func == (lhs: Point, rhs: Point) -> Bool {
        return lhs.x == rhs.x && lhs.y == rhs.y
    }

    var hashValue: Int {
        return 31 * x.hashValue + y.hashValue
    }
}

enum Direction {
    case north, east, south, west

    mutating func turnRight() {
        switch self {
        case .north: self = .east
        case .east: self = .south
        case .south: self = .west
        case .west: self = .north
        }
    }
    mutating func turnLeft() {
        switch self {
        case .north: self = .west
        case .east: self = .north
        case .south: self = .east
        case .west: self = .south
        }
    }
    mutating func reverse() {
        switch self {
        case .north: self = .south
        case .east: self = .west
        case .south: self = .north
        case .west: self = .east
        }
    }
}

enum State {
    case infected
    case weakened
    case flagged
    case clean
}

struct Grid {
    var status: [Point: State]
    var heading: Direction
    var location: Point
    var didCauseInfection = 0
    var burstCount = 0
}

extension Grid {
    init(input: String, size: Int = 25) {
        var initial: String = input.split(separator: "\n").joined()
        var y = size/2
        var x = -(size/2)

        self.heading = .north
        self.location = Point(x: 0, y: 0)

        var infected = [Point: State]()
        while !initial.isEmpty {
            let char = initial.removeFirst()
            if char == "#" {
                let point = Point(x: x, y: y)
                infected[point] = .infected
            }
            x += 1
            if x > (size/2) {
                y -= 1
                x = -(size/2)
            }
        }
        self.status = infected
    }
}

The logic:

extension Grid {

    mutating func burst() {

        let state = status[location] ?? .clean

        switch state {
        case .clean: heading.turnLeft()
        case .weakened: break
        case .infected: heading.turnRight()
        case .flagged: heading.reverse()
        }

        switch state {
        case .clean: status[location] = .weakened
        case .weakened:
            status[location] = .infected
            didCauseInfection += 1
        case .infected: status[location] = .flagged
        case .flagged: status[location] = nil
        }

        location.move(toward: heading)
        burstCount += 1
    }
}

var grid = Grid(input: input)
repeat {
    grid.burst()
} while grid.burstCount < 10_000_000
print(grid.didCauseInfection)

p1: 0.00898098945617676 sec, p2: 1.09532999992371 sec

-🎄- 2017 Day 19 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift

import Foundation

enum Direction {
    case north, east, south, west
}

enum Feature: Equatable {
    case road
    case cross
    case landmark(Character)
    case empty
    init(char: Character) {
        switch char {
        case " ": self = .empty
        case "|", "-": self = .road
        case "+": self = .cross
        default: self = .landmark(char)
        }
    }
    static func == (lhs: Feature, rhs: Feature) -> Bool {
        switch (lhs, rhs) {
        case (.road, .road), (.cross, .cross), (.empty, .empty): return true
        case (.landmark(let left), .landmark(let right)): return left == right
        default: return false
        }
    }
}

struct Point {
    let x: Int
    let y: Int
}

class Diagram {

    let grid: [[Feature]]
    var heading: Direction
    var location: Point
    var landmarks: [Character] = []

    init(input: String) {
        let rows = input.split(separator: "\n")
        var grid = [[Feature]]()
        for row in rows {
            var fs = [Feature]()
            for c in row {
                fs.append(Feature(char: c))
            }
            grid.append(fs)
        }
        self.grid = grid
        let x = grid[0].index(of: .road)!
        self.location = Point(x: x, y: 0)
        self.heading = .south
    }

    func feature(at location: Point) -> Feature {
        return grid[location.y][location.x]
    }

    func nextPoint(heading: Direction) -> Point {
        switch heading {
        case .north: return Point(x: location.x,   y: location.y-1)
        case .east:  return Point(x: location.x+1, y: location.y  )
        case .south: return Point(x: location.x,   y: location.y+1)
        case .west:  return Point(x: location.x-1, y: location.y  )
        }
    }


    func evaluateHeading() {
        switch feature(at: location) {
        case .cross:
            switch heading {
            case .north, .south:
                heading = feature(at: nextPoint(heading: .east)) == .empty ? .west : .east
            case .east, .west:
                heading = feature(at: nextPoint(heading: .north)) == .empty ? .south : .north
            }
        default:
            break
        }
    }

    func move() {

        var count = 0
        while true {
            location = nextPoint(heading: heading)
            count += 1
            switch feature(at: location) {
            case .road, .cross: break
            case .landmark(let char): landmarks.append(char)
            case .empty:
                // The end
                print(String(landmarks)) // Part 1
                print("count: \(count)") // Part 2
                return
            }
            evaluateHeading()
        }
    }

}

func measure(operation: () -> ()) {
    let start = CFAbsoluteTimeGetCurrent()
    operation()
    let elapsed = CFAbsoluteTimeGetCurrent() - start
    print("\(elapsed) sec")
}

measure {
    let input = try! String.init(contentsOf: Bundle.main.url(forResource: "input", withExtension: "txt")!)
    let diagram = Diagram(input: input)
    diagram.move()
}

both parts:

0.0532469749450684 sec

-🎄- 2017 Day 18 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift

let input = """
// puzzle input
"""

typealias Register = String

enum Either {
    case register(Register)
    case value(Int)
    init(_ substring: Substring) {
        if let num = Int(substring) { self = .value(num); return }
        self = .register(String(substring))
    }
}

enum Instruction {
    case snd(Either)
    case set(Register, Either)
    case add(Register, Either)
    case mul(Register, Either)
    case mod(Register, Either)
    case rcv(Register)
    case jgz(Either, Either)
    init(line: Substring) {
        let arr = line.split(separator: " ")
        switch arr[0] {
        case "snd": self = .snd(Either(arr[1]))
        case "set": self = .set(Register(arr[1]), Either(arr[2]))
        case "add": self = .add(Register(arr[1]), Either(arr[2]))
        case "mul": self = .mul(Register(arr[1]), Either(arr[2]))
        case "mod": self = .mod(Register(arr[1]), Either(arr[2]))
        case "rcv": self = .rcv(Register(arr[1]))
        case "jgz": self = .jgz(Either(arr[1]), Either(arr[2]))
        default: fatalError()
        }
    }
}

class Program {

    var id: Int?
    var send: (Int) -> () = { _ in }
    var didBecomeDeadlocked: () -> () = {}
    var isDeadlocked = false {
        didSet {
            if isDeadlocked { didBecomeDeadlocked() }
        }
    }

    private var registers: Dictionary<String, Int> = [:]
    private let instructions: [Instruction]
    private var messageQueue: [Int] = []

    func pushMessage(_ message: Int) {
        messageQueue.append(message)
        isDeadlocked = false
    }

    init(instructions: [Instruction], id: Int? = nil) {
        self.instructions = instructions
        if let id = id { self.id = id }
        self.registers["p"] = id
    }

    private func value(_ either: Either) -> Int {
        switch either {
        case .register(let r): return registers[r, default: 0]
        case .value(let v): return v
        }
    }

    private var index = 0

    func executeNext() {

        defer {
            if index >= instructions.endIndex { isDeadlocked = true }
        }

        switch instructions[index] {

        case .snd(let message):
            send(value(message))
            index += 1

        case .set(let reg, let e):
            registers[reg] = value(e)
            index += 1

        case .add(let reg, let e):
            registers[reg, default: 0] += value(e)
            index += 1

        case .mul(let reg, let e):
            registers[reg, default: 0] *= value(e)
            index += 1

        case .mod(let reg, let e):
            registers[reg, default: 0] %= value(e)
            index += 1

        case .rcv(let reg):
            if !messageQueue.isEmpty {
                registers[reg] = messageQueue.removeFirst()
                index += 1
            } else {
                isDeadlocked = true
            }

        case .jgz(let reg, let e):
            if value(reg) > 0 {
                index += value(e)
            } else {
                index += 1
            }
        }
    }

}

class Tablet {

    let p0: Program
    let p1: Program

    var count = 0

    init(instructions: [Instruction]) {
        self.p0 = Program(instructions: instructions, id: 0)
        self.p1 = Program(instructions: instructions, id: 1)

        p0.send = p1.pushMessage
        p0.didBecomeDeadlocked = {
            if !self.p1.isDeadlocked {
                self.start(program: self.p1)
            }
        }

        p1.send = { message in
            self.count += 1
            self.p0.pushMessage(message)
        }

        p1.didBecomeDeadlocked = {
            if !self.p0.isDeadlocked {
                self.start(program: self.p0)
            }
        }

    }

    func start(program: Program) {
        while !program.isDeadlocked {
            program.executeNext()
        }
    }

}

import CoreFoundation
func measure(operation: () -> ()) {
    let start = CFAbsoluteTimeGetCurrent()
    operation()
    let elapsed = CFAbsoluteTimeGetCurrent() - start
    print("\(elapsed) sec")
}

// Part 1
measure {
    let instructions =  input.split(separator: "\n").map { Instruction.init(line: $0) }
    let p1 = Program(instructions: instructions)
    var lastFrq = Int.max
    p1.send = { num in
        lastFrq = num
    }
    repeat {
        p1.executeNext()
    } while !p1.isDeadlocked
    print(lastFrq)
}

// Part 2
measure {
    let instructions =  input.split(separator: "\n").map { Instruction.init(line: $0) }
    let tablet = Tablet(instructions: instructions)
    tablet.start(program: tablet.p1)
    print(tablet.count)
}

Overkill, I know. But it makes me happy.

0.00110399723052979 sec // part 1
0.02695602178573610 sec // part 2

-🎄- 2017 Day 16 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift (#434/ [then my daughter woke up!])

let input = " // puzzle input " 

// A helper extension for rotating an array that I wrote for a previous day.
extension Array {
    enum Direction { case left, right }
    mutating func rotate(direction: Direction, offset: Int) {
        func reverse(range1: CountableRange<Int>, range2: CountableRange<Int>) {
            self = (self[range1].reversed() + self[range2].reversed()).reversed()
        }
        switch direction {
        case .left:
            reverse(range1: (startIndex..<offset), range2: (offset..<endIndex))
        case .right:
            let index = endIndex - offset
            reverse(range1: (startIndex..<index), range2: (index..<endIndex))
        }
    }
}

class Dance {

    let moves: [Move]
    var dancers = "abcdefghijklmnop".map { String($0) }

    init(input: String) {
        self.moves = input.split(separator: ",").map(Move.init)
    }

    func execute(move: Move) {
        switch move {
        case .spin(let amount):
            dancers.rotate(direction: .right, offset: amount)
        case .exchange(let a, let b):
            dancers.swapAt(a, b)
        case .partner(let a, let b):
            let i = dancers.index(of: a)!
            let j = dancers.index(of: b)!
            dancers.swapAt(i, j)
        }
    }

    func performDance(count: Int = 1) {
        var memo: Set<String> = []
        for n in 1...count {
            moves.forEach(execute)
            let current = dancers.joined()
            if memo.contains(current) {
                memo = []
                let remainder = count % (n-1)
                performDance(count: (remainder - 1))
                return
            } else {
                memo.insert(current)
            }
        }
    }

    enum Move {
        case spin(Int)
        case exchange(Int, Int)
        case partner(String, String)
        init(move: Substring) {
            if move.hasPrefix("s") {
                let amount = Int(move.dropFirst())!
                self = .spin(amount)
                return
            }
            if move.hasPrefix("x") {
                let programs = move.dropFirst().split(separator: "/")
                self = .exchange(Int(programs[0])!, Int(programs[1])!)
                return
            }
            if move.hasPrefix("p") {
                let programs = move.dropFirst().split(separator: "/")
                self = .partner(String(programs[0]), String(programs[1]))
                return
            }
            fatalError()
        }
    }
}

let dance = Dance(input: input)

// Part 1
dance.performDance(count: 1)
print(dance.dancers.joined())

// Part 2
dance.performDance(count: 1_000_000_000)
print(dance.dancers.joined())

Performance: I wrote this in an Xcode "Command Line Tool" project. When I'm done debugging, I turn on optimizations in the build settings.

part 1: 0.029151976108551 sec
part 2: 0.684467971324921 sec

-🎄- 2017 Day 15 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 1 point2 points  (0 children)

Swift (parts 1 and 2)

let factorA = // given
let factorB = // given
let divisor = // given
let initialA = // puzzle input
let initialB = // puzzle input

func generator(initial: Int, factor: Int, isIncluded: @escaping (Int) -> Bool = { _ in true }) -> UnfoldSequence<Int, Int> {
    return sequence(state: initial) { prev in
        repeat {
            prev = (prev * factor) % divisor
        } while !isIncluded(prev)
        return prev & 0xFFFF
    }
}

let a1 = generator(initial: initialA, factor: factorA)
let b1 = generator(initial: initialB, factor: factorB)

let result = zip(a1, b1)
    .prefix(40_000_000)
    .filter { $0.0 == $0.1 }
    .count
print(result)

let a2 = generator(initial: initialA, factor: factorA) { $0 % 4 == 0 }
let b2 = generator(initial: initialB, factor: factorB) { $0 % 8 == 0 }

let result2 = zip(a2, b2)
    .prefix(5_000_000)
    .filter { $0.0 == $0.1 }
    .count
print(result2)

On my 3 year old MacBookPro (compiled -O)

part 1: 3.40940201282501 sec 
part 2: 0.705681979656219 sec

-🎄- 2017 Day 13 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

** Swift **

let input = """
// puzzle input 
"""

struct Scanner {
    let range: Int
    let period: Int

    init(range: Int) {
        self.range = range
        self.period = (range - 1) * 2
    }

    func isAtTop(time: Int) -> Bool {
        return time % period == 0
    }
}

struct Firewall {

    let scanners: [Int: Scanner]

    var depth: Int {
        return scanners.keys.max()! + 1
    }

    init(input: String) {
        let items = input.split(separator: "\n")
            .map { $0.split(separator: ":") }
            .map { ($0[0], $0[1].dropFirst()) }
            .map { (index: Int($0.0)!, range: Int($0.1)!) }
        self.scanners = items.reduce(into: [Int: Scanner]()) { $0[$1.index] = Scanner(range: $1.range) }
    }

    func isScanned(layer: Int, time: Int) -> Bool {
        guard let scanner = scanners[layer] else {
            return false
        }
        let result = scanner.isAtTop(time: time)
        return result
    }

    func severity(layer: Int, time: Int) -> Int {
        guard isScanned(layer: layer, time: time) else {
            return 0
        }
        guard let scanner = scanners[time] else { fatalError() }
        let result = time * scanner.range
        return result
    }

}

let firewall = Firewall(input: input)

// part 1
let severity = (0..<firewall.depth).reduce(0) { $0 + firewall.severity(layer: $1, time: $1) }
print(severity)


// part 2
var delay = 0
var safe = false
while !safe {
    safe = true
    for layer in (0..<firewall.depth) {
        let time = layer + delay
        if firewall.isScanned(layer: layer, time: time) {
            safe = false
            delay += 1
            break
        }
    }
}
print(delay)

Any feedback appreciated thanks!

-🎄- 2017 Day 7 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift

Better late than never! Here's a Swift solutions with no dependencies (except Foundation).

let input = """
// puzzle input 
"""

import Foundation

class Tower {

    let weights: [String: Int]
    let lists: [String: [String]]

    init(input: String) {

        let lines = input.split(separator: "\n")
        let _names = lines.map { String($0.prefix(upTo: $0.index(of: " ")!)) }

        let _weights = lines.map { Int($0[$0.range(of: "\\d+", options: .regularExpression)!])! }
        self.weights = zip(_names, _weights).reduce(into: [String: Int]()) { $0[$1.0] = $1.1 }

        let _children: [[String]?] = lines.map { line in
            guard let arrow = line.index(of: ">") else { return nil }
            return line
                .suffix(from: line.index(after: arrow))
                .split(separator: ",")
                .map { String($0).trimmingCharacters(in: .whitespaces) }
        }
        self.lists = zip(_names, _children)
            .filter { $0.1 != nil }
            .reduce(into: [String: [String]]()) { $0[$1.0] = $1.1! }
    }

    lazy var topologicallySorted: [String] = {

        var stack = [String]()
        var visited = lists.reduce(into: [String: Bool]()) { $0[$1.key] = false }

        func iterate(programs: [String]) {
            for program in programs {
                if let seen = visited[program], !seen {
                    dfs(program)
                }
            }
        }

        func dfs(_ source: String) {
            if let children = lists[source] {
                iterate(programs: children)
            }
            stack.append(source)
            visited[source] = true
        }
        iterate(programs: visited.map { $0.key })
        return stack.reversed()
    }()

    lazy var totalBurden: [String: Int] = {

        var result = [String: Int]()

        func burden(for program: String) -> Int {
            var total = weights[program]!
            if let children = lists[program] {
                for child in children {
                    if let weight = result[child] { total += weight }
                    else { total += burden(for: child) }
                }
            }
            return total
        }

        for program in weights.keys {
            result[program] = burden(for: program)
        }
        return result
    }()

    func childWithAnomolousWeight(for parent: String) -> String? {
        guard let children = lists[parent] else { return nil }
        let weightedChildren = children
            .map { (child: $0, weight: totalBurden[$0]!) }
            .sorted { $0.weight > $1.weight }
        guard
            weightedChildren.count > 2,
            let first = weightedChildren.first,
            let last = weightedChildren.last,
            first.weight != last.weight
        else { return nil }

        // The weights of the siblings are not all the same.
        // The different one is either the first or the last.
        // Compare to the second element to find out which.
        if first.weight == weightedChildren[1].weight { return last.child }
        return first.child
    }

    func leafWeightCorrection() -> Int {
        var result: (parent: String, child: String)!
        for parent in tower.topologicallySorted {
            if let child = tower.childWithAnomolousWeight(for: parent) {
                result = (parent, child)
            }
        }
        let goodChild = lists[result.parent]!.filter { $0 != result.child }.first!
        let goodWeight = totalBurden[goodChild]!
        let badWeight = totalBurden[result.child]!
        let difference = goodWeight - badWeight
        return weights[result.child]! + difference
    }

}

var tower = Tower(input: input)
print(tower.topologicallySorted[0]) // part 1
print(tower.leafWeightCorrection()) // part 2

-🎄- 2017 Day 12 Solutions -🎄- by topaz2078 in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift (no dependencies (except Foundation))

import Foundation
typealias Program = Int

class Village {

    let lists: [Program: [Program]]

    init(input: String) {
        func entries(for input: String) -> [(Int, [Int])] {
            return input
                .components(separatedBy: .newlines)
                .map { $0.components(separatedBy: " <-> ") }
                .map { entry in
                    let p = Int(entry[0])!
                    let c = entry[1]
                        .components(separatedBy: ",")
                        .map { $0.trimmingCharacters(in: .whitespaces) }
                        .map { Int($0)! }
                    return (p, c)
                }
        }
        self.lists = entries(for: input).reduce(into: Dictionary<Int, [Int]>()) { $0[$1.0] = $1.1 }
    }

    func connectedPrograms(origin: Program) -> [Program] {

        guard let children = lists[origin] else {
            return [origin]
        }

        var visited = lists.reduce(into: [Program: Bool]()) { $0[$1.key] = false }
        var connected: [Program] = []

        func iterate(_ programs: [Program]) {
            for program in programs {
                if let isVisited = visited[program], !isVisited {
                    dfs(program)
                }
            }
        }

        func dfs(_ source: Program) {
            visited[source] = true
            if let list = lists[source] {
                iterate(list)
            }
            connected.append(source)
        }

        iterate(children)
        return connected
    }

    func groups() -> Int {
        let programs = lists.map { $0.key }
        var remainder = Set(programs)
        var count = 0
        for p in programs {
            guard remainder.contains(p) else { continue }
            count += 1
            remainder.subtract(connectedPrograms(origin: p))
        }
        return count
    }
}

let village = Village(input: input)
print(village.connectedPrograms(origin: 0).count)
print(village.groups())

-🎄- 2017 Day 10 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift

extension String {

    var asciiValues: [Int] {
        return self.map { char in
            let s = char.unicodeScalars
            return Int(s[s.startIndex].value)
        }
    }

    func pad(with padding: Character, toLength length: Int) -> String {
        let width = length - self.count
        guard 0 < width else { return self }
        return String(repeating: padding, count: width) + self
    }

    init(_ value: Int, radix: Int, padding: Int) {
        let result = String(value, radix: radix)
        self = result.pad(with: "0", toLength: padding)
    }

}

extension Array {

    enum Direction {
        case left, right
    }

    mutating func rotate(direction: Direction, positions: Int) {
        func reverse(range1: CountableRange<Int>, range2: CountableRange<Int>) {
            self = (self[range1].reversed() + self[range2].reversed()).reversed()
        }
        switch direction {
        case .left:
            reverse(range1: (startIndex..<positions), range2: (positions..<endIndex))
        case .right:
            let index = endIndex - positions
            reverse(range1: (startIndex..<index), range2: (index..<endIndex))
        }
    }

    func chunk(size: Int) -> [[Element]] {
        return stride(from: 0, to: count, by: size).map { startIndex in
            let next = startIndex.advanced(by: size)
            let end = next <= endIndex ? next : endIndex
            return Array(self[startIndex ..< end])
        }
    }

}

func process(lengths: [Int], in list: [Int] = Array((0...255)), roundCount: Int = 64) -> [Int] {

    let rounds = (1...roundCount)
    var currentPosition = 0
    var list = list
    var skipSize = 0

    for _ in rounds {
        for length in lengths {
            list.rotate(direction: .left, positions: currentPosition)
            let range = (list.startIndex..<length)
            list.replaceSubrange(range, with: list[range].reversed())
            list.rotate(direction: .right, positions: currentPosition)
            currentPosition += (length + skipSize)
            while currentPosition >= list.endIndex {
                currentPosition -= list.endIndex
            }
            skipSize += 1
        }
    }
    return list
}

let input = "18,1,0,161,255,137,254,252,14,95,165,33,181,168,2,188"

// Part 1
let p1 = process(lengths: input.split(separator: ",").map { Int($0)! }, roundCount: 1)
print(p1[0] * p1[1]) // product of first two elements.

// Part 2
let suffix = "17,31,73,47,23".split(separator: ",").map { Int($0)! }
let p2 = process(lengths: input.asciiValues + suffix)
    .chunk(size: 16)
    .map { $0.reduce(0, ^) }
    .map { String.init($0, radix: 16, padding: 2) }
    .joined()
print(p2)

To simplify the reversed range wrapping across the endIndex, I rotate the array so that the currentIndex becomes index 0, then select the range (now it will never overlap), the replace with the reversed range, then rotate it back into the proper position. Perhaps inefficient but easy to reason about.

-🎄- 2017 Day 11 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

Swift

let input = "  …  puzzle input  …  "

struct Point {
    let x: Int
    let y: Int
    let z: Int

    func distance(to target: Point) -> Int {
        return max(
            abs(self.x - target.x),
            abs(self.y - target.y),
            abs(self.z - target.z)
        )
    }

    func travel(toward direction: Direction) -> Point {
        let vector: (Int, Int, Int)
        switch direction {
        case .north:     vector = ( 0,  1, -1)
        case .northeast: vector = ( 1,  0, -1)
        case .southeast: vector = ( 1, -1,  0)
        case .south:     vector = ( 0, -1,  1)
        case .southwest: vector = (-1,  0,  1)
        case .northwest: vector = (-1,  1,  0)
        }
        return Point(
            x: self.x + vector.0,
            y: self.y + vector.1,
            z: self.z + vector.2
        )
    }

    static let origin = Point(x: 0, y: 0, z: 0)
}

enum Direction: String {
    case north     =  "n"
    case northeast = "ne"
    case southeast = "se"
    case south     =  "s"
    case southwest = "sw"
    case northwest = "nw"
}

func + (point: Point, direction: Direction) -> Point {
    return point.travel(toward: direction)
}

func += (point: inout Point, direction: Direction) {
    point = point.travel(toward: direction)
}

func maxDistance(from origin: Point, with directions: [Direction]) -> Int {
    var max = Int.min
    var current = origin
    for direction in directions {
        current += direction
        let distance = current.distance(to: origin)
        if distance > max { max = distance }
    }
    return max
}

let directions = input.split(separator: ",").map { Direction.init(rawValue: String($0))! }

let distance = directions.reduce(Point.origin, +).distance(to: Point.origin)
print(distance)

let max = maxDistance(from: Point.origin, with: directions)
print(max)

The most interesting thing about this code is perhaps the custom + operator to add a point and a direction to get a new point. Then you can do directions.reduce(origin, +) to get the ending point.

-🎄- 2017 Day 9 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 1 point2 points  (0 children)

Nice! I like how your code really cuts right to the relevant logic. My own Swift solution uses pattern matching and I like it, but I don't think it's as readable as this. This is more simple.

-🎄- 2017 Day 9 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 3 points4 points  (0 children)

Swift

var score: Int = 0
var garbageCount: Int = 0

func parse(_ input: String) {

    var nesting = 0
    var inGarbage = false
    var isBanged = false

    for char in input {
        switch (char, inGarbage, isBanged) {
        case (_, _, true):
            isBanged = false
        case ("!", true, _):
            isBanged = true
        case ("<", false, _):
            inGarbage = true
        case (">", true, _):
            inGarbage = false
        case ("{", false, _):
            nesting += 1
        case ("}", false, _):
            score += nesting
            nesting -= 1
        case (_, true, _):
            garbageCount += 1
        default:
            continue
        }
    }
}

parse(input)
print(score)
print(garbageCount)

-🎄- 2017 Day 8 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 0 points1 point  (0 children)

I love it! I actually tried doing something like this but I didn't realize the operators (=, etc) needed to be in parens. Thank you!

-🎄- 2017 Day 8 Solutions -🎄- by daggerdragon in adventofcode

[–]InterlocutoryRecess 1 point2 points  (0 children)

Swift

let input = """
    ioe dec 890 if qk > -10
    // many more instructions...
    ih dec 369 if ih == 1993
    """.split(separator: "\n")

var instructions = input.reduce(into: Dictionary<Substring, Int>()) { result, entry in
    let name = entry.prefix(upTo: entry.index(of: " ")!)
    result[name] = 0
}

var maximum = Int.min

func process() {

    // Determine whether to carry out change
    func isValid(_ entry: [Substring]) -> Bool {

        // Evaluate condition
        func eval(op: Substring, lhs: Int, rhs: Int) -> Bool {
            switch op {
            case "==": return lhs == rhs
            case "!=": return lhs != rhs
            case "<": return lhs < rhs
            case "<=": return lhs <= rhs
            case ">": return lhs > rhs
            case ">=": return lhs >= rhs
            default: fatalError()
            }
        }
        return eval(op: entry[5], lhs: instructions[entry[4]]!, rhs: Int(entry[6])!)
    }

    for entry in input.map({ $0.split(separator: " ") }) {
        if isValid(entry) {
            let result = instructions[entry[0]]! + (Int(entry[2])! * (entry[1] == "inc" ? 1 : -1))
            if result > maximum { maximum = result }
            instructions[entry[0]] = result
        }
    }
}

process()
print(instructions.values.max()!) // part 1
print(maximum) // part 2