in

Create a Wordle using TDD in Javascript

RIiBoPtpMiRsMKX3dnzl5gb1Urj1 7z93pge

[ad_1]

We continue to practice and learn this wonderful form. You can follow the steps!

TL;DR: Javascript is also great for TDD

During the January 2022 Wordle rush, I wrote an article explaining how to create Wordle with TDD using PHP.

How to develop a Wordle game in 25 minutes using TDD

A few months later, I transcribed a UI version of Wordle made with Codex artificial intelligence.

Step-by-step wordle creation with Codex AI

Combine both worlds and program as a centaur.

Also compare the process and output from different language versions.

This is the Javascript version.


As usual, we’ll focus on the business logic of the game, knowing that we can build the user interface using natural language commands.

This article uses repl.it with Jest.

Javascript has many unit testing frameworks.

You can use whatever you like.

Let’s get started…

Following the same principles as the previous article, we’ll start by defining a Wordle Word.

The smallest amount of information in Wordle is a word.

we can claim that letter is smaller, but all the letter protocols you need are already defined (I could be wrong).

the word is char(5).

the word is arrangement.

the word is string.

This is a common mistake and a bijection violation.

a word and string Although they may intersect, they have different responsibilities.

Mixing (accidental) implementation details with (intrinsic) behavior is a common mistake.

So you have to define what is the word.

Wordle words are Effectiveness Five letter word.

Let’s start with our happy path:

test("test01ValidWordLettersAreValid", async function() {
  const word = new Word('valid');
  expect(['v', 'a', 'l', 'i', 'd']).toStrictEqual(word.letters());
});

Asserts that when you ask for a character to be entered with “valid” it returns an array of characters.

The results are as follows.

Message: letters from word must be 'valid'
Stack Trace:
ReferenceError: Word is not defined
    at Object.<anonymous> (/home/runner/Wordle-TDD/_test_runnertest_suite.js:6:18)
    at Promise.then.completed (/home/runner/Wordle-TDD/node_modules/jest-circus/build/utils.js:333:28)    

That’s fine, because you haven’t defined what the word is.

news

  • This is a TDD pattern.
  • Give your object a name. action even before they existed.
  • word No classes have been defined yet.
  • Our word’s first minor responsibility is to answer the letter.
  • This is not a getter. Every word must answer that letter.
  • I don’t care about sorting the letters.that is premature optimization and gilding scenarios.
  • Start with a simple example. No duplication.
  • I haven’t validated the word yet (the word could be XXXXX).
  • We can start with a simple test to verify that the word was created. This violates the test structure which always requires assertions.
  • The expected value should always be first in the assertion.

I need to create a word with letter() function.

class Word {
  letters() {
    return ['v', 'a', 'l', 'i', 'd'];
  }  
}

news

  • No constructor is needed (yet).
  • I hardcode the character function because this is the easiest solution for now.
  • Fake it until we make it.

Run all tests (only one) and it’s OK.

✅  test01ValidWordLettersAreValid

  All tests have passed 1/1  

Let’s write another test:

test("test02FewWordLettersShouldRaiseException", async function() {
  expect(() => { 
    new Word('vali');                 
               }).toThrow(Error);
});

The test fails as expected…

❌  test02FewWordLettersShouldRaiseException
Stack Trace:
Error: expect(received).toThrow(expected)

Expected constructor: Error

Received function did not throw
    at Object.toThrow (/home/runner/Wordle-TDD/_test_runnertest_suite.js:10:23)

✅  test01ValidWordLettersAreValid

  1/2 passed, see errors above  

news

  • first test passed
  • The second test is expected to throw an exception. it didn’t.
  • It just declares that a generic exception is raised.
  • It just raises a generic error.
  • Creating special exceptions is a code smell that pollutes the namespace. (Unless we catch it, but this has not happened so far).

you need to change your implementation test02 pass (and also test01).

class Word {
  constructor(word) {
    if (word.length < 5)
      throw new Error('Too few letters. Should be 5');
  }
  letters() {
      return ['v', 'a', 'l', 'i', 'd'];
  }  
}

