Kata Challenge Wordle JLDD

Kata are code puzzles that will help you hone your skills.

I enjoy creating and solving code katas. Code Kata are programming puzzles to help you hone your programming skills.

I wrote an article called “Learn to Kata and Kata to Learn” for the book “97 Things Every Java Programmer Should Know”, and a link to the article is available for free here at publications on Medium.

The Wonderful World of Wordle

Wordle is a very popular online puzzle game where you have six chances to guess a five letter word.

Each guess leads to learning which characters correspond to the characters in the word. You are given hints with colors that let you know if you have correctly inserted a letter in the correct position, if the letter is in the word but in the wrong position, or if the letter does not correspond to any letter in the word.

You can learn more about the Wordle craze in this article.

Wordle explained: Everything you need to know to master the viral word game

Wordle is a viral word game recently acquired by the New York Times for several million. But…

www.cnet.com

JLDD = Jet Lag Driven Development

In the old days, when tech conferences and world travel were commonplace, a few Java champions(Jose Pomar, Nikhil Naniwadekarand I) shared coding challenges on Twitter while we were at conferences (usually JavaOne/Oracle CodeOne).

We all had varying degrees of jet lag during conferences, so we decided to use the hashtag JLDD (jet lag driven development) when posting programming challenges.

Most often, programming tasks were aimed at solving problems with Java collections or streams. I usually posted solutions using Eclipse Collections.

Throughout the pandemic, we have shared JLDD issues with each other on Twitter from time to time, even though the jet lag has long since ended. A few weeks ago I shared Haiku Kata using Java Text Blocks and Eclipse Collections.

Haiku in Java using Text Blocks

Creative writing combined with Java text blocks and Eclipse collections

medium.com

Jose Pomar then went above and beyond and coded the JLDD challenge live in the 25 minute JEP Cafe #9 video. He does an amazing job explaining Eclipse Collections and Java 17 solutions. Great job!

Wordle Kata

Jose Pomar This week I was sent a Wordle Kata JLDD challenge as a test, for which I had to write code that passes this test. I like this type of kata that follows the classic TDD style using a “test first” approach. Above is test code for kata using simple JUnit 5 assertions.

A few hidden words and related guesses that lead to some result
A few hidden words and related guesses that lead to some result

The rules based on this test are pretty simple.

  1. If the letter in the guess string does not match the letter in the hidden word, replace the character in the output with “.”

  2. If the letter in the guess string matches the letter in the hidden word and the letter is in the same position, then replace the character with a capital letter.

  3. If the letter in the guess string matches the letter in the hidden word, but the letter is in a different position, then replace the character with a lowercase letter.

  4. If the letter matches but appears more times in the guessed string than in the hidden word, then replace the additional characters in the output with “.”.

My first solution using Eclipse Collections

The solution I came up with looked like this and passed all the tests.

Code for guessing Wordle using Eclipse Collections
Code for guessing Wordle using Eclipse Collections

The code takes a guess, compares each letter to the hidden word, and if there is a direct match, prints an uppercase letter, otherwise prints a lowercase letter if there is an indirect match, or “.” if there is no match or the letter is an additional matching character.

Method collectWithIndex type CharAdapter converts the string guessChars one character at a time. At first I thought that none char values ​​will not be boxed as objects Characterbut it turned out I was wrong.

Method collectWithIndex accepts CharIntToObjectFunctionwhich means that the values char for each output symbol will be packed as objects Character.

This also means that we don’t have a purely primitive version collectWithIndex in Eclipse Collections, as is the case with collectChar. I think this is probably acceptable in most cases and shouldn’t be too costly for this particular use case.

I don’t think adding a purely primitive named version collectCharWithIndex would make sense.

However, there is a bigger problem than packaging. char values ​​in the form Character objects before generating output String. I found that there is no test case and an additional rule that we need to add to the kata.

Rule: favor direct matches over indirect ones.

I have added the following test case which causes my first solution to fail.

A word that has a direct match of one letter, but the indirect match precedes the direct match.
A word that has a direct match of one letter, but the indirect match precedes the direct match.

Let me zoom in so you can see the test case more clearly.

The output for the word
The output for the word “bbubb” with the guess “aaaah” should be “..Ah..”

In this case, the letter “a” will have a direct match in third position, but indirect matches in guess should be ignored in favor of a direct match in third position.

My updated solution

For those who know me, you should know that I am somewhat obsessed. ensuring good symmetry in the Eclipse Collection API.

In this particular use case, it would be ideal if there was an equivalent selectWithIndex And rejectWithIndexavailable for primitive collection types in Eclipse Collections. This particular use case might make me agree and add the missing methods.

However, there is an alternative method that I can use to implement the equivalent of these two methods. This is the method injectIntoWithIndex.

Here is my updated solution using injectIntoWithIndex for creating CharBag with the remaining characters that don’t have direct matches.

