|
|
|
|
@ -4,13 +4,9 @@
|
|
|
|
|
|
|
|
|
|
import Foundation
|
|
|
|
|
|
|
|
|
|
// Traverse the string
|
|
|
|
|
// Find pairs of like letters, but opposite capitalization - < recursive? >
|
|
|
|
|
// -- reduce -- and repeat
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Reduction {
|
|
|
|
|
var polymer: [Character] = []
|
|
|
|
|
var acc: [Character] = []
|
|
|
|
|
|
|
|
|
|
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt'
|
|
|
|
|
init(withFile filename: String) {
|
|
|
|
|
@ -20,84 +16,48 @@ class Reduction {
|
|
|
|
|
guard polymerString.count > 0 else { return }
|
|
|
|
|
polymerString.removeLast() // new-line
|
|
|
|
|
polymer = Array(polymerString)
|
|
|
|
|
acc = Array(repeating: " ", count: polymer.count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Supply test data in the form of a String
|
|
|
|
|
init(withString poly: String) {
|
|
|
|
|
polymer = Array(poly)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// return an array of indicies of the first in an opposing pair
|
|
|
|
|
func getOpposites()-> [Int] {
|
|
|
|
|
var retVal: [Int] = []
|
|
|
|
|
|
|
|
|
|
if polymer.count > 1 {
|
|
|
|
|
var hit = false
|
|
|
|
|
for index in 0..<polymer.count-1 {
|
|
|
|
|
if hit {
|
|
|
|
|
hit = false
|
|
|
|
|
} else {
|
|
|
|
|
if (polymer[index] != polymer[index+1]) && (String(polymer[index]).lowercased() == String(polymer[index+1]).lowercased()) {
|
|
|
|
|
hit = true
|
|
|
|
|
retVal.append(index)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return retVal
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// blast the units and then "compact" the ploymer
|
|
|
|
|
func reduce(withRanges ranges: [Int]) {
|
|
|
|
|
for range in ranges {
|
|
|
|
|
polymer[range] = " "
|
|
|
|
|
polymer[range+1] = " "
|
|
|
|
|
}
|
|
|
|
|
polymer = polymer.filter {$0 != " "}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reducePolymer() {
|
|
|
|
|
var opp = getOpposites()
|
|
|
|
|
while opp.count > 0 {
|
|
|
|
|
reduce(withRanges: opp)
|
|
|
|
|
opp = getOpposites()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func removeUnit(withType unit: Character) {
|
|
|
|
|
for index in 0..<polymer.count {
|
|
|
|
|
if polymer[index] == unit {
|
|
|
|
|
polymer[index] = " "
|
|
|
|
|
acc = Array(repeating: " ", count: polymer.count)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func reducer() {
|
|
|
|
|
var aIdx = 0
|
|
|
|
|
acc[aIdx] = polymer[0]
|
|
|
|
|
for index in 1..<polymer.count {
|
|
|
|
|
if (aIdx > -1 && acc[aIdx] != polymer[index]) && (String(acc[aIdx]).lowercased() == String(polymer[index]).lowercased()) {
|
|
|
|
|
acc[aIdx] = " "
|
|
|
|
|
aIdx -= 1
|
|
|
|
|
} else {
|
|
|
|
|
aIdx += 1
|
|
|
|
|
acc[aIdx] = polymer[index]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let upperUnit = Character(String(unit).uppercased())
|
|
|
|
|
for index in 0..<polymer.count {
|
|
|
|
|
if polymer[index] == upperUnit {
|
|
|
|
|
polymer[index] = " "
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
polymer = polymer.filter {$0 != " "}
|
|
|
|
|
polymer = acc.filter {$0 != " "}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func findSmallestFullyReactedAndRemovedPolymer() -> Int {
|
|
|
|
|
let start = Unicode.Scalar("a").value
|
|
|
|
|
let end = Unicode.Scalar("z").value
|
|
|
|
|
let myrange = start...end
|
|
|
|
|
let polyCopy = polymer
|
|
|
|
|
var minLen = polymer.count
|
|
|
|
|
for i in myrange {
|
|
|
|
|
for i in start...end {
|
|
|
|
|
polymer = polyCopy
|
|
|
|
|
acc = Array(repeating: " ", count: polymer.count)
|
|
|
|
|
if let type = Unicode.Scalar(i) {
|
|
|
|
|
let unit = Character(type)
|
|
|
|
|
removeUnit(withType: unit)
|
|
|
|
|
reducePolymer()
|
|
|
|
|
let upperUnit = Character(String(unit).uppercased())
|
|
|
|
|
polymer = polymer.filter { $0 != unit && $0 != upperUnit }
|
|
|
|
|
reducer()
|
|
|
|
|
minLen = min(minLen, polymer.count)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return minLen
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Day05: AOCDay {
|
|
|
|
|
@ -119,99 +79,55 @@ class Day05: AOCDay {
|
|
|
|
|
XCTAssertEqual(test: "testReductionInit with File", withExpression: (reduc.polymer.last == "Y"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testGetOpposites() {
|
|
|
|
|
var reduc = Reduction(withString: "aA")
|
|
|
|
|
var opp = reduc.getOpposites()
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites count", withExpression: (opp.count == 1))
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites range", withExpression: (opp == [0]))
|
|
|
|
|
reduc = Reduction(withString: "abBA")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites count", withExpression: (opp.count == 1))
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites range", withExpression: (opp == [1]))
|
|
|
|
|
reduc = Reduction(withString: "abAB")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites count", withExpression: (opp.count == 0))
|
|
|
|
|
reduc = Reduction(withString: "aabAAB")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites count", withExpression: (opp.count == 0))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites count", withExpression: (opp.count == 2))
|
|
|
|
|
XCTAssertEqual(test: "testGetOpposites range", withExpression: (opp == [4, 10]))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testReduce() {
|
|
|
|
|
var reduc = Reduction(withString: "aA")
|
|
|
|
|
var opp = reduc.getOpposites()
|
|
|
|
|
reduc.reduce(withRanges: opp)
|
|
|
|
|
XCTAssertEqual(test: "testReduce count after", withExpression: (reduc.polymer.count == 0))
|
|
|
|
|
reduc = Reduction(withString: "abBA")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
reduc.reduce(withRanges: opp)
|
|
|
|
|
XCTAssertEqual(test: "testReduce count after", withExpression: (reduc.polymer.count == 2))
|
|
|
|
|
reduc = Reduction(withString: "abAB")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
reduc.reduce(withRanges: opp)
|
|
|
|
|
XCTAssertEqual(test: "testReduce count after", withExpression: (reduc.polymer.count == 4))
|
|
|
|
|
reduc = Reduction(withString: "aabAAB")
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
reduc.reduce(withRanges: opp)
|
|
|
|
|
XCTAssertEqual(test: "testReduce count after", withExpression: (reduc.polymer.count == 6))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
opp = reduc.getOpposites()
|
|
|
|
|
reduc.reduce(withRanges: opp)
|
|
|
|
|
XCTAssertEqual(test: "testReduce count after", withExpression: (reduc.polymer.count == 12))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testReducePolymer() {
|
|
|
|
|
var reduc = Reduction(withString: "aA")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == ""))
|
|
|
|
|
reduc = Reduction(withString: "abBA")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == ""))
|
|
|
|
|
reduc = Reduction(withString: "abAB")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "abAB"))
|
|
|
|
|
reduc = Reduction(withString: "aabAAB")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "aabAAB"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "dabCBAcaDA"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testRemoveUnit() {
|
|
|
|
|
var reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "a")
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "a" && $0 != "A" }
|
|
|
|
|
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "dbcCCBcCcD"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "b")
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "b" && $0 != "B" }
|
|
|
|
|
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "daAcCaCAcCcaDA"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "c")
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "c" && $0 != "C" }
|
|
|
|
|
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "dabAaBAaDA"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "d")
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "d" && $0 != "D" }
|
|
|
|
|
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "abAcCaCBAcCcaA"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func testRemoveAndReact() {
|
|
|
|
|
var reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "a")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "a" && $0 != "A" }
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testRemoveAndReact a", withExpression: (String(reduc.polymer) == "dbCBcD"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "b")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "b" && $0 != "B" }
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testRemoveAndReact b", withExpression: (String(reduc.polymer) == "daCAcaDA"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "c")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "c" && $0 != "C" }
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testRemoveAndReact c", withExpression: (String(reduc.polymer) == "daDA"))
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.removeUnit(withType: "d")
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
reduc.polymer = reduc.polymer.filter { $0 != "d" && $0 != "D" }
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
XCTAssertEqual(test: "testRemoveAndReact d", withExpression: (String(reduc.polymer) == "abCBAc"))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -223,8 +139,6 @@ class Day05: AOCDay {
|
|
|
|
|
|
|
|
|
|
func day05Tests() {
|
|
|
|
|
testReductionInit()
|
|
|
|
|
testGetOpposites()
|
|
|
|
|
testReduce()
|
|
|
|
|
testReducePolymer()
|
|
|
|
|
testRemoveUnit()
|
|
|
|
|
testRemoveAndReact()
|
|
|
|
|
@ -232,13 +146,10 @@ class Day05: AOCDay {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func day05Final() {
|
|
|
|
|
// var reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
|
|
|
|
// Run test data as the real data takes too long
|
|
|
|
|
var reduc = Reduction(withString: testData)
|
|
|
|
|
reduc.reducePolymer()
|
|
|
|
|
var reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
|
|
|
|
reduc.reducer()
|
|
|
|
|
print("Answer to part 1 is: \(reduc.polymer.count)")
|
|
|
|
|
// reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
|
|
|
|
reduc = Reduction(withString: testData)
|
|
|
|
|
reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
|
|
|
|
let answer = reduc.findSmallestFullyReactedAndRemovedPolymer()
|
|
|
|
|
print("Answer to part 2 is: \(answer)")
|
|
|
|
|
}
|
|
|
|
|
|