Oh My Wordle...

Starter Project Idea

… are we really implementing Wordle one more time? Of course not, that would be ridiculous. We are going to do it two more times! There are two-2-three reasons why.

Zeroth, I (still) like Wordle, and this is part of my approach to work-life balance.

First, you’ve worked on Wordle clones a couple times already, but that was all in the distant past. Now you know a lot more about functional programming and Haskell. So, this is an opportunity to revisit your work with an eye towards polishing and refactoring.

Second, although command-line user interfaces should not be viewed askance, it would be fun to clone the original web-based GUI. Pure functional programming is a great tool for any programming job, and is particularly so for reactive systems like web applications. (As a form of empirical evidence, see the popularity of React/Redux.) This assignment will hardly scratch the surface of web development, but it will get you set up with a couple libraries and perhaps whet the appetite for more serious Haskell web development in the future.

Well, I don’t know about you, but I’m convinced!

Overview

Here is a basic (HTML/CSS) setup for a Wordle interface. This GUI is inert: keyboard and mouse presses have no effect, and there is no (JavaScript) logic implementing the gameplay. In this assignment, you will give life to this interface (using Haskell) in a couple ways.

Static Web Pages

If you are not familiar with HTML or web programming, don’t fret. You don’t need to know much about HTML in order to complete this assignment. An HTML page is essentially a tree of nodes (also called elements), where each node has:

  1. A tag that describes the purpose of its content: <h1> for headings, <p> for paragraphs, <img> for images, <ol> for ordered lists, <li> for list items, etc.

  2. Zero or more global attributes such as a unique identifier ("id") for the node, and zero or more tag-specific attributes such as the URL for an image ("src") or the target of a hyperlink ("href").

  3. Zero or more child nodes. For example, an <ol> will typically have one or more <li> nodes. Some kinds of nodes, such as <p>aragraphs, typically have one text node which is a leaf of the tree — it doesn’t have any attributes or children, but rather carries actual text content to be displayed.

HTML

Open the HTML file that draws the GUI above — click something like View > Developer > Page Source in Chrome, or Tools > Browser Tools > Page Source in Firefox — and you will see something like:

<html>
  <head>
    <title>Oh My Wordle!</title>
    <link rel="stylesheet" type="text/css" href="style.css" />
    <meta charset="utf-8" />
  </head>
  <body>
    <h1>Oh My Wordle!</h1>
    <p id="message-panel">A place to display messages...</p>
    <div id="tile-board">
      <div class="row">
        <span class="tile"></span>
        <span class="tile"></span>
        <span class="tile"></span>
        <span class="tile"></span>
        <span class="tile"></span>
      </div>
      ...
    </div>
    ...
    <div id="visual-keyboard">
      <div>
        <button id="key-q" onclick="keyOrButtonPressed('Q');">Q</button>
        <button id="key-w" onclick="keyOrButtonPressed('W');">W</button>
        <button id="key-e" onclick="keyOrButtonPressed('E');">E</button>
        <button id="key-r" onclick="keyOrButtonPressed('R');">R</button>
        <button id="key-t" onclick="keyOrButtonPressed('T');">T</button>
        <button id="key-y" onclick="keyOrButtonPressed('Y');">Y</button>
      ...
    </div>
  </body>
</html>

The <body> is where all the content goes.

Notice the <h1>, <p>aragraph, <button>s: these all have text nodes as children, with text to render in the final document.

There are a couple ways to logically group one or more elements: a <div> typically creates a new vertical block, whereas a <span> creates a group within a particular line.

Several nodes have "id" attributes that assign unique identifiers, and several nodes have "class" attributes that define what set of CSS classes a node belongs to. Identifiers and CSS classes are useful for styling elements (discussed below).

Notice also the "onlick" attribute of buttons, which specifies what JavaScript code to run in response to button clicks. (The JavaScript functions here, which have suggestive names, are undefined. In our implementations, we will specify event handlers in a couple library-specific ways.)

CSS

The HTML markup above defines the content of the document, which is rendered by default according to browser and user settings.

If you open up the CSS file, you will see definitions for how to style the different pieces of the interface. This stylesheet is why the interface above looks the way that it does. (Try commenting out the <link rel="stylesheet" ... /> line in the index.html file and see what the defaults look like.)

H A S K

You don’t need to know anything about CSS stylesheets for this assignment; the given CSS file will be included verbatim in your projects. All you need to notice are the following four CSS classes, which can be used to style letters as shown on the right.

When you generate each letter tile in your Wordle interfaces (see the <span>s above), define its "class" as "tile" to set its size and font; further attach one of the "letter-" classes to color the letter.

Dynamic Web Pages

In this assignment, you are going to implement the interface above as a dynamic web page. That is, your (Haskell) code will run and produce an HTML document that resembles the static HTML version above. To get a small taste of different aspects of web development, you’ll do this two ways: first as a client-side dynamic page and then as a server-side dynamic page.

Client-Side Dynamic Page via Miso

In the first version, the user interface logic will run entirely on the client-side within the (JavaScript) environment in the browser.

We will use a a tasty Haskell front-end framework called Miso (v1.8.2.0) that exposes a simple MVC architecture to implement our Wordle application, which gets compiled to JavaScript via GHCJS.

Note: GHCJS, and hence Miso, does not work with the latest version of Haskell. So, we will use a handy tool, called GHCup, that allows installing and running older versions of Haskell (see instructions below). ❗TRY IT OUT ASAP❗

Suggested Pairing: Looking for an easy and tasty miso dish? This miso cod is a family favorite.

Server-Side Dynamic Page via Happstack.Lite

Non-trivial web apps, of course, also need to do some server-side processing.

