Scrabble in React

Jack Overby
4 min readDec 27, 2020

The other day, I wrote a few functions to handle the word search functions of a Scrabble rack: given seven letters and a dictionary of valid words, said functions collect all combinations of letters (2–7 letters) that form valid words. However, there is a lot more to Scrabble than just this: you need 100 tiles, a board, two players… the ability to lay tiles on the board, check whether the word/s formed is/are valid and then calculate the total score of your play… and all this has to look good enough for people to want to play it! Ahh, the joys of game design… gamers are tremendously loyal to games they love, but it’s the job of the programmer to gain their love in the first place. That is no easy task! There’s no way I’ll be able to code an effective Scrabble replica by myself over the course of one article/afternoon, but let’s see what we can do:

Components

Here are some of the classes we might want to represent in a Scrabble game. Of course, being advanced users of React, class components are a thing of the past, so we’ll represent them as functional components:

  1. Game- this includes letters, two players, a score and a winner/loser
  2. Player- a player has a score and a rack, with up to 7 letters
  3. Board- gotta give the players some place to put their tiles!
  4. Cell- a board is made up of 225 cells (15x15), which can take 5 different forms: double/triple letter/word, or normal, i.e. blank
  5. Tile- a tile has a letter and a value and is located in one of three places: the board, the bag or a player’s rack
  6. Word- Scrabble is nothing without words… all 200K of them!

Words

For words, we’ll use a file from the Collins Zyzzyva Github page. A link to the file itself can be found here. The words look something like this:

AA a rough cindery lava [n -S]
AB an abdominal muscle [n -S]
AD advertisement [n -S]
AE one [adj]
AG pertaining to agriculture [adj]
AH to make a sound of delight [v -ED, -ING, -S]
AI a three-toed sloth [n -S]
AL an East Indian tree [n -S]
AM <be=v> [v]
AN one; the indefinite article [adj]
AR the letter R [n -S]
AS a Norse god living in Asgard [n AESIR] / a ridge left by a glacier [n ASAR] / an old Roman coin [n ASSES]

We don’t need the definitions for the purposes of this article, so let’s parse the file by taking each row, splitting on the space character and taking the first member:

import wordString from './wordString';const scrabbleWords = wordString.split("\n").map(row=>row.split(" ")[0]);
export default scrabbleWords;

Here’s the getDictObj() function from our helper folder:

import scrabbleWords from './scrabbleWords';export default function getDictObj() {
const dictObj = {};
for (const word of scrabbleWords) {
const sortedWord=word.split('').sort((a,b)=>a>b?1:-1).join('');
if (dictObj[sortedWord]) dictObj[sortedWord].push(word);
else dictObj[sortedWord] = [word];
}
return dictObj;
}

Finally, here’s our Word class:

import letterData from './letterData';export default class Word {
constructor(word) {
this.word=word;
}
wordSorted() {
return this.word.split('').sort((a,b)=>a>b?1:-1);
}
wordValue() {
return this.wordSorted().reduce((sum, curr)=>sum+letterData[curr].val, 0);
}
}

Cell

Here’s our Tile class, which takes in an Object with ‘letter’ and ‘value’ keys and puts out a square that displays the letter and value (e.g. A-1, Z-10):

import { useEffect, useState } from 'react';
import letterData from '../letterData';
export default function Tile(props) {
const [letter, setLetter] = useState(null);
const [value, setValue] = useState(0);
useEffect(()=>{
setLetter(props.letter);
setValue(props.value);
})
return (
<div style={{
backgroundColor: 'lightbrown',
width: '40px',
height: '40px'
}}>
{{letter} ({value})}
</div>
)
}

Cell

First, we create an Object that holds all the possible values for Cells:

const cellBonusMap={
0: {
"value": "",
"color": "white"
},
1: {
"value": "DLS",
"color": "lightblue"
},
2: {
"value": "TLS",
"color": "purple"
},
3: {
"value": "DWS",
"color": "pink"
},
4: {
"value": "TWS",
"color": "red"
}
};
export default cellBonusMap;

So our Cell object takes in props containing a “letter” and a “value”:

import {useState, useEffect} from 'react';export default function Cell(props) {
const [letter, setLetter] = useState('');
const [value, setValue] = useState(0);
useEffect(()=>{setValue(props.value)}, [props.value]) return (<div style={{ backgroundColor: props.color, width: '40px', height: '40px', textAlign: 'center', verticalAlign: 'middle' }}>{letter || value}</div>);
}

Board

For Board, we first create a helper function to generate board coordinates:

import Cell from '../components/Cell';
import cellBonusMap from './cellBonusMap';
const boardCoords=[
[4,0,0,1,0,0,0,4,0,0,0,1,0,0,4],
[0,3,0,0,0,2,0,0,0,2,0,0,0,3,0],
[1,0,0,3,0,0,0,1,0,0,0,3,0,0,1],
[0,0,3,0,0,0,1,0,1,0,0,0,3,0,0],
[0,0,0,0,3,0,0,0,0,0,3,0,0,0,0],
[0,2,0,0,0,2,0,0,0,2,0,0,0,2,0],
[0,0,1,0,0,0,1,0,1,0,0,0,1,0,0],
[4,0,0,1,0,0,0,3,0,0,0,1,0,0,4],
[0,0,1,0,0,0,1,0,1,0,0,0,1,0,0],
[0,2,0,0,0,2,0,0,0,2,0,0,0,2,0],
[0,0,0,0,3,0,0,0,0,0,3,0,0,0,0],
[1,0,0,3,0,0,0,1,0,0,0,3,0,0,1],
[0,0,3,0,0,0,1,0,1,0,0,0,3,0,0],
[0,3,0,0,0,2,0,0,0,2,0,0,0,3,0],
[4,0,0,1,0,0,0,4,0,0,0,1,0,0,4],
]
export default function createBoard() {
return boardCoords.map(row=>row.map(col=>Cell(cellBonusMap[col])));
}

Basically, we take the board coordinates and, via the map function, return an Array of Arrays of Cells.

export default function Board(props) {
return (
<div>
<table>
<tbody>
{props.board.map(row=>
<tr>
{row.map(col=><td>{col}</td>)}
</tr>
)}
</tbody>
</table>
</div>
)
}

Here’s what this yields:

Yes, I know, the middle Cell should be a star…

Cool! Looks kinda like a Scrabble board! With a bit more of Cell styling, we can center those texts, but not a bad start.

Conclusion

I’m going to pause for now. There’s still a long, long, long way to go… but at the very least, we have some functional components and a board! Our journey of 1,000 miles has taken its first steps. Thanks for the inspirational quote, Chairman Mao!

--

--