react-scrabble
Version:
The scrabble board game written in React
208 lines (185 loc) • 6.38 kB
JavaScript
import React, {Component, PropTypes} from 'react'
import {GridDiv, BoardLayout, RightPane, CounterDiv} from '../styles/Grid'
import Tile from './Tile'
import update from 'immutability-helper';
import Score from './Score'
import Hint from './Hint'
import FinalPopup from './FinalPopup'
import Generator from '../helpers/generator'
import {calculteScore} from '../helpers/scorer'
import ReactCountdownClock from 'react-countdown-clock'
export default class Grid extends Component {
state = {
showFinalPopup: false,
curSelectionPos: [],
curSelectionLetters: [],
completedSelectionPos: [],
completedWords: [],
hintsTaken: 0,
wordFound: false
}
sameRow = (selection, row) => {
return selection.every((s) => s.row === row)
}
formattedCurrentTime = () => {
const date = new Date()
return new Date(date.getTime() - (date.getTimezoneOffset() * 60000)).toJSON();
}
componentWillMount() {
const {matrix, hintDirection} = Generator.generateGrid(this.words);
this.setState({grid: matrix, hintDirection: hintDirection, startedAt: this.formattedCurrentTime()})
}
sameCol = (selection, col) => {
return selection.every((s) => s.col === col)
}
areAdjacentTiles = (selection) => {
for (let i = 0; i < selection.length - 1; i++) {
if (Math.abs(selection[i] - selection[i + 1]) !== 1) {
return false
}
}
return true
}
get words() {
return this.props.data.map((w) => w.word)
}
adjacentColumn = (selection, newCol) => {
const selectedCols = selection.map((s) => s.col)
selectedCols.push(newCol)
return this.areAdjacentTiles(selectedCols)
}
adjacentRow = (selection, newRow) => {
const selectedRows = selection.map((s) => s.row)
selectedRows.push(newRow)
return this.areAdjacentTiles(selectedRows)
}
requestedHint = () => {
this.setState({hintsTaken: this.state.hintsTaken + 1})
}
hasCompleted = () => {
return this.state.completedWords.length === this.words.length
}
checkIfPlayerWon = () => {
if (this.hasCompleted()) {
this.setState({showFinalPopup: true})
}
}
timeup = () => {
this.props.updateStats(this.stats())
this.setState({showFinalPopup: true})
}
stats = () => {
const {completedWords, hintsTaken, startedAt} = this.state
const stats = {
completed: this.hasCompleted(),
score: completedWords.length,
started_at: startedAt,
total_score: this.words.length,
ended_at: this.formattedCurrentTime(),
metadata: {hints_taken: hintsTaken}
}
return stats
}
checkIfWordCompleted = () => {
const {curSelectionPos, curSelectionLetters} = this.state
const currentWord = curSelectionLetters.join('')
if (this.words.some((w) => w === currentWord)) {
this.setState(update(this.state,
{
wordFound: {$set: true},
curSelectionPos: {$set: []},
curSelectionLetters: {$set: []},
completedSelectionPos: {
$push: curSelectionPos
},
completedWords: {
$push: [currentWord]
}
}), () => {
this.props.updateStats(this.stats())
this.checkIfPlayerWon()
})
} else {
this.setState({wordFound: false})
}
}
handleSelect = (row, col, letter) => {
const {curSelectionPos} = this.state
const firstSelection = curSelectionPos.length === 0
const adjacentTileSelected = (this.sameRow(curSelectionPos, row) && this.adjacentColumn(curSelectionPos, col)) ||
(this.sameCol(curSelectionPos, col) && this.adjacentRow(curSelectionPos, row))
if (firstSelection || adjacentTileSelected) {
this.setState(update(this.state, {
curSelectionPos: {
$push: [{...{row, col}}]
},
curSelectionLetters: {
$push: [letter]
}
}), this.checkIfWordCompleted)
} else {
this.setState({curSelectionPos: [{row, col}], curSelectionLetters: [letter]})
}
}
render() {
const {completedWords, hintDirection, grid, showFinalPopup, curSelectionPos, completedSelectionPos, wordFound} = this.state
const [lastElement] = completedSelectionPos.slice(-1)
const [lastFoundWord] = completedWords.slice(-1)
return <BoardLayout>
<GridDiv>
{
grid && grid.map((row, i) => {
return row.map((letter, j) => {
const id = `${i}${j}`
const selected = curSelectionPos.some((sel) => sel.row === i && sel.col === j)
const completed = completedSelectionPos.some((sel) => sel.row === i && sel.col === j)
const victoryTile = wordFound && lastElement && lastElement.row === i && lastElement.col === j
const victoryScore = victoryTile ? calculteScore(lastFoundWord) : 0
return <Tile id={id}
key={id}
letter={letter}
selected={selected}
completed={completed}
row={i}
col={j}
notifySelect={this.handleSelect}
victoryTile={victoryTile}
victoryScore={victoryScore}
/>
})
})
}
</GridDiv>
<RightPane>
<Score score={completedWords.length} total={this.words.length}/>
<FinalPopup show={showFinalPopup} completed={this.hasCompleted()} onExit={this.props.onExit}/>
{
!showFinalPopup &&
<CounterDiv>
<ReactCountdownClock seconds={this.props.duration}
color="#F0F4C3"
alpha={0.9}
size={85}
weight={10}
onComplete={this.timeup}/>
</CounterDiv>
}
</RightPane>
<RightPane>
<Hint allWords={this.props.data}
hintDirection={hintDirection}
completedWords={completedWords}
opened={this.requestedHint}/>
</RightPane>
</BoardLayout>
}
}
Grid.PropTypes = {
data: PropTypes.array.isRequired,
updateStats: PropTypes.func,
onExit: PropTypes.func,
duration: PropTypes.number
}
Grid.defaultProps = {
duration: 600
};