Skip to content

Commit

Permalink
2024-11 Rust & Scala 2
Browse files Browse the repository at this point in the history
  • Loading branch information
jurisk committed Dec 11, 2024
1 parent bca501b commit 4703326
Show file tree
Hide file tree
Showing 14 changed files with 288 additions and 101 deletions.
1 change: 1 addition & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/y2024/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ advent-of-code-common = { path = "../common" }
chumsky.workspace = true
itertools.workspace = true
regex.workspace = true
memoize.workspace = true
67 changes: 67 additions & 0 deletions rust/y2024/src/bin/solution_2024_11.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use memoize::memoize;

type N = u64;

const DATA: [N; 8] = [6_563_348, 67, 395, 0, 6, 4425, 89567, 739_318];

type R = usize;

#[expect(
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::cast_precision_loss
)]
fn halves(n: N) -> Option<(N, N)> {
let digits = (n as f64).log10() as usize + 1;
(digits % 2 == 0).then_some({
let half_length = digits / 2;
let divisor = 10u64.pow(half_length as u32);
let left = n / divisor;
let right = n % divisor;
(left, right)
})
}

#[memoize]
#[allow(clippy::collapsible_else_if)]
fn solve_one(n: N, blinks: usize) -> R {
if blinks == 0 {
1
} else {
if n == 0 {
solve_one(1, blinks - 1)
} else {
match halves(n) {
Some((left, right)) => solve_one(left, blinks - 1) + solve_one(right, blinks - 1),
None => solve_one(n * 2024, blinks - 1),
}
}
}
}

fn solve(data: &[N], blinks: usize) -> R {
data.iter().map(|n| solve_one(*n, blinks)).sum()
}

fn main() {
let result_1 = solve(&DATA, 25);
println!("Part 1: {result_1}");

let result_2 = solve(&DATA, 75);
println!("Part 2: {result_2}");
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_solve_1_real() {
assert_eq!(solve(&DATA, 25), 184_927);
}

#[test]
fn test_solve_2_real() {
assert_eq!(solve(&DATA, 75), 220_357_186_726_677);
}
}
Empty file.
Empty file.
1 change: 1 addition & 0 deletions scala2/src/main/resources/2024/12-test-00.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
noop
1 change: 1 addition & 0 deletions scala2/src/main/resources/2024/12.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
noop
83 changes: 53 additions & 30 deletions scala2/src/main/scala/jurisk/adventofcode/y2024/Advent11.scala
Original file line number Diff line number Diff line change
@@ -1,47 +1,70 @@
package jurisk.adventofcode.y2024

import jurisk.utils.FileInput._
import jurisk.utils.Parsing.StringOps
import jurisk.math.pow
import jurisk.utils.Memoize
import mouse.all.booleanSyntaxMouse

import scala.math.log10

