Initial commit

This commit is contained in:
Sven Weidauer 2020-12-11 22:25:14 +01:00
commit dae7c34265
17 changed files with 2001 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.xcodeproj/**/xcuserdata/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View file

@ -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
View 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!
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}
}
}