And the test passes.


✅  test02FewWordLettersShouldRaiseException

✅  test01ValidWordLettersAreValid

  All tests have passed 2/2  

news

  • I haven’t used the constructor argument to set the actual character (yet).
  • Just check some characters. Not too many as we don’t have a cover test yet.
  • TDD requires full coverage. Adding another check without a test is a technical violation.

Let’s check if there are too many.

test("test03TooManyWordLettersShouldRaiseException", async function() {
  expect(() => { 
    new Word('toolong');                 
               }).toThrow(Error);

});

we run them:

❌  test03TooManyWordLettersShouldRaiseException
Stack Trace:
Error: expect(received).toThrow(expected)

Expected constructor: Error

Received function did not throw
    at Object.toThrow (/home/runner/Wordle-TDD/_test_runnertest_suite.js:10:23)

✅  test02FewWordLettersShouldRaiseException

✅  test01ValidWordLettersAreValid

  2/3 passed, see errors above  

Add validation.

class Word {
  constructor(letters) {
    if (letters.length < 5)
      throw new Error('Too few letters. Should be 5');
    if (letters.length > 5)
      throw new Error('Too many letters. Should be 5');
  }
  letters() {
      return ['v', 'a', 'l', 'i', 'd'];
  }  
}

And all tests passed.

All tests have passed 3/3  

You can now create an (optional) refactoring and change the function to assert a range instead of two bounds. We’ll leave it this way because it’s more declarative.

You can also add a zero word test following the Zombie methodology.

let’s do it.

test("test04EmptyLettersShouldRaiseException", async function() {
  expect(() => { 
    new Word('');                 
               }).toThrow(Error);

});
✅  test04EmptyLettersShouldRaiseException

✅  test03TooManyWordLettersShouldRaiseException

✅  test02FewWordLettersShouldRaiseException

✅  test01ValidWordLettersAreValid

We already have tests that cover this scenario, so it’s no surprise that the tests pass.

This test adds no value and should be removed.


Check for valid characters.

test("test05InvalidLettersShouldRaiseException", async function() {
   expect(() => { 
    new Word('vali*');                 
               }).toThrow(Error);

});

…and the test breaks because the assertion never occurs.


❌  test05InvalidLettersShouldRaiseException
Stack Trace:
Error: expect(received).toThrow(expected)

Expected constructor: Error

Received function did not throw

I need to fix my code…

class Word {
  constructor(word) {
    if (word.length < 5)
      throw new Error('Too few letters. Should be 5');
    if (word.length > 5)
      throw new Error('Too many letters. Should be 5');
    if (word.indexOf('*') > -1) 
      throw new Error('Word has invalid letters');
  }
}

All the tests pass because you’re obviously hardcoding them.

All tests have passed 5/5  

news

  • Hardcode the asterisk to be the only invalid character (as far as we know).
  • You can put your check code Previous Also rear previous verification. — You cannot assume expected behavior until you detect invalid cases (invalid characters and invalid lengths).

Let’s fix the code by adding more invalid characters.

test("test06PointShouldRaiseException", async function() {
   expect(() => { 
    new Word('val.d');                 
               }).toThrow(Error);

});

// Solution

 constructor(word) {
    if (word.indexOf('*') > -1) 
      throw new Error('Word has invalid letters');
    if (word.indexOf('.') > -1) 
      throw new Error('Word has invalid letters');
}

news

  • I haven’t (yet) written a more general function, because you can’t fix and refactor tests at the same time (which is prohibited by this technique).

All tests are fine.

You can refactor.

Replace the last two sentences.

class Word {
  constructor(word) {
    if (word.length < 5)
      throw new Error('Too few letters. Should be 5');
    if (word.length > 5)
      throw new Error('Too many letters. Should be 5');
    // Refactor  
    if (!word.match(/^[a-z]+$/i)) 
      throw new Error('word has invalid letters');
    //   
}

news

