qwörtle/index.js

316 lines
54 KiB
JavaScript
Raw Normal View History

2022-02-23 11:32:40 +00:00
"use strict";
const words = "abbau abend abzug adler akten aktie aktiv akute alarm album alias allem allen aller alles alles allzu alten alten alter alter älter altes ampel ämter amtes angst anruf april areal arena ärger armee armen armut arten ärzte asien augen autor autos babys baden bande bands basis bauch bauen bauen bauer bäume beach beben beide beide beine bekam berge beruf beste beste beute bevor bevor bezog bezos bezug biete bitte bitte blatt blick blieb blume boden bogen bombe börse brach brand braun breit brief bruch brust buben bühne bunte büros busse chaos circa clubs couch dabei dabei dafür dafür daher daher dahin damen damit damit danke danke daran daran darin darin darum darum daten datum dauer davon davon davor davor debüt decke deckt deine delta denen denke denkt deren deren desto dicht diebe dient diese diese dinge dosen dosis drama drauf dreht droht druck duell durch durch dürfe ebene echte edeka einem einem einen einen einer einer eines eines einig einst enden endet engen enger enorm ergab erich ernst ernst ernte erste erste essen essen etwas etwas exakt extra fahrt fährt falle falle fälle falls falls fällt fängt farbe fasst faust fazit fehle fehlt feier feste feuer figur filme films final finde firma flick flüge fluss fokus folge folgt forum fotos frage frage fragt freie freie freue freut frist frust fuchs fügte fühle fühlt führe führt funke gaben ganze ganze gänze gäste geben gegen gegen gehen geist gelbe gelte genau genau genug gerät gerät gerne getan ginge glück götze gramm griff griff grill gross grund grüne grüne gutem guten guten guter gutes gutes haare haben haben hafen hagel haken halbe halle halte hände handy hängt harte hatte hätte hätte hause heben helfe heran herrn herum hetze heute heute hielt hilfe hilft hinzu hinzu hitze hobby hoffe hofft hohem hohen höhen hoher höher hohes holen holte hören hörte hotel humor hunde hürde ideal ideen ihnen ihnen ihrem ihrem ihren ihren ihrer ihrer ihres ihres immer immer indem indes index infos innen insel jäger jahre jedem jeden jeden jeder jeder jedes jedes jenem jenen jener jenes jetzt jetzt jubel junge junge jungs kabel kader kalte kälte kamen kämen kampf kanal karte kasse katze kauft kehrt keine keine kenne kennt kinos kitas klage klang klare klärt kleid klein klein klima klubs knapp knapp kohle komme kommt kommt könig könne könnt konto köpfe kraft krank krebs kreis kreuz krieg krise krone küche kugel kunde kunst kurse kurve kurze kürze küste labor lacht laden laden läden lagen lager lange lange länge lasse lässt latte laufe laufe läuft laune leben leben lebte leder leere leere legen legte lehnt lehre leise lernt lesen lesen leser leute licht liebe liebe liebt liege liegt liest linie linke linke links liste liter lobte lockt löhne lohnt lokal lösen löste löwen lücke mache macht macht mahnt manch marke markt maske masse mauer meine meine meint meist meist menge merkt messe messi meter miete milch minus minus mitte model modus mögen monat motiv motor motto musik müsse musst müsst mutig nacht nagel nahen näher namen natur nazis neben neben nebst nehme nennt neuem neuen neuen neuer neuer neues neues nicht nicht nimmt noten nötig nutzt offen öfter ohren opfer orten osten paare packt paket panik papst parat parks passt pause pferd pfund phase pixel pläne plant platz preis prinz probe profi prüft punkt quasi quote räder radio rande rasch rasen raten rauch räume räumt recht recht reden reden redet regel regen regie reich reihe reine reise rente rolle rollt roman rosen roten roten rückt rufen ruhig runde sache sagen sagen sagte sahen sätze sauer schon schon schön schub sechs sechs seele sehen seien seine seine seite senat serie setze setzt sicht siege siehe sieht singt sinkt sinne sitze sitzt sogar sogar solch solle somit somit sonne sonst sonst sorge sorgt sowie spart spass spiel sport spürt staat stadt stahl stall stand stand starb stark start statt statt staub stehe steht stein stern stets stich stieg still stock stoff stolz stolz stopp strom stück stufe stuft sturm sturz suche suche sucht
2022-02-23 09:38:57 +00:00
.split(" ")
.map(x => x.toUpperCase());
const validGuesses = "aahhh aalen aalto aarau aaron aasee abaci abbas abbau abbey abdel abdos abdul abeba abela abels abend abend aberl abert abgab abhob abiys abkam abney abort about abruf abtei abzug achen achim achja achse achte ächzt acken acker äcker acryl activ adams adamu adars addis addon adele adept adern adham adina adler adlon admir adolf adria adyen aeets aerni aerts aesch affen after again ägäis agata agatz agder agent ägide agikb agile agius agnes agora agrar ahaus ahlen ahmad ahmed ahmet ahnen ahnen ahner ahnte ahorn aigen aires aisch ajoie ajsic akbar akeem akika akins akito akkus akman aksel akten aktie aktiv aktiv akute akute alaaf alaba alain alamo alana alarm alarm albas alben albin albis album alcan alcon alder aleix alena alert alesi alexa algen alias alibi alice alien align alina aline alisa aliti alive alken allah allee allem allem allen allen aller alles alles alles allzu allzu almen almin almke alois alpen alpha alpin alpin alpro altar altem alten alten alter alter älter altes altes altöl altos alves alvim alvin alway amann amato ambri amell amess ameti amica amine amini aminu amira amiri amman ammar ammer ammon among ampel amros amsel ämter amtes anand anbau anbei anbot anden andie andrä andre andri anett angab angel anger angst angst angus anhat anhin anhob anhui anika anime anina anita anjes ankam ankea anker anmer anmut annan annen annet annie anouk anruf ansah ansel antal antje anton antti antun anuga anvil anzog anzug aorta aorus aorus apart apeel apfel äpfel aping appel appen apple april arabi arata arauz arbil arbvg arche areal areas arena argen ärger argos arias ariel arien arike arild arjan arket armed armee armel ärmel armen armen ärmer armin armut arndt arnim aroma aroov arosa array arrow arsch artan artem arten artig artur aruba arwag arwen ärzte ärzte asadi asahi asano asche ascot asean ashot asian asien askew aslan aslin asmik aspen aspyr assad assen asset assim asten ästen aster aston astra atapu athen atlas atmen atmen atmet atmos atome attac attac attal ätzen ätzen aubin aubry audio audit auers augen auhof aukus aukus aulas aunty aures autel autor autos avant avery avira avivs avond avorb avril award axios ayala aydin aying aymen ayuso aztec azubi azure babic babis babsi babys bachs backe backe bacon badel baden baden bader bäder bades badet badia badio bafin bafin bagel bahar bahls bahnt baida baidu baier baile baird baker bakkr baksi baldi balea balfe balga balge balic balis balko bälle baloh baloo balor balou balve bamba banal banas banco bande bände bands bange bange bangs bangt banja bänke baran barca bären bares bares baris barna baron barra barry bärte barth barty barum basar basel basel basic basic basis bässe basta baten bates batka bauch baude bauen bauen bauer bauke bäume baumi baums bause baute bawag bayat bayaz bayer beach beale beame bears beate beats beben beben bebra bebte becom beeke beeko beers beete beetz begab begum beide beide beier beige beine beins beira beitz bekam bekim belag beleg belek belga bella belli belli bellt belog below belta belta belur bemer benad bench benes benfe benin benko benkö benli benni benno benny bente beppo berät berch berge berge beria berka berme bernd berne berns berra berry berta berta berti bertl beruf beruf besen beste beste beten beten beter beths betis beton betty beuel beule beuse beust beute beuth beuys bevor bevor beweg bewig beyer bezog bezos bezug bfarm bfarm bfmtv bhaga bheki bibel biber bichl biden biege biegt biene biere bieri biers biest biete biete bihac bijol bikar biken biker bikes bilal bilde bilde biles billa billa bills billy bilyk binde bingo birgt birke birne birol birte bison bisop bissl bitar bites bitte bitte bizet björn black bläck blade blake blanc blank blank blase bläst blatt blaue blaue blaze blech bleib bleib blick blick blieb bliem blies blind blini bliss blitz blobs bloch block blöde blöds blogs blond blood bloom bloss bloss blubb blues bluff bluhm blüht blume blunt bluse blute blüte bmeia bmvit bngee boals board boban bobby bobic bobis bobst boden böden boese
2022-02-23 09:38:57 +00:00
.split(" ")
.map(x => x.toUpperCase());
2022-02-22 21:53:28 +00:00
const valid = "abcdefghijklmnopqrstuvwxyzäöü".toUpperCase();
function xmur3(str) {
for(var i = 0, h = 1779033703 ^ str.length; i < str.length; i++) {
h = Math.imul(h ^ str.charCodeAt(i), 3432918353);
h = h << 13 | h >>> 19;
} return function() {
h = Math.imul(h ^ (h >>> 16), 2246822507);
h = Math.imul(h ^ (h >>> 13), 3266489909);
return (h ^= h >>> 16) >>> 0;
}
}
function mulberry32(a) {
return function() {
var t = a += 0x6D2B79F5;
t = Math.imul(t ^ t >>> 15, t | 1);
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
return ((t ^ t >>> 14) >>> 0) / 4294967296;
}
}
2022-02-23 10:26:58 +00:00
const LOCALSTORAGE_KEY = "qwörtle.data";
let saveData = JSON.parse(localStorage.getItem(LOCALSTORAGE_KEY) || "{}");
2022-02-23 11:00:26 +00:00
function saveLocalStorage() {
localStorage.setItem(LOCALSTORAGE_KEY, JSON.stringify(saveData));
}
2022-02-23 10:26:58 +00:00
2022-02-23 11:32:40 +00:00
const daysSinceEpoch = Math.floor(new Date()/8.64e7);
const startDay = 19046;
2022-02-23 11:00:26 +00:00
const daysSinceStart = daysSinceEpoch - startDay + 1;
2022-02-22 21:53:28 +00:00
const rng = mulberry32(daysSinceEpoch);
const chosenWords = [];
for (let i = 0; i < 4; i++) {
while (true) {
const idx = Math.floor((rng() * words.length) % words.length);
const chosen = words[idx];
if (chosenWords.indexOf(chosen) !== -1) {
continue;
}
chosenWords.push(chosen);
break;
}
}
const WORD_LENGTH = 5;
const MAX_GUESSES = 9;
const guessed = [];
let input = "";
const data = [];
const done = [false, false, false, false];
2022-02-23 11:32:40 +00:00
function gameOver() {
const NUMBERS = "0⃣1⃣2⃣3⃣4⃣5⃣6⃣7⃣8⃣9⃣";
const ANY = "⬜";
const POSITION = "🟨";
const CORRECT = "🟩";
const DONE = "⬛⬛⬛⬛⬛";
const URL = "https://studwww.informatik.kit.edu/~s_keller/qwörtle/";
let text = "";
text += "Tägliches Qwörtle #" + daysSinceStart + "\n";
for (let i = 0; i < 4; i++) {
if (done[i]) {
text += NUMBERS.slice(done[i] * 3, done[i] * 3 + 3);
}
if (i % 2 === 1) {
text += "\n";
}
}
text += URL + "\n";
for (let i = 0; i < 2; i++) {
for (let j = 0; j < MAX_GUESSES; j++) {
const rowA = data[2*i * MAX_GUESSES + j];
const rowB = data[(2*i+1) * MAX_GUESSES + j];
let part1 = "";
let part2 = "";
for (let k = 0; k < WORD_LENGTH; k++) {
if (rowA[k].classList.contains("correct")) {
part1 += CORRECT;
} else if (rowA[k].classList.contains("wrong-position")) {
part1 += POSITION;
} else {
part1 += ANY;
}
if (rowB[k].classList.contains("correct")) {
part2 += CORRECT;
} else if (rowB[k].classList.contains("wrong-position")) {
part2 += POSITION;
} else {
part2 += ANY;
}
}
if (done[2*i] && done[2*i] <= j) {
part1 = DONE;
}
if (done[2*i+1] && done[2*i+1] <= j) {
part2 = DONE;
}
if (part1 === DONE && part2 === DONE) {
break;
}
text += part1 + " " + part2 + "\n";
}
if (i === 0) {
text += "\n";
}
}
const textarea = document.getElementById("copyarea");
textarea.style.visibility = "visible";
textarea.value = text;
textarea.select();
document.execCommand("copy");
setTimeout(() => {
textarea.value = text; // not sure why this is needed..
textarea.select();
}, 0);
}
2022-02-22 21:53:28 +00:00
function processKey(e) {
if (e.key === "Enter" && guessed.length < MAX_GUESSES) {
if (input.length < 5) {
console.error("input not long enough");
return;
}
2022-02-23 09:38:57 +00:00
if (validGuesses.indexOf(input) === -1) {
2022-02-22 21:53:28 +00:00
console.error("word not in list");
return;
}
for (let pos = 0; pos < 4; pos++) {
if (done[pos]) {
continue;
}
let used = [];
let hints = [];
let word = chosenWords[pos];
for (let i = 0; i < WORD_LENGTH; i++) {
// check whether character i of input is correct
if (word[i] === input[i]) {
hints[i] = "correct";
used[i] = true;
}
}
for (let i = 0; i < WORD_LENGTH; i++) {
// check whether character i of input is positioned incorrectly
if (hints[i]) {
continue;
}
for (let j = 0; j < WORD_LENGTH; j++) {
if (used[j]) {
continue;
}
if (input[i] === word[j]) {
hints[i] = "position";
used[j] = true;
break;
}
}
}
for (let i = 0; i < WORD_LENGTH; i++) {
const el = data[pos * MAX_GUESSES + guessed.length][i];
if (hints[i] === "correct") {
el.className = "correct cell";
} else if (hints[i] === "position") {
el.className = "wrong-position cell";
}
}
if (input === word) {
2022-02-23 11:32:40 +00:00
done[pos] = guessed.length + 1;
2022-02-22 21:53:28 +00:00
}
}
guessed.push(input);
2022-02-23 11:00:26 +00:00
if (daysSinceEpoch in saveData) {
saveData[daysSinceEpoch].guesses.push(input);
} else {
saveData[daysSinceEpoch] = { guesses: [input] };
}
saveLocalStorage();
2022-02-22 21:53:28 +00:00
input = "";
2022-02-23 11:32:40 +00:00
if (guessed.length == MAX_GUESSES || done.indexOf(false) === -1) {
gameOver();
}
2022-02-22 21:53:28 +00:00
return;
} else if (e.key === "Backspace" && input.length > 0) {
for (let pos = 0; pos < 4; pos++) {
data[pos * MAX_GUESSES + guessed.length][input.length - 1].innerText = "";
}
input = input.substring(0, input.length - 1);
} else {
if (e.key.length > 1) {
return;
}
const key = e.key.toUpperCase();
if (valid.indexOf(key) === -1) {
console.error("invalid key");
return;
}
if (input.length == 5) {
console.error("input full");
return;
}
for (let pos = 0; pos < 4; pos++) {
if (done[pos]) {
continue;
}
data[pos * MAX_GUESSES + guessed.length][input.length].innerText = key;
}
input += key;
}
2022-02-23 09:38:57 +00:00
const validGuess = input.length < 5 || validGuesses.indexOf(input) !== -1;
2022-02-22 21:53:28 +00:00
for (let pos = 0; pos < 4; pos++) {
if (done[pos]) {
continue;
}
if (!validGuess) {
data[pos * MAX_GUESSES + guessed.length][0].parentElement.className = "row invalid";
} else {
data[pos * MAX_GUESSES + guessed.length][0].parentElement.className = "row";
}
}
}
document.addEventListener("keydown", processKey);
2022-02-23 10:16:34 +00:00
// setup keyboard
const layout = [
"qwertzuiopü",
"asdfghjklöä",
["Backspace", "y", "x", "c", "v", "b", "n", "m", "Enter"]
];
const keyboard = document.getElementById("keyboard");
for (let i = 0; i < 3; i++) {
const row = keyboard.children[i];
for (const c of layout[i]) {
const button = document.createElement("button");
if (c === "Backspace") {
button.id = "backspace";
button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="26.9px"><title>Backspace Key Icon</title><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M22 3H7c-.69 0-1.23.35-1.59.88L0 12l5.41 8.11c.36.53.9.89 1.59.89h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H7.07L2.4 12l4.66-7H22v14zm-11.59-2L14 13.41 17.59 17 19 15.59 15.41 12 19 8.41 17.59 7 14 10.59 10.41 7 9 8.41 12.59 12 9 15.59z"></path></svg>';
} else if (c === "Enter") {
button.id = "enter";
button.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" height="26.9px"><title>Enter Key Icon</title><path d="M0 0h24v24H0V0z" fill="none"></path><path d="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7h-2z"></path></svg>';
} else {
button.innerText = c.toUpperCase();
}
button.className = "key";
button.addEventListener("click", e => processKey({ key: c }));
row.appendChild(button);
}
}
2022-02-22 21:53:28 +00:00
function createGameGrid(container) {
for (let pos = 0; pos < 4; pos++) {
container.appendChild(createWordle());
}
}
function createWordle() {
const x = document.createElement("div");
x.className = "wordle";
for (let i = 0; i < MAX_GUESSES; i++) {
x.appendChild(createRow(data));
}
return x;
}
function createRow() {
const x = document.createElement("div");
x.className = "row";
const cells = [];
for (let i = 0; i < 5; i++) {
const cell = document.createElement("span");
x.appendChild(cell);
cell.className = "cell";
cells.push(cell);
}
data.push(cells);
return x;
}
const main = document.getElementById("main");
createGameGrid(main);
2022-02-23 10:26:58 +00:00
let wideOpt = document.getElementById("wide");
if (saveData["wide"] !== undefined) {
wideOpt.checked = saveData["wide"];
}
function setWideMode() {
saveData.wide = wideOpt.checked;
if (saveData.wide) {
document.body.className = "wide";
} else {
document.body.className = "";
}
2022-02-23 11:00:26 +00:00
saveLocalStorage();
2022-02-23 10:26:58 +00:00
}
wideOpt.addEventListener("change", setWideMode);
setWideMode();
2022-02-23 11:00:26 +00:00
// load previous run
if (daysSinceEpoch in saveData) {
const guesses = saveData[daysSinceEpoch].guesses.slice();
saveData[daysSinceEpoch].guesses = [];
for (const guess of guesses) {
for (const c of guess) {
processKey({ key: c });
}
processKey({ key: "Enter" });
}
}