|
|
@ -7,7 +7,17 @@
|
|
|
|
export default {
|
|
|
|
export default {
|
|
|
|
data() {
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
return {
|
|
|
|
|
|
|
|
// Constants
|
|
|
|
|
|
|
|
LETTER_ANIMATION_DURATION: 0.3,
|
|
|
|
|
|
|
|
SHAKE_ANIMATION_DURATION: 0.3,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Variables
|
|
|
|
|
|
|
|
words: [],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
listen: true,
|
|
|
|
|
|
|
|
|
|
|
|
gridState: null,
|
|
|
|
gridState: null,
|
|
|
|
|
|
|
|
rowsAnimations: null,
|
|
|
|
keyboardMask: null,
|
|
|
|
keyboardMask: null,
|
|
|
|
|
|
|
|
|
|
|
|
currentAttempt: 0,
|
|
|
|
currentAttempt: 0,
|
|
|
@ -23,8 +33,7 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
created() {
|
|
|
|
async created() {
|
|
|
|
this.setupGame();
|
|
|
|
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
|
|
if (e.key === 'Backspace') {
|
|
|
|
if (e.key === 'Backspace') {
|
|
|
|
this.keyPress('Delete');
|
|
|
|
this.keyPress('Delete');
|
|
|
@ -34,6 +43,11 @@
|
|
|
|
this.keyPress(e.key.toUpperCase())
|
|
|
|
this.keyPress(e.key.toUpperCase())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let req = await fetch('./mathematical-wordle-words-list.json');
|
|
|
|
|
|
|
|
this.words = await req.json();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.setupGame();
|
|
|
|
},
|
|
|
|
},
|
|
|
|
components: {
|
|
|
|
components: {
|
|
|
|
GuessGrid,
|
|
|
|
GuessGrid,
|
|
|
@ -42,6 +56,33 @@
|
|
|
|
PopUp,
|
|
|
|
PopUp,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
methods: {
|
|
|
|
|
|
|
|
generatePair() {
|
|
|
|
|
|
|
|
let words = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let validPair = false;
|
|
|
|
|
|
|
|
while (!validPair) {
|
|
|
|
|
|
|
|
let firstWord = this.words[Math.floor(Math.random() * this.words.length)];
|
|
|
|
|
|
|
|
let possibleSecondWords = []
|
|
|
|
|
|
|
|
for(let i = 0; i < this.words.length; i++) {
|
|
|
|
|
|
|
|
let validSecondWord = true;
|
|
|
|
|
|
|
|
for (let l = 0; l < firstWord.length; l++) {
|
|
|
|
|
|
|
|
if (this.words[i].includes(firstWord[l])) {
|
|
|
|
|
|
|
|
validSecondWord = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (validSecondWord) {
|
|
|
|
|
|
|
|
possibleSecondWords.push(this.words[i]);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (possibleSecondWords.length > 0) {
|
|
|
|
|
|
|
|
validPair = true;
|
|
|
|
|
|
|
|
words = [firstWord, possibleSecondWords[Math.floor(Math.random() * possibleSecondWords.length)]];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return words;
|
|
|
|
|
|
|
|
},
|
|
|
|
setupGame() {
|
|
|
|
setupGame() {
|
|
|
|
this.gridState = Array.from({length: 6}, () => {
|
|
|
|
this.gridState = Array.from({length: 6}, () => {
|
|
|
|
return Array.from({length: 5}, () => {
|
|
|
|
return Array.from({length: 5}, () => {
|
|
|
@ -49,44 +90,26 @@
|
|
|
|
letter: '',
|
|
|
|
letter: '',
|
|
|
|
state: 'unset'
|
|
|
|
state: 'unset'
|
|
|
|
};
|
|
|
|
};
|
|
|
|
})
|
|
|
|
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
this.rowsAnimations = new Array(6).fill(false);
|
|
|
|
|
|
|
|
|
|
|
|
this.keyboardMask = {
|
|
|
|
this.keyboardMask = Object.fromEntries(
|
|
|
|
'Q': 'unused',
|
|
|
|
Array.from({length: 26}, (v, i) => [
|
|
|
|
'W': 'unused',
|
|
|
|
String.fromCharCode(i + 65), 'unused'
|
|
|
|
'E': 'unused',
|
|
|
|
])
|
|
|
|
'R': 'unused',
|
|
|
|
);
|
|
|
|
'T': 'unused',
|
|
|
|
this.keyboardMask['Enter'] = 'unused';
|
|
|
|
'Y': 'unused',
|
|
|
|
this.keyboardMask['Delete'] = 'unused';
|
|
|
|
'U': 'unused',
|
|
|
|
console.log(this.keyboardMask);
|
|
|
|
'I': 'unused',
|
|
|
|
|
|
|
|
'O': 'unused',
|
|
|
|
|
|
|
|
'P': 'unused',
|
|
|
|
|
|
|
|
'A': 'unused',
|
|
|
|
|
|
|
|
'S': 'unused',
|
|
|
|
|
|
|
|
'D': 'unused',
|
|
|
|
|
|
|
|
'F': 'unused',
|
|
|
|
|
|
|
|
'G': 'unused',
|
|
|
|
|
|
|
|
'H': 'unused',
|
|
|
|
|
|
|
|
'J': 'unused',
|
|
|
|
|
|
|
|
'K': 'unused',
|
|
|
|
|
|
|
|
'L': 'unused',
|
|
|
|
|
|
|
|
'Z': 'unused',
|
|
|
|
|
|
|
|
'X': 'unused',
|
|
|
|
|
|
|
|
'C': 'unused',
|
|
|
|
|
|
|
|
'V': 'unused',
|
|
|
|
|
|
|
|
'B': 'unused',
|
|
|
|
|
|
|
|
'N': 'unused',
|
|
|
|
|
|
|
|
'M': 'unused',
|
|
|
|
|
|
|
|
'Enter': 'unused',
|
|
|
|
|
|
|
|
'Delete': 'unused',
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.currentAttempt = 0;
|
|
|
|
this.currentAttempt = 0;
|
|
|
|
this.currentIndex = 0;
|
|
|
|
this.currentIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
this.secretWords = [ 'SNARK', 'ITCHY' ];
|
|
|
|
this.secretWords = this.generatePair();
|
|
|
|
|
|
|
|
console.log(this.secretWords);
|
|
|
|
|
|
|
|
console.log(this.secretWords[0]);
|
|
|
|
|
|
|
|
console.log(this.secretWords[1]);
|
|
|
|
|
|
|
|
|
|
|
|
this.gameOver = false;
|
|
|
|
this.gameOver = false;
|
|
|
|
this.won = false;
|
|
|
|
this.won = false;
|
|
|
@ -125,6 +148,9 @@
|
|
|
|
this.overlayVisible = true;
|
|
|
|
this.overlayVisible = true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
keyPress(event) {
|
|
|
|
keyPress(event) {
|
|
|
|
|
|
|
|
if (!this.listen) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
if (event === "Delete") {
|
|
|
|
if (event === "Delete") {
|
|
|
|
if(this.currentIndex > 0) {
|
|
|
|
if(this.currentIndex > 0) {
|
|
|
|
this.currentIndex -= 1;
|
|
|
|
this.currentIndex -= 1;
|
|
|
@ -133,19 +159,49 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (event == "Enter") {
|
|
|
|
} else if (event == "Enter") {
|
|
|
|
if(this.currentIndex === 5) {
|
|
|
|
if(this.currentIndex === 5) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Validate Word
|
|
|
|
|
|
|
|
let currentWord = "";
|
|
|
|
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
|
|
|
|
currentWord += this.gridState[this.currentAttempt][i].letter;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!this.words.includes(currentWord)) {
|
|
|
|
|
|
|
|
// Invalid attempt, signal error
|
|
|
|
|
|
|
|
this.listen = false;
|
|
|
|
|
|
|
|
this.rowsAnimations[this.currentAttempt] = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
this.listen = true;
|
|
|
|
|
|
|
|
this.rowsAnimations[this.currentAttempt] = false;
|
|
|
|
|
|
|
|
}, this.SHAKE_ANIMATION_DURATION * 1000);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calculate result
|
|
|
|
|
|
|
|
let wordsFlags = [ this.secretWords[0].split(''), this.secretWords[1].split('') ];
|
|
|
|
let matches = [ [-1,false], [-1,false], [-1,false], [-1,false], [-1,false] ];
|
|
|
|
let matches = [ [-1,false], [-1,false], [-1,false], [-1,false], [-1,false] ];
|
|
|
|
for(let i = 0; i < 5; i++)
|
|
|
|
|
|
|
|
{
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
let letter = this.gridState[this.currentAttempt][i].letter;
|
|
|
|
let letter = this.gridState[this.currentAttempt][i].letter;
|
|
|
|
for(let j = 0; j < 2; j++)
|
|
|
|
for(let j = 0; j < 2; j++) {
|
|
|
|
{
|
|
|
|
if (wordsFlags[j][i] === letter) {
|
|
|
|
if (this.secretWords[j].includes(letter)) {
|
|
|
|
wordsFlags[j][i] = null;
|
|
|
|
let matchType = this.secretWords[j][i] === letter;
|
|
|
|
matches[i] = [j, true];
|
|
|
|
matches[i] = [j, matchType];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
|
|
|
|
let letter = this.gridState[this.currentAttempt][i].letter;
|
|
|
|
|
|
|
|
for(let j = 0; j < 2; j++) {
|
|
|
|
|
|
|
|
for(let k = 0; k < 5; k++) {
|
|
|
|
|
|
|
|
if(wordsFlags[j][k] === letter) {
|
|
|
|
|
|
|
|
wordsFlags[j][k] = null;
|
|
|
|
|
|
|
|
matches[i] = [j, false];
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let oneWordMatch = true;
|
|
|
|
let oneWordMatch = true;
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
for(let j = 0; j < 5; j++) {
|
|
|
|
for(let j = 0; j < 5; j++) {
|
|
|
@ -155,27 +211,49 @@
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Update grid to tease result
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
if (matches[i][0] !== -1 && matches[i][1]) {
|
|
|
|
if (matches[i][0] !== -1 && matches[i][1]) {
|
|
|
|
this.gridState[this.currentAttempt][i].state = `correct-${oneWordMatch ? 'full' : 'half'}`;
|
|
|
|
this.gridState[this.currentAttempt][i].state = `correct-${oneWordMatch ? 'full' : 'half'}`;
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'correct';
|
|
|
|
|
|
|
|
} else if (matches[i][0] !== -1 && !matches[i][1]) {
|
|
|
|
} else if (matches[i][0] !== -1 && !matches[i][1]) {
|
|
|
|
this.gridState[this.currentAttempt][i].state = `misplaced-${oneWordMatch ? 'full' : 'half'}`;
|
|
|
|
this.gridState[this.currentAttempt][i].state = `misplaced-${oneWordMatch ? 'full' : 'half'}`;
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'misplaced';
|
|
|
|
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
this.gridState[this.currentAttempt][i].state = 'wrong';
|
|
|
|
this.gridState[this.currentAttempt][i].state = 'wrong';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
this.listen = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Resolve
|
|
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
|
|
for(let i = 0; i < 5; i++) {
|
|
|
|
|
|
|
|
if (matches[i][0] !== -1 && matches[i][1]) {
|
|
|
|
|
|
|
|
if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused' ||
|
|
|
|
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'misplaced') {
|
|
|
|
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'correct';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (matches[i][0] !== -1 && !matches[i][1]) {
|
|
|
|
|
|
|
|
if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused') {
|
|
|
|
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'misplaced';
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if(this.keyboardMask[this.gridState[this.currentAttempt][i].letter] === 'unused') {
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'wrong';
|
|
|
|
this.keyboardMask[this.gridState[this.currentAttempt][i].letter] = 'wrong';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.currentAttempt += 1;
|
|
|
|
this.currentAttempt += 1;
|
|
|
|
this.currentIndex = 0;
|
|
|
|
this.currentIndex = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.checkWin(matches, oneWordMatch)) {
|
|
|
|
if (this.checkWin(matches, oneWordMatch)) {
|
|
|
|
this.resolveGame(true);
|
|
|
|
this.resolveGame(true);
|
|
|
|
} else if (this.currentAttempt === 6) {
|
|
|
|
} else if (this.currentAttempt === 6) {
|
|
|
|
this.resolveGame(false);
|
|
|
|
this.resolveGame(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.listen = true;
|
|
|
|
|
|
|
|
}, this.LETTER_ANIMATION_DURATION * 1000 * 5);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
if (this.currentIndex < 5) {
|
|
|
|
if (this.currentIndex < 5) {
|
|
|
@ -190,8 +268,8 @@
|
|
|
|
</script>
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
<template>
|
|
|
|
<div class="page">
|
|
|
|
<div class="page" :style="`--letter-animation-duration: ${LETTER_ANIMATION_DURATION}s; --shake-animation-duration: ${SHAKE_ANIMATION_DURATION}s`">
|
|
|
|
<div class="interface">
|
|
|
|
<div v-if="this.gridState !== null" class="interface">
|
|
|
|
<div class="header">
|
|
|
|
<div class="header">
|
|
|
|
<div>Qwordle</div>
|
|
|
|
<div>Qwordle</div>
|
|
|
|
<div class="end">
|
|
|
|
<div class="end">
|
|
|
@ -199,8 +277,8 @@
|
|
|
|
<div v-if="gameOver" class="material-icons-outlined" @click="overlayVisible = true">military_tech</div>
|
|
|
|
<div v-if="gameOver" class="material-icons-outlined" @click="overlayVisible = true">military_tech</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<GuessGrid :state="gridState" />
|
|
|
|
<GuessGrid :state="gridState" :rows-animations="rowsAnimations"/>
|
|
|
|
<Keyboard :mask="keyboardMask" @add="keyPress($event)" />
|
|
|
|
<Keyboard :mask="keyboardMask" @add="keyPress($event)" :listen="true"/>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<Overlay v-if="overlayVisible"
|
|
|
|
<Overlay v-if="overlayVisible"
|
|
|
|
:secret-words="secretWords"
|
|
|
|
:secret-words="secretWords"
|
|
|
@ -250,8 +328,6 @@
|
|
|
|
--misplaced-cell-dark: #eab308;
|
|
|
|
--misplaced-cell-dark: #eab308;
|
|
|
|
--misplaced-cell-light: #facc15;
|
|
|
|
--misplaced-cell-light: #facc15;
|
|
|
|
--wrong-cell: #334155;
|
|
|
|
--wrong-cell: #334155;
|
|
|
|
|
|
|
|
|
|
|
|
--animation-duration: 0.3s;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
* {
|
|
|
|
* {
|
|
|
|