Lo siguiente que necesitamos hacer es implementar la funcionalidad para mostrar por consola el estado del juego:
def renderState(state: State): URIO[Console, Unit] = {
/*
--------
| |
| 0
| \|/
| |
| / \
-
f n c t o
- - - - - - -
Guesses: a, z, y, x
*/
val hangman = ZIO(hangmanStages(state.failuresCount)).orDieWith(_ => new Error("Boom!"))
val word =
state.word.toList
.map(c => if (state.guesses.map(_.char).contains(c)) s" $c " else " ")
.mkString
val line = List.fill(state.word.length)(" - ").mkString
val guesses = s" Guesses: ${state.guesses.map(_.char).mkString(", ")}"
hangman.flatMap { hangman =>
putStrLn(
s"""
#$hangman
#
#$word
#$line
#
#$guesses
#
#""".stripMargin('#')
)
}
}
No entraremos en mucho detalle de cómo esta función está implementada, más bien nos centraremos en una sola línea, que es quizás la más interesante:
val hangman = ZIO(hangmanStages(state.failuresCount)).orDie
Podemos ver que lo que hace esta línea es, primeramente, seleccionar cuál es la figura que se deberá mostrar para representar al ahorcado, la cual es diferente de acuerdo a la cantidad de fallas que haya tenido el jugador (el archivo com/example/package.scala contiene una variable hangmanStates que consiste en una lista con las seis posibles figuras). Por ejemplo:
Ahora bien, la expresión hangmanStages(state.failuresCount)no es puramente funcional porque podría lanzar una excepción si es que state.failuresCount fuera mayor a 6. Entonces, como estamos trabajando con programación funcional no podemos permitir que nuestro código produzca efectos colaterales, como lanzar excepciones, es por ello que hemos encapsulado la anterior expresión dentro de ZIO, que en realidad es una llamada al método ZIO.apply, el cual permite construir un efecto funcional a partir de una expresión que produce efectos colaterales (también podemos usar el método equivalente ZIO.effect). Por cierto, el resultado de llamar a ZIO.apply es un efecto que puede fallar con un Throwable, pero de acuerdo al diseño de nuestra aplicación esperamos que en realidad nunca exista un fallo al intentar obtener el dibujo del ahorcado (puesto que state.failuresCount nunca debería ser mayor a 6). Por tanto, podemos descartar el caso erróneo llamando al método ZIO#orDie, el cual retorna un efecto que nunca falla (ya sabemos que si existiera alguna falla, sería en realidad un defecto y nuestra aplicación debería fallar inmediatamente). Por cierto, ZIO#orDie es muy similar a ZIO#orDieWith, pero sólo puede ser usado con efectos que fallan con un Throwable.