DEV Community

Discussion on: Advent of Code 2019 Solution Megathread - Day 15: Oxygen System

Collapse
 
rizzu26 profile image
Rizwan

let to the party. Swift here --->

enum Direction: Int, CaseIterable {
    case north  = 1
    case south  = 2
    case west = 3
    case east = 4

    func next() -> Self {
        switch self {
        case .north:
            return .south
        case .south :
            return .west
        case .west:
            return .east
        case .east:
            return .north
        }
    }
}

enum Status: Int, CustomStringConvertible {
    var description: String {
        get {
            desc()
        }
    }

    func desc() -> String {
        switch self {
        case .wall:
            return "Wall"
        case .moved:
            return "Moved"
        case .oxygen:
            return "Oxygen System"
        }
    }

    case wall = 0
    case moved = 1
    case oxygen = 2
}

struct Point: CustomStringConvertible, Equatable, Hashable {
    var x: Int
    var y: Int

    var description: String {
        get {
            return "X: \(self.x) Y: \(self.y)"
        }
    }

    func move(_ direction: Direction) -> Point {
        switch direction {
        case .north:
            return Point.init(x: x, y: y + 1)
        case .south:
            return Point.init(x: x, y: y - 1)
        case .west:
            return Point.init(x: x - 1, y: y)
        case .east:
            return Point.init(x: x + 1, y: y)
        }
    }

    func distance() -> Int {
        return abs(x) + abs(y)
    }
}

class Driod {
    var grid: [Point: Status] = [:]
    var memory: [Int]
    let computer: Opcode

    init(_ memory: [Int]) {
        self.memory = memory
        self.computer = Opcode.init(memory, 0)
    }

    func search() {
        var queue: [Point] = [Point]()
        queue.insert(Point.init(x: 0, y: 0), at: 0)
        var seen:[Point:Opcode] = [Point.init(x: 0, y: 0): Opcode.init(memory)]

        while !queue.isEmpty {
            let currentP = queue.removeLast()
            Direction.allCases.forEach { (direction) in
                let pos = currentP.move(direction)
                guard seen[pos] == nil else { return }
                let computer = seen[currentP]!.clone()
                computer.inputIds.removeAll()
                computer.inputIds.append(direction.rawValue)
                let status = Status.init(rawValue: computer.run())
                seen[pos] = computer
                grid[pos] = status
                if status != .wall {
                    queue.insert(pos, at: 0)
                }
            }
        }
    }

    func stepsToO2() -> Int {
        var queue: Array<(steps:Int,point:Point)> = Array<(steps:Int,point:Point)>()
        queue.insert((steps:0,point:Point.init(x: 0, y: 0)), at: 0)
        var seen: Set<Point> = Set<Point>()
        var stepsToO2 = -1
        while !queue.isEmpty && stepsToO2 == -1 {
            let tuple = queue.removeLast()
            let steps = tuple.steps
            let point = tuple.point
            seen.insert(point)

            Direction.allCases.forEach { (direction) in
                let pos = point.move(direction)
                if !seen.contains(pos) {
                    let status = grid[pos, default: .wall]
                    if status != .wall {
                        queue.insert((steps:steps+1,point:pos), at:0)
                    }
                    if status == .oxygen {
                        stepsToO2 = steps + 1
                    }
                }
            }
        }
        return stepsToO2
    }

    func minToFillO2() -> Int {
        let o2Point = grid.first(where: { $0.value == .oxygen })!.key
        var queue: [Point] = [Point]()
        queue.insert(o2Point, at: 0)
        var seen: Set<Point> = Set<Point>()
        var min = -1

        while !queue.isEmpty {
            var queueNext: [Point] = []
            while !queue.isEmpty {
                let currentP = queue.removeLast()
                seen.insert(currentP)

                Direction.allCases.forEach { (direction) in
                    let pos = currentP.move(direction)

                    guard !seen.contains(pos),
                        grid[pos, default: .wall] == .moved else {
                        return
                    }
                    queueNext.append(pos)
                }
            }
            queue = queueNext
            min += 1
        }

        return min
    }
}

func partOne() {
    let driod = Driod(input)
    driod.search()
    print("Part 1 answer is :\(driod.stepsToO2())")
}

func partTwo() {
    let driod = Driod(input)
    driod.search()
    print("Part 2 answer is :\(driod.minToFillO2())")
}

partOne()
partTwo()