Skip to content

Instantly share code, notes, and snippets.

@imkrish
Last active September 23, 2018 09:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imkrish/9e1e424d091eae8280abe84b50e85b62 to your computer and use it in GitHub Desktop.
Save imkrish/9e1e424d091eae8280abe84b50e85b62 to your computer and use it in GitHub Desktop.
import { Observable, BehaviorSubject, Subject } from 'rxjs'
import { withLatestFrom } from 'rxjs/operators'
import { deepEqual } from 'assert'
interface IGameState {
status: GameStatus
selectedLetters: string[]
lifeLeft: number
secretWordLength: number
knownSecretWord: string
}
enum GameStatus {
IN_PROGRESS = 'in-progress',
WON = 'won',
LOSE = 'lose',
}
function reactiveHangman(secretWord: string, letter$: Observable<string>) {
const initGameState = getInitGameState(secretWord, 7)
const gameState$ = new BehaviorSubject(initGameState)
const letterWithGameState$ = letter$.pipe(withLatestFrom(gameState$))
letterWithGameState$.subscribe(([letter, gameState]) => {
const nextGameState = getNextGameState(gameState, secretWord, letter)
gameState$.next(nextGameState)
const { status } = nextGameState
const winOrLose = status === GameStatus.WON || status === GameStatus.LOSE
if (winOrLose) {
gameState$.complete()
}
})
return gameState$.asObservable()
}
function getInitGameState(secretWord: string, lives: number) {
const secretWordLength = secretWord.length
const initGameState: IGameState = {
status: GameStatus.IN_PROGRESS,
selectedLetters: [],
lifeLeft: lives,
secretWordLength: secretWord.length,
knownSecretWord: '_'.repeat(secretWordLength),
}
return initGameState
}
function fillCorrectLetters(word: string, filledWord: string, letter: string) {
const newFilledWord = [...word].reduce(
(filedWord, char, idx) => {
if (char === letter) {
filedWord[idx] = char
}
return filedWord
},
[...filledWord]
)
return newFilledWord.join('')
}
function isWin(knowSecretWord: string) {
return [...knowSecretWord].every(char => char !== '_')
}
function getNextGameState(
gameState: IGameState,
secretWord: string,
letter: string
): IGameState {
const {
selectedLetters,
lifeLeft,
secretWordLength,
knownSecretWord,
} = gameState
const newSelectedLetters = selectedLetters.concat(letter)
const correctLetter = secretWord.includes(letter)
const newLifeLeft = correctLetter ? lifeLeft : lifeLeft - 1
const newKnownSecretWord = fillCorrectLetters(
secretWord,
knownSecretWord,
letter
)
const win = isWin(newKnownSecretWord)
const lose = newLifeLeft === 0
const newStatus = win
? GameStatus.WON
: lose
? GameStatus.LOSE
: GameStatus.IN_PROGRESS
return {
status: newStatus,
selectedLetters: newSelectedLetters,
lifeLeft: newLifeLeft,
secretWordLength,
knownSecretWord: newKnownSecretWord,
}
}
// Test
function test(secretWord: string, letters: string, expected: IGameState[]) {
const letterSubject = new Subject<string>()
const letter$ = letterSubject.asObservable()
const gameState$ = reactiveHangman(secretWord, letter$)
const actual: IGameState[] = []
gameState$.subscribe(
gameState => actual.push(gameState),
() => {},
() => {
deepEqual(actual, expected)
}
)
;[...letters].forEach(letter => letterSubject.next(letter))
}
// Test case 1
const expected1: IGameState[] = [
{
status: GameStatus.IN_PROGRESS,
selectedLetters: [],
lifeLeft: 7,
secretWordLength: 7,
knownSecretWord: '_______',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b'],
lifeLeft: 7,
secretWordLength: 7,
knownSecretWord: 'b__b___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'b__b___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'i'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'bi_b___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'i', 'g'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'bigb___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'i', 'g', 'a'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'bigb_a_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'i', 'g', 'a', 'e'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'bigbea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'i', 'g', 'a', 'e', 'y'],
lifeLeft: 5,
secretWordLength: 7,
knownSecretWord: 'bigbea_',
},
{
status: GameStatus.WON,
selectedLetters: ['b', 'o', 'i', 'g', 'a', 'e', 'y', 'r'],
lifeLeft: 5,
secretWordLength: 7,
knownSecretWord: 'bigbear',
},
]
test('bigbear', 'boigaeyr', expected1)
// Test Case 2
const expected2: IGameState[] = [
{
status: GameStatus.IN_PROGRESS,
selectedLetters: [],
lifeLeft: 7,
secretWordLength: 7,
knownSecretWord: '_______',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b'],
lifeLeft: 7,
secretWordLength: 7,
knownSecretWord: 'b__b___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'b__b___',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'b__b_a_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e'],
lifeLeft: 6,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e', 'n'],
lifeLeft: 5,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e', 'n', 'u'],
lifeLeft: 4,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e', 'n', 'u', 't'],
lifeLeft: 3,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e', 'n', 'u', 't', 'z'],
lifeLeft: 2,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.IN_PROGRESS,
selectedLetters: ['b', 'o', 'a', 'e', 'n', 'u', 't', 'z', 'x'],
lifeLeft: 1,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
{
status: GameStatus.LOSE,
selectedLetters: ['b', 'o', 'a', 'e', 'n', 'u', 't', 'z', 'x', 'v'],
lifeLeft: 0,
secretWordLength: 7,
knownSecretWord: 'b__bea_',
},
]
test('bigbear', 'boaenutzxv', expected2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment