1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/** @jsxImportSource https://esm.sh/react */
import { render } from "https://deno.land/x/resvg_wasm/mod.ts";
import { html } from "https://esm.town/v/stevekrouse/html";
import { reloadOnSaveFetchMiddleware } from "https://esm.town/v/stevekrouse/reloadOnSave";
import { renderToString } from "npm:react-dom/server";
const GREEN = "#6aaa64";
const YELLOW = "#c9b458";
const GRAY = "#787c7e";
const LIGHT_GRAY = "#d3d6da";
// TODO - more words
const wordleWords = [
"array",
"cache",
"class",
"clone",
"codec",
"debug",
"fetch",
"frame",
"gigot",
"index",
"input",
"logic",
"patch",
"query",
"proxy",
"regex",
"route",
"stack",
"token",
"tuple",
];
interface Game {
answer: string;
guesses: string[];
currentGuess: string;
}
function encodeGame(game: Game): string {
return btoa(JSON.stringify(game));
}
function decodeGame(encoded: string): Game {
return JSON.parse(atob(encoded));
}
function nextStateEncoded(url, game: Game, action: string): string | undefined {
const next = nextState(game, action);
return next ? `https://${url}/${encodeGame(next)}` : undefined;
}
function nextState(game: Game, action: string): Game | undefined {
// TODO - check if action is valid and return null if not, ie no link
const { answer, guesses, currentGuess } = game;
if (guesses.some((guess) => [...guess].some((x, i) => x === action && !answer.includes(action)))) {
return null;
}
if (answer === guesses.at(-1)) return null;
if (action === "Enter" && currentGuess.length !== 5) return null;
if (action === "Enter") {
return {
...game,
guesses: [...game.guesses, currentGuess],
currentGuess: "",
};
}
if (action === "Delete") {
return {
...game,
currentGuess: currentGuess.slice(0, -1),
};
}
if (currentGuess.length === 5) return null;
return {
...game,
currentGuess: currentGuess + action,
};
}
function rightPad(str: string, length: number): string {
return str + " ".repeat(length - str.length);
}
function letterColor({ answer, guesses }: Game, letter: string): string {
if (guesses.some((guess) => [...guess].some((x, i) => x === letter && answer[i] === letter))) {
return GREEN;
}
if (
guesses.some((guess) => [...guess].some((x, i) => x === letter && answer[i] !== letter && answer.includes(letter)))
) {
return YELLOW;
}
if (guesses.some((guess) => [...guess].some((x, i) => x === letter && !answer.includes(letter)))) {
return GRAY;
}
return LIGHT_GRAY;
}
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === "/") {