Expose HN: A Wordle clone made in Google Sheets

Expose HN: A Wordle clone made in Google Sheets

  1. Set a duplicate of the spreadsheet linked here.
  2. Form in a note in the left field. Entering a genuine, 5 letter note in step with the listing of phrases in the Settings sheet will show the checkbox to submit your wager.
  3. Repeat row by row till you derive the Wordle.
  4. To originate a brand unusual recreation, bound to the menu and click into Wordle> Originate Contemporary Sport.

(For first-time customers, starting a brand unusual recreation would require script authorization. When led to, provide authorization and note step 4 as soon as more.)

  • Make no longer kind straight into the squares as they have formulas.
  • The script makes teach of onEdit to trace any modifications to the spreadsheet, and executes the code if the checkbox is clicked.
  • If a note shorter or longer than 5 letters is entered and the checkbox is circumvented, the script will reject it.
  • Initiating a brand unusual recreation from the menu will increment the ID on the discontinuance of the Settings by +1, and this can reset all square and keyboard background colours.
  • To produce a personalized listing of Wordles, bound to Settings and change the blacked out cells in column C with different phrases.
  • To ogle the script, bound to Extensions> Apps Script or the code.gs on this listing.

This recreation utilizes a hybrid of formulas/conditional formatting and Google Apps Script.

We are in a position to originate by organising the constructing blocks:

The Settings sheet will retain an ID on the discontinuance, representing the present recreation (i.e. note):

  • Every ID corresponds to a note in a listing of Wordles.
  • A 2d, longer listing of “generous” phrases is additionally equipped.

The Play sheet comprises 5 squares, 6 rows, and a mock keyboard:

  • On the left aspect, a field is geared up to kind into.
  • On the staunch aspect, a checkbox and system are equipped:
  • The system =IFERROR(IF(MATCH($C3, {SETTINGS!$C$5:$C;SETTINGS!$G$5:$G}, 0), "← SUBMIT YOUR ANSWER"), "NOT A VALID WORD") stipulates that if the wager is generous in step with the two note lists in the Settings, this can return “SUBMIT YOUR ANSWER”, otherwise if the wager is invalid, this can return “NOT A VALID WORD”.
  • Conditional formatting determines when the checkbox is unhidden, in step with if the submission is 5 letters lengthy: =LEN($C3)5.
  • The checkbox remains hidden if the system returns “NOT A VALID WORD”.

The Menu presents an technique to originate a brand unusual recreation, which resets the sheet from all text and colours, and increments the ID on the discontinuance of the Settings by +1.

The Apps Script does every little thing else.

First produce the menu item thru the UI SpreadsheetApp.getUi():

characteristic onOpen() {
  var ui=SpreadsheetApp.getUi();
  ui.createMenu('► Wordle ◄')
    .addItem('🔄  Originate Contemporary Sport', 'newGame')

The characteristic newGame referenced above resets the sheet and strikes on to the next Wordle by incrementing the ID:

characteristic newGame() {

First, we particular the guesses written in column C:

var inputRange=play.getRange('C3:C13');

We unchecked the checkboxes in column BE:

var checkBoxRange=play.getRange('BE3:BE13');
var checkBoxValues=checkBoxRange.getValues();

for (var i=0; i 

We reset the squares and keyboard keys of all their coloration:

var allRows=play.getRangeList(["K3", "T3", "AC3", "AL3", "AU3", "K5", "T5", "AC5", "AL5", "AU5", "K7", "T7", "AC7", "AL7", "AU7", "K9", "T9", "AC9", "AL9", "AU9", "K11", "T11", "AC11", "AL11", "AU11", "K13", "T13", "AC13", "AL13", "AU13"]);

var allKeys=play.getRangeList(["G17","AK19","Y19","S17","P15","Y17","AE17","AK17","AT15","AQ17","AW17","BC17","AW19","AQ19","AZ15","BF15","D15","V15","M17","AB15","AN15","AE19","J15","S19","AH15","M19"]);

Then, the ID price in the Settings, column C, row 2 might perhaps be incremented by one:

var idRange=settings.getRange("C2");
var currentId=idRange.getDisplayValues();
idRange.setValue(parseInt(currentId) + 1);

This makes up the newGame characteristic.

Finally, we derive into the Wordle characteristic:

We originate with an onEdit characteristic:

characteristic onEdit(e) {

Which means of we need the script to test if the checkbox has been clicked, we make a choice to derive which row has been checked off (out of the 6). We are in a position to retailer this insist in var index. We additionally make a choice to specify that the checkbox being checked off is in column BE/#57, where the checkboxes sit.

var index=e.vary.getRow();
var checkboxColumnInt=57;

With these two facts, we can consult with the checkbox that is checked off onEdit as var runBox:

var runBox=play.getRange(index, checkboxColumnInt);

All the characteristic is then in step with three if statements. Basically the most important two:

if (e.vary.getColumn()==checkboxColumnInt) {
  if (runBox.isChecked()==staunch) {

Ensures that the checkbox that used to be checked off onEdit is situated in column BE/#57, and that the checkbox in ask has been made staunch (as against made misguided, i.e. unchecked).

If the above operations are staunch, we can blueprint extra code, getting the row’s wager and changing it to an array in all lowercase, then checking whether it is exactly 5 characters with one extra nested if advise:

var guessString=play.getRange("C" + index).getDisplayValue().toLowerCase(); //.substring(0, 5) can ruin up consequence
var guessArray=guessString.ruin up("");

  if (guessArray.dimension==5) {
  } else {
    ss.toast("Bet ought to composed be exactly 5 letters. Are trying as soon as more!");

If the dimensions of the wager is rather than 5 characters, it triggers a toast.

If the wager is 5 characters, we make a choice to derive the Wordle, evaluate it with the guessArray and beget the squares and keyboard keys with the staunch colours.

To derive the Wordle, first we derive the ID, i.e. the present recreation, in the Settings in cell C2, then we can search the vary of IDs equivalent to their Wordles (B5:B) with a for loop, and offset one cell to the staunch as soon as we to find the ID to derive the Wordle as a string in all lowercase.

var idRange=settings.getRange("C2");
var currentId=idRange.getDisplayValues();
var searchIdRange=settings.getRange("B5:B").getValues();
var wordPosition;
var depend=4; //ID ranges originate from row 5

for (var i=0; i 

Next, we will create an array of objects to represent each letter in the current guess. Along with each letter from the guess, we will designate the “fill” that determines if the letter from the guess is an exact match by index, valid for being in the Wordle, or completely invalid: green, yellow, gray.

row=[]; creates an empty array that will hold all the objects (i.e. letters + their fill).

Then, three forEach functions will (1) add objects to the empty array row starting by designating all fills as “invalid”, (2) iterate over each object and determine if a letter is a “match” — if so, replace “invalid” with “match”, and (3) iterate over each object and determine if a letter is “valid” — if so, replace “invalid” with a “valid”. All invalids will otherwise remain invalid.

(1) We access the guess that was submitted through its guessArray, take each element (letter) from the array (5 letters in total), and push 5 objects in total to the row empty array:

guessArray.forEach(i=> {
    letter: i,
    beget: "invalid"

This would consequence in something worship this:

person's wager: CRANE

var guessArray=['c', 'r', 'a', 'n', 'e']

row=[ [letter: c, fill: invalid], [letter: r, fill: invalid], [letter: a, fill: invalid], [letter: n, fill: invalid], [letter: e, fill: invalid] ]

(2) Then, we can access this newly created row array of objects. We are in a position to test each and each letter and evaluate it to the Wordle string, currentWordle, utilizing the forEach index that test if there might perhaps be an true letter match in an true situation/index match.

If the wager is CRANE and we are in situation 2, we are having access to the “A” in CR[A]NE. If the Wordle is TRAIN, then the currentWordle[index] would consequence in a match for CR[A]NE and TR[A]IN, as both letters match in the identical situation.

This would presumably well additionally change the beget from “invalid” to “match” and then handle the letter from the var currentWordle by replacing it with a 0 so as that it could no longer be regarded as “generous” in the next forEach characteristic.

row.forEach((i, index)=> {
  if (i.letter==currentWordle[index]) {
    currentWordle=currentWordle.change(i.letter, "0");

All suits will become 0s, so wager CRANE for Wordle TRAIN would revise the currentWordle var to T00IN since the “RA” had been true suits.

(3) This would presumably well additionally enable the next forEach characteristic to anticipate if a letter simply exists (is incorporated) in the Wordle, however are no longer a situation match. We test if the wager letter exists in the currentWordle var with .comprises(i.letter)i.letter referencing the present letter of the present object in the row array that we are iterating over.

row.forEach((i)=> {
  if (i.beget !="match" && currentWordle.comprises(i.letter)) {
    currentWordle=currentWordle.change(i.letter, "0");

Finally, we can loop thru the spreadsheet row’s squares and space a background coloration for each and each square. If the first object in the row array is a match, the first square will flip inexperienced. If it be generous, this can flip yellow. If it be invalid, this can flip gray.

As a result of the spreadsheet’s produce, the squares bound from column Okay to column AU. There are 9 columns we now accept as true with got to jump from square to square, going from left to staunch.

First, we can initialize var y=0 so as that we offset +9 horizontally one day of the sheet and cease the loop as soon as we reach 36 columns (5 squares) one day of.

We are in a position to teach a while loop to make determined expend y at a most of 36, as we originate from 0 and bound to 36, which might perhaps be 5 jumps one day of.

we can additionally teach forEach to fight thru the row array of objects. For each and each object we access, we can test its letter and look for that letter in the keyboard var, which is an array that substances each and each letter in the mock keyboard to the final note cell:

var keyboard=[{letter: "a", cell: "G17"}, 
                {letter: "b", cell: "AK19"}, 
                {letter: "c", cell: "Y19"}, 
                {letter: "d", cell: "S17"}, 
                {letter: "e", cell: "P15"}, 
                {letter: "f", cell: "Y17"}, 
                {letter: "g", cell: "AE17"}, 
                {letter: "h", cell: "AK17"}, 
                {letter: "i", cell: "AT15"}, 
                {letter: "j", cell: "AQ17"}, 
                {letter: "k", cell: "AW17"}, 
                {letter: "l", cell: "BC17"}, 
                {letter: "m", cell: "AW19"}, 
                {letter: "n", cell: "AQ19"}, 
                {letter: "o", cell: "AZ15"}, 
                {letter: "p", cell: "BF15"}, 
                {letter: "q", cell: "D15"}, 
                {letter: "r", cell: "V15"}, 
                {letter: "s", cell: "M17"}, 
                {letter: "t", cell: "AB15"}, 
                {letter: "u", cell: "AN15"}, 
                {letter: "v", cell: "AE19"}, 
                {letter: "w", cell: "J15"}, 
                {letter: "x", cell: "S19"}, 
                {letter: "y", cell: "AH15"}, 
                {letter: "z", cell: "M19"}];

This would presumably well additionally enable us to space a coloration for each and each key that we now accept as true with got to in the keyboard, in step with the letters equipped in the array of objects.

Then, we can test the beget for each and each object. If it is a “match”, we can space the background coloration of the present square we are iterating over to inexperienced.

If the beget is “generous” then we can space it to yellow, however we must test if the keyboard is already inexperienced, which scheme in a outdated wager this letter might perhaps presumably well additionally were a match, however now it be moved to the corrupt pickle in the unusual wager. We elect to preserve the keyboard inexperienced on this case. On this logic, a “match” supersedes a genuine.

Closing, if the beget is “invalid”, this time we can test if the letter in the keyboard is either inexperienced or yellow already. If that’s the case, this can most productive space the square to gray, however expend the keyboard as-is. Here is to make determined a letter in the keyboard is no longer made gray when it will additionally were inexperienced or yellow because it used to be venerable prior, much like in the note “DEEDS”. Basically the most important “E” might perhaps presumably well additionally very effectively be inexperienced, however the 2d “E” might perhaps presumably well additionally very effectively be invalid, because the Wordle is “GEARS”, and most productive the first “E” in the wager is correct. The keyboard ought to composed flip inexperienced, and as soon as traversing over the 2d “E” it sets the square to gray as it will composed, however would now not coloration over the keyboard key that ought to composed be inexperienced.


let key=keyboard.find(key=> key.letter==letter).cell;

if (i.beget==”match”) {
squareOne.offset(offsetIndex, y).setBackground(“#6aaa64”);
squareOne.offset(offsetIndex, y).setFontColor(“#ffffff”);

} else if (i.beget==”generous”) {
if (play.getRange(key).getBackground()==”#6aaa64″) {
squareOne.offset(offsetIndex, y).setBackground(“#c9b458”);
squareOne.offset(offsetIndex, y).setFontColor(“#ffffff”);
} else {
squareOne.offset(offsetIndex, y).setBackground(“#c9b458”);
squareOne.offset(offsetIndex, y).setFontColor(“#ffffff”);

} else if (i.beget==”invalid”) {
if (play.getRange(key).getBackground()==”#6aaa64″ || play.getRange(key).getBackground()==”#c9b458″) {
squareOne.offset(offsetIndex, y).setBackground(“#787c7e”);
squareOne.offset(offsetIndex, y).setFontColor(“#ffffff”);
} else {
squareOne.offset(offsetIndex, y).setBackground(“#787c7e”);
squareOne.offset(offsetIndex, y).setFontColor(“#ffffff”);

y +=9;



var squareOne=play.getRange("K3");
var y=0;

while (y  {

    let key=keyboard.to find(key=> key.letter==letter).cell;

    if (i.beget=="match") {
      squareOne.offset(offsetIndex, y).setBackground("#6aaa64");
      squareOne.offset(offsetIndex, y).setFontColor("#ffffff");
    } else if (i.beget=="generous") {
      if (play.getRange(key).getBackground()=="#6aaa64") {
        squareOne.offset(offsetIndex, y).setBackground("#c9b458");


Read More

Charlie Layers

Charlie Layers

Fill your life with experiences so you always have a great story to tell