From bed6019e90860ed800747208f230d4e11589e3c6 Mon Sep 17 00:00:00 2001 From: Anurag Date: Sun, 15 Mar 2026 23:39:01 +0530 Subject: [PATCH 1/2] add : Number Guessing Game --- .../gsoc/contributors/Anurag300705.scala | 163 ++++++++++++++++++ src/main/scala/gsoc/contributors/all.scala | 1 + 2 files changed, 164 insertions(+) create mode 100644 src/main/scala/gsoc/contributors/Anurag300705.scala diff --git a/src/main/scala/gsoc/contributors/Anurag300705.scala b/src/main/scala/gsoc/contributors/Anurag300705.scala new file mode 100644 index 0000000..eecb800 --- /dev/null +++ b/src/main/scala/gsoc/contributors/Anurag300705.scala @@ -0,0 +1,163 @@ +package gsoc +package contributors + +import calico.* +import calico.html.io.{*, given} +import cats.effect.* +import fs2.concurrent.* +import fs2.dom.* +import scala.util.Random + +val Anurag300705 = Contributor("Anurag300705"): + import RandomNumberGuesser.* + SignallingRef[IO].of(freshState).toResource.flatMap { state => + val submit: IO[Unit] = state.get.flatMap { s => + if s.won then IO.unit + else if s.attempts >= maxAttempts then + state.update( + _.copy( + won = true, + message = s"Game Over! The number was ${s.secret}" + )) + else + parseGuess(s.input) match + case None => + state.update( + _.copy( + input = "", + message = "Please enter a valid integer." + )) + case Some(guess) => + val updatedGuesses = guess :: s.guesses + if guess == s.secret then + state.update( + _.copy( + hint = Hint.Correct, + attempts = s.attempts + 1, + guesses = updatedGuesses, + won = true, + input = "", + message = s"🎉 Correct! You got it in ${s.attempts + 1} attempts." + )) + else if guess < s.secret then + state.update( + _.copy( + hint = Hint.TooLow, + attempts = s.attempts + 1, + guesses = updatedGuesses, + input = "", + message = "Too low, try again." + )) + else if guess > max then + state.update( + _.copy( + hint = Hint.OverBound, + attempts = s.attempts + 1, + guesses = updatedGuesses, + input = "", + message = + s"This is more than $max, please enter a number between 1 and $max" + )) + else + state.update( + _.copy( + hint = Hint.TooHigh, + attempts = s.attempts + 1, + guesses = updatedGuesses, + input = "", + message = "Too high, try again." + )) + } + + div( + p( + styleAttr := "font-size: 0.9em; color: gray", + "Hello, I'm @Anurag300705 on GitHub. I agree to follow the Typelevel CoC and GSoC AI policy." + ), + + p(styleAttr := "font-weight: bold", s"Guess a number between 1 and $max"), + + state.map(_.message).changes.map { msg => + p(styleAttr := "margin: 8px 0", msg) + }, + + state.map(_.hint).map { + case Hint.NoGuess => p("Make your guess!") + case Hint.TooLow => p(styleAttr := "color: red", "↑ Too low, try again.") + case Hint.TooHigh => p(styleAttr := "color: blue", "↓ Too high, try again.") + case Hint.OverBound => + p(styleAttr := "color: indigo", "OverBound, please try within the limits.") + case Hint.Correct => + p(styleAttr := "color: green; font-weight: bold", "✓ Correct!") + }, + + state.map(s => s"Attempts: ${s.attempts} / $maxAttempts").changes.map { t => + p(styleAttr := "font-size: 0.85em; color: gray", t) + }, + + state + .map(s => s"Previous guesses: ${s.guesses.reverse.mkString(", ")}") + .changes + .map(t => p(styleAttr := "color: darkorange", t)), + + input.withSelf { self => + ( + typ := "number", + disabled <-- state.map(_.won), + value <-- state.map(_.input), + onInput --> (_.foreach { _ => + self.value.get.flatMap(e => state.update(_.copy(input = e))) + }), + onKeyDown --> (_.foreach { ev => + if ev.key == "Enter" then submit else IO.unit + }) + ) + }, + + button( + disabled <-- state.map(_.won), + onClick --> (_.foreach(_ => submit)), + state.map(s => if s.won then "✓ Done" else "Guess").changes + ), + + button( + onClick --> (_.foreach(_ => state.set(freshState))), + "Reset Game" + ) + ) + } + +private object RandomNumberGuesser: + val max = 20 + val maxAttempts = 7 + + enum Hint: + case NoGuess + case TooLow + case TooHigh + case OverBound + case Correct + + case class GameState( + secret: Int, + input: String, + hint: Hint, + attempts: Int, + guesses: List[Int], + won: Boolean, + message: String + ) + + def freshState: GameState = + GameState( + secret = Random.nextInt(max) + 1, + input = "", + hint = Hint.NoGuess, + attempts = 0, + guesses = List(), + won = false, + message = s"I'm thinking of a number between 1 and $max. Can you guess it?" + ) + + def parseGuess(str: String): Option[Int] = + str.trim.toIntOption \ No newline at end of file diff --git a/src/main/scala/gsoc/contributors/all.scala b/src/main/scala/gsoc/contributors/all.scala index 0ff96f2..8cccadc 100644 --- a/src/main/scala/gsoc/contributors/all.scala +++ b/src/main/scala/gsoc/contributors/all.scala @@ -23,6 +23,7 @@ val allContributors = NonEmptyList.of( parthmozarkar, `scala-steward`, `the-ivii`, + `Anurag300705`, `piyush-t24`, malladinagarjuna2, ankitkumarrain, From 8f3e5ceabb753adedf3f39a59fd22471c07a76d1 Mon Sep 17 00:00:00 2001 From: Anurag Date: Sun, 15 Mar 2026 23:50:25 +0530 Subject: [PATCH 2/2] Fix scalafmt formatting --- .../gsoc/contributors/Anurag300705.scala | 34 ++++++------------- 1 file changed, 10 insertions(+), 24 deletions(-) diff --git a/src/main/scala/gsoc/contributors/Anurag300705.scala b/src/main/scala/gsoc/contributors/Anurag300705.scala index eecb800..3fa2b3a 100644 --- a/src/main/scala/gsoc/contributors/Anurag300705.scala +++ b/src/main/scala/gsoc/contributors/Anurag300705.scala @@ -49,15 +49,13 @@ val Anurag300705 = Contributor("Anurag300705"): message = "Too low, try again." )) else if guess > max then - state.update( - _.copy( - hint = Hint.OverBound, - attempts = s.attempts + 1, - guesses = updatedGuesses, - input = "", - message = - s"This is more than $max, please enter a number between 1 and $max" - )) + state.update(_.copy( + hint = Hint.OverBound, + attempts = s.attempts + 1, + guesses = updatedGuesses, + input = "", + message = s"This is more than $max, please enter a number between 1 and $max" + )) else state.update( _.copy( @@ -74,13 +72,8 @@ val Anurag300705 = Contributor("Anurag300705"): styleAttr := "font-size: 0.9em; color: gray", "Hello, I'm @Anurag300705 on GitHub. I agree to follow the Typelevel CoC and GSoC AI policy." ), - p(styleAttr := "font-weight: bold", s"Guess a number between 1 and $max"), - - state.map(_.message).changes.map { msg => - p(styleAttr := "margin: 8px 0", msg) - }, - + state.map(_.message).changes.map { msg => p(styleAttr := "margin: 8px 0", msg) }, state.map(_.hint).map { case Hint.NoGuess => p("Make your guess!") case Hint.TooLow => p(styleAttr := "color: red", "↑ Too low, try again.") @@ -90,16 +83,13 @@ val Anurag300705 = Contributor("Anurag300705"): case Hint.Correct => p(styleAttr := "color: green; font-weight: bold", "✓ Correct!") }, - state.map(s => s"Attempts: ${s.attempts} / $maxAttempts").changes.map { t => p(styleAttr := "font-size: 0.85em; color: gray", t) }, - state .map(s => s"Previous guesses: ${s.guesses.reverse.mkString(", ")}") .changes .map(t => p(styleAttr := "color: darkorange", t)), - input.withSelf { self => ( typ := "number", @@ -108,18 +98,14 @@ val Anurag300705 = Contributor("Anurag300705"): onInput --> (_.foreach { _ => self.value.get.flatMap(e => state.update(_.copy(input = e))) }), - onKeyDown --> (_.foreach { ev => - if ev.key == "Enter" then submit else IO.unit - }) + onKeyDown --> (_.foreach { ev => if ev.key == "Enter" then submit else IO.unit }) ) }, - button( disabled <-- state.map(_.won), onClick --> (_.foreach(_ => submit)), state.map(s => if s.won then "✓ Done" else "Guess").changes ), - button( onClick --> (_.foreach(_ => state.set(freshState))), "Reset Game" @@ -160,4 +146,4 @@ private object RandomNumberGuesser: ) def parseGuess(str: String): Option[Int] = - str.trim.toIntOption \ No newline at end of file + str.trim.toIntOption