|
|
|
@ -17,12 +17,26 @@ struct Instruction: Equatable, Comparable {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct Worker: Equatable, Comparable {
|
|
|
|
|
|
|
|
var job = Character(" ")
|
|
|
|
|
|
|
|
var duration = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func == (lhs: Worker, rhs: Worker) -> Bool {
|
|
|
|
|
|
|
|
return (lhs.job == rhs.job) && (lhs.duration == rhs.duration)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static func < (lhs: Worker, rhs: Worker) -> Bool {
|
|
|
|
|
|
|
|
return lhs.duration < rhs.duration
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class SumParts {
|
|
|
|
class SumParts {
|
|
|
|
var instrList: [Instruction] = []
|
|
|
|
var instrList: [Instruction] = []
|
|
|
|
var inventoryComplete: [Character] = []
|
|
|
|
var workers: [Worker] = []
|
|
|
|
|
|
|
|
var masterClock = 0
|
|
|
|
|
|
|
|
|
|
|
|
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt'
|
|
|
|
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt'
|
|
|
|
init(withFile filename: String) {
|
|
|
|
init(withFile filename: String, numWorkers: Int) {
|
|
|
|
let instrString = Tools.readFile(fromPath: filename)
|
|
|
|
let instrString = Tools.readFile(fromPath: filename)
|
|
|
|
var instrArray = instrString.components(separatedBy: "\n")
|
|
|
|
var instrArray = instrString.components(separatedBy: "\n")
|
|
|
|
// This gurd statement is just an excuse to use guard
|
|
|
|
// This gurd statement is just an excuse to use guard
|
|
|
|
@ -30,12 +44,14 @@ class SumParts {
|
|
|
|
guard let lastStr = instrArray.last, lastStr.count == 0 else { return }
|
|
|
|
guard let lastStr = instrArray.last, lastStr.count == 0 else { return }
|
|
|
|
instrArray.removeLast() // empty string
|
|
|
|
instrArray.removeLast() // empty string
|
|
|
|
parseData(withInstructions: instrArray)
|
|
|
|
parseData(withInstructions: instrArray)
|
|
|
|
|
|
|
|
workers = Array(repeating: Worker(), count: numWorkers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Supply test data in the form of a String
|
|
|
|
// Supply test data in the form of a String
|
|
|
|
init(withString instrString: String) {
|
|
|
|
init(withString instrString: String, numWorkers: Int) {
|
|
|
|
let instrArray = instrString.components(separatedBy: "\n")
|
|
|
|
let instrArray = instrString.components(separatedBy: "\n")
|
|
|
|
parseData(withInstructions: instrArray)
|
|
|
|
parseData(withInstructions: instrArray)
|
|
|
|
|
|
|
|
workers = Array(repeating: Worker(), count: numWorkers)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func parseData(withInstructions instr: [String]) {
|
|
|
|
func parseData(withInstructions instr: [String]) {
|
|
|
|
@ -45,6 +61,7 @@ class SumParts {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func getNextAvailable() -> Character? {
|
|
|
|
func getNextAvailable() -> Character? {
|
|
|
|
|
|
|
|
var retVal: Character?
|
|
|
|
var availDict: [Character : Bool] = [:]
|
|
|
|
var availDict: [Character : Bool] = [:]
|
|
|
|
for index in 0..<instrList.count {
|
|
|
|
for index in 0..<instrList.count {
|
|
|
|
if !instrList.contains(where: { $0.before == instrList[index].complete} ) {
|
|
|
|
if !instrList.contains(where: { $0.before == instrList[index].complete} ) {
|
|
|
|
@ -53,10 +70,22 @@ class SumParts {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var available = Array(availDict.keys)
|
|
|
|
var available = Array(availDict.keys)
|
|
|
|
available.sort()
|
|
|
|
available.sort()
|
|
|
|
return available.first
|
|
|
|
if workers.count == 0 {
|
|
|
|
|
|
|
|
retVal = available.first
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Exclude 'available' if already scheduled to a worker
|
|
|
|
|
|
|
|
for job in available {
|
|
|
|
|
|
|
|
// if workers.contains(where: { $0.job == job && $0.duration == 0} ) {
|
|
|
|
|
|
|
|
if !workers.contains(where: { $0.job == job} ) || workers.contains(where: { $0.job == job && $0.duration == 0} ) {
|
|
|
|
|
|
|
|
retVal = job
|
|
|
|
|
|
|
|
break
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return retVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func completeInstructions() -> String {
|
|
|
|
func completeAllInstructions() -> String {
|
|
|
|
var retVal = ""
|
|
|
|
var retVal = ""
|
|
|
|
var last: Character = "*"
|
|
|
|
var last: Character = "*"
|
|
|
|
while let instruction = getNextAvailable() {
|
|
|
|
while let instruction = getNextAvailable() {
|
|
|
|
@ -69,6 +98,77 @@ class SumParts {
|
|
|
|
retVal.append(last)
|
|
|
|
retVal.append(last)
|
|
|
|
return retVal
|
|
|
|
return retVal
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func complete(instruction: Character) {
|
|
|
|
|
|
|
|
if instrList.count == 1 && instrList[0].complete != " " {
|
|
|
|
|
|
|
|
instrList.append(Instruction(complete: instrList[0].before, before: Character(" ")))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
instrList.removeAll(where: { $0.complete == instruction })
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//-------------->> Code for Part 2 <<--------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// We'll cheat here and assume the duration is 1 second + offset for One worker and 60 seconds + offset for more than one
|
|
|
|
|
|
|
|
func duration(forInstruction instr: Character) -> Int {
|
|
|
|
|
|
|
|
guard let val = instr.ascii else { return -1 }
|
|
|
|
|
|
|
|
let offset = Int(val) - 64
|
|
|
|
|
|
|
|
return (workers.count > 2) ? 60 + offset : offset
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Go through the list of workers and find the min() time-to-finish
|
|
|
|
|
|
|
|
func getTimeUntilWorkerFree() -> Int {
|
|
|
|
|
|
|
|
guard workers.count > 0 else { return 0 }
|
|
|
|
|
|
|
|
let sortedByDuration = workers.sorted(by: { $0.duration < $1.duration })
|
|
|
|
|
|
|
|
for worker in sortedByDuration {
|
|
|
|
|
|
|
|
if worker.duration != 0 {
|
|
|
|
|
|
|
|
return worker.duration
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func advanceTime(by elapsedtime: Int) {
|
|
|
|
|
|
|
|
for index in 0..<workers.count {
|
|
|
|
|
|
|
|
if workers[index].duration > 0 {
|
|
|
|
|
|
|
|
workers[index].duration -= elapsedtime
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
masterClock += elapsedtime
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// subtract XX seconds from the workers time and add that to the master-clock
|
|
|
|
|
|
|
|
// Since we advance until worker(s) done, we should complete the tasks finsished by the worker(s)
|
|
|
|
|
|
|
|
// return true if more work to do
|
|
|
|
|
|
|
|
func advanceClockAndCompleteInstructions() -> Bool {
|
|
|
|
|
|
|
|
guard workers.count > 0 else { return false }
|
|
|
|
|
|
|
|
let advTime = getTimeUntilWorkerFree()
|
|
|
|
|
|
|
|
if advTime == 0 {
|
|
|
|
|
|
|
|
let test = workers.contains(where: { $0.duration != 0} )
|
|
|
|
|
|
|
|
if !test {
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// subtract this time from all workers
|
|
|
|
|
|
|
|
// add this time to the Master-Clock
|
|
|
|
|
|
|
|
advanceTime(by: advTime)
|
|
|
|
|
|
|
|
// Complete the "finished" worker(s) instruction
|
|
|
|
|
|
|
|
for index in 0..<workers.count {
|
|
|
|
|
|
|
|
if workers[index].duration == 0 {
|
|
|
|
|
|
|
|
complete(instruction: workers[index].job)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Look for available workers and assign them instructions
|
|
|
|
|
|
|
|
func scheduleWorkers() {
|
|
|
|
|
|
|
|
for index in 0..<workers.count {
|
|
|
|
|
|
|
|
if workers[index].duration == 0 {
|
|
|
|
|
|
|
|
guard let job = getNextAvailable() else { return }
|
|
|
|
|
|
|
|
workers[index] = Worker(job: job, duration: duration(forInstruction: job))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class Day07: AOCDay {
|
|
|
|
class Day07: AOCDay {
|
|
|
|
@ -86,16 +186,16 @@ class Day07: AOCDay {
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
func testSumPartsInit() {
|
|
|
|
func testSumPartsInit() {
|
|
|
|
var instr = SumParts(withString: testData)
|
|
|
|
var instr = SumParts(withString: testData, numWorkers: 0)
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit string 0", withExpression: (instr.instrList[0].before == "A"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit string 0", withExpression: (instr.instrList[0].before == "A"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit string 4", withExpression: (instr.instrList[4].complete == "B"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit string 4", withExpression: (instr.instrList[4].complete == "B"))
|
|
|
|
instr = SumParts(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt")
|
|
|
|
instr = SumParts(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt", numWorkers: 0)
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit file 0", withExpression: (instr.instrList[0].before == "X"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit file 0", withExpression: (instr.instrList[0].before == "X"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit file 4", withExpression: (instr.instrList[4].complete == "O"))
|
|
|
|
XCTAssertEqual(test: "testSumPartsInit file 4", withExpression: (instr.instrList[4].complete == "O"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func testGetNextAvailable() {
|
|
|
|
func testGetNextAvailable() {
|
|
|
|
let instr = SumParts(withString: testData)
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 0)
|
|
|
|
if let avail = instr.getNextAvailable() {
|
|
|
|
if let avail = instr.getNextAvailable() {
|
|
|
|
XCTAssertEqual(test: "testGetNextAvailable", withExpression: (avail == "C"))
|
|
|
|
XCTAssertEqual(test: "testGetNextAvailable", withExpression: (avail == "C"))
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
@ -104,22 +204,102 @@ class Day07: AOCDay {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func testCompleteInstructions() {
|
|
|
|
func testCompleteInstructions() {
|
|
|
|
let instr = SumParts(withString: testData)
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 0)
|
|
|
|
let answer = instr.completeInstructions()
|
|
|
|
let answer = instr.completeAllInstructions()
|
|
|
|
XCTAssertEqual(test: "testCompleteInstructions", withExpression: (answer == "CABDFE"))
|
|
|
|
XCTAssertEqual(test: "testCompleteInstructions", withExpression: (answer == "CABDFE"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func testDuration() {
|
|
|
|
|
|
|
|
var instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
var dur = instr.duration(forInstruction: "A")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 1 worker A", withExpression: (dur == 1))
|
|
|
|
|
|
|
|
dur = instr.duration(forInstruction: "K")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 1 worker K", withExpression: (dur == 11))
|
|
|
|
|
|
|
|
dur = instr.duration(forInstruction: "Z")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 1 worker Z", withExpression: (dur == 26))
|
|
|
|
|
|
|
|
instr = SumParts(withString: testData, numWorkers: 5)
|
|
|
|
|
|
|
|
dur = instr.duration(forInstruction: "A")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 5 worker A", withExpression: (dur == 60 + 1))
|
|
|
|
|
|
|
|
dur = instr.duration(forInstruction: "K")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 5 worker K", withExpression: (dur == 60 + 11))
|
|
|
|
|
|
|
|
dur = instr.duration(forInstruction: "Z")
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testDuration 5 worker Z", withExpression: (dur == 60 + 26))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Go through the list of workers and find the min() time-to-finish
|
|
|
|
|
|
|
|
func testGetTimeUntilWorkerFree() {
|
|
|
|
|
|
|
|
var instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
var time = instr.getTimeUntilWorkerFree()
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testGetTimeUntilWorkerFree 1 worker", withExpression: (time == 0))
|
|
|
|
|
|
|
|
instr = SumParts(withString: testData, numWorkers: 5)
|
|
|
|
|
|
|
|
time = instr.getTimeUntilWorkerFree()
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testGetTimeUntilWorkerFree 5 workers", withExpression: (time == 0))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func testAdvanceTime() {
|
|
|
|
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
let oldClock = instr.masterClock
|
|
|
|
|
|
|
|
let oldWorkers = instr.workers
|
|
|
|
|
|
|
|
instr.advanceTime(by: 0)
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceTime by 0 seconds clock", withExpression: (oldClock == instr.masterClock))
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceTime by 0 seconds workers", withExpression: (oldWorkers == instr.workers))
|
|
|
|
|
|
|
|
instr.advanceTime(by: 10)
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceTime by 10 seconds clock", withExpression: (instr.masterClock == 10))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// subtract XX seconds from the workers time and add that to the master-clock
|
|
|
|
|
|
|
|
// Since we advance until worker(s) done, we should complete the tasks finsished by the worker(s)
|
|
|
|
|
|
|
|
func testAdvanceClockAndCompleteInstructions() {
|
|
|
|
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
let oldWorkers = instr.workers
|
|
|
|
|
|
|
|
var done = instr.advanceClockAndCompleteInstructions()
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceClockAndCompleteInstructions done=false before first schedule", withExpression: (!done))
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceClockAndCompleteInstructions workers unchanged", withExpression: (oldWorkers == instr.workers))
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceClockAndCompleteInstructions masterClock unchanged", withExpression: (instr.masterClock == 0))
|
|
|
|
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
|
|
|
|
done = instr.advanceClockAndCompleteInstructions()
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAdvanceClockAndCompleteInstructions done=true after first schedule", withExpression: (done))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Look for available workers and assign them instructions
|
|
|
|
|
|
|
|
func testScheduleWorkers() {
|
|
|
|
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
let oldWorkers = instr.workers
|
|
|
|
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testScheduleWorkers workers changed", withExpression: (oldWorkers != instr.workers))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func testAnswerPart2() {
|
|
|
|
|
|
|
|
let instr = SumParts(withString: testData, numWorkers: 2)
|
|
|
|
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
|
|
|
|
while instr.advanceClockAndCompleteInstructions() {
|
|
|
|
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
XCTAssertEqual(test: "testAnswerPart2", withExpression: (instr.masterClock == 15))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func day07Tests() {
|
|
|
|
func day07Tests() {
|
|
|
|
testSumPartsInit()
|
|
|
|
testSumPartsInit()
|
|
|
|
testGetNextAvailable()
|
|
|
|
testGetNextAvailable()
|
|
|
|
testCompleteInstructions()
|
|
|
|
testCompleteInstructions()
|
|
|
|
|
|
|
|
testDuration()
|
|
|
|
|
|
|
|
testGetTimeUntilWorkerFree()
|
|
|
|
|
|
|
|
testAdvanceTime()
|
|
|
|
|
|
|
|
testAdvanceClockAndCompleteInstructions()
|
|
|
|
|
|
|
|
testScheduleWorkers()
|
|
|
|
|
|
|
|
testAnswerPart2()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func day07Final() {
|
|
|
|
func day07Final() {
|
|
|
|
let retVal = "None"
|
|
|
|
var instr = SumParts(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt", numWorkers: 0)
|
|
|
|
let instr = SumParts(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt")
|
|
|
|
let answerPart1 = instr.completeAllInstructions()
|
|
|
|
let answer = instr.completeInstructions()
|
|
|
|
instr = SumParts(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day07.txt", numWorkers: 5)
|
|
|
|
print("Answer to part 1 is: \(answer)")
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
print("Answer to part 2 is: \(retVal)")
|
|
|
|
while instr.advanceClockAndCompleteInstructions() {
|
|
|
|
|
|
|
|
instr.scheduleWorkers()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
let answerPart2 = instr.masterClock
|
|
|
|
|
|
|
|
print("Answer to part 1 is: \(answerPart1)")
|
|
|
|
|
|
|
|
print("Answer to part 2 is: \(answerPart2)")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|