Initial commit
This commit is contained in:
commit
dae7c34265
17 changed files with 2001 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.xcodeproj/**/xcuserdata/
|
1236
AdventOfCode2020.xcodeproj/project.pbxproj
Normal file
1236
AdventOfCode2020.xcodeproj/project.pbxproj
Normal file
File diff suppressed because it is too large
Load diff
7
AdventOfCode2020.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
AdventOfCode2020.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Workspace
|
||||||
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "self:">
|
||||||
|
</FileRef>
|
||||||
|
</Workspace>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
30
common/LoadData.swift
Normal file
30
common/LoadData.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
func loadData(day: Int) -> String {
|
||||||
|
guard let session = getenv("SESSION") else {
|
||||||
|
fatalError("Missing session env var")
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = URLRequest(url: URL(string: "https://adventofcode.com/2020/day/\(day)/input")!)
|
||||||
|
request.setValue("session=\(String(cString: session))", forHTTPHeaderField: "Cookie")
|
||||||
|
|
||||||
|
var result: String? = nil
|
||||||
|
|
||||||
|
let group = DispatchGroup()
|
||||||
|
|
||||||
|
group.enter()
|
||||||
|
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
|
||||||
|
guard error == nil, let data = data, let response = response as? HTTPURLResponse, response.statusCode == 200 else {
|
||||||
|
fatalError("Cannot get input from server")
|
||||||
|
}
|
||||||
|
|
||||||
|
result = String(data: data, encoding: .utf8)
|
||||||
|
group.leave()
|
||||||
|
}
|
||||||
|
|
||||||
|
task.resume()
|
||||||
|
|
||||||
|
group.wait()
|
||||||
|
|
||||||
|
return result!
|
||||||
|
}
|
26
common/Scanner+Extensions.swift
Normal file
26
common/Scanner+Extensions.swift
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Scanner {
|
||||||
|
@discardableResult
|
||||||
|
func string(_ string: String) -> Bool {
|
||||||
|
return scanString(string) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func integers() -> [Int]? {
|
||||||
|
var numbers: [Int] = []
|
||||||
|
while !isAtEnd {
|
||||||
|
guard let num = scanInt() else { return nil }
|
||||||
|
numbers.append(num)
|
||||||
|
}
|
||||||
|
return numbers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func lines() -> [String] {
|
||||||
|
var result: [String] = []
|
||||||
|
enumerateLines { line, _ in result.append(line) }
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
21
day1/main.swift
Normal file
21
day1/main.swift
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = Scanner(string: loadData(day: 1)).integers()!
|
||||||
|
|
||||||
|
for i in 0..<input.count {
|
||||||
|
for j in i..<input.count {
|
||||||
|
for k in j..<input.count {
|
||||||
|
let a = input[i]
|
||||||
|
let b = input[j]
|
||||||
|
let c = input[k]
|
||||||
|
|
||||||
|
if k == j && a + b == 2020 {
|
||||||
|
print("1st", a * b)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a + b + c == 2020 {
|
||||||
|
print("2nd", a * b * c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
day10/main.swift
Normal file
64
day10/main.swift
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 10)
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
|
||||||
|
var adapters = scanner.integers()!.sorted { $0 > $1 }
|
||||||
|
adapters.append(0)
|
||||||
|
|
||||||
|
|
||||||
|
let device = adapters[0] + 3
|
||||||
|
|
||||||
|
var current = device
|
||||||
|
var ones = 0
|
||||||
|
var threes = 0
|
||||||
|
|
||||||
|
for adapter in adapters {
|
||||||
|
let step = current - adapter
|
||||||
|
if step == 3 {
|
||||||
|
threes += 1
|
||||||
|
} else if step == 1 {
|
||||||
|
ones += 1
|
||||||
|
} else if step != 2 {
|
||||||
|
print("fail")
|
||||||
|
}
|
||||||
|
current = adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
print("part 1", ones * threes)
|
||||||
|
|
||||||
|
struct Key: Hashable {
|
||||||
|
var start: Int
|
||||||
|
var max: Int
|
||||||
|
}
|
||||||
|
var memo: [Key: Int] = [:]
|
||||||
|
|
||||||
|
func findPaths(start: Int, max: Int) -> Int {
|
||||||
|
if let value = memo[Key(start: start, max: max)] {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
let diff = max - adapters[start]
|
||||||
|
if diff > 3 || diff < 1 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if start == adapters.count - 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = 0
|
||||||
|
for n in (start + 1)..<(adapters.count) {
|
||||||
|
let next = findPaths(start: n, max: adapters[start])
|
||||||
|
if next == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
result += next
|
||||||
|
}
|
||||||
|
|
||||||
|
memo[Key(start: start, max: max)] = result
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
print(findPaths(start: 0, max: device))
|
116
day11/main.swift
Normal file
116
day11/main.swift
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 11)
|
||||||
|
|
||||||
|
enum Spot: Equatable {
|
||||||
|
case empty
|
||||||
|
case occupied
|
||||||
|
case floor
|
||||||
|
}
|
||||||
|
typealias Map = [[Spot]]
|
||||||
|
|
||||||
|
var map: Map = []
|
||||||
|
|
||||||
|
input.enumerateLines { (line, _) in
|
||||||
|
map.append(line.map {
|
||||||
|
switch $0 {
|
||||||
|
case ".": return .floor
|
||||||
|
case "#": return .occupied
|
||||||
|
case "L": return .empty
|
||||||
|
default: fatalError("Invalid input")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let adjacents = [
|
||||||
|
(-1, -1),
|
||||||
|
(0, -1),
|
||||||
|
(1, -1),
|
||||||
|
(-1, 0),
|
||||||
|
(1, 0),
|
||||||
|
(-1, 1),
|
||||||
|
(0, 1),
|
||||||
|
(1, 1)
|
||||||
|
]
|
||||||
|
|
||||||
|
/*
|
||||||
|
func step(map: Map) -> Map {
|
||||||
|
var result = map
|
||||||
|
let width = map[0].count
|
||||||
|
|
||||||
|
for i in 0..<map.count {
|
||||||
|
for j in 0..<width where map[i][j] != .floor {
|
||||||
|
let occupieds = adjacents.reduce(0) { count, offset in
|
||||||
|
let (x, y) = (offset.0 + j, offset.1 + i)
|
||||||
|
guard 0..<map.count ~= y, 0..<width ~= x else { return count }
|
||||||
|
return count + (map[y][x] == .occupied ? 1 : 0)
|
||||||
|
}
|
||||||
|
switch (map[i][j], occupieds) {
|
||||||
|
case (.empty, 0): result[i][j] = .occupied
|
||||||
|
case (.occupied, let n) where n >= 4: result[i][j] = .empty
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
func occupiedInDirection(at pos: (Int, Int), map: Map, direction: (Int, Int)) -> Int {
|
||||||
|
var pos = pos
|
||||||
|
|
||||||
|
let yRange = 0..<map.count
|
||||||
|
let xRange = 0..<map[0].count
|
||||||
|
|
||||||
|
while true {
|
||||||
|
var (i, j) = pos
|
||||||
|
i+=direction.1
|
||||||
|
j += direction.0
|
||||||
|
pos = (i, j)
|
||||||
|
|
||||||
|
guard yRange ~= pos.0, xRange ~= pos.1 else { break }
|
||||||
|
|
||||||
|
let state = map[i][j]
|
||||||
|
switch state {
|
||||||
|
case .floor: break
|
||||||
|
case .occupied: return 1
|
||||||
|
case .empty: return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func step(map: Map) -> Map {
|
||||||
|
var result = map
|
||||||
|
let width = map[0].count
|
||||||
|
|
||||||
|
for i in 0..<map.count {
|
||||||
|
for j in 0..<width where map[i][j] != .floor {
|
||||||
|
let occupieds = adjacents.reduce(0) { count, offset in
|
||||||
|
count + occupiedInDirection(at: (i, j), map: map, direction: offset)
|
||||||
|
}
|
||||||
|
switch (map[i][j], occupieds) {
|
||||||
|
case (.empty, 0): result[i][j] = .occupied
|
||||||
|
case (.occupied, let n) where n >= 5: result[i][j] = .empty
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while true {
|
||||||
|
let next = step(map: map)
|
||||||
|
if next == map {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
map = next
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalOccupieds = map.lazy.flatMap { $0 }.filter { $0 == .occupied }.count
|
||||||
|
print(totalOccupieds)
|
39
day2/main.swift
Normal file
39
day2/main.swift
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 2)
|
||||||
|
|
||||||
|
var valids = 0
|
||||||
|
var valid2 = 0
|
||||||
|
|
||||||
|
var scanner = Scanner(string: input)
|
||||||
|
while !scanner.isAtEnd {
|
||||||
|
guard let min = scanner.scanInt(),
|
||||||
|
scanner.scanString("-") != nil,
|
||||||
|
let max = scanner.scanInt(),
|
||||||
|
let character = scanner.scanCharacter(),
|
||||||
|
scanner.scanString(":") != nil,
|
||||||
|
let password = scanner.scanUpToCharacters(from: .newlines)
|
||||||
|
else {
|
||||||
|
fatalError("Invalid input");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let count = password.lazy.filter { $0 == character }.count
|
||||||
|
|
||||||
|
if min <= count && count <= max {
|
||||||
|
print("valid", min, max, character, password)
|
||||||
|
valids += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
let first = password.index(password.startIndex, offsetBy: min - 1, limitedBy: password.endIndex).map { password[$0] }
|
||||||
|
let second = password.index(password.startIndex, offsetBy: max - 1, limitedBy: password.endIndex).map { password[$0] }
|
||||||
|
|
||||||
|
if (first == character || second == character) && first != second {
|
||||||
|
print("valid 2", password)
|
||||||
|
valid2 += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
print("Valid old", valids)
|
||||||
|
print("Valid new", valid2)
|
32
day3/main.swift
Normal file
32
day3/main.swift
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
let input = loadData(day: 3).lines()
|
||||||
|
|
||||||
|
/// - returns true if a tree is at that position, false if it is empty
|
||||||
|
func get(x: Int, y: Int) -> Bool {
|
||||||
|
let line = input[y]
|
||||||
|
let index = line.index(line.startIndex, offsetBy: x % line.count)
|
||||||
|
return line[index] == "#"
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculate(dx: Int, dy: Int) -> Int {
|
||||||
|
var x = 0
|
||||||
|
var y = 0
|
||||||
|
|
||||||
|
var trees = 0
|
||||||
|
|
||||||
|
while y < input.count {
|
||||||
|
if get(x: x, y: y) {
|
||||||
|
trees += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
x += dx
|
||||||
|
y += dy
|
||||||
|
}
|
||||||
|
|
||||||
|
return trees
|
||||||
|
}
|
||||||
|
|
||||||
|
let trees = calculate(dx: 3, dy: 1)
|
||||||
|
print("trees", trees)
|
||||||
|
|
||||||
|
|
||||||
|
print("combined", calculate(dx: 1, dy: 1) * calculate(dx: 3, dy: 1) * calculate(dx: 5, dy: 1) * calculate(dx: 7, dy: 1) * calculate(dx: 1, dy: 2))
|
99
day4/main.swift
Normal file
99
day4/main.swift
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 4)
|
||||||
|
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
scanner.charactersToBeSkipped = nil
|
||||||
|
|
||||||
|
extension Scanner {
|
||||||
|
func field() -> (String, String)? {
|
||||||
|
guard let name = scanUpToString(":"),
|
||||||
|
scanString(":") != nil,
|
||||||
|
let value = scanUpToCharacters(from: .whitespacesAndNewlines)
|
||||||
|
else { return nil }
|
||||||
|
return (name, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func passport() -> [String: String]? {
|
||||||
|
var fields = [(String, String)]()
|
||||||
|
while !isAtEnd && scanString("\n\n") == nil {
|
||||||
|
_ = scanner.scanCharacters(from: .whitespacesAndNewlines)
|
||||||
|
guard let field = field() else { return nil }
|
||||||
|
fields.append(field)
|
||||||
|
}
|
||||||
|
return try? Dictionary(fields, uniquingKeysWith: { _, _ in throw NSError(domain: "error", code: 1, userInfo: nil) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var valids = 0
|
||||||
|
var valid2 = 0
|
||||||
|
|
||||||
|
let requiredFields = [
|
||||||
|
"byr",
|
||||||
|
"iyr",
|
||||||
|
"eyr",
|
||||||
|
"hgt",
|
||||||
|
"hcl",
|
||||||
|
"ecl",
|
||||||
|
"pid",
|
||||||
|
]
|
||||||
|
|
||||||
|
func validHeight(_ height: String) -> Bool {
|
||||||
|
let scanner = Scanner(string: height)
|
||||||
|
guard let number = scanner.scanInt() else { return false }
|
||||||
|
if scanner.scanString("cm") != nil, 150...193 ~= number {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanner.scanString("in") != nil, 59...76 ~= number {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func validHairColor(_ hcl: String) -> Bool {
|
||||||
|
let scanner = Scanner(string: hcl)
|
||||||
|
guard scanner.scanString("#") != nil else { return false }
|
||||||
|
guard scanner.scanCharacters(from: CharacterSet(charactersIn: "0123456789abcdef"))?.count == 6 else { return false }
|
||||||
|
return scanner.isAtEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
let validEyeColors: Set<String> = [
|
||||||
|
"amb",
|
||||||
|
"blu",
|
||||||
|
"brn",
|
||||||
|
"gry",
|
||||||
|
"grn",
|
||||||
|
"hzl",
|
||||||
|
"oth"
|
||||||
|
]
|
||||||
|
|
||||||
|
func isValidPassportId(_ pid: String) -> Bool {
|
||||||
|
return pid.count == 9 && Int(pid) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
while !scanner.isAtEnd {
|
||||||
|
guard let passport = scanner.passport() else { fatalError() }
|
||||||
|
print(passport)
|
||||||
|
|
||||||
|
let valid = requiredFields.allSatisfy { passport.keys.contains($0) }
|
||||||
|
if valid {
|
||||||
|
valids += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let year = passport["byr"].flatMap(Int.init), 1920...2002 ~= year else { continue }
|
||||||
|
guard let issueYear = passport["iyr"].flatMap(Int.init), 2010...2020 ~= issueYear else { continue }
|
||||||
|
guard let expireYear = passport["eyr"].flatMap(Int.init), 2020...2030 ~= expireYear else { continue }
|
||||||
|
guard let height = passport["hgt"], validHeight(height) else { continue }
|
||||||
|
guard let hairColor = passport["hcl"], validHairColor(hairColor) else { continue }
|
||||||
|
guard let eyeColor = passport["ecl"], validEyeColors.contains(eyeColor) else { continue }
|
||||||
|
guard let pid = passport["pid"], isValidPassportId(pid) else { continue }
|
||||||
|
|
||||||
|
valid2 += 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
print("valids", valids)
|
||||||
|
print("valid2", valid2)
|
||||||
|
|
35
day5/main.swift
Normal file
35
day5/main.swift
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
let input = loadData(day: 5).lines()
|
||||||
|
|
||||||
|
func decode(_ s: Substring, lower: Character, upper: Character, range: Range<Int>) -> (Int, Substring)
|
||||||
|
{
|
||||||
|
var range = range
|
||||||
|
var start = s.startIndex
|
||||||
|
while start != s.endIndex && (s[start] == lower || s[start] == upper) {
|
||||||
|
let mid = (range.lowerBound + range.upperBound) / 2
|
||||||
|
if s[start] == lower {
|
||||||
|
range = range.lowerBound..<mid
|
||||||
|
} else if s[start] == upper {
|
||||||
|
range = mid..<range.upperBound
|
||||||
|
}
|
||||||
|
start = s.index(after: start)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (range.lowerBound, s[start...])
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeSeat(_ s: String) -> Int {
|
||||||
|
let (row, rest) = decode(s[...], lower: "F", upper: "B", range: 0..<128)
|
||||||
|
let (column, _) = decode(rest, lower: "L", upper: "R", range: 0..<8)
|
||||||
|
let id = row * 8 + column
|
||||||
|
print(s, row, column, id)
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
let foundSeats = Set(input.map { decodeSeat($0) })
|
||||||
|
print("max seat id", foundSeats.max() ?? -1)
|
||||||
|
|
||||||
|
let allSeats = Set(0...(7+127*8))
|
||||||
|
let freeSeats = allSeats.subtracting(foundSeats)
|
||||||
|
|
||||||
|
let yours = freeSeats.first(where: { foundSeats.contains($0 + 1) && foundSeats.contains($0 - 1)})
|
||||||
|
print("yours", yours ?? -1)
|
34
day6/main.swift
Normal file
34
day6/main.swift
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
var input = loadData(day: 6)
|
||||||
|
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
scanner.charactersToBeSkipped = nil
|
||||||
|
|
||||||
|
var count = 0
|
||||||
|
var countB = 0
|
||||||
|
var currentGroup: [Character:Int] = [:]
|
||||||
|
var peopleInGroup = 0
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
guard let answers = scanner.scanUpToString("\n") else { fatalError() }
|
||||||
|
peopleInGroup += 1
|
||||||
|
_ = scanner.scanString("\n")
|
||||||
|
|
||||||
|
for answer in answers {
|
||||||
|
currentGroup[answer, default: 0] += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanner.isAtEnd || scanner.scanString("\n") != nil {
|
||||||
|
count += currentGroup.count
|
||||||
|
|
||||||
|
countB += currentGroup.lazy.filter { $1 == peopleInGroup }.count
|
||||||
|
|
||||||
|
currentGroup.removeAll(keepingCapacity: true)
|
||||||
|
peopleInGroup = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
} while !scanner.isAtEnd
|
||||||
|
|
||||||
|
print("count", count)
|
||||||
|
print("part 2", countB)
|
74
day7/main.swift
Normal file
74
day7/main.swift
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 7)
|
||||||
|
|
||||||
|
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
|
||||||
|
struct Rule {
|
||||||
|
var count: Int
|
||||||
|
var color: String
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Scanner {
|
||||||
|
func scanRule() -> Rule? {
|
||||||
|
guard let count = scanInt(),
|
||||||
|
let color = scanUpToString(count == 1 ? " bag" : " bags"),
|
||||||
|
scanString(count == 1 ? "bag" : "bags") != nil
|
||||||
|
else { return nil }
|
||||||
|
return Rule(count: count, color: color)
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanRuleSet() -> (String, [Rule])? {
|
||||||
|
guard
|
||||||
|
let color = scanUpToString(" bags contain"),
|
||||||
|
scanString("bags contain") != nil else { return nil }
|
||||||
|
if scanString("no other bags.") != nil {
|
||||||
|
return (color, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
var rules: [Rule] = []
|
||||||
|
repeat {
|
||||||
|
guard let rule = scanRule() else { return nil }
|
||||||
|
rules.append(rule)
|
||||||
|
} while scanString(",") != nil
|
||||||
|
|
||||||
|
guard scanString(".") != nil else { return nil }
|
||||||
|
|
||||||
|
return (color, rules)
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanAllRules() -> [String: [Rule]]? {
|
||||||
|
var result: [String: [Rule]] = [:]
|
||||||
|
while !isAtEnd {
|
||||||
|
guard let (color, rules) = scanRuleSet() else { return nil }
|
||||||
|
result[color] = rules
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let rules = scanner.scanAllRules() else { fatalError() }
|
||||||
|
//print(rules)
|
||||||
|
|
||||||
|
func findPath(rules: [String: [Rule]], from: String, to: String) -> Bool {
|
||||||
|
guard let from = rules[from] else { return false }
|
||||||
|
if from.contains(where: { $0.color == to }) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return from.contains(where: { findPath(rules: rules, from: $0.color, to: to) })
|
||||||
|
}
|
||||||
|
|
||||||
|
let count = rules.lazy.filter { findPath(rules: rules, from: $0.key, to: "shiny gold") }.count
|
||||||
|
|
||||||
|
print("count", count)
|
||||||
|
|
||||||
|
func bagsInside(rules: [String: [Rule]], color: String) -> Int {
|
||||||
|
guard let from = rules[color] else { return 0 }
|
||||||
|
return from.reduce(0) { accum, bag in
|
||||||
|
accum + bag.count + bag.count * bagsInside(rules: rules, color: bag.color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("total bags", bagsInside(rules: rules, color: "shiny gold"))
|
134
day8/main.swift
Normal file
134
day8/main.swift
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 8)
|
||||||
|
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
|
||||||
|
enum Instruction: String {
|
||||||
|
case nop
|
||||||
|
case acc
|
||||||
|
case jmp
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Line {
|
||||||
|
var instruction: Instruction
|
||||||
|
var argument: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Scanner {
|
||||||
|
func instruction() -> Instruction? {
|
||||||
|
guard let string = scanUpToCharacters(from: .whitespaces),
|
||||||
|
let instruction = Instruction(rawValue: string)
|
||||||
|
else { return nil }
|
||||||
|
return instruction
|
||||||
|
}
|
||||||
|
|
||||||
|
func line() -> Line? {
|
||||||
|
guard let ins = instruction(),
|
||||||
|
let argument = scanInt()
|
||||||
|
else { return nil }
|
||||||
|
return Line(instruction: ins, argument: argument)
|
||||||
|
}
|
||||||
|
|
||||||
|
func program() -> [Line]? {
|
||||||
|
var program = [Line]()
|
||||||
|
while !isAtEnd {
|
||||||
|
guard let line = self.line() else { return nil }
|
||||||
|
program.append(line)
|
||||||
|
}
|
||||||
|
return program
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Computer {
|
||||||
|
var program: [Line]
|
||||||
|
var ip: Int = 0
|
||||||
|
var acc: Int = 0
|
||||||
|
|
||||||
|
init(program: [Line]) {
|
||||||
|
self.program = program
|
||||||
|
}
|
||||||
|
|
||||||
|
var visited: Set<Int> = []
|
||||||
|
|
||||||
|
func runLine() {
|
||||||
|
let line = program[ip]
|
||||||
|
switch line.instruction {
|
||||||
|
case .nop:
|
||||||
|
ip += 1
|
||||||
|
|
||||||
|
case .acc:
|
||||||
|
acc += line.argument
|
||||||
|
ip += 1
|
||||||
|
|
||||||
|
case .jmp:
|
||||||
|
ip += line.argument
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() {
|
||||||
|
ip = 0
|
||||||
|
acc = 0
|
||||||
|
|
||||||
|
while ip < program.count {
|
||||||
|
let (inserted, _) = visited.insert(ip)
|
||||||
|
if !inserted {
|
||||||
|
print("Repeated instruction", acc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
runLine()
|
||||||
|
}
|
||||||
|
print("Finished", acc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func find(ip: Int, acc: Int, changed: Bool, visited: Set<Int>) -> Int?
|
||||||
|
{
|
||||||
|
if ip >= program.count {
|
||||||
|
return acc
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited.contains(ip)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
let newVisited = visited.union([ip])
|
||||||
|
|
||||||
|
let line = program[ip]
|
||||||
|
switch line.instruction {
|
||||||
|
case .acc:
|
||||||
|
return find(ip: ip + 1, acc: acc + line.argument, changed: changed, visited: newVisited)
|
||||||
|
|
||||||
|
case .nop:
|
||||||
|
if let result = find(ip: ip + 1, acc: acc, changed: changed, visited: newVisited) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if !changed, let result = find(ip: ip + line.argument, acc: acc, changed: true, visited: newVisited) {
|
||||||
|
print("Found result by changing nop to jmp at", ip)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
case .jmp:
|
||||||
|
if let result = find(ip: ip + line.argument, acc: acc, changed: changed, visited: newVisited) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
if !changed, let result = find(ip: ip + 1, acc: acc, changed: true, visited: newVisited) {
|
||||||
|
print("Found result by changing jmp to nop at", ip)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let program = scanner.program() else { fatalError() }
|
||||||
|
let computer = Computer(program: program)
|
||||||
|
computer.run()
|
||||||
|
if let found = computer.find(ip: 0, acc: 0, changed: false, visited: []) {
|
||||||
|
print("found solution", found)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
45
day9/main.swift
Normal file
45
day9/main.swift
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let input = loadData(day: 9)
|
||||||
|
let scanner = Scanner(string: input)
|
||||||
|
|
||||||
|
let numbers = scanner.integers()!
|
||||||
|
|
||||||
|
var weakness: Int? = nil
|
||||||
|
outer: for i in 25..<numbers.count {
|
||||||
|
let search = numbers[i]
|
||||||
|
|
||||||
|
for j in (i - 25)..<i {
|
||||||
|
for k in j..<i {
|
||||||
|
if search == numbers[j] + numbers[k] {
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print("first which is not sum", i, search)
|
||||||
|
weakness = search
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
guard let weakness = weakness else { fatalError("No weakness found") }
|
||||||
|
|
||||||
|
outer: for i in 0..<numbers.count {
|
||||||
|
var sum = 0
|
||||||
|
var min = Int.max
|
||||||
|
var max = Int.min
|
||||||
|
for j in (i+1)..<numbers.count {
|
||||||
|
let n = numbers[j]
|
||||||
|
sum += n
|
||||||
|
if n < min { min = n }
|
||||||
|
if n > max { max = n }
|
||||||
|
if sum == weakness {
|
||||||
|
print(min + max)
|
||||||
|
break outer
|
||||||
|
}
|
||||||
|
if sum > weakness {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue