Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chaotic3quilibrium/5af7342e6d2c31b91ae1eb426cdc039c to your computer and use it in GitHub Desktop.
Save chaotic3quilibrium/5af7342e6d2c31b91ae1eb426cdc039c to your computer and use it in GitHub Desktop.
An update of John deGoes original PurelyFunctionalHangman to ZIO 1.0
package org.public_domain
import zio.console._
import zio.{ExitCode, UIO, URIO, ZIO}
import java.io.IOException
object PurelyFunctionalHangman extends zio.App {
def run(args: List[String]) : URIO[Console, ExitCode] =
hangman.exitCode
//lazy val dictionary : List[String] = scala.io.Source.fromResource("words.txt").getLines.toList
val dictionary: List[String] = List("apple", "bananas", "cherry", "dates", "elderberry")
val maxGuesses: Int = 10
case class State(name: String, guesses: Set[Char] = Set.empty[Char], word: String) {
final def failures : Int = (guesses -- word.toSet).size
final def playerLost: Boolean = failures >= maxGuesses
final def playerWon : Boolean = (word.toSet -- guesses).isEmpty
final def guessesRemaining: Int = maxGuesses - failures
}
val getName : ZIO[Console, IOException, String] =
putStrLn("What is your name: ") *> getStrLn
def nextInt(max: Int) : UIO[Int] =
UIO.effectTotal(scala.util.Random.nextInt(max))
val chooseWord: UIO[String] =
for {
rand <- nextInt(dictionary.length)
} yield dictionary(rand)
def gameLoop(state: State) : ZIO[Console, IOException, State] =
for {
guess <- getChoice
state <- ZIO.succeed(state.copy(guesses = state.guesses + guess))
_ <- renderState(state)
loop <-
if (state.playerWon) putStrLn(s"Congratulations ${state.name} you won the game!").as(false)
else if (state.playerLost) putStrLn(s"Sorry ${state.name} you lost the game. The word was ${state.word}").as(false)
else if (state.word.contains(guess)) putStrLn(s"You guessed correctly!").as(true)
else putStrLn(s"That's wrong. but keep trying!").as(true)
state <- if (loop) gameLoop(state) else ZIO.succeed(state)
} yield state
val getChoice : ZIO[Console, IOException, Char] = for {
line <- putStrLn(s"Please enter a letter") *> getStrLn
char <- line.toLowerCase.trim.headOption match {
case None => putStrLn(s"You did not enter a character") *> getChoice
case Some(x) => ZIO.succeed(x)
}
} yield char
def renderState(state: State) : ZIO[Console, IOException, Unit] = {
val word = state.word.toList.map(c =>
if (state.guesses.contains(c)) s" $c " else " "
).mkString("")
val line = List.fill(state.word.length)(" - ").mkString("")
val guesses = " Guesses: " + state.guesses.toList.sorted.mkString("")
val remaining = " Remaining: " + state.guessesRemaining
val text = word + "\n" + line + "\n\n" + guesses + "\n" + remaining + "\n"
putStrLn(text)
}
val hangman : ZIO[Console, IOException, Unit] = for {
_ <- putStrLn("Welcome to Purely Functional Hangman")
name <- getName
_ <- putStrLn(s"Welcome $name. Let's begin!")
word <- chooseWord
state = State(name, Set(), word)
_ <- renderState(state)
_ <- gameLoop(state)
} yield()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment