create wordle archive upto day 204
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": false,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100
|
||||
}
|
||||
17
CONTRIBUTING.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Contributing to this repository
|
||||
|
||||
### Introduction
|
||||
|
||||
- This site is built with React and TailwindCSS
|
||||
- Check out the existing issues for ways to contribute
|
||||
|
||||
### Have a new feature request or see a bug?
|
||||
|
||||
Create a new issue! On the issue we can discuss the problem and assign the work.
|
||||
|
||||
### Ready to contribute?
|
||||
|
||||
1. Comment on the issue to claim it
|
||||
2. Create a fork of the repo
|
||||
3. Work on your fork, then open a pull request. Tag the issue in your pull request
|
||||
4. Your PR will be reviewed, and if it is approved it will be merged into `main`
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Katherine Peterson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
22
README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Word Master
|
||||
|
||||
🔗 https://octokatherine.github.io/word-master/
|
||||
|
||||
Heavily inspired by [Wordle](https://www.powerlanguage.co.uk/wordle/), Word Master is a word guessing game similar to Mastermind. I created this because I love Wordle, but the once a day limit leaves me wanting more.
|
||||
|
||||
## Rules
|
||||
|
||||
You have 6 guesses to guess the correct word.
|
||||
Each guess can be any valid word.
|
||||
|
||||
After submitting a guess, the letters will turn gray, green, or yellow.
|
||||
|
||||
- Green: The letter is correct, in the correct position.
|
||||
- Yellow: The letter is correct, but in the wrong position.
|
||||
- Gray: The letter is incorrect.
|
||||
|
||||
## Contributing
|
||||
|
||||
Feel free to open an issue for any bugs or feature requests.
|
||||
|
||||
To contribute to the code, see [CONTRIBUTING.md](https://github.com/octokatherine/word-master/blob/main/CONTRIBUTING.md)
|
||||
25796
package-lock.json
generated
Normal file
50
package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "word-master",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"homepage": "http://www.deavngthakkar.com/wordle_archive",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.4.2",
|
||||
"@testing-library/jest-dom": "^5.16.1",
|
||||
"@testing-library/react": "^12.1.2",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-modal": "^3.14.4",
|
||||
"react-scripts": "5.0.0-next.58",
|
||||
"tailwindcss-neumorphism": "^0.1.0",
|
||||
"web-vitals": "^2.1.2"
|
||||
},
|
||||
"scripts": {
|
||||
"predeploy": "npm run build",
|
||||
"deploy": "gh-pages -d build",
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.4.0",
|
||||
"gh-pages": "^3.2.3",
|
||||
"postcss": "^8.4.4",
|
||||
"tailwindcss": "^3.0.1"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
||||
BIN
public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 4.7 KiB |
BIN
public/assets/Ranade-Variable.ttf
Normal file
9
public/browserconfig.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="https://octokatherine.github.io/word-master/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 862 B |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
49
public/index.html
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="description" content="Word Master word game" />
|
||||
<title>Word Master</title>
|
||||
<!-- Favicon icons -->
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="https://octokatherine.github.io/word-master/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="https://octokatherine.github.io/word-master/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="https://octokatherine.github.io/word-master/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="https://octokatherine.github.io/word-master/site.webmanifest" />
|
||||
<link
|
||||
rel="mask-icon"
|
||||
href="https://octokatherine.github.io/word-master/safari-pinned-tab.svg"
|
||||
color="#5bbad5"
|
||||
/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico" />
|
||||
<meta name="msapplication-TileColor" content="#da532c" />
|
||||
<meta
|
||||
name="msapplication-config"
|
||||
content="https://octokatherine.github.io/word-master/browserconfig.xml"
|
||||
/>
|
||||
|
||||
<script
|
||||
defer
|
||||
data-domain="octokatherine.github.io/word-master"
|
||||
src="https://plausible.io/js/plausible.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
public/mstile-150x150.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
15
public/safari-pinned-tab.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="850.000000pt" height="850.000000pt" viewBox="0 0 850.000000 850.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,850.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M0 4255 l0 -4245 4245 0 4245 0 0 4245 0 4245 -4245 0 -4245 0 0
|
||||
-4245z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 603 B |
19
public/site.webmanifest
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "",
|
||||
"short_name": "",
|
||||
"icons": [
|
||||
{
|
||||
"src": "https://octokatherine.github.io/word-master/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "https://octokatherine.github.io/word-master/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
430
src/App.js
Normal file
@@ -0,0 +1,430 @@
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { letters, status } from './constants'
|
||||
import { Keyboard } from './components/Keyboard'
|
||||
import words from './data/words'
|
||||
|
||||
import { useLocalStorage } from './hooks/useLocalStorage'
|
||||
import { ReactComponent as Info } from './data/Info.svg'
|
||||
import { ReactComponent as Settings } from './data/Settings.svg'
|
||||
|
||||
import { InfoModal } from './components/InfoModal'
|
||||
import { SettingsModal } from './components/SettingsModal'
|
||||
import { EndGameModal } from './components/EndGameModal'
|
||||
|
||||
const state = {
|
||||
playing: 'playing',
|
||||
won: 'won',
|
||||
lost: 'lost',
|
||||
}
|
||||
|
||||
const getDayAnswer = (day_) => {
|
||||
return wordle_answers[day_-1].toUpperCase()
|
||||
}
|
||||
|
||||
const getDay = () => {
|
||||
const today = new Date()
|
||||
const date1 = new Date('6/20/21')
|
||||
const diffTime = Math.abs(today - date1)
|
||||
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
||||
return diffDays
|
||||
}
|
||||
|
||||
const wordle_answers = ["rebut", "sissy", "humph", "awake", "blush", "focal", "evade", "naval", "serve", "heath", "dwarf", "model", "karma", "stink", "grade", "quiet", "bench", "abate", "feign", "major", "death", "fresh", "crust", "stool", "colon", "abase", "marry", "react", "batty", "pride", "floss", "helix", "croak", "staff", "paper", "unfed", "whelp", "trawl", "outdo", "adobe", "crazy", "sower", "repay", "digit", "crate", "cluck", "spike", "mimic", "pound", "maxim", "linen", "unmet", "flesh", "booby", "forth", "first", "stand", "belly", "ivory", "seedy", "print", "yearn", "drain", "bribe", "stout", "panel", "crass", "flume", "offal", "agree", "error", "swirl", "argue", "bleed", "delta", "flick", "totem", "wooer", "front", "shrub", "parry", "biome", "lapel", "start", "greet", "goner", "golem", "lusty", "loopy", "round", "audit", "lying", "gamma", "labor", "islet", "civic", "forge", "corny", "moult", "basic", "salad", "agate", "spicy", "spray", "essay", "fjord", "spend", "kebab", "guild", "aback", "motor", "alone", "hatch", "hyper", "thumb", "dowry", "ought", "belch", "dutch", "pilot", "tweed", "comet", "jaunt", "enema", "steed", "abyss", "growl", "fling", "dozen", "boozy", "erode", "world", "gouge", "click", "briar", "great", "altar", "pulpy", "blurt", "coast", "duchy", "groin", "fixer", "group", "rogue", "badly", "smart", "pithy", "gaudy", "chill", "heron", "vodka", "finer", "surer", "radio", "rouge", "perch", "retch", "wrote", "clock", "tilde", "store", "prove", "bring", "solve", "cheat", "grime", "exult", "usher", "epoch", "triad", "break", "rhino", "viral", "conic", "masse", "sonic", "vital", "trace", "using", "peach", "champ", "baton", "brake", "pluck", "craze", "gripe", "weary", "picky", "acute", "ferry", "aside", "tapir", "troll", "unify", "rebus", "boost", "truss", "siege", "tiger", "banal", "slump", "crank", "gorge", "query"]
|
||||
var day = getDay()
|
||||
const og_day = getDay()
|
||||
|
||||
function App() {
|
||||
const initialStates = {
|
||||
answer: () => getDayAnswer(day),
|
||||
gameState: state.playing,
|
||||
board: [
|
||||
['', '', '', '', ''],
|
||||
['', '', '', '', ''],
|
||||
['', '', '', '', ''],
|
||||
['', '', '', '', ''],
|
||||
['', '', '', '', ''],
|
||||
['', '', '', '', ''],
|
||||
],
|
||||
cellStatuses: () => Array(6).fill(Array(5).fill(status.unguessed)),
|
||||
currentRow: 0,
|
||||
currentCol: 0,
|
||||
letterStatuses: () => {
|
||||
const letterStatuses = {}
|
||||
letters.forEach((letter) => {
|
||||
letterStatuses[letter] = status.unguessed
|
||||
})
|
||||
return letterStatuses
|
||||
},
|
||||
}
|
||||
const [answer, setAnswer] = useState(initialStates.answer)
|
||||
const [gameState, setGameState] = useState(initialStates.gameState)
|
||||
const [board, setBoard] = useState(initialStates.board)
|
||||
const [cellStatuses, setCellStatuses] = useState(initialStates.cellStatuses)
|
||||
const [currentRow, setCurrentRow] = useState(initialStates.currentRow)
|
||||
const [currentCol, setCurrentCol] = useState(initialStates.currentCol)
|
||||
const [letterStatuses, setLetterStatuses] = useState(initialStates.letterStatuses)
|
||||
const [submittedInvalidWord, setSubmittedInvalidWord] = useState(false)
|
||||
const [currentStreak, setCurrentStreak] = useLocalStorage('current-streak', 0)
|
||||
const [longestStreak, setLongestStreak] = useLocalStorage('longest-streak', 0)
|
||||
const streakUpdated = useRef(false)
|
||||
const [modalIsOpen, setIsOpen] = useState(false)
|
||||
const [firstTime, setFirstTime] = useLocalStorage('first-time', true)
|
||||
const [infoModalIsOpen, setInfoModalIsOpen] = useState(firstTime)
|
||||
const [settingsModalIsOpen, setSettingsModalIsOpen] = useState(false)
|
||||
|
||||
const openModal = () => setIsOpen(true)
|
||||
const closeModal = () => setIsOpen(false)
|
||||
const handleInfoClose = () => {
|
||||
setFirstTime(false)
|
||||
setInfoModalIsOpen(false)
|
||||
}
|
||||
|
||||
const [darkMode, setDarkMode] = useLocalStorage('dark-mode', false)
|
||||
const toggleDarkMode = () => setDarkMode((prev) => !prev)
|
||||
|
||||
useEffect(() => {
|
||||
if (gameState !== state.playing) {
|
||||
setTimeout(() => {
|
||||
openModal()
|
||||
}, 500)
|
||||
}
|
||||
}, [gameState])
|
||||
|
||||
useEffect(() => {
|
||||
if (!streakUpdated.current) {
|
||||
if (gameState === state.won) {
|
||||
if (currentStreak >= longestStreak) {
|
||||
setLongestStreak((prev) => prev + 1)
|
||||
}
|
||||
setCurrentStreak((prev) => prev + 1)
|
||||
streakUpdated.current = true
|
||||
} else if (gameState === state.lost) {
|
||||
setCurrentStreak(0)
|
||||
streakUpdated.current = true
|
||||
}
|
||||
}
|
||||
}, [gameState, currentStreak, longestStreak, setLongestStreak, setCurrentStreak])
|
||||
|
||||
const getCellStyles = (rowNumber, colNumber, letter) => {
|
||||
if (rowNumber === currentRow) {
|
||||
if (letter) {
|
||||
return `nm-inset-background dark:nm-inset-background-dark text-primary dark:text-primary-dark ${
|
||||
submittedInvalidWord ? 'border border-red-800' : ''
|
||||
}`
|
||||
}
|
||||
return 'nm-flat-background dark:nm-flat-background-dark text-primary dark:text-primary-dark'
|
||||
}
|
||||
|
||||
switch (cellStatuses[rowNumber][colNumber]) {
|
||||
case status.green:
|
||||
return 'nm-inset-n-green text-gray-50'
|
||||
case status.yellow:
|
||||
return 'nm-inset-yellow-500 text-gray-50'
|
||||
case status.gray:
|
||||
return 'nm-inset-n-gray text-gray-50'
|
||||
default:
|
||||
return 'nm-flat-background dark:nm-flat-background-dark text-primary dark:text-primary-dark'
|
||||
}
|
||||
}
|
||||
|
||||
const addLetter = (letter) => {
|
||||
setSubmittedInvalidWord(false)
|
||||
setBoard((prev) => {
|
||||
if (currentCol > 4) {
|
||||
return prev
|
||||
}
|
||||
const newBoard = [...prev]
|
||||
newBoard[currentRow][currentCol] = letter
|
||||
return newBoard
|
||||
})
|
||||
if (currentCol < 5) {
|
||||
setCurrentCol((prev) => prev + 1)
|
||||
}
|
||||
}
|
||||
|
||||
const isValidWord = (word) => {
|
||||
if (word.length < 5) return false
|
||||
return words[word.toLowerCase()]
|
||||
}
|
||||
|
||||
const onEnterPress = () => {
|
||||
const word = board[currentRow].join('')
|
||||
if (!isValidWord(word)) {
|
||||
setSubmittedInvalidWord(true)
|
||||
return
|
||||
}
|
||||
|
||||
if (currentRow === 6) return
|
||||
|
||||
updateCellStatuses(word, currentRow)
|
||||
updateLetterStatuses(word)
|
||||
setCurrentRow((prev) => prev + 1)
|
||||
setCurrentCol(0)
|
||||
}
|
||||
|
||||
const onDeletePress = () => {
|
||||
setSubmittedInvalidWord(false)
|
||||
if (currentCol === 0) return
|
||||
|
||||
setBoard((prev) => {
|
||||
const newBoard = [...prev]
|
||||
newBoard[currentRow][currentCol - 1] = ''
|
||||
return newBoard
|
||||
})
|
||||
|
||||
setCurrentCol((prev) => prev - 1)
|
||||
}
|
||||
|
||||
const updateCellStatuses = (word, rowNumber) => {
|
||||
setCellStatuses((prev) => {
|
||||
const newCellStatuses = [...prev]
|
||||
newCellStatuses[rowNumber] = [...prev[rowNumber]]
|
||||
const wordLength = word.length
|
||||
const answerLetters = answer.split('')
|
||||
|
||||
// set all to gray
|
||||
for (let i = 0; i < wordLength; i++) {
|
||||
newCellStatuses[rowNumber][i] = status.gray
|
||||
}
|
||||
|
||||
// check greens
|
||||
for (let i = wordLength - 1; i >= 0; i--) {
|
||||
if (word[i] === answer[i]) {
|
||||
newCellStatuses[rowNumber][i] = status.green
|
||||
answerLetters.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// check yellows
|
||||
for (let i = 0; i < wordLength; i++) {
|
||||
if (answerLetters.includes(word[i]) && newCellStatuses[rowNumber][i] !== status.green) {
|
||||
newCellStatuses[rowNumber][i] = status.yellow
|
||||
answerLetters.splice(answerLetters.indexOf(word[i]), 1)
|
||||
}
|
||||
}
|
||||
|
||||
return newCellStatuses
|
||||
})
|
||||
}
|
||||
|
||||
const isRowAllGreen = (row) => {
|
||||
return row.every((cell) => cell === status.green)
|
||||
}
|
||||
|
||||
// every time cellStatuses updates, check if the game is won or lost
|
||||
useEffect(() => {
|
||||
const cellStatusesCopy = [...cellStatuses]
|
||||
const reversedStatuses = cellStatusesCopy.reverse()
|
||||
const lastFilledRow = reversedStatuses.find((r) => {
|
||||
return r[0] !== status.unguessed
|
||||
})
|
||||
|
||||
if (lastFilledRow && isRowAllGreen(lastFilledRow)) {
|
||||
setGameState(state.won)
|
||||
} else if (currentRow === 6) {
|
||||
setGameState(state.lost)
|
||||
}
|
||||
}, [cellStatuses, currentRow])
|
||||
|
||||
const updateLetterStatuses = (word) => {
|
||||
setLetterStatuses((prev) => {
|
||||
const newLetterStatuses = { ...prev }
|
||||
const wordLength = word.length
|
||||
for (let i = 0; i < wordLength; i++) {
|
||||
if (newLetterStatuses[word[i]] === status.green) continue
|
||||
|
||||
if (word[i] === answer[i]) {
|
||||
newLetterStatuses[word[i]] = status.green
|
||||
} else if (answer.includes(word[i])) {
|
||||
newLetterStatuses[word[i]] = status.yellow
|
||||
} else {
|
||||
newLetterStatuses[word[i]] = status.gray
|
||||
}
|
||||
}
|
||||
return newLetterStatuses
|
||||
})
|
||||
}
|
||||
|
||||
const modalStyles = {
|
||||
overlay: {
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundColor: darkMode ? 'hsl(231, 16%, 25%)' : 'hsl(231, 16%, 92%)',
|
||||
},
|
||||
content: {
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
right: 'auto',
|
||||
bottom: 'auto',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
height: 'calc(100% - 2rem)',
|
||||
width: 'calc(100% - 2rem)',
|
||||
backgroundColor: darkMode ? 'hsl(231, 16%, 25%)' : 'hsl(231, 16%, 92%)',
|
||||
boxShadow: `${
|
||||
darkMode
|
||||
? '0.2em 0.2em calc(0.2em * 2) #252834, calc(0.2em * -1) calc(0.2em * -1) calc(0.2em * 2) #43475C'
|
||||
: '0.2em 0.2em calc(0.2em * 2) #A3A7BD, calc(0.2em * -1) calc(0.2em * -1) calc(0.2em * 2) #FFFFFF'
|
||||
}`,
|
||||
border: 'none',
|
||||
borderRadius: '1rem',
|
||||
maxWidth: '475px',
|
||||
maxHeight: '650px',
|
||||
position: 'relative',
|
||||
},
|
||||
}
|
||||
|
||||
const play = () => {
|
||||
setAnswer(initialStates.answer)
|
||||
setGameState(initialStates.gameState)
|
||||
setBoard(initialStates.board)
|
||||
setCellStatuses(initialStates.cellStatuses)
|
||||
setCurrentRow(initialStates.currentRow)
|
||||
setCurrentCol(initialStates.currentCol)
|
||||
setLetterStatuses(initialStates.letterStatuses)
|
||||
}
|
||||
const playFirst = () => {
|
||||
day = 1
|
||||
play()
|
||||
}
|
||||
const playPrevious = () => {
|
||||
day = day - 1
|
||||
play()
|
||||
}
|
||||
const playRandom = () => {
|
||||
day = Math.floor(Math.random() * og_day)
|
||||
play()
|
||||
}
|
||||
const playNext = () => {
|
||||
if (day < og_day) {
|
||||
day = day + 1
|
||||
}
|
||||
play()
|
||||
}
|
||||
const playLast = () => {
|
||||
day = og_day
|
||||
play()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={darkMode ? 'dark' : ''}>
|
||||
<div className={`flex flex-col justify-between h-fill bg-background dark:bg-background-dark`}>
|
||||
<header className="flex items-center py-2 px-3 text-primary dark:text-primary-dark">
|
||||
<button type="button" onClick={() => setSettingsModalIsOpen(true)}>
|
||||
<Settings />
|
||||
</button>
|
||||
<h1 className="flex-1 text-center text-xl xxs:text-2xl -mr-6 sm:text-4xl tracking-wide font-bold font-og">
|
||||
WORDLE ARCHIVE {day}
|
||||
</h1>
|
||||
<button type="button" onClick={() => setInfoModalIsOpen(true)}>
|
||||
<Info />
|
||||
</button>
|
||||
</header>
|
||||
<div className="flex flex-force-center items-center py-3">
|
||||
<div className="flex items-center px-1">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playFirst}>First
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center px-2">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playPrevious}>Previous
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center px-3">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playRandom}>Random
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center px-4">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playNext}>Next
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center px-5">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playLast}>Last
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center flex-col py-4">
|
||||
<div className="grid grid-cols-5 grid-flow-row gap-4">
|
||||
{board.map((row, rowNumber) =>
|
||||
row.map((letter, colNumber) => (
|
||||
<span
|
||||
key={colNumber}
|
||||
className={`${getCellStyles(
|
||||
rowNumber,
|
||||
colNumber,
|
||||
letter
|
||||
)} inline-flex items-center font-medium justify-center text-lg w-[14vw] h-[14vw] xs:w-14 xs:h-14 sm:w-20 sm:h-20 rounded`}
|
||||
>
|
||||
{letter}
|
||||
</span>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<InfoModal
|
||||
isOpen={infoModalIsOpen}
|
||||
handleClose={handleInfoClose}
|
||||
darkMode={darkMode}
|
||||
styles={modalStyles}
|
||||
/>
|
||||
<EndGameModal
|
||||
isOpen={modalIsOpen}
|
||||
handleClose={closeModal}
|
||||
styles={modalStyles}
|
||||
darkMode={darkMode}
|
||||
gameState={gameState}
|
||||
state={state}
|
||||
currentStreak={currentStreak}
|
||||
longestStreak={longestStreak}
|
||||
answer={answer}
|
||||
playAgain={() => {
|
||||
setAnswer(initialStates.answer)
|
||||
setGameState(initialStates.gameState)
|
||||
setBoard(initialStates.board)
|
||||
setCellStatuses(initialStates.cellStatuses)
|
||||
setCurrentRow(initialStates.currentRow)
|
||||
setCurrentCol(initialStates.currentCol)
|
||||
setLetterStatuses(initialStates.letterStatuses)
|
||||
closeModal()
|
||||
streakUpdated.current = false
|
||||
}}
|
||||
day={day}
|
||||
currentRow={currentRow}
|
||||
cellStatuses={cellStatuses}
|
||||
/>
|
||||
<SettingsModal
|
||||
isOpen={settingsModalIsOpen}
|
||||
handleClose={() => setSettingsModalIsOpen(false)}
|
||||
styles={modalStyles}
|
||||
darkMode={darkMode}
|
||||
toggleDarkMode={toggleDarkMode}
|
||||
/>
|
||||
<Keyboard
|
||||
letterStatuses={letterStatuses}
|
||||
addLetter={addLetter}
|
||||
onEnterPress={onEnterPress}
|
||||
onDeletePress={onDeletePress}
|
||||
gameDisabled={gameState !== state.playing}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
8
src/App.test.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import App from './App'
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />)
|
||||
const linkElement = screen.getByText(/learn react/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
})
|
||||
116
src/components/EndGameModal.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import Modal from 'react-modal'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { status } from '../constants'
|
||||
import Success from '../data/Success.png'
|
||||
import Fail from '../data/Cross.png'
|
||||
|
||||
Modal.setAppElement('#root')
|
||||
|
||||
export const EndGameModal = ({
|
||||
isOpen,
|
||||
handleClose,
|
||||
styles,
|
||||
darkMode,
|
||||
gameState,
|
||||
state,
|
||||
currentStreak,
|
||||
longestStreak,
|
||||
answer,
|
||||
playAgain,
|
||||
day,
|
||||
currentRow,
|
||||
cellStatuses,
|
||||
}) => {
|
||||
const CloseButton = () => {
|
||||
return (
|
||||
<div className={darkMode ? 'dark' : ''}>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={playAgain}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const ShareButton = (props) => {
|
||||
const [buttonPressed, setButtonPressed] = useState(false)
|
||||
useEffect(() => {
|
||||
if (buttonPressed !== false) {
|
||||
setTimeout(() => setButtonPressed(false), [3000])
|
||||
}
|
||||
}, [buttonPressed])
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="rounded px-6 py-2 mt-8 text-lg nm-flat-background dark:nm-flat-background-dark hover:nm-inset-background dark:hover:nm-inset-background-dark text-primary dark:text-primary-dark"
|
||||
onClick={() => {
|
||||
setButtonPressed(true)
|
||||
navigator.clipboard.writeText(
|
||||
`Wordle ${day} ${currentRow}/6\n\n` +
|
||||
cellStatuses
|
||||
.map((row) => {
|
||||
if (row.every((item) => item !== status.unguessed)) {
|
||||
return (
|
||||
row
|
||||
.map((state) => {
|
||||
switch (state) {
|
||||
case status.gray:
|
||||
return '⬛'
|
||||
case status.green:
|
||||
return '🟩'
|
||||
case status.yellow:
|
||||
return '🟨'
|
||||
default:
|
||||
return ' '
|
||||
}
|
||||
})
|
||||
.join('') + '\n'
|
||||
)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
.join('')
|
||||
)
|
||||
}}
|
||||
>
|
||||
{buttonPressed ? 'Copied!' : 'Share'}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={handleClose}
|
||||
style={styles}
|
||||
contentLabel="Game End Modal"
|
||||
>
|
||||
<div className={darkMode ? 'dark' : ''}>
|
||||
<div className="h-full flex flex-col items-center justify-center max-w-[300px] mx-auto text-primary dark:text-primary-dark">
|
||||
{gameState === state.won && (
|
||||
<>
|
||||
<img src={Success} alt="success" height="auto" width="auto" />
|
||||
<h1 className=" text-3xl">Congrats!</h1>
|
||||
</>
|
||||
)}
|
||||
{gameState === state.lost && (
|
||||
<>
|
||||
<img src={Fail} alt="success" height="auto" width="80%" />
|
||||
<div className="text-primary dark:text-primary-dark text-4xl text-center">
|
||||
<p>Oops!</p>
|
||||
<p className="mt-3 text-2xl">
|
||||
The word was <strong>{answer}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<ShareButton />
|
||||
<CloseButton />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
64
src/components/InfoModal.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import { ReactComponent as Github } from '../data/Github.svg'
|
||||
import { ReactComponent as Close } from '../data/Close.svg'
|
||||
import Modal from 'react-modal'
|
||||
|
||||
Modal.setAppElement('#root')
|
||||
|
||||
export const InfoModal = ({ isOpen, handleClose, darkMode, styles }) => (
|
||||
<Modal isOpen={isOpen} onRequestClose={handleClose} style={styles} contentLabel="Game Info Modal">
|
||||
<div className={`h-full ${darkMode ? 'dark' : ''}`}>
|
||||
<button
|
||||
className="absolute top-4 right-4 rounded-full nm-flat-background dark:nm-flat-background-dark text-primary dark:text-primary-dark p-1 w-6 h-6 sm:p-2 sm:h-8 sm:w-8"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<Close />
|
||||
</button>
|
||||
<div className="h-full flex flex-col items-center justify-center max-w-[390px] mx-auto pt-9 text-primary dark:text-primary-dark">
|
||||
<div className="flex-1 w-full sm:text-base text-sm">
|
||||
<h1 className="text-center sm:text-3xl text-2xl">What is this?</h1>
|
||||
<ul className="list-disc pl-5 block sm:text-base text-sm">
|
||||
<li className="mt-6 mb-2">This is an archive for <a href="https://www.powerlanguage.co.uk/wordle/">Wordle</a> by <a href="https://twitter.com/powerlanguish">Josh Wardle</a> built on <a href="https://twitter.com/katherinecodes">Katherine Peterson</a>'s <a href="https://octokatherine.github.io/word-master">WordMaster</a></li>
|
||||
</ul>
|
||||
<h1 className="text-center sm:text-3xl text-2xl">How to play?</h1>
|
||||
<ul className="list-disc pl-5 block sm:text-base text-sm">
|
||||
<li className="mt-6 mb-2">You have 6 guesses to guess the correct word.</li>
|
||||
<li className="mb-2">You can guess any valid word.</li>
|
||||
<li className="mb-2">
|
||||
After each guess, each letter will turn green, yellow, or gray.
|
||||
</li>
|
||||
</ul>
|
||||
<div className="mb-3 mt-8 flex items-center">
|
||||
<span className="nm-inset-n-green text-gray-50 inline-flex items-center justify-center text-3x w-10 h-10 rounded-full">
|
||||
W
|
||||
</span>
|
||||
<span className="mx-2">=</span>
|
||||
<span>Correct letter, correct spot</span>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<span className="nm-inset-yellow-500 text-gray-50 inline-flex items-center justify-center text-3x w-10 h-10 rounded-full">
|
||||
W
|
||||
</span>
|
||||
<span className="mx-2">=</span>
|
||||
<span>Correct letter, wrong spot</span>
|
||||
</div>
|
||||
<span className="nm-inset-n-gray text-gray-50 inline-flex items-center justify-center text-3x w-10 h-10 rounded-full">
|
||||
W
|
||||
</span>
|
||||
<span className="mx-2">=</span>
|
||||
<span>Wrong letter</span>
|
||||
</div>
|
||||
<div className="flex justify-center sm:text-base text-sm">
|
||||
<span>This project is open source on</span>
|
||||
<a
|
||||
className="ml-[6px] rounded-full h-5 w-5 sm:h-6 sm:w-6"
|
||||
href="https://github.com/devangthakkar/wordle_archive"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Github />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
105
src/components/Keyboard.js
Normal file
@@ -0,0 +1,105 @@
|
||||
import { keyboardLetters, status, letters } from '../constants'
|
||||
import { useEffect, useCallback } from 'react'
|
||||
|
||||
const Keyboard = ({ letterStatuses, addLetter, onEnterPress, onDeletePress, gameDisabled }) => {
|
||||
const getKeyStyle = (letter) => {
|
||||
switch (letterStatuses[letter]) {
|
||||
case status.green:
|
||||
return 'bg-n-green text-gray-50'
|
||||
case status.yellow:
|
||||
return 'bg-yellow-500 text-gray-50'
|
||||
case status.gray:
|
||||
return 'bg-n-gray text-gray-50'
|
||||
default:
|
||||
return 'text-primary dark:text-primary-dark'
|
||||
}
|
||||
}
|
||||
|
||||
const onKeyButtonPress = (letter) => {
|
||||
letter = letter.toLowerCase()
|
||||
window.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: letter,
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(event) => {
|
||||
if (gameDisabled) return
|
||||
|
||||
const letter = event.key.toUpperCase()
|
||||
|
||||
if (letters.includes(letter)) {
|
||||
addLetter(letter)
|
||||
} else if (letter === 'ENTER') {
|
||||
onEnterPress()
|
||||
event.preventDefault()
|
||||
} else if (letter === 'BACKSPACE') {
|
||||
onDeletePress()
|
||||
}
|
||||
},
|
||||
[addLetter, onEnterPress, onDeletePress, gameDisabled]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
|
||||
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||
}, [handleKeyDown])
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-col items-center mb-3 select-none">
|
||||
{keyboardLetters.map((row, idx) => (
|
||||
<div key={idx} className="w-full flex justify-center my-[5px]">
|
||||
{idx === 2 && (
|
||||
<button
|
||||
onClick={onEnterPress}
|
||||
className="h-10 xxs:h-14 w-12 px-1 text-xs font-medium mx-[3.5px] rounded nm-flat-background-sm dark:nm-flat-background-dark-sm text-primary dark:text-primary-dark"
|
||||
>
|
||||
ENTER
|
||||
</button>
|
||||
)}
|
||||
{row.map((letter) => (
|
||||
<button
|
||||
onClick={() => onKeyButtonPress(letter)}
|
||||
key={letter}
|
||||
className="h-10 xxs:h-14 w-[2rem] sm:w-10 mx-[3.5px] text-sm font-medium rounded-[4px] nm-flat-background-sm dark:nm-flat-background-dark-sm"
|
||||
>
|
||||
<div
|
||||
className={`h-full w-full rounded-[3px] flex items-center justify-center ${getKeyStyle(
|
||||
letter
|
||||
)}`}
|
||||
>
|
||||
{letter}
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
{idx === 2 && (
|
||||
<button
|
||||
onClick={onDeletePress}
|
||||
className="h-10 xxs:h-14 w-12 flex items-center justify-center nm-flat-background-sm dark:nm-flat-background-dark-sm text-primary dark:text-primary-dark mx-[3.5px] text-sm rounded"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={1.5}
|
||||
d="M12 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2M3 12l6.414 6.414a2 2 0 001.414.586H19a2 2 0 002-2V7a2 2 0 00-2-2h-8.172a2 2 0 00-1.414.586L3 12z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export { Keyboard }
|
||||
54
src/components/SettingsModal.js
Normal file
@@ -0,0 +1,54 @@
|
||||
import { ReactComponent as Close } from '../data/Close.svg'
|
||||
import Modal from 'react-modal'
|
||||
import { Switch } from '@headlessui/react'
|
||||
|
||||
Modal.setAppElement('#root')
|
||||
|
||||
export const SettingsModal = ({ isOpen, handleClose, styles, darkMode, toggleDarkMode }) => {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onRequestClose={handleClose}
|
||||
style={styles}
|
||||
contentLabel="Settings Modal"
|
||||
>
|
||||
<div className={`h-full ${darkMode ? 'dark' : ''}`}>
|
||||
<div
|
||||
className={`h-full flex flex-col items-center justify-center max-w-[390px] mx-auto pt-9 text-primary dark:text-primary-dark `}
|
||||
>
|
||||
<h1 className="text-center mb-4 sm:text-3xl text-2xl">Settings</h1>
|
||||
<div className="flex-1 w-full border-b border-slate-400 mb-4">
|
||||
<button
|
||||
className="absolute top-4 right-4 rounded-full nm-flat-background dark:nm-flat-background-dark text-primary dark:text-primary-dark p-1 w-6 h-6 sm:p-2 sm:h-8 sm:w-8"
|
||||
onClick={handleClose}
|
||||
>
|
||||
<Close />
|
||||
</button>
|
||||
|
||||
<Switch.Group as="div" className="flex items-center">
|
||||
<Switch
|
||||
checked={darkMode}
|
||||
onChange={toggleDarkMode}
|
||||
className={`${
|
||||
darkMode
|
||||
? 'nm-inset-yellow-500 border-background-dark'
|
||||
: 'nm-inset-background border-transparent'
|
||||
} relative inline-flex flex-shrink-0 h-8 w-14 p-1 border-2 rounded-full cursor-pointer transition ease-in-out duration-200`}
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className={`${
|
||||
darkMode ? 'translate-x-[1.55rem]' : 'translate-x-0'
|
||||
} absolute pointer-events-none inline-block top-1/2 -translate-y-1/2 h-5 w-5 shadow rounded-full bg-white transform ring-0 transition ease-in-out duration-200`}
|
||||
/>
|
||||
</Switch>
|
||||
<Switch.Label as="span" className="ml-3 cursor-pointer">
|
||||
Dark Mode
|
||||
</Switch.Label>
|
||||
</Switch.Group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
41
src/constants.js
Normal file
@@ -0,0 +1,41 @@
|
||||
export const keyboardLetters = [
|
||||
['Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P'],
|
||||
['A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L'],
|
||||
['Z', 'X', 'C', 'V', 'B', 'N', 'M'],
|
||||
]
|
||||
|
||||
export const letters = [
|
||||
'A',
|
||||
'B',
|
||||
'C',
|
||||
'D',
|
||||
'E',
|
||||
'F',
|
||||
'G',
|
||||
'H',
|
||||
'I',
|
||||
'J',
|
||||
'K',
|
||||
'L',
|
||||
'M',
|
||||
'N',
|
||||
'O',
|
||||
'P',
|
||||
'Q',
|
||||
'R',
|
||||
'S',
|
||||
'T',
|
||||
'U',
|
||||
'V',
|
||||
'W',
|
||||
'X',
|
||||
'Y',
|
||||
'Z',
|
||||
]
|
||||
|
||||
export const status = {
|
||||
green: 'green',
|
||||
yellow: 'yellow',
|
||||
gray: 'gray',
|
||||
unguessed: 'unguessed',
|
||||
}
|
||||
3
src/data/Close.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="100%" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 229 B |
BIN
src/data/Cross.png
Normal file
|
After Width: | Height: | Size: 378 KiB |
BIN
src/data/Forbidden.png
Normal file
|
After Width: | Height: | Size: 479 KiB |
1
src/data/Github.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg role="img" height="100%" width="100%" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||
|
After Width: | Height: | Size: 870 B |
3
src/data/Info.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
4
src/data/Settings.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 792 B |
BIN
src/data/Success.png
Normal file
|
After Width: | Height: | Size: 269 KiB |
2085
src/data/answers.js
Normal file
9335
src/data/words.js
Normal file
23
src/hooks/useLocalStorage.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
export const useLocalStorage = (key, initialValue) => {
|
||||
const [storedValue, setStoredValue] = useState(() => {
|
||||
try {
|
||||
const item = window.localStorage.getItem(key)
|
||||
return item ? JSON.parse(item) : initialValue
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return initialValue
|
||||
}
|
||||
})
|
||||
const setValue = (value) => {
|
||||
try {
|
||||
const valueToStore = value instanceof Function ? value(storedValue) : value
|
||||
setStoredValue(valueToStore)
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
return [storedValue, setValue]
|
||||
}
|
||||
48
src/index.css
Normal file
@@ -0,0 +1,48 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@font-face {
|
||||
font-family: 'Ranade';
|
||||
src: url('/public/assets/Ranade-Variable.ttf') format('ttf');
|
||||
}
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Righteous&display=swap');
|
||||
|
||||
*,
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Ranade', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',
|
||||
'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
box-sizing: border-box;
|
||||
/* background-color: hsl(231, 16%, 92%); */
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
|
||||
}
|
||||
#root,
|
||||
body,
|
||||
html {
|
||||
/* Prevent overscroll on chrome (not supported on safari) */
|
||||
overscroll-behavior-y: none;
|
||||
}
|
||||
.h-fill {
|
||||
height: 100vh;
|
||||
max-height: -webkit-fill-available;
|
||||
}
|
||||
|
||||
.font-righteous {
|
||||
font-family: 'Righteous', cursive;
|
||||
}
|
||||
|
||||
.font-og {
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.flex-force-center {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
6
src/index.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
5
src/setupTests.js
Normal file
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom'
|
||||
31
tailwind.config.js
Normal file
@@ -0,0 +1,31 @@
|
||||
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: ['./src/**/*.{js,jsx}'],
|
||||
theme: {
|
||||
screens: {
|
||||
xxs: '321px',
|
||||
xs: '475px',
|
||||
...defaultTheme.screens,
|
||||
},
|
||||
extend: {
|
||||
colors: {
|
||||
background: 'hsl(231, 16%, 92%)',
|
||||
primary: 'hsl(231, 16%, 25%)',
|
||||
'background-dark': 'hsl(231, 16%, 25%)',
|
||||
'primary-dark': 'hsl(231, 16%, 92%)',
|
||||
'n-green': 'hsl(110, 33%, 50%)',
|
||||
'n-gray': 'hsl(231, 16%, 45%)',
|
||||
},
|
||||
},
|
||||
neumorphismSize: {
|
||||
xs: '0.05em',
|
||||
sm: '0.1em',
|
||||
default: '0.2em',
|
||||
lg: '0.4em',
|
||||
xl: '0.8em',
|
||||
},
|
||||
},
|
||||
plugins: [require('tailwindcss-neumorphism')],
|
||||
}
|
||||
28
wordler.py
Normal file
@@ -0,0 +1,28 @@
|
||||
import os
|
||||
import selenium
|
||||
from time import sleep
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.chrome.options import Options
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
|
||||
path = "/home/devang/Downloads/Wordle/chromedriver"
|
||||
driver = webdriver.Chrome(path, options=Options().add_argument('ignore-certificate-errors'))
|
||||
|
||||
driver.get("https://www.powerlanguage.co.uk/wordle/")
|
||||
|
||||
close_button = driver.execute_script("return document.querySelector('game-app').shadowRoot.querySelector('game-theme-manager').querySelector('#game').querySelector('game-modal').shadowRoot.querySelector('.close-icon')")
|
||||
close_button.click()
|
||||
|
||||
for i in range(3):
|
||||
actions = ActionChains(driver)
|
||||
actions.send_keys('hello', Keys.RETURN).perform()
|
||||
sleep(3)
|
||||
actions.send_keys('world', Keys.RETURN).perform()
|
||||
sleep(3)
|
||||
|
||||
answer = driver.execute_script("return document.querySelector('game-app').shadowRoot.querySelector('game-theme-manager').querySelector('#game').querySelector('#game-toaster')")
|
||||
print(answer.text)
|
||||
|
||||
driver.quit()
|
||||