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)")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue