From b5934a56551cbe6cc163bc96b18061eec00911cf Mon Sep 17 00:00:00 2001 From: horothesun Date: Fri, 29 Mar 2024 02:02:57 +0000 Subject: [PATCH] Day 6: recursion schemes version --- src/main/scala/Day6.scala | 104 ++++++++++++++++++++------------------ 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/main/scala/Day6.scala b/src/main/scala/Day6.scala index dcf1762..4cd7c1c 100644 --- a/src/main/scala/Day6.scala +++ b/src/main/scala/Day6.scala @@ -17,6 +17,19 @@ object Distance: case class Race(allowance: Time, record: Distance) +enum RaceOutcome: + case Win + case Loss(travelled: Distance) + +import RaceOutcome.* + +enum Bounds: + case Within, Outside + +def getRaceOutcome(race: Race, hold: Time): RaceOutcome = + val travelled = getTravelledDistance(race.allowance, hold) + if (travelled > race.record) Win else Loss(travelled) + def getTravelledDistance(allowance: Time, hold: Time): Distance = val raceDuration = allowance - hold val speed = hold.millis @@ -33,27 +46,31 @@ def countWaysToWin_bruteForce(race: Race): Long = getAllPossibleDistances(race.a def getMultipliedWaysToWin_bruteForce(rs: List[Race]): Long = rs.map(countWaysToWin_bruteForce).product -def countWaysToLoseLeft(race: Race): Long = countWaysToLose( - startHold = Time.zero, - isHoldInBounds = _ <= race.allowance, - nextHold = _.inc, - race -) - -def countWaysToLoseRight(race: Race): Long = countWaysToLose( - startHold = race.allowance, - isHoldInBounds = _ >= Time.zero, - nextHold = _.dec, - race -) - -def countWaysToLose(startHold: Time, isHoldInBounds: Time => Boolean, nextHold: Time => Time, race: Race): Long = +def countWaysToLoseLeft(race: Race): Long = + countWaysToLose( + startHold = Time.zero, + holdBounds = hold => if (hold <= race.allowance) Bounds.Within else Bounds.Outside, + nextHold = _.inc, + race + ) + +def countWaysToLoseRight(race: Race): Long = + countWaysToLose( + startHold = race.allowance, + holdBounds = hold => if (hold >= Time.zero) Bounds.Within else Bounds.Outside, + nextHold = _.dec, + race + ) + +def countWaysToLose(startHold: Time, holdBounds: Time => Bounds, nextHold: Time => Time, race: Race): Long = List .unfold[Distance, Time](startHold) { hold => - if (isHoldInBounds(hold)) - val travelled = getTravelledDistance(race.allowance, hold) - if (race.record >= travelled) Some((travelled, nextHold(hold))) else None - else None + holdBounds(hold) match + case Bounds.Outside => None + case Bounds.Within => + getRaceOutcome(race, hold) match + case Win => None + case Loss(travelled) => Some((travelled, nextHold(hold))) } .length @@ -63,36 +80,13 @@ def countWaysToWin(race: Race): Long = val allLosses = lossesFromLeft + (if (lossesFromLeft == totalOutcomes) 0 else countWaysToLoseRight(race)) totalOutcomes - allLosses -// droste 🧪🔬 +// recursion schemes 🧪🔬 +import cats.Eval import higherkindness.droste.* import higherkindness.droste.data.* import higherkindness.droste.data.list.* -enum RaceOutcome: - case Win - case Loss(travelled: Distance) - -import RaceOutcome.* - -enum Bounds: - case Within, Outside - -def getRaceOutcome(race: Race, hold: Time): RaceOutcome = - val travelled = getTravelledDistance(race.allowance, hold) - if (travelled > race.record) Win else Loss(travelled) - -//case class StreamF[A, B](a: A, b: B) -//type StreamFAlg[A] = [B] =>> StreamF[A, B] - -//import cats.data.NonEmptyList -//def raceOutcomeCoAlg(r: Race): Coalgebra[[B] =>> StreamF[RaceOutcome, B], NonEmptyList[Time]] = Coalgebra { -// case nel @ NonEmptyList(hold, _) => StreamF(getRaceOutcome(r, hold), hold.inc :: nel) -//} - -//def raceOutcomeStreamCoAlg(r: Race): Coalgebra[StreamFAlg[RaceOutcome], Time] = -// Coalgebra(hold => StreamF(getRaceOutcome(r, hold), hold.inc)) - type ListFAlg[A] = [B] =>> ListF[A, B] def losingDistancesCoAlg( @@ -122,8 +116,22 @@ def losingDistancesRightCoAlg(race: Race): Coalgebra[ListFAlg[Distance], Time] = race ) -//def getLosingDistancesLeft(race: Race): List[Distance] = -// scheme.ana(losingDistancesLeftCoAlg(race)).apply(Time.zero) +def losingDistancesAlg: Algebra[ListFAlg[Distance], Long] = Algebra { + case ConsF(_, tailLength) => 1 + tailLength + case NilF => 0 +} -//def getLosingDistancesRight(race: Race): List[Distance] = -// scheme.ana(losingDistancesRightCoAlg(race)).apply(race.allowance) +def countWaysToLoseLeft_(race: Race): Long = scheme + .hyloM( + losingDistancesAlg.lift[Eval], + losingDistancesLeftCoAlg(race).lift[Eval] + ) + .apply(Time.zero) + .value +def countWaysToLoseRight_(race: Race): Long = scheme + .hyloM( + losingDistancesAlg.lift[Eval], + losingDistancesRightCoAlg(race).lift[Eval] + ) + .apply(race.allowance) + .value