  • You can refactor only if you don’t change the tests at the same time.
  • The assertion checks for uppercase letters only. Since we have dealt with these examples so far.
  • Design decisions are deferred as long as possible.
  • Defined regular expressions based on alphabetic characters. I’m pretty sure it doesn’t accept Spanish (ñ), German (ë), etc.

As a checkpoint, there are only five characters from now on.

let’s assert letter() function.

I left it hardcoded.

TDD opens many avenues.

You’ll have to keep track of them all until you open a new one.

I need to compare words.

test("test07TwoWordsAreNotTheSame", async function() {
    const firstWord = new Word('valid');
    const secondWord = new Word('happy');
    expect(firstWord).not.toStrictEqual(secondWord);
});

test("test08TwoWordsAreTheSame", async function() {
    const firstWord = new Word('valid');
    const secondWord = new Word('valid');
    expect(firstWord).toStrictEqual(secondWord);
});

And the test fails.

Let’s use parameters to send.

class Word {
  constructor(word) { 
    // ...
    this._word = word;
  }
  letters() {
      return ['v', 'a', 'l', 'i', 'd'];
  }  
}
✅  test08TwoWordsAreTheSame

✅  test07TwoWordsAreNotTheSame

✅  test06PointShouldRaiseException

✅  test05InvalidLettersShouldRaiseException

✅  test04EmptyLettersShouldRaiseException

✅  test03TooManyWordLettersShouldRaiseException

✅  test02FewWordLettersShouldRaiseException

✅  test01ValidWordLettersAreValid

  All tests have passed 8/8  

news

  • It saves characters and is sufficient for comparing objects (may vary by language).
  • letters() function is still hardcoded

Add another word for letter comparison.

Remember letter() Functions have been hardcoded until now.

test("test09LettersForGrassWord", async function() {
  const grassWord = new Word('grass'); 
  expect(['g','r','a','s','s']).toStrictEqual(grassWord.letters());
});

And the test fails as expected.

❌  test09LettersForGrassWord
Stack Trace:
Error: expect(received).toStrictEqual(expected) // deep equality

- Expected  - 4
+ Received  + 4

  Array [
-   "v",
+   "g",
+   "r",
    "a",
-   "l",
-   "i",
-   "d",
+   "s",
+   "s",
  ]
    at Object.toStrictEqual (/home/runner/Wordle-TDD/_test_runnertest_suite.js:9:37)

news

  • It is very important to check for equality/inequality instead of assertTrue() as many IDEs open their comparison tools based on objects.
  • This is another reason to use an IDE instead of a text editor.

let’s change letter() We faked it and since then it works.

class Word {
  letters() {
      return this._word.split("");
  }  
}

The comparison should be case insensitive.

test("test10ComparisonIsCaseInsensitve", async function() {
    const firstWord = new Word('valid');
    const secondWord = new Word('VALID');
    expect(firstWord).toStrictEqual(secondWord); 
});

the test fails.

You have to make a decision.

I decided to make all domains lowercase.

The UI has capital letters, but capital letters are not allowed.

No magic conversion.

Change your test to catch and fix invalid uppercase letters.

test("test10NoUppercaseAreAllowed", async function() {
   expect(() => { 
    new Word('vAliD');                 
               }).toThrow(Error);
});

class Word {
  constructor(word) {
    // We remove the /i modifier on the regular expression  
    if (!word.match(/^[a-z]+$/)) 
      throw new Error('word has invalid letters');   
  }

Our language is bijective with the English Wordle language. or not?

Try non-English words.

test("test11XXXXIsnotAValidWord", async function() {
  expect(() => { 
    new Word('XXXXX');                 
               }).toThrow(Error);
});

This test will fail.

Invalid English five-letter words are not detected.

news

