Compare commits

...
3 Commits
Author SHA1 Message Date
peterr 8062f0a2be Add code for day 16
Day 16: Chronal Classification
2019-01-09 20:05:32 -06:00
peterr 5531412a55 Add code for Day 15
Day 15: Beverage Bandits
2019-01-08 23:15:45 -06:00
peterr 14c93b8482 Add Code for Day 14
Day 14: Chocolate Charts
(brute forced part 2, not elegant)
2018-12-15 12:22:25 -06:00
8 changed files with 5607 additions and 6 deletions
+32
View File
@@ -0,0 +1,32 @@
################################
#########################.G.####
#########################....###
##################.G.........###
##################.##.......####
#################...#.........##
################..............##
######..########...G...#.#....##
#####....######.G.GG..G..##.####
#######.#####G............#.####
#####.........G..G......#...####
#####..G......G..........G....##
######GG......#####........E.###
#######......#######..........##
######...G.G#########........###
######......#########.....E..###
#####.......#########........###
#####....G..#########........###
######.##.#.#########......#####
#######......#######.......#####
#######.......#####....E...#####
##.G..#.##............##.....###
#.....#........###..#.#.....####
#.........E.E...#####.#.#....###
######......#.....###...#.#.E###
#####........##...###..####..###
####...G#.##....E####E.####...##
####.#########....###E.####....#
###...#######.....###E.####....#
####..#######.##.##########...##
####..######################.###
################################
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+125
View File
@@ -0,0 +1,125 @@
//
// Advent of Code 2018 "Day 14: Chocolate Charts"
//
import Foundation
class Chocolate {
var score: [Int] = [3, 7, 1, 0]
var scoreStr = "3710"
var elf: [Int] = [0, 1]
var lf2 = 3
func moveForward() {
for index in 0..<elf.count {
let currentRecipe = elf[index]
let moveCount = score[elf[index]] + 1
elf[index] = moveCount + currentRecipe
if elf[index] >= score.count {
elf[index] -= score.count
}
}
}
func nextRecipe() {
var sum = 0
for index in 0..<elf.count {
sum += score[elf[index]]
}
let strsum = Array(String(sum))
for i in 0..<strsum.count {
let newScore = String(strsum[i])
score.append(Int(newScore)!)
scoreStr += newScore
}
}
func get10RecipeScore(after index: Int) -> String {
var retVal = ""
repeat {
nextRecipe()
moveForward()
} while score.count < index+10
for i in index..<index+10 {
retVal += String(score[i])
}
return retVal
}
func getNumBefore(pattern: String) -> Int {
var distance = 0
let range = scoreStr.range(of: pattern)
if let range = range {
distance = scoreStr.distance(from: scoreStr.startIndex, to: range.lowerBound)
}
return distance
}
}
class Day14: AOCDay {
lazy var tests: (() -> ()) = day14Tests
lazy var final: (() -> ()) = day14Final
func testNextRecipe() {
let cho = Chocolate()
cho.nextRecipe()
guard cho.score.count == 6 else {
XCTAssertEqual(test: "testNextRecipe fail count", withExpression: (false))
return
}
XCTAssertEqual(test: "testNextRecipe", withExpression: (cho.score[4] == 1 && cho.score[5] == 0))
}
func testMoveForward() {
let cho = Chocolate()
cho.nextRecipe()
cho.moveForward()
XCTAssertEqual(test: "testMoveForward", withExpression: (cho.elf[0] == 4 && cho.elf[1] == 3))
}
func testGet10RecipeScore() {
var cho = Chocolate()
var answer = cho.get10RecipeScore(after: 9)
XCTAssertEqual(test: "testGet10RecipeScore 9", withExpression: (answer == "5158916779"))
cho = Chocolate()
answer = cho.get10RecipeScore(after: 5)
XCTAssertEqual(test: "testGet10RecipeScore 5", withExpression: (answer == "0124515891"))
cho = Chocolate()
answer = cho.get10RecipeScore(after: 18)
XCTAssertEqual(test: "testGet10RecipeScore 18", withExpression: (answer == "9251071085"))
cho = Chocolate()
answer = cho.get10RecipeScore(after: 2018)
XCTAssertEqual(test: "testGet10RecipeScore 2018", withExpression: (answer == "5941429882"))
}
func testGetNumBefore() {
let cho = Chocolate()
_ = cho.get10RecipeScore(after: 2018)
var answer = cho.getNumBefore(pattern: "51589")
XCTAssertEqual(test: "testGetNumBefore 51589", withExpression: (answer == 9))
answer = cho.getNumBefore(pattern: "01245")
XCTAssertEqual(test: "testGetNumBefore 01245", withExpression: (answer == 5))
answer = cho.getNumBefore(pattern: "92510")
XCTAssertEqual(test: "testGetNumBefore 92510", withExpression: (answer == 18))
answer = cho.getNumBefore(pattern: "59414")
XCTAssertEqual(test: "testGetNumBefore 59414", withExpression: (answer == 2018))
}
func day14Tests() {
testNextRecipe()
testMoveForward()
testGet10RecipeScore()
testGetNumBefore()
}
func day14Final() {
let cho = Chocolate()
let answer = cho.get10RecipeScore(after: 580741)
print("Answer to part 1 is: \(answer)")
// let answer = cho.get10RecipeScore(after: 112580741)
// let dist = cho.getNumBefore(pattern: "580741")
// print("Answer to part 2 is: \(dist)")
print("Brute forced part 2 - takes too long")
}
}
+789
View File
@@ -0,0 +1,789 @@
//
// Advent of Code 2018 "Day 15: Beverage Bandits"
//
import Foundation
enum State: Int {
case Unknown = -4
case Wall = -3
case Open = 0
case Elf = 1
case Goblin = 2
}
struct Entity: Equatable, Comparable {
var kind: State
var loc: GridPoint
var power: Int
var hitPts: Int
init(kind: State, loc: GridPoint, power: Int = 3, hitPts: Int = 200) {
self.kind = kind
self.loc = loc
self.power = power
self.hitPts = hitPts
}
static func == (lhs: Entity, rhs: Entity) -> Bool {
return (lhs.loc == rhs.loc) && (lhs.power == rhs.power) && (lhs.hitPts == rhs.hitPts)
}
static func < (lhs: Entity, rhs: Entity) -> Bool {
return lhs.loc < rhs.loc
}
}
class Beverage {
var entities: [Entity] = []
var maze: [[Character]] = []
var nummaze: [[State]] = []
var entityDict: [GridPoint : Int] = [:]
var width = 0
var height = 0
var rounds = 0
// Supply filename for input ex: '/home/peterr/AOC2018/Sources/AOC2018/data/day15.txt'
init(withFile filename: String) {
let mazeFile = Tools.readFile(fromPath: filename)
var mazeStrings = mazeFile.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 = mazeStrings.last, lastStr.count == 0 else { return }
mazeStrings.removeLast() // empty string
parseData(intoGrid: mazeStrings)
}
// Supply test data in the form of a String
init(withString mazeFile: String) {
let mazeStrings = mazeFile.components(separatedBy: "\n")
parseData(intoGrid: mazeStrings)
}
func parseData(intoGrid mazeArray: [String]) {
guard mazeArray.count > 0 else { print("Error Parsing"); return }
width = mazeArray[0].count
height = mazeArray.count
for line in mazeArray {
maze.append(Array(line))
}
func newEntity(type kind: State, at loc: (Int, Int)) {
var power = 3
if kind == .Elf {
power = 12
}
entities.append(Entity(kind: kind, loc: GridPoint(X: loc.0, Y: loc.1), power: power))
}
for _ in 0..<height {
nummaze.append(Array(repeating: .Wall, count: width))
}
for j in 0..<height {
for i in 0..<width {
switch maze[j][i] {
case "#": break // we initialized this grid to all "walls"
case ".": nummaze[j][i] = .Open
case "G": nummaze[j][i] = .Goblin
newEntity(type: .Goblin,at: (i, j))
case "E": nummaze[j][i] = .Elf
newEntity(type: .Elf, at: (i, j))
default: nummaze[j][i] = .Unknown
}
}
}
updateEntityDict()
entities.sort()
}
func updateEntityDict() {
entityDict = [:]
for index in 0..<entities.count {
if entities[index].hitPts > 0 {
entityDict[entities[index].loc] = index
}
}
}
// Sort the entities (reading order) and repopulate the nummaze map
func updateMaze() {
for j in 0..<height {
for i in 0..<width {
if nummaze[j][i] == .Elf || nummaze[j][i] == .Goblin {
nummaze[j][i] = .Open
}
}
}
for entity in entities {
if entity.hitPts > 0 {
switch entity.kind {
case .Elf: nummaze[entity.loc.Y][entity.loc.X] = .Elf
case .Goblin: nummaze[entity.loc.Y][entity.loc.X] = .Goblin
default: break
}
}
}
}
func printMaze(with maze: [[Int]]) {
let width = maze[0].count
let height = maze.count
for j in 0..<height {
for i in 0..<width {
switch maze[j][i] {
case -4: print("?", terminator: "")
case -3: print("#", terminator: "")
case -2: print("G", terminator: "")
case -1: print("E", terminator: "")
case 0: print(".", terminator: "")
default: print("\(maze[j][i])", terminator: "")
}
}
print("")
}
print("")
}
// Return the distance and the preferred direction (direction only valid by reversing the request; i.e. swapping orig and dest)
func distance(from origin: GridPoint, to dest: GridPoint) -> (dist: Int, dir: GridPoint) {
var retVal = (dist: -1, dir: GridPoint(X:0, Y:0))
var distMaze: [[Int]] = []
for row in nummaze {
distMaze.append(row.map { if $0.rawValue > 0 { return -$0.rawValue } else { return $0.rawValue } })
}
func testAndQueue(for point: GridPoint, on wave: Int, in queue: inout [GridPoint]) {
if distMaze[point.Y][point.X] == 0 {
distMaze[point.Y][point.X] = wave
queue.append(point)
}
}
var wave = 1
var done = false
var queue: [GridPoint] = [origin]
distMaze[dest.Y][dest.X] = 0
while !done {
var newQueue: [GridPoint] = []
for point in queue {
testAndQueue(for: point.up, on: wave, in: &newQueue)
testAndQueue(for: point.down, on: wave, in: &newQueue)
testAndQueue(for: point.left, on: wave, in: &newQueue)
testAndQueue(for: point.right, on: wave, in: &newQueue)
}
queue = newQueue
if queue.contains(dest) {
done = true
retVal.dist = distMaze[dest.Y][dest.X]
if wave == 1 {
retVal.dir = origin - dest
}
}
done = done || (newQueue.count == 0)
wave += 1
}
// printMaze(with: distMaze)
// Determin direction by locating the lowest distance count via reading-order
if retVal.dir == GridDir.none {
if distMaze[dest.up.Y][dest.up.X] == retVal.dist-1 {
retVal.dir = GridDir.up
} else if distMaze[dest.left.Y][dest.left.X] == retVal.dist-1 {
retVal.dir = GridDir.left
} else if distMaze[dest.right.Y][dest.right.X] == retVal.dist-1 {
retVal.dir = GridDir.right
} else if distMaze[dest.down.Y][dest.down.X] == retVal.dist-1 {
retVal.dir = GridDir.down
}
}
return retVal
}
func findNearestTarget(for attacker: Entity) -> GridPoint {
var retVal = GridPoint(X: -1, Y: -1)
var list: [GridPoint : Bool] = [:]
func testAnAddToList(for point: GridPoint) {
if nummaze[point.Y][point.X] == .Open {
list[point] = true
}
}
// Create a list of reachables
for entity in entities {
if entity.hitPts > 0 && entity.kind == enemy(of: attacker) {
testAnAddToList(for: entity.loc.up)
testAnAddToList(for: entity.loc.down)
testAnAddToList(for: entity.loc.left)
testAnAddToList(for: entity.loc.right)
}
}
let inRange = Array(list.keys)
var reachable: [GridPoint : Int] = [:]
// Create a sorted list of Nearest targets from reachables
var minDist = width + height
for target in inRange {
let dist = distance(from: attacker.loc, to: target).dist
if dist > 0 {
minDist = min(dist, minDist)
reachable[target] = dist
}
}
var nearest = Array(reachable.filter { $0.value == minDist }.keys)
nearest.sort()
if nearest.count > 0 {
retVal = nearest[0]
}
return retVal
}
func moveDirection(from entity: Entity, to dest: GridPoint) -> GridPoint {
// use the reverse map in 'distance' to come up with a preferred direction
var retVal = GridPoint(X:0,Y:0)
if dest != GridPoint(X: -1, Y: -1) {
let dist = distance(from: dest, to: entity.loc)
if dist.dist > 0 {
retVal = dist.dir
}
}
return retVal
}
func round() -> Bool {
var done = false
// Only sort once per round
entities.sort()
for index in 0..<entities.count {
if entities[index].hitPts > 0 {
done = takeTurn(with: index)
if done {
break
}
}
}
if !done {
rounds += 1
}
return done
}
func tabulateScore() -> Int {
// sum remaining hitPts
var sum = 0
for entity in entities {
if entity.hitPts > 0 {
sum += entity.hitPts
}
}
return sum * rounds
}
let watchIndex = -1
func takeTurn(with entityIndex: Int) -> Bool {
//done test
let viableEnemy = entities.filter { $0.kind == enemy(of: entities[entityIndex]) && $0.hitPts > 0 }
guard viableEnemy.count > 0 else { return true }
// entityDict must be updated for every action (move or attack)
updateEntityDict()
// Adjacent to target?
var indexOfAdjeacentTarget = adjacent(to: entityIndex)
if indexOfAdjeacentTarget == -1 {
// find nearest enemy
let nearest = findNearestTarget(for: entities[entityIndex])
// determine direction to go
let direction = moveDirection(from: entities[entityIndex], to: nearest)
// print("nearest \(nearest), direction = \(direction)")
// move and update dict and map
entities[entityIndex].loc = entities[entityIndex].loc + direction
if entityIndex == watchIndex {
print("\(entities[entityIndex])")
print("nearest : \(nearest)")
print("direction : \(direction)")
}
updateEntityDict()
updateMaze()
}
// Check if we moved within range
indexOfAdjeacentTarget = adjacent(to: entityIndex)
if entityIndex == watchIndex && indexOfAdjeacentTarget > 0 {
print("adjacent Target : \(entities[indexOfAdjeacentTarget])")
}
if indexOfAdjeacentTarget != -1 {
attack(from: entities[entityIndex], to: indexOfAdjeacentTarget)
updateEntityDict()
updateMaze()
}
return false
}
func enemy(of entity: Entity) -> State {
var retVal = State.Goblin
if entity.kind == .Goblin {
retVal = .Elf
}
return retVal
}
// Return the index number of the target to attack (or -1 if no target)
func adjacent(to subjectIndex: Int) -> Int {
var retVal = -1
var enemyDict: [GridPoint:Int] = [:] // Loc : HitPts
func determineEnemyAndQueue(for loc: GridPoint) {
if nummaze[loc.Y][loc.X] == enemy(of: entities[subjectIndex]) {
updateEntityDict()
if let index = entityDict[loc] {
enemyDict[loc] = entities[index].hitPts
}
}
}
// create list of adjacent enemies
determineEnemyAndQueue(for: entities[subjectIndex].loc.up)
determineEnemyAndQueue(for: entities[subjectIndex].loc.left)
determineEnemyAndQueue(for: entities[subjectIndex].loc.right)
determineEnemyAndQueue(for: entities[subjectIndex].loc.down)
// sort list by HitPoints, fewest to most order
if enemyDict.count > 0 {
// find min hit point
let minHP = enemyDict.min { a, b in a.value < b.value }?.value ?? -1
// gather array of min-hitpoint enemies
let minDict = enemyDict.filter {$0.value == minHP}
// sort enemies by location in reading-order
let enemy = minDict.sorted(by: <)
// select the first one
retVal = entityDict[enemy[0].key]!
}
return retVal
}
func attack(from attacker: Entity, to victimIndex: Int) {
entities[victimIndex].hitPts = entities[victimIndex].hitPts - attacker.power
// print("attacker power = \(attacker.power)")
if entities[victimIndex].hitPts <= 0 && entities[victimIndex].kind == .Elf {
print("Fail")
while true {}
}
}
}
class Day15: AOCDay {
lazy var tests: (() -> ()) = day15Tests
lazy var final: (() -> ()) = day15Final
let testData1 = """
#######
#E..G.#
#...#.#
#.G.#G#
#######
"""
let testData2 = """
#######
#.E...#
#.....#
#...G.#
#######
"""
let testData3 = """
#########
#G.....G#
#...G...#
#...E...#
#G.....G#
#.......#
#.......#
#G..G..G#
#########
"""
let testData4 = """
#########
#G..G..G#
#.......#
#.......#
#G..E..G#
#.......#
#.......#
#G..G..G#
#########
"""
let testData5 = """
#######
#...G.#
#..G.G#
#.#.#G#
#...#E#
#.....#
#######
"""
let testData6 = """
#######
#G....#
#..G..#
#..EG.#
#..G..#
#...G.#
#######
"""
let testData7 = """
#######
#.G...#
#...EG#
#.#.#G#
#..G#E#
#.....#
#######
"""
// Combat ends after 37 full rounds
// Elves win with 982 total hit points left
// Outcome: 37 * 982 = 36334
let testData8 = """
#######
#G..#E#
#E#E.E#
#G.##.#
#...#E#
#...E.#
#######
"""
// Combat ends after 46 full rounds
// Elves win with 859 total hit points left
// Outcome: 46 * 859 = 39514
let testData9 = """
#######
#E..EG#
#.#G.E#
#E.##E#
#G..#.#
#..E#.#
#######
"""
// Combat ends after 35 full rounds
// Goblins win with 793 total hit points left
// Outcome: 35 * 793 = 27755
let testData10 = """
#######
#E.G#.#
#.#G..#
#G.#.G#
#G..#.#
#...E.#
#######
"""
// Combat ends after 54 full rounds
// Goblins win with 536 total hit points left
// Outcome: 54 * 536 = 28944
let testData11 = """
#######
#.E...#
#.#..G#
#.###.#
#E#G#G#
#...#G#
#######
"""
// Combat ends after 20 full rounds
// Goblins win with 937 total hit points left
// Outcome: 20 * 937 = 18740
let testData12 = """
#########
#G......#
#.E.#...#
#..##..G#
#...##..#
#...#...#
#.G...G.#
#.....G.#
#########
"""
func testInitFile() {
let bev = Beverage(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day15.txt")
printMaze(with: bev)
}
func testInitString() {
let bev = Beverage(withString: testData1)
printMaze(with: bev)
}
func testEntities() {
let bev = Beverage(withString: testData1)
var myentities = bev.entities
func show(entities: [Entity]) {
var id = 0
for entity in entities {
print("\(id) : \(entity)")
id += 1
}
}
// show(entities: myentities) // Demonstrated that sorting works when force-read in the entities in reversse order
myentities.sort()
// show(entities: myentities)
}
func testFindNearestTarget() {
var bev = Beverage(withString: testData1)
var nearest = bev.findNearestTarget(for: bev.entities[0])
XCTAssertEqual(test: "testFindNearestTarget (Elf)", withExpression: (nearest == GridPoint(X: 3, Y: 1)))
nearest = bev.findNearestTarget(for: bev.entities[1])
XCTAssertEqual(test: "testFindNearestTarget (Goblin 0)", withExpression: (nearest == GridPoint(X: 2, Y: 1)))
nearest = bev.findNearestTarget(for: bev.entities[2])
XCTAssertEqual(test: "testFindNearestTarget (Goblin 1)", withExpression: (nearest == GridPoint(X: 2, Y: 1)))
nearest = bev.findNearestTarget(for: bev.entities[3])
XCTAssertEqual(test: "testFindNearestTarget (Goblin 2)", withExpression: (nearest == GridPoint(X: -1, Y: -1)))
bev = Beverage(withString: testData5)
nearest = bev.findNearestTarget(for: bev.entities[3])
XCTAssertEqual(test: "testFindNearestTarget is Goblin", withExpression: (bev.entities[3].kind == .Goblin))
XCTAssertEqual(test: "testFindNearestTarget is Elf", withExpression: (bev.entities[4].kind == .Elf))
XCTAssertEqual(test: "testFindNearestTarget (Goblin)", withExpression: (nearest == GridPoint(X: 3, Y: 3)))
}
func testDistance() {
let bev = Beverage(withString: testData1)
var dist = bev.distance(from: bev.entities[0].loc, to: GridPoint(X: 2, Y: 2)).dist
XCTAssertEqual(test: "testDistance (2, 2) = 2", withExpression: (dist == 2))
dist = bev.distance(from: bev.entities[0].loc, to: GridPoint(X: 5, Y: 2)).dist
XCTAssertEqual(test: "testDistance (5, 2) = -1", withExpression: (dist == -1))
dist = bev.distance(from: bev.entities[0].loc, to: GridPoint(X: 2, Y: 1)).dist
XCTAssertEqual(test: "testDistance (2, 1) = 0", withExpression: (dist == 1))
dist = bev.distance(from: bev.entities[3].loc, to: GridPoint(X: 2, Y: 1)).dist
XCTAssertEqual(test: "testDistance last Goblin to` Elf", withExpression: (dist == -1))
}
func testMoveDirection() {
var bev = Beverage(withString: testData1)
var nearest = bev.findNearestTarget(for: bev.entities[0])
var direction = bev.moveDirection(from: bev.entities[0], to: nearest)
XCTAssertEqual(test: "testMoveDirection testData1", withExpression: (direction == GridDir.right))
bev = Beverage(withString: testData2)
nearest = bev.findNearestTarget(for: bev.entities[0])
direction = bev.moveDirection(from: bev.entities[0], to: nearest)
XCTAssertEqual(test: "testMoveDirection testData2", withExpression: (direction == GridDir.right))
bev = Beverage(withString: testData5)
nearest = bev.findNearestTarget(for: bev.entities[1])
direction = bev.moveDirection(from: bev.entities[1], to: nearest)
print("Nearest: \(nearest)")
print("Direction: \(direction)")
XCTAssertEqual(test: "testMoveDirection is Goblin", withExpression: (bev.entities[1].kind == .Goblin))
XCTAssertEqual(test: "testMoveDirection is Elf", withExpression: (bev.entities[4].kind == .Elf))
XCTAssertEqual(test: "testMoveDirection (nearest)", withExpression: (nearest == GridPoint(X: 5, Y: 5)))
XCTAssertEqual(test: "testMoveDirection (dir)", withExpression: (direction == GridDir.down))
}
func testAdjacent() {
var bev = Beverage(withString: testData3)
var adjacent = bev.adjacent(to: 3)
XCTAssertEqual(test: "testAdjacent is Elf", withExpression: (bev.entities[3].kind == .Elf))
XCTAssertEqual(test: "testAdjacent is adjacent", withExpression: (adjacent == 2))
bev = Beverage(withString: testData4)
adjacent = bev.adjacent(to: 4)
XCTAssertEqual(test: "testAdjacent is Elf", withExpression: (bev.entities[4].kind == .Elf))
XCTAssertEqual(test: "testAdjacent is not adjacent", withExpression: (adjacent == -1))
}
func testTakeTurn() {
var bev = Beverage(withString: testData3)
XCTAssertEqual(test: "testTakeTurn is Elf", withExpression: (bev.entities[3].kind == .Elf))
// printMaze(with: bev)
_ = bev.takeTurn(with: 3)
// printMaze(with: bev)
bev = Beverage(withString: testData4)
XCTAssertEqual(test: "testTakeTurn is Elf", withExpression: (bev.entities[4].kind == .Elf))
// printMaze(with: bev)
_ = bev.takeTurn(with: 4)
// printMaze(with: bev)
}
func testAttack() {
let bev = Beverage(withString: testData6)
// G.... 9 G.... 9
// ..G.. 4 ..G.. 4
// ..EG. 2 --> ..E..
// ..G.. 2 ..G.. 2
// ...G. 1 ...G. 1
bev.entities[0].hitPts = 9
bev.entities[1].hitPts = 4
bev.entities[3].hitPts = 2
bev.entities[4].hitPts = 2
bev.entities[5].hitPts = 1
printMaze(with: bev, withHP: true)
let indexOfAdjeacentTarget = bev.adjacent(to: 2)
if indexOfAdjeacentTarget != -1 {
bev.attack(from: bev.entities[2], to: indexOfAdjeacentTarget)
bev.updateEntityDict()
bev.updateMaze()
}
printMaze(with: bev, withHP: true)
}
func testRound() {
let bev = Beverage(withString: testData4)
// printMaze(with: bev)
_ = bev.round()
// printMaze(with: bev)
_ = bev.round()
// printMaze(with: bev)
_ = bev.round()
printMaze(with: bev)
let adjacent = bev.adjacent(to: 4)
// print("adjacent=\(adjacent)")
// print("entity = \(bev.entities[adjacent])")
XCTAssertEqual(test: "testRound is Elf", withExpression: (bev.entities[4].kind == .Elf))
XCTAssertEqual(test: "testRound attack entity 1", withExpression: (bev.entities[adjacent].loc == GridPoint(X: 4, Y: 2)))
}
func testSampleGame() {
var bev = Beverage(withString: testData7)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
var final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 7", withExpression: (final == 27730))
bev = Beverage(withString: testData8)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 8", withExpression: (final == 36334))
bev = Beverage(withString: testData9)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 9", withExpression: (final == 39514))
bev = Beverage(withString: testData10)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 10", withExpression: (final == 27755))
bev = Beverage(withString: testData11)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 11", withExpression: (final == 28944))
bev = Beverage(withString: testData12)
// printMaze(with: bev, withHP: true)
while !bev.round() {}
// printMaze(with: bev, withHP: true)
final = bev.tabulateScore()
// print("Final score = \(final)")
XCTAssertEqual(test: "testSampleGame 12", withExpression: (final == 18740))
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// for _ in 2..<23 {
// bev.round()
// }
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
// for _ in 28..<46 {
// bev.round()
// }
// printMaze(with: bev, withHP: true)
// bev.round()
// printMaze(with: bev, withHP: true)
}
func printMaze(with bev: Beverage, withHP: Bool = false) {
let width = bev.nummaze[0].count
let height = bev.nummaze.count
func toEntityHP(i: Int, j: Int) -> Int {
var retVal = 0
if let index = bev.entityDict[GridPoint(X: i, Y: j)] {
retVal = bev.entities[index].hitPts
}
return retVal
}
print(" ROUND \(bev.rounds)")
for j in 0..<height {
for i in 0..<width {
switch bev.nummaze[j][i] {
case .Wall: print(" # ", terminator: "")
case .Unknown: print(" ? ", terminator: "")
case .Open: print(" . ", terminator: "")
case .Goblin: withHP ? print("\u{001B}[0;31m\(String(format: "%03d", toEntityHP(i:i, j:j)))\u{001B}[0;37m", terminator: "") : print(" G ", terminator: "")
case .Elf: withHP ? print("\u{001B}[0;32m\(String(format: "%03d", toEntityHP(i:i, j:j)))\u{001B}[0;37m", terminator: "") : print(" E ", terminator: "")
}
}
print("")
}
print("")
}
func day15Tests() {
// testInitFile()
// testInitString()
// testEntities()
// testDistance()
// testMoveDirection()
// testFindNearestTarget()
// testAdjacent()
// testTakeTurn()
// testRound()
// testAttack()
// testSampleGame()
}
func day15Final() {
let retVal = "None"
let bev = Beverage(withFile: "/home/peterr/AOC2018/Sources/AOC2018/data/day15.txt")
printMaze(with: bev, withHP: true)
while !bev.round() {
printMaze(with: bev, withHP: true)
}
// printMaze(with: bev, withHP: true)
let final = bev.tabulateScore()
print("Answer to part 1 is: \(final)")
print("Answer to part 2 is: \(retVal)")
}
}
+373
View File
@@ -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)")
}
}
+7 -1
View File
@@ -5,7 +5,7 @@
import Foundation import Foundation
let showTests = true let showTests = true
let onlyOneDay = 13 let onlyOneDay = 16
var allTests: [(() -> ())] = [] var allTests: [(() -> ())] = []
var allFinal: [(() -> ())] = [] var allFinal: [(() -> ())] = []
@@ -25,6 +25,9 @@ allTests.append(Day10().tests)
allTests.append(Day11().tests) allTests.append(Day11().tests)
allTests.append(Day12().tests) allTests.append(Day12().tests)
allTests.append(Day13().tests) allTests.append(Day13().tests)
allTests.append(Day14().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)
@@ -40,6 +43,9 @@ allFinal.append(Day10().final)
allFinal.append(Day11().final) allFinal.append(Day11().final)
allFinal.append(Day12().final) allFinal.append(Day12().final)
allFinal.append(Day13().final) allFinal.append(Day13().final)
allFinal.append(Day14().final)
allFinal.append(Day15().final)
allFinal.append(Day16().final)
if onlyOneDay > 0 { if onlyOneDay > 0 {
print("\nDay \(onlyOneDay)") print("\nDay \(onlyOneDay)")
+22 -5
View File
@@ -19,14 +19,23 @@ func == <T:Equatable> (tuple1:(T,T),tuple2:(T,T)) -> Bool
return (tuple1.0 == tuple2.0) && (tuple1.1 == tuple2.1) return (tuple1.0 == tuple2.0) && (tuple1.1 == tuple2.1)
} }
struct GridDir {
static let none = GridPoint(X: 0, Y: 0)
static let up = GridPoint(X: 0, Y: -1)
static let down = GridPoint(X: 0, Y: 1)
static let left = GridPoint(X: -1, Y: 0)
static let right = GridPoint(X: 1, Y: 0)
}
struct GridPoint: Equatable, Comparable, Hashable { struct GridPoint: Equatable, Comparable, Hashable {
var X = 0 var X = 0
var Y = 0 var Y = 0
var hashValue: Int { var up: GridPoint { return GridPoint(X: self.X, Y: self.Y-1) }
get { var down: GridPoint { return GridPoint(X: self.X, Y: self.Y+1) }
return X.hashValue ^ Y.hashValue var left: GridPoint { return GridPoint(X: self.X-1, Y: self.Y) }
} var right: GridPoint {return GridPoint(X: self.X+1, Y: self.Y) }
}
var hashValue: Int { return X.hashValue ^ Y.hashValue }
static func == (lhs: GridPoint, rhs: GridPoint) -> Bool { static func == (lhs: GridPoint, rhs: GridPoint) -> Bool {
return (lhs.X == rhs.X) && (lhs.Y == rhs.Y) return (lhs.X == rhs.X) && (lhs.Y == rhs.Y)
@@ -35,6 +44,14 @@ struct GridPoint: Equatable, Comparable, Hashable {
static func < (lhs: GridPoint, rhs: GridPoint) -> Bool { static func < (lhs: GridPoint, rhs: GridPoint) -> Bool {
return lhs.Y == rhs.Y ? lhs.X < rhs.X : lhs.Y < rhs.Y return lhs.Y == rhs.Y ? lhs.X < rhs.X : lhs.Y < rhs.Y
} }
static func + (lhs: GridPoint, rhs: GridPoint) -> GridPoint {
return GridPoint(X: lhs.X + rhs.X, Y: lhs.Y + rhs.Y)
}
static func - (lhs: GridPoint, rhs: GridPoint) -> GridPoint {
return GridPoint(X: lhs.X - rhs.X, Y: lhs.Y - rhs.Y)
}
} }
struct Tools { struct Tools {