Add code for day 16

Day 16: Chronal Classification
master
Peter 7 years ago
parent 5531412a55
commit 8062f0a2be

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,373 @@
//
// Advent of Code 2018 "Day 16: Chronal Classification"
//
import Foundation
typealias Opfn = () -> Void
// Create an array of functions for the op codes such that we can make a dictionary to map them
// 16 op-codes
// 4 regisers
class Z8 {
var reg: [Int]
var op: Int
var A: Int
var B: Int
var C: Int
lazy var fn = [addr, addi, mulr, muli, banr, bani, borr, bori, setr, seti, gtir, gtri, gtrr, eqir, eqri, eqrr]
init(reg: [Int], op: Int, A: Int, B: Int, C: Int) {
self.reg = reg
self.op = op
self.A = A
self.B = B
self.C = C
}
// addr (add register) stores into register C the result of adding register A and register B. C = 7
func addr() { reg[C] = reg[A] + reg[B] }
// addi (add immediate) stores into register C the result of adding register A and value B.reg[C] = 5
func addi() { reg[C] = reg[A] + B }
// mulr (multiply register) stores into register C the result of multiplying register A and register B. reg[C] = 12
func mulr() { reg[C] = reg[A] * reg[B] }
// muli (multiply immediate) stores into register C the result of multiplying register A and value B. reg[C] = 6
func muli() { reg[C] = reg[A] * B }
// banr (bitwise AND register) stores into register C the result of the bitwise AND of register A and register B. reg[C] = 0
func banr() { reg[C] = reg[A] & reg[B] }
// bani (bitwise AND immediate) stores into register C the result of the bitwise AND of register A and value B. reg[C] = 2
func bani() { reg[C] = reg[A] & B }
// borr (bitwise OR register) stores into register C the result of the bitwise OR of register A and register B. reg[C] = 7
func borr() { reg[C] = reg[A] | reg[B] }
// bori (bitwise OR immediate) stores into register C the result of the bitwise OR of register A and value B. reg[C] = 3
func bori() { reg[C] = reg[A] | B }
// setr (set register) copies the contents of register A into register C. (Input B is ignored.) reg[C] = 3
func setr() { reg[C] = reg[A] }
// seti (set immediate) stores value A into register C. (Input B is ignored.) reg[C] = 1
func seti() { reg[C] = A}
// gtir (greater-than immediate/register) sets register C to 1 if value A is greater than register B. Otherwise, register C is set to 0. reg[C] = 0
func gtir() { reg[C] = A > reg[B] ? 1 : 0 }
// gtri (greater-than register/immediate) sets register C to 1 if register A is greater than value B. Otherwise, register C is set to 0. reg[C] = 1
func gtri() { reg[C] = reg[A] > B ? 1 : 0 }
// gtrr (greater-than register/register) sets register C to 1 if register A is greater than register B. Otherwise, register C is set to 0. reg[C] = 0
func gtrr() { reg[C] = reg[A] > reg[B] ? 1 : 0 }
// eqir (equal immediate/register) sets register C to 1 if value A is equal to register B. Otherwise, register C is set to 0. reg[C] = 0
func eqir() { reg[C] = A == reg[B] ? 1 : 0 }
// eqri (equal register/immediate) sets register C to 1 if register A is equal to value B. Otherwise, register C is set to 0. reg[C] = 0
func eqri() { reg[C] = reg[A] == B ? 1 : 0 }
// eqrr (equal register/register) sets register C to 1 if register A is equal to register B. Otherwise, register C is set to 0.reg[C] =0
func eqrr() { reg[C] = reg[A] == reg[B] ? 1 : 0 }
}
struct TestCase {
var before: [Int]
var after: [Int]
var op: Int
var A: Int
var B: Int
var C: Int
init(before: [Int], after: [Int], op: Int, A: Int, B: Int, C: Int) {
self.before = before
self.after = after
self.op = op
self.A = A
self.B = B
self.C = C
}
func validate(withIndex fnIndex: Int) -> Bool {
let z8 = Z8(reg: before, op: op, A: A, B: B, C: C)
z8.fn[fnIndex]()
return z8.reg == after
}
func validate(withMapping map: [Int:Int]) -> Bool {
let z8 = Z8(reg: before, op: op, A: A, B: B, C: C)
z8.fn[map[op]!]()
return z8.reg == after
}
func countOpsValid() -> Int {
let tempz8 = Z8(reg: before, op: op, A: A, B: B, C: C)
var count = 0
for opIndex in 0..<tempz8.fn.count {
count += validate(withIndex: opIndex) ? 1 : 0
}
return count
}
}
struct Code {
var op: Int
var A: Int
var B: Int
var C: Int
init(op: Int, A: Int, B: Int, C: Int) {
self.op = op
self.A = A
self.B = B
self.C = C
}
}
class Classification {
var tests: [TestCase] = []
var program: [Code] = []
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day16.txt'
init(withFile filename: String) {
let testFile = Tools.readFile(fromPath: filename)
var testStrings = testFile.components(separatedBy: "\n")
// 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 let lastStr = testStrings.last, lastStr.count == 0 else { return }
testStrings.removeLast() // empty string
parse(data: testStrings)
}
// Supply test data in the form of a String
init(withString testFile: String) {
let testStrings = testFile.components(separatedBy: "\n")
parse(data: testStrings)
}
func parse(data testStrings: [String]) {
guard testStrings.count > 0 else { print("Error Parsing"); return }
func parseArray(with line: String) -> [Int] {
var retVal: [Int] = []
let split = line.components(separatedBy: "[")
guard split.count == 2 else { print("Error Array 1"); return retVal }
let split2 = split[1].components(separatedBy: "]")
guard split2.count == 2 else { print("Error Array 2"); return retVal }
let dataString = split2[0].replacingOccurrences(of: " ", with: "")
let data = dataString.components(separatedBy: ",")
retVal = data.map { Int($0)!}
return retVal
}
var testIntermediate = TestCase(before: [], after: [], op: 0, A: 0, B: 0, C: 0)
for line in testStrings {
if line.hasPrefix("Before") {
testIntermediate.before = parseArray(with: line)
} else if line.hasPrefix("After") {
testIntermediate.after = parseArray(with: line)
tests.append(testIntermediate)
} else if line.count > 4 {
let split = line.components(separatedBy: " ")
guard split.count == 4 else { print("Error code \(line)"); return }
testIntermediate.op = Int(split[0])!
testIntermediate.A = Int(split[1])!
testIntermediate.B = Int(split[2])!
testIntermediate.C = Int(split[3])!
} else {
testIntermediate = TestCase(before: [], after: [], op: 0, A: 0, B: 0, C: 0)
}
}
}
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day16Prog.txt'
func loadProgram(withFile filename: String) {
let progFile = Tools.readFile(fromPath: filename)
var progStrings = progFile.components(separatedBy: "\n")
// 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 let lastStr = progStrings.last, lastStr.count == 0 else { return }
progStrings.removeLast() // empty string
var progIntermediate = Code(op: 0, A: 0, B: 0, C: 0)
for line in progStrings {
if line.count > 4 {
let split = line.components(separatedBy: " ")
guard split.count == 4 else { print("Error code 2 \(line)"); return }
progIntermediate.op = Int(split[0])!
progIntermediate.A = Int(split[1])!
progIntermediate.B = Int(split[2])!
progIntermediate.C = Int(split[3])!
program.append(progIntermediate)
progIntermediate = Code(op: 0, A: 0, B: 0, C: 0)
}
}
}
func runTests() -> Int {
var validCount: [Int : Int] = [:]
for testNum in 0..<tests.count {
validCount[testNum] = tests[testNum].countOpsValid()
}
return validCount.filter {$0.value >= 3 }.count
}
func validateMap(with map: [Int:Int]) -> Bool {
for test in tests {
if !test.validate(withMapping: map) {
return false
}
}
return true
}
// returns the content of register zero
func runProgram(with map: [Int:Int]) -> Int {
let z8 = Z8(reg: [0, 0, 0, 0], op: 0, A: 0, B: 0, C: 0)
for lineIndex in 0..<program.count {
z8.A = program[lineIndex].A
z8.B = program[lineIndex].B
z8.C = program[lineIndex].C
z8.fn[map[program[lineIndex].op]!]()
}
return z8.reg[0]
}
// Return a map of [SysOp : myOp]
func determineOps() -> [Int:Int] {
var possibleOpCodes: [Int : [Int]] = [:]
for myOpCode in 0..<16 {
var sysOp: [Int: Bool] = [:]
for test in tests {
if test.validate(withIndex: myOpCode) {
sysOp[test.op] = true
}
}
possibleOpCodes[myOpCode] = Array(sysOp.keys)
}
// We have 16 sets off opcodes, now reduce them
func doneTest() -> Bool {
for possible in possibleOpCodes {
if possible.value.count > 1 {
return false
}
}
return true
}
while !doneTest() {
for index in 0..<possibleOpCodes.count {
if possibleOpCodes[index]!.count == 1 {
for inner in 0..<possibleOpCodes.count {
if inner != index {
var possibleArray = possibleOpCodes[inner]!
possibleArray.removeAll(where: { $0 == possibleOpCodes[index]![0] })
possibleOpCodes[inner] = possibleArray
}
}
}
}
}
var retVal: [Int:Int] = [:]
for myOpCode in 0..<16 {
let sysOp = possibleOpCodes[myOpCode]![0]
retVal[sysOp] = myOpCode
}
return retVal
}
}
class Day16: AOCDay {
lazy var tests: (() -> ()) = day16Tests
lazy var final: (() -> ()) = day16Final
let testData1 = """
Before: [3, 2, 1, 1]
9 2 1 2
After: [3, 2, 2, 1]
"""
func testZ8Instructions() {
let z8 = Z8(reg: [0, 0, 0, 0], op: 0, A: 0, B: 0, C: 0)
func initZ8() { z8.A = 1; z8.B = 2; z8.reg = [1, 3, 4, 2] }
initZ8(); z8.addr()
XCTAssertEqual(test: "testZ8Instructions addr", withExpression: (z8.reg[z8.C] == 7))
initZ8(); z8.addi()
XCTAssertEqual(test: "testZ8Instructions addi", withExpression: (z8.reg[z8.C] == 5))
initZ8(); z8.mulr()
XCTAssertEqual(test: "testZ8Instructions mulr", withExpression: (z8.reg[z8.C] == 12))
initZ8(); z8.muli()
XCTAssertEqual(test: "testZ8Instructions muli", withExpression: (z8.reg[z8.C] == 6))
initZ8(); z8.banr()
XCTAssertEqual(test: "testZ8Instructions banr", withExpression: (z8.reg[z8.C] == 0))
initZ8(); z8.bani()
XCTAssertEqual(test: "testZ8Instructions bani", withExpression: (z8.reg[z8.C] == 2))
initZ8(); z8.borr()
XCTAssertEqual(test: "testZ8Instructions borr", withExpression: (z8.reg[z8.C] == 7))
initZ8(); z8.bori()
XCTAssertEqual(test: "testZ8Instructions bori", withExpression: (z8.reg[z8.C] == 3))
initZ8(); z8.setr()
XCTAssertEqual(test: "testZ8Instructions setr", withExpression: (z8.reg[z8.C] == 3))
initZ8(); z8.seti()
XCTAssertEqual(test: "testZ8Instructions seti", withExpression: (z8.reg[z8.C] == 1))
initZ8(); z8.gtir()
XCTAssertEqual(test: "testZ8Instructions gtir", withExpression: (z8.reg[z8.C] == 0))
initZ8(); z8.gtri()
XCTAssertEqual(test: "testZ8Instructions gtri", withExpression: (z8.reg[z8.C] == 1))
initZ8(); z8.gtrr()
XCTAssertEqual(test: "testZ8Instructions gtrr", withExpression: (z8.reg[z8.C] == 0))
initZ8(); z8.eqir()
XCTAssertEqual(test: "testZ8Instructions eqir", withExpression: (z8.reg[z8.C] == 0))
initZ8(); z8.eqri()
XCTAssertEqual(test: "testZ8Instructions eqri", withExpression: (z8.reg[z8.C] == 0))
initZ8(); z8.eqrr()
XCTAssertEqual(test: "testZ8Instructions eqrr", withExpression: (z8.reg[z8.C] == 0))
}
func testInitFile() {
let cla = Classification(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day16.txt")
XCTAssertEqual(test: "testInitFile count", withExpression: (cla.tests.count > 0))
XCTAssertEqual(test: "testInitFile before", withExpression: (cla.tests.last!.before == [3, 1, 2, 2]))
XCTAssertEqual(test: "testInitFile after", withExpression: (cla.tests.last!.after == [3, 1, 2, 0]))
XCTAssertEqual(test: "testInitFile op", withExpression: (cla.tests.last!.op == 11))
XCTAssertEqual(test: "testInitFile A", withExpression: (cla.tests.last!.A == 1))
XCTAssertEqual(test: "testInitFile B", withExpression: (cla.tests.last!.B == 2))
XCTAssertEqual(test: "testInitFile C", withExpression: (cla.tests.last!.C == 3))
}
func testInitString() {
let cla = Classification(withString: testData1)
XCTAssertEqual(test: "testInitString count", withExpression: (cla.tests.count > 0))
XCTAssertEqual(test: "testInitString before", withExpression: (cla.tests[0].before == [3, 2, 1, 1]))
XCTAssertEqual(test: "testInitString after", withExpression: (cla.tests[0].after == [3, 2, 2, 1]))
XCTAssertEqual(test: "testInitString op", withExpression: (cla.tests[0].op == 9))
XCTAssertEqual(test: "testInitString A", withExpression: (cla.tests[0].A == 2))
XCTAssertEqual(test: "testInitString B", withExpression: (cla.tests[0].B == 1))
XCTAssertEqual(test: "testInitString C", withExpression: (cla.tests[0].C == 2))
}
func testCountValidOps3orMore() {
let cla = Classification(withString: testData1)
let testCount = cla.runTests()
XCTAssertEqual(test: "testCountValidOps3orMore count", withExpression: (testCount == 1))
}
func testValidateWithMapping() {
let cla = Classification(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day16.txt")
let mapping = cla.determineOps()
let works = cla.validateMap(with: mapping)
XCTAssertEqual(test: "testValidateWithMapping", withExpression: (works == true))
}
func day16Tests() {
testZ8Instructions()
testInitFile()
testInitString()
testCountValidOps3orMore()
testValidateWithMapping()
}
func day16Final() {
let cla = Classification(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day16.txt")
let testCount = cla.runTests()
print("Answer to part 1 is: \(testCount)")
let mapping = cla.determineOps()
cla.loadProgram(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day16Prog.txt")
let reg0 = cla.runProgram(with: mapping)
print("Answer to part 2 is: \(reg0)")
}
}

@ -5,7 +5,7 @@
import Foundation import Foundation
let showTests = true let showTests = true
let onlyOneDay = 15 let onlyOneDay = 16
var allTests: [(() -> ())] = [] var allTests: [(() -> ())] = []
var allFinal: [(() -> ())] = [] var allFinal: [(() -> ())] = []
@ -27,6 +27,7 @@ allTests.append(Day12().tests)
allTests.append(Day13().tests) allTests.append(Day13().tests)
allTests.append(Day14().tests) allTests.append(Day14().tests)
allTests.append(Day15().tests) allTests.append(Day15().tests)
allTests.append(Day16().tests)
// Compile list of Answers // Compile list of Answers
allFinal.append(Day01().final) allFinal.append(Day01().final)
@ -44,6 +45,7 @@ allFinal.append(Day12().final)
allFinal.append(Day13().final) allFinal.append(Day13().final)
allFinal.append(Day14().final) allFinal.append(Day14().final)
allFinal.append(Day15().final) allFinal.append(Day15().final)
allFinal.append(Day16().final)
if onlyOneDay > 0 { if onlyOneDay > 0 {
print("\nDay \(onlyOneDay)") print("\nDay \(onlyOneDay)")

Loading…
Cancel
Save