  • You have to make a decision. According to our bijection, we have an external dictionary that claims valid words.
  • You can validate against a dictionary when creating a word. But I need to store valid wordle words in the dictionary. No strings.
  • It’s an egg-hen problem.
  • We want to handle invalid words in the dictionary instead of Wordle words.
  • Delete the test.
  • After a while you will find a better way.

Let’s make a game.

Start talking about games that don’t exist.

test("test11EmptyGameHasNoWinner", async function() {
  const game = new Game()
  expect(false).toStrictEqual(game.hasWon());
});

the test fails.

I need to create a class and a function.

class Game {
  hasWon() {
      return false;
  }  
}

Implement the words you tried.

And the easiest solution.

Hardcoded as usual.

test("test12EmptyGameWordsAttempted", async function() {
  const game = new Game()
  expect([]).toStrictEqual(game.wordsAttempted());
});

class Game {
  wordsAttempted() {
    return [];
  }
}

✅  test12EmptyGameWordsAttempted
...
  All tests have passed 12/12  
test("test13TryOneWordAndRecordIt", async function() {
  var game = new Game();
  game.addAttempt(new Word('loser'));
  expect([new Word('loser')]).toStrictEqual(game.wordsAttempted());   
});

class Game {
  constructor() {
    this._attempts = [];
  }
  hasWon() {
      return false;
  }
  wordsAttempted() {
    return this._attempts;
  }
  addAttempt(word) {
    this._attempts.push(word);    
  }
}

news

  • Add attempts by saving them locally, and also change the actual implementation of wordsAttempted().

You can implement hasLost() if it fails 6 times.

As usual with the simplest implementation.

test("test14TryOneWordAndDontLooseYet", async function() {
  const game = new Game();
  game.addAttempt(new Word('loser'));
  expect(false).toStrictEqual(game.hasLost());   
});

class Game { 
  hasLost() {
      return false;
  }
}

news

  • As the model grows, it is learning the rules.

As usual. We stopped faking it and decided to make it.

test("test15TryFiveWordsLoses", async function() {
  const game = new Game([new Word('loser'), new Word('music')], new Word('music'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  expect(false).toStrictEqual(game.hasLost());  
  // last attempt
  game.addAttempt(new Word('loser'));
  expect(true).toStrictEqual(game.hasLost());  
});

class Game {
  hasLost() {
    return this._attempts.length > 5;
  }
}

We have most mechanics.

Let’s add and disable valid word dictionaries.

test("test16TryToPlayInvalid", async function() {
  const game = new Game([]);  
  expect(() => { 
    game.addAttempt(new Word('xxxxx'));            
               }).toThrow(Error);
});

The test fails as expected.

to correct.

class Game {
  constructor(validWords) {
    this._attempts = [];
    this._validWords = validWords;
  }   
  addAttempt(word) {
    if (!this._validWords.some(validWord => validWord.sameAs(word))) {
      throw new Error(word.letters() + " is not a valid word");
    }
    this._attempts.push(word);    
  }
}

// fix previous tests
// change 

const game = new Game([]);

// to 

const game = new Game([new Word('loser')]);

Also add: 
Class Word {
 sameAs(word) {
    return word.word() == this.word();
  }
}

And the test was fixed, but…

  test16TryToPlayInvalid

❌  test15TryFiveWordsLoses
Stack Trace:
TypeError: Cannot read properties of undefined (reading 'includes')

❌  test14TryOneWordAndDontLooseYet
Stack Trace:
TypeError: Cannot read properties of undefined (reading 'includes') 

❌  test13TryOneWordAndRecordIt
Stack Trace:
TypeError: Cannot read properties of undefined (reading 'includes')

✅  test12EmptyGameWordsAttempted

✅  test10EmptyGameHasNoWinner

  12/15 passed, see errors above  

news

  • test13, test14, and test15 used to work.
  • Now that we have added new business rules, they are broken.
  • I need to pass a dictionary when creating a game.
  • Add an array containing the words you want to use and modify 3 of them.
  • Complex setups to keep creating valid scenarios is a good sign.

Now we play to win.

You should add a test and change hasWon() accordingly.

test("test17GuessesWord", async function() {
  const words = [new Word('happy')];
  const correctWord = new Word('happy');
  const game = new Game(words, correctWord);  
  expect(game.hasWon()).toStrictEqual(false);
  game.addAttempt(new Word('happy'));
  expect(game.hasWon()).toStrictEqual(true);
});

// we need to store the correct word
class Game {
  constructor(validWords, correctWord) {
    this._attempts = [];
    this._validWords = validWords;
    this._correctWord = correctWord;
  }
  hasWon() {
    return this._attempts.some(attempt => attempt.sameAs(this._correctWord)); 
}

news

  • No flags are used to check if someone won. You can check it directly.
  • I don’t care if I won the previous attempt.
  • we addParameter Refactor your previous game definition with this new element.

Added correct word.

I need to make sure this word is in the dictionary.

test("test18CorrectWordNotInDictionary", async function() {
  const words = [new Word('happy')];
  const correctWord = new Word('heros');  
   expect(() => { 
     new Game(words, correctWord);                 
               }).toThrow(Error);
});

class Game {
  constructor(validWords, correctWord) {
    if (!validWords.some(validWord => validWord.sameAs(correctWord)))
      throw new Error("Correct word " + word.word() + " is not a valid word");  
  }

news

  • All previous games had to be changed as the winner game had to be passed before it started
  • This is a nice side effect as it favors complete, immutable objects.

✅  test18CorrectWordNotInDictionary
...

✅  test01ValidWordLettersAreValid

  All tests have passed 17/17  

What happens if you win on your last attempt?

Zombies require us to always check the (B) boundaries where bugs hide.

test("test19TryFiveWordsWins", async function() {
  const game = new Game([new Word('loser'),new Word('heros')],new Word('heros'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  game.addAttempt(new Word('loser'));
  expect(false).toStrictEqual(game.hasLost());  
  expect(false).toStrictEqual(game.hasWon());  
  // last attempt
  game.addAttempt(new Word('heros'));
  expect(false).toStrictEqual(game.hasLost());  
  expect(true).toStrictEqual(game.hasWon());  
});

// And the correction

hasLost() {
    return !this.hasWon() && this._attempts.length > 5;
  }

we have all the mechanics.

Let’s add character positions.

You can do it with the Word class.

test("test20LettersDoNotMatch", async function() {
  const firstWord = new Word('trees');
  const secondWord = new Word('valid');
  expect([]).toStrictEqual(firstWord.matchesPositionWith(secondWord));
});

As usual, undefined function error:

❌  test20LettersDoNotMatch
Stack Trace:
TypeError: firstWord.matchesPositionWith is not a function

Disguise as usual.

class Word {
  matchesPositionWith(correctWord) {
    return [];    
  }
}

news

  • Names are always very important.
  • Parameters can be named another word.
  • we prefer correct word.
  • We recognize that complex algorithms will soon be needed, and roles must be clear and contextual.

let’s match

test("test21MatchesFirstLetter", async function() {
  const guessWord = new Word('trees');
  const correctWord = new Word('table');
  expect([1]).toStrictEqual(guessWord.matchesPositionWith(correctWord));
});

I fail.

should be better defined

That’s a good enough algorithm.

ugly and imperative

Sure, we’ll refactor it later.

matchesPositionWith(correctWord) {
   var positions = [];
   for (var currentPosition = 0; 
      currentPosition < this.letters().length; 
      currentPosition++) {
       if (this.letters()[currentPosition] == correctWord.letters()[currentPosition]) {
             positions.push(currentPosition + 1); 
             //Humans start counting on 1
       }
   }
   return positions;
}

And all tests pass.

news

  • Matching properties are not symmetrical

Now we need the final step.

Matching in inappropriate position.

And always the easiest solution…

test("test23MatchesIncorrectPositions", async function() {
  const guessWord = new Word('trees');
  const correctWord = new Word('drama');
  expect([2]).toStrictEqual(guessWord.matchesPositionWith(correctWord));
  expect([]).toStrictEqual(guessWord.matchesIncorrectPositionWith(correctWord));
});

// The simplest solution

class Word {
  matchesIncorrectPositionWith(correctWord) {
     return [];
  }
}

news

  • By adding these safe zero cases you miss a lot of the usual bugs.

A spicier test case.

test("test24MatchesIncorrectPositionsWithMatch", async function() {
  const guessWord = new Word('alarm');
  const correctWord = new Word('drama');
  expect([3]).toStrictEqual(guessWord.matchesPositionWith(correctWord));
  expect([1, 4, 5]).toStrictEqual(guessWord.matchesIncorrectPositionWith(correctWord));
  // A*ARM vs *RAMA
  expect([3]).toStrictEqual(correctWord.matchesPositionWith(guessWord));
  expect([2, 4, 5]).toStrictEqual(correctWord.matchesIncorrectPositionWith(guessWord));
});

let’s go to the implementation

 class Word {
  matchesIncorrectPositionWith(correctWord) {
      var positions = [];
      for (var currentPosition = 0; currentPosition < 5; currentPosition++) {
        if (correctWord.letters().includes(this.letters()[currentPosition])) {
          positions.push(currentPosition + 1);
        }
      }
      return positions.filter(function(position) {
        return !this.matchesPositionWith(correctWord).includes(position);
     }.bind(this));
    }
  }
}

that’s it.

I implemented a very small model with all meaningful rules.

All tests have passed 21/21  
test("test20220911", async function() {
  const correctWord = new Word('tibia');
    // Sorry for the spoiler
  const words = [
    // all the words I've tried
    new Word('paper'), 
    new Word('tools'),
    new Word('music'),
    new Word('think'), 
    new Word('twins'),
    new Word('tight'),
    // plus the winning word
    correctWord
  ];
  
  const game = new Game(words, correctWord);  
  expect(game.hasWon()).toStrictEqual(false);
  expect(game.hasLost()).toStrictEqual(false);
  // P(A)PER vs TIBIA
  game.addAttempt(new Word('paper'));
  expect([]).toStrictEqual((new Word('paper')).matchesPositionWith(correctWord));
  expect([2]).toStrictEqual((new Word('paper')).matchesIncorrectPositionWith(correctWord));
  // [T]OOLS vs TIBIA
  expect([1]).toStrictEqual((new Word('tools')).matchesPositionWith(correctWord));
  expect([]).toStrictEqual((new Word('tools')).matchesIncorrectPositionWith(correctWord));  
  game.addAttempt(new Word('tools'));
  // MUS[I]C vs TIBIA
  expect([4]).toStrictEqual((new Word('music')).matchesPositionWith(correctWord));
  expect([]).toStrictEqual((new Word('music')).matchesIncorrectPositionWith(correctWord));
  game.addAttempt(new Word('music'));
  // [T]H(I)NK vs TIBIA
  expect([1]).toStrictEqual((new Word('think')).matchesPositionWith(correctWord));
  expect([3]).toStrictEqual((new Word('think')).matchesIncorrectPositionWith(correctWord));
  game.addAttempt(new Word('think'));
  // [T]W(I)NS vs TIBIA
  expect([1]).toStrictEqual((new Word('twins')).matchesPositionWith(correctWord));
  expect([3]).toStrictEqual((new Word('twins')).matchesIncorrectPositionWith(correctWord));  
  game.addAttempt(new Word('twins'));  
  expect(game.hasWon()).toStrictEqual(false);
  expect(game.hasLost()).toStrictEqual(false);
  // [T][I]GHT vs TIBIA
  expect([1, 2]).toStrictEqual((new Word('tight')).matchesPositionWith(correctWord));
  expect([]).toStrictEqual((new Word('tight')).matchesIncorrectPositionWith(correctWord));  
  
  game.addAttempt(new Word('tight'));
  expect(game.hasWon()).toStrictEqual(false);
  expect(game.hasLost()).toStrictEqual(true);
});

(You can find more daily examples in the repo)

I was very happy with my job terms.

Then I read about that complicated rule

With TDD, learning new rules is no problem.

Let’s take the article example

test("test25VeryComplexWrongPositions", async function() {

  const guessWord = new Word('geese');
  const correctWord = new Word('those');
  expect([4, 5]).toStrictEqual(guessWord.matchesPositionWith(correctWord));
  expect(['s','e']).toStrictEqual(guessWord.lettersAtCorrectPosition(correctWord));
  expect([]).toStrictEqual(guessWord.lettersAtWrongtPosition(correctWord));
  expect([]).toStrictEqual(guessWord.matchesIncorrectPositionWith(correctWord));
  // GEE[S][E] vs THOSE

  const anotherGuessWord = new Word('added');
  const anotherCorrectWord = new Word('dread');
  expect([5]).toStrictEqual(anotherGuessWord.matchesPositionWith(anotherCorrectWord));
  expect(['d']).toStrictEqual(anotherGuessWord.lettersAtCorrectPosition(anotherCorrectWord));
  expect(['a', 'd', 'e']).toStrictEqual(anotherGuessWord.lettersAtWrongtPosition(anotherCorrectWord));
  expect([1, 2, 4]).toStrictEqual(anotherGuessWord.matchesIncorrectPositionWith(anotherCorrectWord));
  // (A)(D)D(E)[D] vs DREAD
  
  const yetAnotherGuessWord = new Word('mamma');
  const yetAnotherCorrectWord = new Word('maxim');
  expect([1, 2]).toStrictEqual(yetAnotherGuessWord.matchesPositionWith(yetAnotherCorrectWord));
  expect(['m', 'a']).toStrictEqual(yetAnotherGuessWord.lettersInCorrectPosition(yetAnotherCorrectWord));
  expect(['m']).toStrictEqual(yetAnotherGuessWord.lettersAtWrongtPosition(yetAnotherCorrectWord));
  expect([3]).toStrictEqual(yetAnotherGuessWord.matchesIncorrectPositionWith(yetAnotherCorrectWord));
  // [M][A](M)MA vs MAXIM
});

Steal algorithms from articles.

matchesIncorrectPositionWith(correctWord) {     
    const correctPositions = this.matchesPositionWith(correctWord);
    var incorrectPositions = [];
    var correctWordLetters = correctWord.letters();
    var ownWordLetters = this.letters();
    for (var currentPosition = 0; currentPosition < 5; currentPosition++) {
      if (correctPositions.includes(currentPosition + 1)) {
        // We can use these wildcards since they are no valid letters
        correctWordLetters.splice(currentPosition, 1, '*');
        ownWordLetters.splice(currentPosition, 1, '+');
      }
    }    
    for (var currentPosition = 0; currentPosition < 5; currentPosition++) {
      const positionInCorrectWord = correctWordLetters.indexOf(ownWordLetters[currentPosition]);
      if (positionInCorrectWord != -1) {        
        correctWordLetters.splice(positionInCorrectWord, 1, '*');
        ownWordLetters.splice(currentPosition, 1, '+');
        incorrectPositions.push(currentPosition + 1); 
      }
    }    
    return incorrectPositions;
  }

You need to add another function (useful for keyboard colors).

lettersAtCorrectPosition(correctWord) {
    return this.matchesPositionWith(correctWord).map(position => this.letters()[position -1 ]);
}
  
lettersAtWrongtPosition(correctWord) {
    return this.matchesIncorrectPositionWith(correctWord).map(position => this.letters()[position -1]);
}

news

  • The algorithm modifies the copy of the correct word by placing a ‘*’ when the correct position is matched
  • It also hides visited characters by changing them to special characters (disabled ‘+’).
DREAD vs ADDED
DREA* vs ADDE+
DRE** vs +DDE+
*RE** vs ++DE+
*R*** vs ++D++

This solution is more complete than the previous one.

The word rules have not changed.

According to David Farley, we need to become learning experts.

And we learn by practicing shapes like this.

You end up with two compact classes that define your business model.

This small model has a real 1:1 bijection to the real world in MAPPER.

Ready to evolve.

This game is a metaphor for real software engineering.

If you find it interesting, please follow Kata with me.

You can play around with a working repl.it.

  • This solution is an AI-generated
  • use a real dictionary
  • Change language and alphabet
  • change the rule to another word

Also posted here

Loading
. . . comment& more!

[ad_2]

Source link

What do you think?

Leave a Reply

GIPHY App Key not set. Please check settings

    C2fYudiRqEfLr4jV4HK33j 1200 80

    iPhone 15: What we know so far

    UeHpVSg7S6sSwoLkjWbMdL 1200 80

    Everything we know about the Nvidia RTX 4060