Skip to content

Commit

Permalink
add gamespace component (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
rccsousa authored Nov 6, 2024
1 parent 2d34a42 commit 67cef0c
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 13 deletions.
24 changes: 19 additions & 5 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import {
getAttempts,
getAlphabet,
getWDTBalance,
getPlayerGuesses,
getHitmap,
canPlay,
useFaucet,
initAttempts,
approveGame,
tryGuess
} from './hooks/WordleHooks'
import {Alphabet} from './components/Alphabet'
import {Alphabet} from './components/Alphabet/Alphabet'
import { GameSpace } from './components/GameSpace/GameSpace'


function App() {
Expand All @@ -27,23 +30,33 @@ function App() {

// Game variables
const [guess, setGuess] = useState('')
const [playerGuesses, setPlayerGuesses] = useState([])
const [playerHitmap, setPlayerHitMap] = useState(undefined)

// Gets the current account's WDT balance, eligibility to play, player attempts, and alphabet.
// Gets the current account's WDT balance, eligibility to play, player attempts, guesses and alphabet.
useEffect(() => {
if (currentAcc) {
getWDTBalance(currentAcc).then(setWdtBalance)
canPlay(currentAcc).then(setEligibility)
getAttempts(currentAcc).then(setPlayerAttempts)
getAlphabet(currentAcc).then(setAlphabet)
getPlayerGuesses(currentAcc).then(setPlayerGuesses)
getHitmap(currentAcc).then(setPlayerHitMap)
}
}, [currentAcc])
}, [currentAcc, playerGuesses, playerHitmap])

// handle the guess input
const handleGuessChange = (e) => setGuess(e.target.value)

// handle new guess
const handleNewGuess = (account, guess) => {
tryGuess(account, guess)
}

return (
<>
<div>

<h4>Here are some addresses:</h4>
<select onChange={(e) => setCurrentAcc(local_accounts.find(a => a.address === e.target.value))}>
<option value="">Select an address</option>
Expand All @@ -67,8 +80,10 @@ function App() {
<h1>Prototype View</h1>
<h4>Attempts left: {playerAttempts ?? '???'}</h4>
<input value={guess} onChange={handleGuessChange} placeholder="guess"/>
<button onClick={() => tryGuess(currentAcc, guess)}>Submit</button>
<button onClick={() => handleNewGuess(currentAcc, guess)}>Submit</button>
</div>)}

<GameSpace playerGuesses={playerGuesses} hitmap={playerHitmap}/>
<Alphabet playerAlphabet={alphabet}/>
</div>
</div>
Expand All @@ -77,4 +92,3 @@ function App() {
}

export default App

67 changes: 66 additions & 1 deletion frontend/src/abis/WordleABI.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[
[
{
"type": "constructor",
"inputs": [
Expand Down Expand Up @@ -63,6 +63,40 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "GUESSES",
"inputs": [
{
"name": "",
"type": "address",
"internalType": "address"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
},
{
"name": "",
"type": "uint256",
"internalType": "uint256"
}
],
"outputs": [
{
"name": "char",
"type": "string",
"internalType": "string"
},
{
"name": "state",
"type": "uint256",
"internalType": "uint256"
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "HIDDEN_WORD",
Expand Down Expand Up @@ -218,6 +252,37 @@
],
"stateMutability": "view"
},
{
"type": "function",
"name": "getPlayerGuesses",
"inputs": [
{
"name": "player",
"type": "address",
"internalType": "address"
}
],
"outputs": [
{
"name": "",
"type": "tuple[][]",
"internalType": "struct StructTypes.CharState[][]",
"components": [
{
"name": "char",
"type": "string",
"internalType": "string"
},
{
"name": "state",
"type": "uint256",
"internalType": "uint256"
}
]
}
],
"stateMutability": "view"
},
{
"type": "function",
"name": "initAttempts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import React from 'react'
import './alphabet.css'

export function Alphabet({ playerAlphabet }) {
Expand Down Expand Up @@ -28,6 +29,7 @@ export function Alphabet({ playerAlphabet }) {
<div className="alphabet">
{qwertyLayout.map((char, index) => (
<p
className={"letter-box"}
key={index}
style={{ backgroundColor: alphabetStates && colorMap[alphabetStates[index]] || 'var(--gray)' }}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
max-width: 40%;
}

.alphabet p {
.letter-box {
display: flex;
align-items: center;
justify-content: center;
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/components/GameSpace/GameSpace.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.guess {
display: flex;
margin: auto;
justify-content: center;
gap: 5px;
margin: 10px auto;
}

.empty-letter-box {
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--gray);
width: 30px;
height: 30px;
margin: 0;
}

.guess-letter-box {
display: flex;
align-items: center;
justify-content: center;
border: 2px solid var(--gray);
width: 30px;
height: 30px;
margin: 0;
}

56 changes: 56 additions & 0 deletions frontend/src/components/GameSpace/GameSpace.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import React from "react";
import "./GameSpace.css";

function EmptyGuess() {
return (
<>
{Array(5)
.fill("\u00A0")
.map((_, i) => (
<p className="empty-letter-box" key={i}>
&nbsp;
</p>
))}
</>
);
}

function PlayerGuess({ playerGuess }) {
const colorMap = {
0: "var(--gray)",
1: "var(--yellow)",
2: "var(--green)",
3: "var(--dark-gray)",
};

return (
<>
{playerGuess.map((char, i) => (
<p
className="guess-letter-box"
style={{ backgroundColor: colorMap[char.state] || "var(--gray)" }}
key={i}
>
{char.char.toUpperCase()}
</p>
))}
</>
);
}

export function GameSpace({ playerGuesses }) {
return (
<div>
{[...Array(6)].map((_, i) => (
<div className="guess" key={i}>
{/* Checks if the guess exists and renders it or an empty cell */}
{playerGuesses[i] ? (
<PlayerGuess playerGuess={playerGuesses[i]} />
) : (
<EmptyGuess />
)}
</div>
))}
</div>
);
}
18 changes: 13 additions & 5 deletions frontend/src/hooks/WordleHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,39 @@ import ERC20ABI from '../abis/ERC20ABI.json'
// Get number of remaining attempts a player still has
export async function getAttempts(account) {
const attempts = await wordle.read.getPlayerAttempts([account.address])
console.log(typeof attempts)
return attempts.toString()
return attempts.toString()
}

// Get the current state of the player's alphabet hitmap
export async function getAlphabet(account) {
return await wordle.read.getAlphabet([account.address])
}

// Get the current player hitmap state
export async function getHitmap(account) {
return await wordle.read.getHiddenWord([account.address])
}

// Gets the WDT balance of a player
export async function getWDTBalance(account) {
const res = await wdt.read.balanceOf([account.address])
return formatEther(res)
}

// Gets player used guesses array
export async function getPlayerGuesses(account) {
const res = await wordle.read.getPlayerGuesses([account.address])
return res
}


// Checks if a player is eligible to play
export async function canPlay(account) {
return await wordle.read.canPlay([account.address])
}

// Give a player some WDT
export async function useFaucet(account) {
console.log(account)
console.log(`contract address is ${wordle.address}`)
const { request } = await client.simulateContract({
account,
address: wordle.address,
Expand All @@ -52,7 +61,6 @@ export async function approveGame(account) {
})
await client.writeContract(request)
const allowance = await wdt.read.allowance([account.address, wordle.address])
console.log(`${account.address} allowed ${wordle.address} to spend ${allowance}`)
}

// Initialize the player's attempts
Expand Down
10 changes: 10 additions & 0 deletions src/Wordle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ contract Wordle {
mapping(address => uint256) public lastAttemptTime;
mapping(address => StructTypes.CharState[]) public HIDDEN_WORD_HITMAP;
mapping(address => StructTypes.CharState[]) public ALPHABET;
mapping(address => StructTypes.CharState[][]) public GUESSES;
mapping(address => uint256) public ATTEMPTS;

// declare hidden word variable
Expand Down Expand Up @@ -122,6 +123,11 @@ contract Wordle {
return ATTEMPTS[player];
}

// gets the player used guesses
function getPlayerGuesses(address player) public view returns (StructTypes.CharState[][] memory) {
return GUESSES[player];
}

/*
Processes the guess, comparing it to the hidden word and assessing
and updating the hitmap and alphabet accordingly.
Expand Down Expand Up @@ -169,6 +175,7 @@ contract Wordle {
*/
if (StringUtils.areEqual(guessHitmap[i].char, HIDDEN_WORD_HITMAP[player][i].char)) {
HIDDEN_WORD_HITMAP[player][i].state = 2; // Final state for correct position
guessHitmap[i].state = 2;
ALPHABET[player][alphaIdx].state = 2; // Final state in alphabet
continue;
}
Expand All @@ -184,8 +191,10 @@ contract Wordle {
HIDDEN_WORD_HITMAP[player][occurrences[j]].state = 1; // Intermediate state for existence
}
ALPHABET[player][alphaIdx].state = 1; // Intermediate state in alphabet
guessHitmap[i].state = 1;
} else {
// The character does not exist in the hidden word; mark it as discarded.
guessHitmap[i].state = 3;
ALPHABET[player][alphaIdx].state = 3; // Discarded state in alphabet
}
}
Expand All @@ -194,6 +203,7 @@ contract Wordle {
// emits a message if the player wins
if (!StringUtils.isHitmapComplete(HIDDEN_WORD_HITMAP[player])) {
emit RemainingAttempts(ATTEMPTS[player], "Attempts left.");
GUESSES[player].push(guessHitmap);
return false;
}

Expand Down
8 changes: 7 additions & 1 deletion test/Wordle.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ contract WordleTest is Test {
}

// test if player balance checking through can play
function test_canPlay() public {
function test_canPlay() public view {
assertTrue(wordle.canPlay(player1));
assertFalse(wordle.canPlay(player2));
}
Expand Down Expand Up @@ -136,6 +136,12 @@ contract WordleTest is Test {
uint256 attempts = wordle.getPlayerAttempts(player1);
assertEq(attempts, 5);

// test guess generation
assertEq(wordle.getPlayerGuesses(player1).length, 1);
StructTypes.CharState[][] memory guesses = wordle.getPlayerGuesses(player1);
assertEq(guesses[0][0].char, "o");
assertEq(guesses[0][0].state, 1);

// test hitmap updates
StructTypes.CharState[] memory hitmap = wordle.getHiddenWord(player1);
StructTypes.CharState[] memory alphabet = wordle.getAlphabet(player1);
Expand Down

0 comments on commit 67cef0c

Please sign in to comment.