Everyday is an API day with Jotto 2020
Tags: mulesofthackathon, jotto2020, wordle
Published: 2020-11-30 04:57:34

For my very first MuleSoft Hackathon, I decided to recreate a word puzzle game known as [Jotto](https://en.wikipedia.org/wiki/Jotto).  The game engine is an API implemented in Mule 4 using DataWeave 2 scripts along with some encryption from the Mule Crypto module. The source code for the game API has been uploaded to GitHub and can be found [here](https://github.com/tonedef71/jotto-2020-api).  

For the game's user interface, I created a simple web client using [EnyoJS](http://enyojs.com/) v2.7.  The majority of my hours spent working on this hackathon were devoted to the web client (learning the basics of the EnyoJs JavaScript application framework and debugging my JavaScript code). The source code for the web client has been uploaded to GitHub and can be found [here](https://github.com/tonedef71/jotto-2020-web-client).

I created a free account with [Fauna](https://fauna.com/) so that I could have an online document database at my disposal for persisting a small amount of game state data.  I used  FaunaDB's API to manage a running count of Jotto games played for each of the different word lengths.  Persisted running counts are leveraged to ensure that a fresh word gets served up with each new game.

For the MuleSoft Hackathon, I created a free [30-day trial Anypoint Platform account](https://anypoint.mulesoft.com/login/signup) to host the game's API along with the game's web client.  

A demo of the **Jotto 2020** game can be played online in your browser >>>>> <a href="http://jotto2020.us-e2.cloudhub.io/ui/index.html" target="_blank" rel="noopener">**right here**</a> <<<<<

![Jotto 2020 demo video](https://s8.gifyu.com/images/Jotto2020_demonstration.gif)

---

##**To Build the Mule Application Project:**
1. Clone the [Jotto 2020 API project from GitHub](https://github.com/tonedef71/jotto-2020-api)
2. You may need to adjust the URLs of the two API endpoints.

In _src/main/resources/web/jotto-2020.js_ (around line 615) is where the endpoint URLs for the Mule APIs are configured.  
The "url" field for "newGameWebService" and "newGuessWebService" must be set appropriately for the web client to function.

```
    {
      kind: WebService,
      name: "newGameWebService",
...
      //url: "http://localhost:8081/api/game/jotto",
      url: "http://jotto2020.us-e2.cloudhub.io/api/game/jotto",
      method: "POST",
...
    },
    {
      kind: WebService,
      name: "newGuessWebService",
...
      //url: "http://localhost:8081/api/game/jotto/guess/",
      url: "http://jotto2020.us-e2.cloudhub.io/api/game/jotto/guess/",
      method: "PUT",
...
    },
```

3. Assuming the Mule application builds and deploys, you can access the web client in your browser with a URL that references the same host that is servicing the API.  For example, if you are running the application in the standalone Mule 4 server and you have the endpoint URLs set to localhost, then you would browse to **http://localhost:8081/ui/index.html**.

---
##**API Endpoints**

The Jotto 2020 API has two endpoints used to manage the game:

* An API endpoint to request the game server to _start a new game_:

**POST:/api/game/jotto?letters=_n_** where _n_ for the `letters` query parameter is an integer value between 2 and 12.

####Sample JSON response body (a new game has started)
  ```
{
  "stats": {
    "puzzleUuid": "46b602ec-cf04-4d39-9b88-18a41c1318c1",
    "puzzleIndex": 109,
    "puzzleLength": 8,
    "maximumTurns": 6,
    "gameOver": false,
    "turnsRemaining": 6,
    "victory": false
  },
  "turnsTaken": [
    
  ],
  "token": "ewogICJzdGF0cyI6IHsKICAgICJwdXp6bGVVdWlkIjogIjQ2YjYwMmVjLWNmMDQtNGQzOS05Yjg4LTE4YTQxYzEzMThjMSIsCiAgICAicHV6emxlSW5kZXgiOiAxMDksCiAgICAicHpbXVtVHVybnMiOiAyMCwKICAgICJnYW1lT3ZlciI6IGZhbHNlLAogICAgInR1cm5zUmVtYWluaW5nIjogMjAsCiAgICAiV6emxlTGVuZ3RoIjogOCwKICAgICJtYXhdmljdG9yeSI6IGZhbHNlCiAgfSwKICAidG9rZW4iOiBudWxsLAogICJ0dXJuc1Rha2VuIjogWwogICAgCiAgXQp9"
}
  ```
* An API endpoint to _submit a guess_ to the game server and receive feedback of the game's running state.

**PUT:/api/game/jotto/guess/{guess}** where `guess` is a word of the proper length (between 2 and 12 letters depending on the length requested when the game was started).

The PUT request must also include an HTTP header parameter named `x-session` which contains the value of the `token` field extracted from the most recent API response (the API will respond with an HTTP 400 status if the `x-session` HTTP header parameter is omitted).  The `x-session` HTTP parameter and the `token` response body field together implement the mechanism used to maintain game state between client and server in a stateless manner.

####Sample JSON response body (the final round of a winning game)
```
{
  "stats": {
    "puzzleUuid": "5b449db2-1aeb-4962-b7e8-ac913f5d38e5",
    "puzzleIndex": 106,
    "puzzleLength": 8,
    "maximumTurns": 6,
    "gameOver": true,
    "turnsRemaining": 0,
    "victory": true,
    "puzzleWord": "document"
  },
  "turnsTaken": [
    {
      "guess": "hooligan",
      "result": ".*.....o"
    },
    {
      "guess": "tonality",
      "result": "o*o....."
    },
    {
      "guess": "national",
      "result": "o.o.o..."
    },
    {
      "guess": "vocation",
      "result": ".**.o..o"
    },
    {
      "guess": "lockment",
      "result": ".**.****"
    },
    {
      "guess": "document",
      "result": "********"
    }
  ],
  "token": "ewogICJzdGF0cyI6IHsKICAgICJwdXp6bGVVdWlkIjogIjViNDQ5ZGIyLTFhZWItNDk2Mi1iN2U4LWFjOTEzZjVkMzhlNSIsCiAgICsKICAgICAgImd1ZXNzIjogImhvb2xpZ2FuIiwKICAgICAgInJlc3VsdCI6ICIuKi4uLi4ubyIKICAgIH0sCiAgICB7CiAgICAgICJndWVzcyI6ICJub3Rpb25hbCIsCiAgICAgICJyZXN1bHQiOiAibypvLi4uLi4iCiAgICB9LAogICAgewogICAgICAiZ3Vlc3MiOiAidG9uYWxpdHkiLAogICAgICAicmVzdWx0IjogIm8qby4uLi4uIgogICAgfSwKICAgIHsKICAgICAgImd1ZXNzIjogIm5hdGlvbmFsIiwKICAgICAgInJlc3VsdCI6ICJvLm8uby4uLiIKICAgIH0sCiAgICB7CiAgICAgAicHV6emxlSW5kZXgiOiAxMDYsCiAgICAicHV6emxlTGVuZ3RoIjogOCwKICAgICJtYXhpbXVtVHVybnMiOiAyMCwKICAgICJnYW1lT3ZlciI6IHRydWUsCiAgICAidHVybnNSZW1haW5pbmciOiAxMywKICAgICJ2aWN0b3J5IjogdHJ1ZQogIH0sCiAgInRva2VuIjogbnVsbCwKICAidHVybnNUYWtlbiI6IFsKICAgIHICJndWVzcyI6ICJ2b2NhdGlvbiIsCiAgICAgICJyZXN1bHQiOiAiLioqLm8uLm8iCiAgICB9LAogICAgewogICAgICAiZ3Vlc3MiOiAibG9ja21lbnQiLAogICAgICAicmVzdWx0IjogIi4qKi4qKioqIgogICAgfSwKICAgIHsKICAgICAgImd1ZXNzIjogImRvY3VtZW50IiwKICAgICAgInJlc3VsdCI6ICIqKioqKioqKiIKICAgIH0KICBdCn0="
}
```
The `puzzleWord` field is only included in the response body when the `gameOver` field has a value of `true`. In the values of the "result" fields, the `.` denotes a non-matching letter, the `o` denotes a matching letter in the incorrect position, and the `*` denotes a matching letter in the correct position.

---
##**How to Play Jotto 2020**

Jotto 2020 is a word puzzle game. The game server will select a secret English word between 2 and 12 letters; you get to select the word length.
 
The object of the game is to guess that secret word. Each guess you make will be answered by a report of the letters (or "jots") in the guess word that match or occur in the secret word. 

You will also be told how many of the letters are in the correct position in the secret word. 

Through a process of elimination, you should be able to deduce the correct letters using logic.

The number of allowed guesses to solve the secret word varies based on the length of the secret word.  The minimum is five guesses.

1. Start by selecting the number of letters for the mystery word via the number picker button on the top-left.  You can select a mystery word as short as two letters, or as long as twelve letters.

2. Type your test or guess word; it must be the same number of letters as the mystery word.

3. After you have entered your guess word, click the "->" button on the on-screen keypad or press the "Enter" key on your computer's keyboard to submit your guess to the game server.  Click the "<-" key on the keypad or press the backspace on your computer keyboard to erase the last entered letter of your guess.  Click the "!" key on the keypad or press the "Esc" key on your computer keyboard to erase all of the letters of your guess.

4. The game server will report which letters of your guess occur in the secret word and whether or not any of those letters are in the correct position.
   Yellow letters will be displayed for those letters that are in the secret word but are in the wrong position.
   Green letters will only be displayed for those letters that are both in the secret word and in the correct position.
   Red letters will be displayed for letters that do not exist in the secret word or for any extra copies of letters that have already been displayed as yellow or green letters.
   
For example:
```
If the secret 5-letter word is "radar"...

  ... and you guessed "arrow", 
  then the game server will report a result displayed as
  three yellow letters and two red letters.

  ... and you guessed "razor", 
  then the game server will report a result displayed as 
  three green letters and two red letters.

  ... and you guessed "raarr", 
  then the game server will report a result displayed as 
  four green letters and one red letter; the extra 'R' is 
  not in the secret word.
```

5. Continue submitting guesses to the game server until you correctly solve the mystery word or you run out of allowed guess attempts.

---
##**Parting Thoughts**
The web client consumed way too much of the free time I budgeted for this hackathon, and as a result, this whole project was badly rushed to meet the submission deadline.  

**Some unfinished business remains:**
1. Remove trace log statements from web client code.
2. Add more tool tips and prepare better help instructions in the web client.
3. Employ better EnyoJS and JavaScript coding best practices by refactoring the web client source code to create reusable components.
4. Enable HTTPS by requesting a proper TLS certificate for the host server's domain.
5. Use the FaunaDB to persist more data for the game server.
6. Do more play-testing.  Are the minimum number of guesses enforced by the game server suitable for the different word lengths?
7. Remedy the "uglify" compatibility bug in the node.js build script that is preventing a production build of the EnyoJS project.
8. Figure out how to make the "x-session" HTTP header response parameter accessible to the web client.  Currently, the "x-session" HTTP header parameter does not show up after invoking the _getAllResponseHeaders()_ function in the browser's _XMLHttpRequest response_ object.  This is why the "token" field had to be included in the API's response body.
9. Move the RAML out of the Mule 4 project and into Exchange, and then manage it with API Manager.
10. Host the web client separately from the API? A proper web server would be best.
11. Secure the API endpoints.
12. I like using the different colors to provide visual feedback for qualifying a guess, but I do realize that an alternate feedback mechanism would be needed to accommodate players with color blindness.

**Game enhancements:**
1. Add features to the game server such as player game history management; the game client could then be enhanced to display all time best scores and personal best scores, etc.
2. Improve the game client and then convert it into a mobile application and/or standalone executable for PC or Mac.

---