In a perfect world, all of the same libraries and tools would help with both client- and server-side concerns. Most of the mature Haskell web frameworks provide full-featured support for server-side development, but they do not provide a simple MVC architecture for client-side programming like Miso.

For our server-side adventure, we will use Happstack.Lite, which is nice and easy to get up and running. To get interactive behavior in our Wordle app (without writing JavaScript code, or somehow connecting to Miso-marinated Haskell), we will fake a pure MVC architecture in a very simple, very hacky way:

In this approach, there is no (non-trivial) JavaScript to write for our game; all the logic is written in Haskell run on the server-side. Were we doing do real web development, we would not structure our application like this. Nevertheless, this is a nice little toy approach for our assignment, and is a fine way to start trying out Happstack.Lite (and thus Happstack).

Starter Code

Part 1: Wordle via Miso

GHCup

There is a nice tool called GHCup to manage the installation of updates to GHC and associated tools.

It is fun to imagine GHCup organizing different versions of the Haskell toolchain into separate cups. Thanks, Sam, for this delightful double entendre!

See what versions of GHC and related tools are installed on your machine:

% ghcup list

To get GHCJS and Miso to work, install the following older version:

% ghcup install ghc 8.10.7

To set which version of GHC is active:

% ghcup set ghc 8.10.7
[ Info  ] GHC 8.10.7 successfully set as default version

Check that you are now running the older version:

%  ghci
GHCi, version 8.10.7: https://www.haskell.org/ghc/  :? for help
>

Note: GHC 8.10.7 uses language Haskell2010, which enables fewer extensions by default than GHC2021. If you want to use, for example, ExplicitForAll and TypeApplications, you will need to update the .cabal file and/or define LANGUAGE pragmas.

When you’re done working on the Miso project, remember to reset things to the most recent version! You can do that by running set without a version number:

% ghcup set ghc
[ Info  ] GHC 9.2.5 successfully set as default version

In addition to the commands above, you can also run the following (on Linux or Mac, but not Windows):

% ghcup tui

Check out this sweet terminal UI! Almost makes you want to say hello again to Hello, Again Wordle, doesn’t it?

Miso

When GHC 8.10.7 is set, try building (which will take a while the first time) and running:

% cabal build
% cabal run wordle-miso

You should now have a web server serving a page at http://localhost:8000/. Surf there.

Your Tasks (WordleMiso.hs)

The starter code sets up the basic structure for your app, which you should read through. Also peruse the documentation for the miso and jsaddle-warp packages as needed; there is a lot going on, but no need to understand all of, or even much of, for our first tour through in this assignment.

There is an Environment type, which gets defined once per server invocation, because the (static) CSS stylesheet and the list of all words does not change across games (i.e. page loads).

data Env =
  Env
    { getCss :: String
    , getWordList :: [String]
    }

In contrast, the Model — which you may populate with your PA 2 version to start, and then adjust, refactor, and polish as appropriate — gets initialized each time the page is loaded (and then updated during gameplay).

data Model =
  Model
    { -- TODO
    } deriving (Eq, Show)

To get your Wordle interface rendered and functioning, here’s a suggested progression of tasks.

Draw (Blank) Tile Board and (Inactive) Visual Keyboard

Much of your work to render the UI will happen in:

viewModel :: Env -> Model -> View Action

Start by looking at the static index.html file, and try to generate the same elements dynamically. To start, the tiles can be empty (no letters) and the buttons inactive. See: Miso.Html. (I’m not sure about the difference between that and: Miso.Html.Element.)

For attribute values (e.g. id_) and text nodes (i.e. text), you have to explicitly convert Strings to MisoStrings, via:

ms :: String -> MisoString

Consider enabling OverloadedStrings to implicitly convert Strings to other string-like types.

Event Handlers and Update

Then, for each button_ in the visual keyboard, attach an onClick attribute to define what Action should be generated when the button is clicked. See: Miso.Html.Event. (Notice that the starter code already connects keyboard events to KeyPress actions, via Miso.Subscription.Keyboard.)

Copy-paste your model-updating code from PA 2, and adjust to fit into the structure here.

Draw Previous and Current Guesses

Now, with current and previous guesses being tracked in the Model, render the guesses on the board.

Color Previous Guesses

Copy-paste-port your letter-coloring logic from PA 2. In this setting, use the CSS classes letter-green, letter-yellow, and letter-gray to style the tiles (via the class_ attribute).

Color Visual Keyboard

Finally, copy-paste-port your visual keyboard summary logic from PA 2, again using the CSS classes to color the buttons.

Part 2: Wordle via Happstack.Lite

Happstack.Lite

This version is easier to get going. Remember to reset to the latest version of Haskell, and then build and run:

% ghcup set ghc
% cabal run wordle-happstack

This should start serving http://localhost:8000/wordle.html. Notice that, unlike the Miso version, this time “wordle.html” is in the URL.

Your Tasks (WordleHappstack.hs)

The starter code sets up a basic application, similar to the Miso version. Read through to get a sense of how it works. Start perusing the documentation for the happstack-lite package.

One difference to note is that, in this version, the answer word is stored in Env rather than Model. The types and structure of this app are more complicated than in the client-side version. If you want to figure out how to pick a different answer every time the page is loaded, rather than every time the server is launched, go for it!

The suggested workflow of tasks is the same:

Having already completed the Miso version, the primary work in this version is that in Happstack and Happstack.Lite, HTML drawing is achieved using the blaze-html package. Here’s a tutorial that demonstrates the API. And here are a few of the relevant modules and functions:

The onclick handlers for each button should call updateAndRedirect, which reloads the page with the updated model as the new URL query parameter.

Note that our Happstack version does not support keyboard events; read the starter code for a brief discussion of why not. (If you’re feeling ambitious, add support for keyboard events.)

Again, considering using OverloadedStrings to implicitly convert Strings as appropriate.