🎨 Render a colorized result

Wordle wouldn't be complete without the results being appropriately hued! 🧑🏼‍🎨 Fortunately, we can rely on a library for bulk of this functionality. Take a look at theansi library hosted on share.We'll use that for producing green and yellow text.

Pull that library down and put the code fromansiin alib.ansinamespace where your wordle code islocated.

See if you can write a function that renders thestubs.Resultof a user's guess appropriately.

  • A character that is in its proper location should be green
  • A character that exists in the target word should be yellow
  • All others can remain unchanged

Users will likely need to see the history of their guesses, so it would be nice if we also had a rendering function that could print out the grid of guesses too.

use stubs Result
renderChar : Result -> Text
renderChar = todo "render single character"
renderRow : [Result] -> Text
renderRow results = todo "render the guessed word"
renderGuesses : [[Result]] -> Text
renderGuesses guesses = todo "render the list of the user's guesses"

Then write a function which exercises all the functionality we've written thus far, including creating thestubs.Target,andstubs.Guessvalues, tallying up their results, and printing them to the console with theprintLinefunction found in base. You can call it whatever you'd like but you should use theruncommand to start it, and it should have the signature:

You may have to play around with different colors in the foreground or background to get a readable colorized letter depending on your console.

📚Learn more about the pull command and namespaces
📚Learn more about the pull command and namespaces

Conceptually, namespaces are similar to a directory tree structure. They help organize a codebase. Unison namespaces represent a mapping between terms and their definitions. Each namespace gets a hash which represents the terms that are found within it, and changes to the terms will change the corresponding namespace hash.

In Unison, incorporating library code unto your own is as simple as adding a namespace from another codebase.

The following commands will pull all of the code inside.stew.ansiinto a namespace in your wordle project calledlib.ansi.

.> cd .wordle
.worldle> pull stew.public.projects.ansi.releases.v1 lib.ansi

The namespacelib.ansiis arelative namespace,meaning it will be created in the namespace where the ucm command was issued. If we prefix that with a dot, as in.lib.ansi,we would be downloading the code to afully qualified namespacestarting from the root of the codebase.

By convention, all of the dependencies of a project should be located in alibnamespace.

Ansi library usage help
Ansi library usage help

Here's an example of how you can set the background color of text using the ansi library.

bg.blue.span "Hello world"

The functions likebg.blue.spanorfg.yellow.spanwork by adding an ansi escape sequence to the given text argument. The terminal can interpet specific byte sequences as commands instead of text.

More on ANSI escape codes

📚printLineand the IO ability
📚printLineand the IO ability

The curly braces indicateprintLinehas twoability requirements.Abilities are one of the ways we encode computational effects in the Unison language. We can read this signature as "printLine is a function which performs the IO and Exception abilities in the process of returning unit."

If you'd like, you can pause here and check outan introduction to abilities.But for now you should know that abilities can be broken down into two things:

  1. An interface which specifies the abstract operations of an effectful computation
  2. Handlers which supply the specific implementation for how the effect should behave

The handler for theIOability is special because it can only be supplied by the Unison runtime itself. You can run a function which performs theIOandExceptionabilities with the UCMruncommand.Instead of wrapping the code that performs an ability with a handler function in the program, you'll issue theruncommand in the UCM console itself. The run command expects adelayed computationwhich has a final return type of Unit,superProgram : Boolean -> Nat superProgram bool = if Boolean.not bool then base.bug (Generic.failure "Fatal Issue Encountered" bool) else 100.

Resources

Solution

🔑Walk through rendering and running what we have so far
🔑Walk through rendering and running what we have so far

Thestubs.renderCharfunction uses the ansi library to set the foreground and background colors with functions prefixedfgandbg,respectively. These functions operate onTextvalues so we first have to turn theCharback intoTextvalues first.

The pipe forward operator,|>is being used here to take the result of running the function on the left hand side and apply it as an argument to the function on the right.

Bothbasic.renderRowandbasic.renderGuessesmake use of thelib.base.Text.joinfunction to concatenateTextvalues with a character delimeter.

basic.renderRow : [basic.Result] -> Text
basic.renderRow results =
  lib.base.Text.join
    "" (lib.base.data.List.map basic.renderChar results)
basic.renderGuesses : [[basic.Result]] -> Text
basic.renderGuesses resultList =
  lib.base.data.List.map basic.renderRow resultList
    |> lib.base.Text.join "\n"

Here's what a running test of what we've written so far might look like:

basic.renderTest : '{IO, Exception} ()
basic.renderTest = do
  use basic Guess.fromText
  use basic.Guess score
  guess1 = Guess.fromText "hello"
  guess2 = Guess.fromText "there"
  target = basic.Target.fromText "world"
  results1 = score guess1 target
  results2 = score guess2 target
  printLine (basic.renderGuesses [results1, results2])
.> run renderTest

Next steps

👉Validation for user guesses