(did my comment on Day 3 not get posted? I had written something up, and I thought I had pushed the button, but I don't see it on the video now. Hm) I mis-read the question the first time, and I missed that words could go diagonally. So, I wrote a rotate function to turn my grid of letters by 90 degrees so that I could just look for "XMAS" in each direction .... and then when I only found half the expected words, I realized my mistake. 🤦♂ Then my proper solution was a straightforward search, when I find an "X" I look in each direction around it for "M-A-S". In my first attempt I forgot that it was possible to have multiple matches on a single X. My solution for Part B was more similar to yours, I look for "A" and then check the diagonals for "M" and "S". I saved the diagonals into string variables so that I could check for "MAS or SAM" in each direction, which I felt simplified the condition checking a little: if (r[c] == "A") { val ul = this.matrix[rowIndex-1][c-1] val ur = this.matrix[rowIndex-1][c+1] val ll = this.matrix[rowIndex+1][c-1] val lr = this.matrix[rowIndex+1][c+1] val ldiag = ul + r[c] + lr val rdiag = ll + r[c] + ur if ((ldiag == "MAS" || ldiag == "SAM") && (rdiag == "MAS" || rdiag == "SAM")) { count++ } I thought this was a fun puzzle. I think they have one like this every year, but they're fun.
Managed to do both parts in one line again but here's the uncompressed part 1 solution first: fun calc(input: List): Int = input.find('X').sumOf { input.valid("XMAS", it) } private fun List.find(character: Char): List = flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c } .filter { it.second == character }.map { it.first to y } } private fun List.valid(word: String, point: Pair): Int = listOf(0 to -1, 1 to -1, 1 to 0, 1 to 1, 0 to 1, -1 to 1, -1 to 0, -1 to -1) .count { valid(word, point, it.first, it.second) } private fun List.valid(word: String, point: Pair, xStep:Int, yStep: Int): Boolean = word.indices.map { getOrNull(point.second + it * yStep)?.getOrNull(point.first + it * xStep) ?: "" } .joinToString("") == word Compressed to one line: fun calc(input: List): Int = input.flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c } .filter { it.second == 'X' }.map { it.first to y } }.sumOf { p -> listOf(0 to -1, 1 to -1, 1 to 0, 1 to 1, 0 to 1, -1 to 1, -1 to 0, -1 to -1) .count { o -> "XMAS".indices.map { input.getOrNull(p.second + it * o.second) ?.getOrNull(p.first + it * o.first) ?: "" }.joinToString("") == "XMAS" } }
Part 2: fun calc(input:List): Int = input.find('A').count { input.valid("MAS", it) } private fun List.find(character: Char): List = flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c } .filter { it.second == character }.map { it.first to y } } private fun List.valid(word: String, point: Pair): Boolean { val w1 = extract(point, listOf(-1 to -1, 0 to 0, 1 to 1)) val w2 = extract(point, listOf(1 to -1, 0 to 0, -1 to 1)) return (w1 == word || w1 == word.reversed()) && (w2 == word || w2 == word.reversed()) } private fun List.extract(point: Pair, steps: List): String = steps.map { s -> getOrNull(point.second + s.second)?.getOrNull(point.first + s.first) ?: "" }.joinToString("") Compressed: fun calc(input:List): Int = input.flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c } .filter { it.second == 'A' }.map { it.first to y } }.count { p -> listOf(listOf(-1 to -1, 0 to 0, 1 to 1), listOf(1 to -1, 0 to 0, -1 to 1)).map{ it.map { s -> input.getOrNull(p.second + s.second) ?.getOrNull(p.first + s.first) ?: "" }.joinToString("") }.all { it in listOf("MAS", "SAM") } }
(did my comment on Day 3 not get posted? I had written something up, and I thought I had pushed the button, but I don't see it on the video now. Hm)
I mis-read the question the first time, and I missed that words could go diagonally. So, I wrote a rotate function to turn my grid of letters by 90 degrees so that I could just look for "XMAS" in each direction .... and then when I only found half the expected words, I realized my mistake. 🤦♂
Then my proper solution was a straightforward search, when I find an "X" I look in each direction around it for "M-A-S". In my first attempt I forgot that it was possible to have multiple matches on a single X.
My solution for Part B was more similar to yours, I look for "A" and then check the diagonals for "M" and "S". I saved the diagonals into string variables so that I could check for "MAS or SAM" in each direction, which I felt simplified the condition checking a little:
if (r[c] == "A") {
val ul = this.matrix[rowIndex-1][c-1]
val ur = this.matrix[rowIndex-1][c+1]
val ll = this.matrix[rowIndex+1][c-1]
val lr = this.matrix[rowIndex+1][c+1]
val ldiag = ul + r[c] + lr
val rdiag = ll + r[c] + ur
if ((ldiag == "MAS" || ldiag == "SAM") && (rdiag == "MAS" || rdiag == "SAM")) {
count++
}
I thought this was a fun puzzle. I think they have one like this every year, but they're fun.
Managed to do both parts in one line again but here's the uncompressed part 1 solution first:
fun calc(input: List): Int = input.find('X').sumOf { input.valid("XMAS", it) }
private fun List.find(character: Char): List =
flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c }
.filter { it.second == character }.map { it.first to y } }
private fun List.valid(word: String, point: Pair): Int =
listOf(0 to -1, 1 to -1, 1 to 0, 1 to 1, 0 to 1, -1 to 1, -1 to 0, -1 to -1)
.count { valid(word, point, it.first, it.second) }
private fun List.valid(word: String, point: Pair, xStep:Int, yStep: Int): Boolean =
word.indices.map { getOrNull(point.second + it * yStep)?.getOrNull(point.first + it * xStep) ?: "" }
.joinToString("") == word
Compressed to one line:
fun calc(input: List): Int = input.flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c }
.filter { it.second == 'X' }.map { it.first to y } }.sumOf { p ->
listOf(0 to -1, 1 to -1, 1 to 0, 1 to 1, 0 to 1, -1 to 1, -1 to 0, -1 to -1)
.count { o -> "XMAS".indices.map { input.getOrNull(p.second + it * o.second)
?.getOrNull(p.first + it * o.first) ?: "" }.joinToString("") == "XMAS" } }
Part 2:
fun calc(input:List): Int = input.find('A').count { input.valid("MAS", it) }
private fun List.find(character: Char): List =
flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c }
.filter { it.second == character }.map { it.first to y } }
private fun List.valid(word: String, point: Pair): Boolean {
val w1 = extract(point, listOf(-1 to -1, 0 to 0, 1 to 1))
val w2 = extract(point, listOf(1 to -1, 0 to 0, -1 to 1))
return (w1 == word || w1 == word.reversed()) && (w2 == word || w2 == word.reversed())
}
private fun List.extract(point: Pair, steps: List): String =
steps.map { s -> getOrNull(point.second + s.second)?.getOrNull(point.first + s.first) ?: "" }.joinToString("")
Compressed:
fun calc(input:List): Int = input.flatMapIndexed { y, s -> s.mapIndexed { x, c -> x to c }
.filter { it.second == 'A' }.map { it.first to y } }.count { p -> listOf(listOf(-1 to -1, 0 to 0, 1 to 1),
listOf(1 to -1, 0 to 0, -1 to 1)).map{ it.map { s -> input.getOrNull(p.second + s.second)
?.getOrNull(p.first + s.first) ?: "" }.joinToString("") }.all { it in listOf("MAS", "SAM") } }