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