Wordle guessing solution that handles all rules
Wordle guessing solution that handles all rules

If you want to understand how injectIntoWithIndex works, you can read the following blog about injectionInto (EC by Example: InjectInto). The injectInto method can be used to implement most of the iteration patterns, which also applies to injectIntoWithIndex. These are both magical and powerful methods.

Update: Alternative solution using zipChar

Sometimes when you work on the same code for 18 years, you forget things. Luckily, a friend will occasionally remind you of things you may have forgotten. It happened this week when Vladimir Zakharov shared another solution for Wordle JLDD Kata on Twitter using the method named zipChar.

Vlad’s solution is pretty cool and it’s definitely not the approach I would have thought of. I forgot that zip a primitive version was available in Eclipse Collections. Every primitive type OrderedIterable in Eclipse Collections supports zip of the same type. So a CharAdapter there is a method zipChar.

I soon remembered that there was a blog I wrote over four years ago about primitive zip.

Once I saw Vlad’s solution, I realized that I could write an alternative to my own solution using zipChar for replacement injectIntoIndex And collectWithIndex.

Wordle Kata using zipChar with reject and collectChar
Wordle Kata using zipChar with reject and collectChar

Two solutions that use zipChardiffer significantly in the algorithm they use to calculate the final result of the guess string.

Source for my final solution with Eclipse Collections

I created my final solution to the Wordle Kata JLDD problem using Eclipse Collections. I’m looking forward to seeing a pure Java 17 solution from Jose Pomar.

. I always learn something new and interesting about Java from José that I didn’t know before.

import org.eclipse.collections.api.bag.primitive.MutableCharBag;
import org.eclipse.collections.impl.factory.Strings;
import org.eclipse.collections.impl.factory.primitive.CharBags;
import org.eclipse.collections.impl.string.immutable.CharAdapter;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class WordleTest
{
    @Test
    public void wordleTest()
    {
        Assertions.assertEquals(".....", new Wordle("aaaaa").guess("bbbbb"));
        Assertions.assertEquals("A....", new Wordle("aaaaa").guess("abbbb"));
        Assertions.assertEquals(".A...", new Wordle("aaaaa").guess("babbb"));
        Assertions.assertEquals("..A..", new Wordle("aaaaa").guess("bbabb"));
        Assertions.assertEquals("...A.", new Wordle("aaaaa").guess("bbbab"));
        Assertions.assertEquals("....A", new Wordle("aaaaa").guess("bbbba"));

        Assertions.assertEquals(".a...", new Wordle("abbbb").guess("caccc"));
        Assertions.assertEquals("..a..", new Wordle("abbbb").guess("ccacc"));
        Assertions.assertEquals("...a.", new Wordle("abbbb").guess("cccac"));
        Assertions.assertEquals("....a", new Wordle("abbbb").guess("cccca"));

        Assertions.assertEquals("A....", new Wordle("abbbb").guess("accca"));
        Assertions.assertEquals("A....", new Wordle("abbbb").guess("accaa"));
        Assertions.assertEquals("A..a.", new Wordle("aabbb").guess("accaa"));
        Assertions.assertEquals("AA...", new Wordle("aabbb").guess("aacaa"));
        Assertions.assertEquals("...aa", new Wordle("aabbb").guess("cccaa"));

        Assertions.assertEquals("..A..", new Wordle("bbabb").guess("aaaaa"));

        Assertions.assertEquals("AAAAA", new Wordle("aaaaa").guess("aaaaa"));
        Assertions.assertEquals("BRAVO", new Wordle("bravo").guess("bravo"));
    }

    record Wordle(String string)
    {
        Wordle(String string)
        {
            this.string = string.toLowerCase();
        }

        public String guess(String guess)
        {
            CharAdapter guessChars = Strings.asChars(guess.toLowerCase());
            CharAdapter hiddenChars = Strings.asChars(this.string);
            MutableCharBag remaining = hiddenChars
                    .injectIntoWithIndex(
                            CharBags.mutable.empty(),
                            (bag, each, i) -> guessChars.get(i) != each ? bag.with(each) : bag);
            return guessChars.collectWithIndex((each, i) -> hiddenChars.get(i) == each ?
                            Character.toUpperCase(each) : this.replaceDifferentPositionOrNoMatch(remaining, each))
                    .makeString("");
        }

        private char replaceDifferentPositionOrNoMatch(MutableCharBag remaining, char each)
        {
            return remaining.remove(each) ? each : '.';
        }
    }
}

Final thoughts

I hope you enjoyed this blog about my JLDD Kata solutions using Eclipse collections. Of course, I would be happy to see other solutions to these problems so that we can all learn new and different solutions to programming problems. These solutions can be in Java using your favorite libraries or even in another programming language.

Thanks for taking the time to read!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *