Ampscript
%%[
/* Calculate how much time is left to show this in the interface */
set @now = Now()
set @tomorrow = DateAdd(FormatDate(@now, 'yyyy-MM-dd'), 1, 'D')
set @timeLeft = DateDiff(@now, @tomorrow, 'MI')
set @timeLeftMinutes = Mod(@timeLeft, 60)
set @timeLeftHours = Divide(Subtract(@timeLeft, @timeLeftMinutes), 60)
/* Build the text string with hours AND minutes (IF they are bigger than 0) */
set @timeLeftString = 'The word resets in '
/* Hours */
IF
@timeLeftHours >= 2
THEN
set @timeLeftString = Concat(@timeLeftString, @timeLeftHours, ' hours ')
ELSEIF
@timeLeftHours == 1
THEN
set @timeLeftString = Concat(@timeLeftString, @timeLeftHours, ' hour ')
ENDIF
/* IF both numbers of hours AND minuter are mentioned place an 'AND' string between them */
IF
@timeLeftHours >= 1
AND
@timeLeftMinutes >= 1
THEN
set @timeLeftString = Concat(@timeLeftString, ' AND ')
ENDIF
/* Minutes */
IF
@timeLeftMinutes >= 2
THEN
set @timeLeftString = Concat(@timeLeftString, @timeLeftMinutes, ' minutes ')
ELSEIF
@timeLeftMinutes == 1
THEN
set @timeLeftString = Concat(@timeLeftString, @timeLeftMinutes, ' minute ')
ELSEIF
@timeLeftHours == 0
AND
@timeLeftMinutes == 0
THEN
set @timeLeftString = Concat(@timeLeftString, 'less than a minute ')
ENDIF
/* Proper Wordle Stuff */
/* Select the word for the day in the encoded form */
set @words = 'U1RFQU0=|T1BFUkE=|Q0xPQUs=|Q0xFQVI=|TklHSFQ=|R0hPU1Q=|U1RBRkY=|U1RPVVQ=|UElMT1Q=|S0VCQUI=|QUJZU1M=|Uk9VR0U=|U0hBUkU=|V09STEQ=|UVVFUlk=|VUxDRVI=|U0lFR0U=|QkxPS0U=|RlJBTUU=|Q0hFQVQ=|RVBPQ0g=|U1BJQ1k=|RUxERVI=|RkVJR04=|Q1JBVEU=|VElNSUQ=|U1BJTEw=|QlJFQUs=|UEFVU0U=|R1JJTUU=|Q0hBTVA='
set @rowset = BuildRowsetFromString(@words, '|')
set @day = DatePart(@now, 'D')
set @row = Row(@rowset, @day)
set @theWordEncoded = Field(@row, 1)
set @theWord = Base64Decode(@theWordEncoded, 'UTF-8')
/* Get the submitted values AND calculate how many attempts are left*/
set @previousWord = RequestParameter('previousWord')
set @lastGuess = Uppercase(RequestParameter('lastGuess'))
set @guesses = UpperCase(RequestParameter('guesses'))
/* Debug value capture before processing */
set @debug1 = Concat('{',
'"@case": "', @case, '",',
'"@feedback": "', @feedback, '",',
'"@previousWord": "', @previousWord, '",',
'"@theWordEncoded": "', @theWordEncoded, '",',
'"@lastGuess": "', @lastGuess, '",',
'"@guesses": "', @guesses, '",',
'"@alphabet": "', @alphabet, '",',
'"@evaluation": "', @evaluation, '",',
'"@evaluation2": null,',
'"@evaluation3": ""',
'}')
/* Check IF the last word has expired */
IF
Empty(@previousWord) == false
AND
@theWord != Base64Decode(@previousWord)
THEN
set @feedback = 'The daily word has reset'
set @guesses = ''
set @lastGuess = ''
ENDIF
/* Calculation of left attempts */
set @guessLimit = 6
set @guessRowset = BuildRowsetFromString(@guesses, '|')
set @guessesMade = RowCount(@guessRowset)
set @guessesLeft = Subtract(@guessLimit, @guessesMade)
set @feedback = ''
/* Validation of incoming words*/
IF
IndexOf(@guesses, @lastGuess) < 1
AND
Empty(@lastGuess) == false
THEN
set @lookup = RowCount(LookupRows('Wordle - Allowed Word List', 'Word', @lastGuess))
IF
@lookup == 1
AND
Empty(@guesses) == true
THEN
set @guesses = @lastGuess
ELSEIF
@lookup == 1
AND
Empty(@guesses) == false
THEN
set @guesses = Concat(@guesses, '|', @lastGuess)
ELSE
set @feedback = Concat("'", @lastGuess, "' is not an allowed word")
ENDIF
ELSEIF
IndexOf(@guesses, @lastGuess) > 0
THEN
set @feedback = 'Repeat guesses are not allowed'
ENDIF
/* Calculation of left attempts */
set @guessLimit = 6
set @guessRowset = BuildRowsetFromString(@guesses, '|')
set @guessesMade = RowCount(@guessRowset)
set @guessesLeft = Subtract(@guessLimit, @guessesMade)
/* Check IF the player has won */
IF
@lastGuess == @theWord
THEN
set @input = ''
set @feedback = '🎉 VICTORY ACHIEVED 🎉'
ELSEIF
@guessesLeft > 0
THEN
set @input = '<input name="lastGuess" type="text" class="mainInput" pattern="[A-Za-z]{5}" title="This needs to be a 5-letter word" id="main"><br>'
set @guessesLeft = Subtract(@guessesLeft, 1)
ENDIF
/* This will determine the colors assigned to each letter for the bottom keyboard output */
set @alphabet = 'QWERTYUIOPASDFGHJKLZXCVBNM'
set @evaluation = '--------------------------'
set @guessesHtml = ''
set @cellTemplate = '<div class="cell {class}">{letter}</div>'
/* Iterate through each letter of the @guesses string */
FOR @i = 1 TO Length(@guesses) DO
set @letter = Substring(@guesses, @i, 1)
IF
@letter == '|'
THEN
set @guessesHtml = Concat(@guessesHtml, "<br>")
ELSE
/* Find the correct class to assign */
IF
Substring(@theWord, Mod(@i, 6), 1) == @letter
THEN
set @class = 'c' /* Correct letter in the right spot */
ELSEIF
IndexOf(@theWord, @letter) > 0
THEN
set @class = 'm' /* Misplaced letter */
ELSE
set @class = 'w' /* Wrong letter */
ENDIF
/* Modify the @cellTemplate */
set @cell = Replace(@cellTemplate, "{letter}", @letter)
set @cell = Replace(@cell, "{class}", @class)
set @guessesHtml = Concat(@guessesHtml, @cell)
/* Only change the color IF the letter was not in the correct position already */
set @index = IndexOf(@alphabet, @letter)
IF Substring(@evaluation, @index, 1) != 'c'
THEN
set @evaluation = Concat(
Substring(@evaluation, 1, Subtract(@index, 1)),
@class,
Substring(@evaluation, Add(1, @index))
)
ENDIF
ENDIF
NEXT @i
/* Build the rows with empty cells to visualize remaining guesses */
FOR @i = 1 TO @guessesLeft DO
set @cell = Replace(@cellTemplate, "{letter}", ' ')
set @cell = Replace(@cell, "{class}", '')
set @remainingHtml = Concat(@remainingHtml, @cell, @cell, @cell, @cell, @cell, "<br>")
NEXT @i
/* Bottom keyboard output */
set @letters = ''
set @letterTemplate = '<div class="letter {class}" name="letter">{letter}</div>'
FOR @i = 1 TO Length(@alphabet) DO
set @instance = @letterTemplate
set @instance = Replace(@instance, '{class}', Substring(@evaluation, @i, 1))
set @instance = Replace(@instance, '{letter}', Substring(@alphabet, @i, 1))
set @instance = Replace(@instance, ' -', '')
set @letters = Concat(@letters, @instance)
IF
@i == 10
or
@i == 19
THEN
set @letters = Concat(@letters, "<br>")
ENDIF
NEXT @i
/* Debugging */
set @debug2 = Concat('{',
'"@case": "', @case, '",',
'"@feedback": "', @feedback, '",',
'"@previousWord": "', @previousWord, '",',
'"@theWordEncoded": "', @theWordEncoded, '",',
'"@lastGuess": "', @lastGuess, '",',
'"@guesses": "', @guesses, '",',
'"@alphabet": "', @alphabet, '",',
'"@evaluation": "', @evaluation, '",',
'}')
]%%
HTML + JavaScript
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Ampscript Wordle by Rafal Wolsztyniak</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=0.86, user-scalable=no, minimum-scale=0.86">
<link rel="stylesheet" href="">
<meta name="robots" content="noindex" />
<meta name="AdsBot-Google" content="noindex" />
<meta name="googlebot" content="noindex">
<meta name="googlebot-news" content="nosnippet">
<link rel="icon" href="https://sfmc.quest/img/sfmc.quest.favicon.ico" crossorigin>
<link href="https://sfmc.quest/wordle/wordle-style.css" rel="stylesheet" rel="preconnect" crossorigin>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto&family=Roboto+Mono&family=Roboto+Slab&display=swap" rel="stylesheet">
<script>
console.table(%%=v(@debug1)=%%)
console.table(%%=v(@debug2)=%%)
</script>
</head>
<body>
<div style="text-align: center; width: 100%">
<div>
<header style="text-align: left">
<img class="logo" src="https://image.s7.sfmc-content.com/lib/fe91137277600c7976/m/1/54db8ea4-2c41-4822-96bc-8189af38f833.png">
<span class="title">Ampscript Wordle</span>
<br>
<ul class="info">
<li>Developed by Rafał Wolsztyniak</li>
<li>This is a tribute to <a href="https://www.nytimes.com/games/wordle/index.html" target="_blank">Wordle</a> by Josh Wardle</li>
<li><a href="https://sfmc.quest">Click here to see how this game was made</a></li>
<li>%%=v(@timeLeftString)=%%</li>
</ul>
</header>
</div>
<!-- Standard Game -->
<form method="post" action="#">
<div class="board">
<input name="previousWord" type="hidden" value="%%=v(@theWordEncoded)=%%">
<input name="guesses" type="hidden" value="%%=v(@guesses)=%%">
%%=v(@guessesHtml)=%% <br>
%%=v(@input)=%%
%%=v(@remainingHtml)=%% <br>
</div>
<div class="feedback">
<b>%%=v(@feedback)=%%</b>
</div>
<br>
%%=v(@letters)=%%
<br>
<div>
<input type="button" onclick="backspace()" value="←" class="letter control">
<input type="submit" value="Enter" class="letter control" style="margin-top: 5px">
</div>
</form>
<div class="hiddenInput">
<!-- Board Reset -->
<form method="post" action="" style="display:inline-block">
<input name="previousWord" type="hidden" value="">
<input name="guesses" type="hidden" value="">
<input type="submit" value="Reset" class="letter control">
</form>
</div>
<div class="info">
How to play:
<ul>
<li>Each day a new word is selected for you to guess</li>
<li>You have 6 attempts to find it</li>
<li>After each attempt, the cells with letters will be styled to give you feedback IF they are used in the word </li>
<li>It's possible for a letter to appear multiple times</li>
</ul>
Example:
<ul>
<li>The daily word is
<span class="mini">Q</span>
<span class="mini">U</span>
<span class="mini">E</span>
<span class="mini">S</span>
<span class="mini">T</span>
</li>
<li>You guess the word
<span class="mini">S</span>
<span class="mini">W</span>
<span class="mini">I</span>
<span class="mini">F</span>
<span class="mini">T</span>
which will be styled this way:
<span class="mini m">S</span>
<span class="mini w">W</span>
<span class="mini w">I</span>
<span class="mini w">F</span>
<span class="mini c">T</span>
</li>
<li>
This gives you the following information:
<ul>
<li>
<span class="mini c">T</span> is a letter in the correct spot of the word
</li>
<li>
<span class="mini m">S</span> appears in the word at least once,<br>but is not in the right position
</li>
<li>
<span class="mini w">W</span>, <span class="mini w">I</span> AND <span class="mini w">F</span> don't appear in the word
</li>
</ul>
</li>
</div>
<script>
inputNode = document.getElementById("main");
document.querySelectorAll('.letter').forEach(item => {
item.addEventListener('click', event => {
console.log(typeof item);
IF(inputNode.value.length < 5){
inputNode.value += event.target.innerText;
}
})
})
function backspace(){
inputNode.value = inputNode.value.substr(0, inputNode.value.length-1);
}
</script>
</body>
</html>
Thank you for reading my thesis 😉