Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
904a46cc3c | ||
|
|
6bff35b952 |
File diff suppressed because one or more lines are too long
@@ -0,0 +1,245 @@
|
||||
//
|
||||
// Advent of Code 2018 "Day 5: Alchemical Reduction"
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Traverse the string
|
||||
// Find pairs of like letters, but opposite capitalization - < recursive? >
|
||||
// -- reduce -- and repeat
|
||||
|
||||
|
||||
class Reduction {
|
||||
var polymer: [Character] = []
|
||||
|
||||
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt'
|
||||
init(withFile filename: String) {
|
||||
var polymerString = Tools.readFile(fromPath: filename)
|
||||
// This gurd statement is just an excuse to use guard
|
||||
// I'm assuming the last string is an empty string, if not DON'T remove the last string
|
||||
guard polymerString.count > 0 else { return }
|
||||
polymerString.removeLast() // new-line
|
||||
polymer = Array(polymerString)
|
||||
}
|
||||
|
||||
// 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] = " "
|
||||
}
|
||||
}
|
||||
let upperUnit = Character(String(unit).uppercased())
|
||||
for index in 0..<polymer.count {
|
||||
if polymer[index] == upperUnit {
|
||||
polymer[index] = " "
|
||||
}
|
||||
}
|
||||
polymer = polymer.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 {
|
||||
polymer = polyCopy
|
||||
if let type = Unicode.Scalar(i) {
|
||||
let unit = Character(type)
|
||||
removeUnit(withType: unit)
|
||||
reducePolymer()
|
||||
minLen = min(minLen, polymer.count)
|
||||
}
|
||||
}
|
||||
return minLen
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Day05: AOCDay {
|
||||
lazy var tests: (() -> ()) = day05Tests
|
||||
lazy var final: (() -> ()) = day05Final
|
||||
|
||||
// In aA, a and A react, leaving nothing behind.
|
||||
// In abBA, bB destroys itself, leaving aA. As above, this then destroys itself, leaving nothing.
|
||||
// In abAB, no two adjacent units are of the same type, and so nothing happens.
|
||||
// In aabAAB, even though aa and AA are of the same type, their polarities match, and so nothing happens.
|
||||
|
||||
let testData = "dabAcCaCBAcCcaDA"
|
||||
|
||||
func testReductionInit() {
|
||||
let testArray = Array(testData)
|
||||
var reduc = Reduction(withString: testData)
|
||||
XCTAssertEqual(test: "testReductionInit with String", withExpression: (reduc.polymer == testArray))
|
||||
reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
||||
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()
|
||||
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == ""))
|
||||
reduc = Reduction(withString: "abBA")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == ""))
|
||||
reduc = Reduction(withString: "abAB")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "abAB"))
|
||||
reduc = Reduction(withString: "aabAAB")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "aabAAB"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testReducePolymer", withExpression: (String(reduc.polymer) == "dabCBAcaDA"))
|
||||
}
|
||||
|
||||
func testRemoveUnit() {
|
||||
var reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "a")
|
||||
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "dbcCCBcCcD"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "b")
|
||||
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "daAcCaCAcCcaDA"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "c")
|
||||
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "dabAaBAaDA"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "d")
|
||||
XCTAssertEqual(test: "testRemoveOneType", withExpression: (String(reduc.polymer) == "abAcCaCBAcCcaA"))
|
||||
}
|
||||
|
||||
func testRemoveAndReact() {
|
||||
var reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "a")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testRemoveAndReact a", withExpression: (String(reduc.polymer) == "dbCBcD"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "b")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testRemoveAndReact b", withExpression: (String(reduc.polymer) == "daCAcaDA"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "c")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testRemoveAndReact c", withExpression: (String(reduc.polymer) == "daDA"))
|
||||
reduc = Reduction(withString: testData)
|
||||
reduc.removeUnit(withType: "d")
|
||||
reduc.reducePolymer()
|
||||
XCTAssertEqual(test: "testRemoveAndReact d", withExpression: (String(reduc.polymer) == "abCBAc"))
|
||||
}
|
||||
|
||||
func testFindSmallestFullyReactedAndRemovedPolymer() {
|
||||
let reduc = Reduction(withString: testData)
|
||||
let answer = reduc.findSmallestFullyReactedAndRemovedPolymer()
|
||||
XCTAssertEqual(test: "testFindSmallestFullyReactedAndRemovedPolymer", withExpression: (answer == 4))
|
||||
}
|
||||
|
||||
func day05Tests() {
|
||||
testReductionInit()
|
||||
testGetOpposites()
|
||||
testReduce()
|
||||
testReducePolymer()
|
||||
testRemoveUnit()
|
||||
testRemoveAndReact()
|
||||
testFindSmallestFullyReactedAndRemovedPolymer()
|
||||
}
|
||||
|
||||
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()
|
||||
print("Answer to part 1 is: \(reduc.polymer.count)")
|
||||
// reduc = Reduction(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day05.txt")
|
||||
reduc = Reduction(withString: testData)
|
||||
let answer = reduc.findSmallestFullyReactedAndRemovedPolymer()
|
||||
print("Answer to part 2 is: \(answer)")
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,14 @@ allTests.append(Day01().tests)
|
||||
allTests.append(Day02().tests)
|
||||
allTests.append(Day03().tests)
|
||||
allTests.append(Day04().tests)
|
||||
allTests.append(Day05().tests)
|
||||
|
||||
// Compile list of Answers
|
||||
allFinal.append(Day01().final)
|
||||
allFinal.append(Day02().final)
|
||||
allFinal.append(Day03().final)
|
||||
allFinal.append(Day04().final)
|
||||
allFinal.append(Day05().final)
|
||||
|
||||
if onlyOneDay > 0 {
|
||||
print("\nDay \(onlyOneDay)")
|
||||
|
||||
@@ -27,6 +27,14 @@ struct Tools {
|
||||
}
|
||||
return retVal
|
||||
}
|
||||
|
||||
// Assumes THE string is 'polymer'
|
||||
static func stringIndexRange(withString str: String, fromRange range: Range<Int>) -> Range<String.Index> {
|
||||
let myrange = Range(uncheckedBounds: (lower: max(0, min(str.length, range.lowerBound)), upper: min(str.length, max(0, range.upperBound))))
|
||||
let start = str.index(str.startIndex, offsetBy: myrange.lowerBound)
|
||||
let end = str.index(start, offsetBy: myrange.upperBound - myrange.lowerBound)
|
||||
return start ..< end
|
||||
}
|
||||
}
|
||||
|
||||
extension Character {
|
||||
@@ -39,7 +47,41 @@ extension Character {
|
||||
}
|
||||
|
||||
extension StringProtocol {
|
||||
// var string: String { return String(self) }
|
||||
|
||||
var ascii: [UInt32] {
|
||||
return compactMap { $0.ascii }
|
||||
}
|
||||
|
||||
// subscript(offset: Int) -> Element {
|
||||
// return self[index(startIndex, offsetBy: offset)]
|
||||
// }
|
||||
}
|
||||
|
||||
extension String {
|
||||
|
||||
var length: Int {
|
||||
return count
|
||||
}
|
||||
|
||||
subscript (i: Int) -> String {
|
||||
return self[i ..< i + 1]
|
||||
}
|
||||
|
||||
func substring(fromIndex: Int) -> String {
|
||||
return self[min(fromIndex, length) ..< length]
|
||||
}
|
||||
|
||||
func substring(toIndex: Int) -> String {
|
||||
return self[0 ..< max(0, toIndex)]
|
||||
}
|
||||
|
||||
subscript (r: Range<Int>) -> String {
|
||||
let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
|
||||
upper: min(length, max(0, r.upperBound))))
|
||||
let start = index(startIndex, offsetBy: range.lowerBound)
|
||||
let end = index(start, offsetBy: range.upperBound - range.lowerBound)
|
||||
return String(self[start ..< end])
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user