object Advent11 {
type Input = List[Command]
type Input = Vector[Long]
type N = Long

sealed trait Command extends Product with Serializable
object Command {
case object Noop extends Command
final case class Something(
values: List[N]
) extends Command
final case class Other(value: String) extends Command

def parse(s: String): Command =
s match {
case "noop" => Noop
case s"something $rem" => Something(rem.extractLongList)
case s if s.nonEmpty => Other(s)
case _ => s.failedToParse
}
private def halves(n: Long): Option[(Long, Long)] = {
val digits = log10(n.doubleValue).toInt + 1

(digits % 2 == 0).option {
val halfLength = digits / 2
val divisor = pow(10, halfLength)
val left = n / divisor
val right = n % divisor
(left, right)
}
}

def parse(input: String): Input =
input.parseLines(Command.parse)
def blink(data: Input): Input =
data.flatMap { n =>
if (n == 0) {
Vector(1)
} else {
halves(n) match {
case Some((left, right)) =>
Vector(left, right)
case None =>
Vector(n * 2024)
}
}
}

def part1(data: Input): N =
0
private val memoizedSolve: (Long, Int) => N = Memoize.memoize2(solve)
private def solve(n: Long, blinks: Int): Long =
if (blinks <= 0) {
1
} else {
if (n == 0) {
memoizedSolve(1, blinks - 1)
} else {
halves(n) match {
case Some((left, right)) =>
memoizedSolve(left, blinks - 1) + memoizedSolve(right, blinks - 1)
case None =>
memoizedSolve(n * 2024, blinks - 1)
}
}
}

def part2(data: Input): N =
0
def blinkNTimes(data: Input, blinks: Int): Input =
(0 until blinks).foldLeft(data)((acc, _) => blink(acc))

def parseFile(fileName: String): Input =
parse(readFileText(fileName))
def part1(data: Input, blinks: Int = 25): N =
blinkNTimes(data, blinks).size

def fileName(suffix: String): String =
s"2024/11$suffix.txt"
def part2(data: Input, blinks: Int = 75): N =
data.map(n => memoizedSolve(n, blinks)).sum

def main(args: Array[String]): Unit = {
val realData: Input = parseFile(fileName(""))
val realData: Input = Vector(6563348, 67, 395, 0, 6, 4425, 89567, 739318)

def main(args: Array[String]): Unit = {
println(s"Part 1: ${part1(realData)}")
println(s"Part 2: ${part2(realData)}")
}
Expand Down
48 changes: 48 additions & 0 deletions scala2/src/main/scala/jurisk/adventofcode/y2024/Advent12.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package jurisk.adventofcode.y2024

import jurisk.utils.FileInput._
import jurisk.utils.Parsing.StringOps

object Advent12 {
type Input = List[Command]
type N = Long

sealed trait Command extends Product with Serializable
object Command {
case object Noop extends Command
final case class Something(
values: List[N]
) extends Command
final case class Other(value: String) extends Command

def parse(s: String): Command =
s match {
case "noop" => Noop
case s"something $rem" => Something(rem.extractLongList)
case s if s.nonEmpty => Other(s)
case _ => s.failedToParse
}
}

def parse(input: String): Input =
input.parseLines(Command.parse)

def part1(data: Input): N =
0

def part2(data: Input): N =
0

def parseFile(fileName: String): Input =
parse(readFileText(fileName))

def fileName(suffix: String): String =
s"2024/12$suffix.txt"

def main(args: Array[String]): Unit = {
val realData: Input = parseFile(fileName(""))

println(s"Part 1: ${part1(realData)}")
println(s"Part 2: ${part2(realData)}")
}
}
21 changes: 11 additions & 10 deletions scala2/src/test/scala/jurisk/adventofcode/y2024/Advent11Spec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,27 @@ import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers._

class Advent11Spec extends AnyFreeSpec {
private def testData = parseFile(fileName("-test-00"))
private def realData = parseFile(fileName(""))

"part 1" - {
"test" in {
part1(testData) shouldEqual 0
blink(Vector(0, 1, 10, 99, 999)) shouldEqual Vector(1, 2024, 1, 0, 9, 9,
2021976)
blink(Vector(125, 17)) shouldEqual Vector(253000, 1, 7)
blinkNTimes(Vector(125, 17), 2) shouldEqual Vector(253, 0, 2024, 14168)
part1(Vector(125, 17), 6) shouldEqual 22
part1(Vector(125, 17)) shouldEqual 55312
}

"real" in {
part1(realData) shouldEqual 0
part1(realData) shouldEqual 184927
}
}

"part 2" - {
"test" in {
part2(testData) shouldEqual 0
"real 25" in {
part2(realData, 25) shouldEqual 184927
}

"real" in {
part2(realData) shouldEqual 0
"real 75" in {
part2(realData) shouldEqual 220357186726677L
}
}
}
30 changes: 30 additions & 0 deletions scala2/src/test/scala/jurisk/adventofcode/y2024/Advent12Spec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package jurisk.adventofcode.y2024

import Advent12._
import org.scalatest.freespec.AnyFreeSpec
import org.scalatest.matchers.should.Matchers._

class Advent12Spec extends AnyFreeSpec {
private def testData = parseFile(fileName("-test-00"))
private def realData = parseFile(fileName(""))

"part 1" - {
"test" in {
part1(testData) shouldEqual 0
}

"real" in {
part1(realData) shouldEqual 0
}
}

"part 2" - {
"test" in {
part2(testData) shouldEqual 0
}

"real" in {
part2(realData) shouldEqual 0
}
}
}
58 changes: 31 additions & 27 deletions scala3/src/main/scala/jurisk/adventofcode/y2020/Advent02.scala
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
package jurisk.adventofcode.y2020

import scala.io.Source

object Advent02 extends App:
final case class Item(
a: Int,
b: Int,
letter: Char,
password: String,
):
def isValid1: Boolean =
val count = password.count(_ == letter)
(count >= a) && (count <= b)

def isValid2: Boolean =
List(password(a - 1), password(b - 1)).count(_ == letter) == 1

def parse(x: String): Item =
import jurisk.adventofcode.AdventApp.ErrorMessage
import jurisk.adventofcode.SingleLineAdventApp
import cats.implicits._

final case class Item(
a: Int,
b: Int,
letter: Char,
password: String,
):
def isValid1: Boolean =
val count = password.count(_ == letter)
(count >= a) && (count <= b)

def isValid2: Boolean =
List(password(a - 1), password(b - 1)).count(_ == letter) == 1

object Advent02 extends SingleLineAdventApp[Item, Int]:
val year: Int = 2020
val exercise: Int = 2

override def parseLine(line: String): Either[ErrorMessage, Item] = {
val Pattern = """(\d+)-(\d+) (\w): (\w+)""".r
x match

line match
case Pattern(a, b, letter, password) if letter.length == 1 =>
Item(a.toInt, b.toInt, letter.head, password)
Item(a.toInt, b.toInt, letter.head, password).asRight
case _ =>
sys.error(s"Couldn't parse $x")

val list = Source.fromResource("2020/02.txt").getLines().map(parse).toList

def solve(f: Item => Boolean): Unit =
println(list.count(f))

List[Item => Boolean](_.isValid1, _.isValid2) foreach solve
ErrorMessage(s"Couldn't parse $line").asLeft
}

override def solution1(input: List[Item]): Int = input.count(_.isValid1)

override def solution2(input: List[Item]): Int = input.count(_.isValid2)
Loading

0 comments on commit 4703326

Please sign in to comment.