[Javascript] Broadcaster + Operator + Listener pattern -- 17. Building a Word Matching Game
It's common for a user to enter values that you want to check against your pre-defined values. Let's make a demo of a word game to demonstrate one approach to solving that use case.
Try consolidating map(hangmanLogic) even further into a stand-alone hangmanLogic operator so that there's no mapping, just the hangmanLogic operator
import { addListener, done, forOf } from "./broadcasters"; import { targetValue } from "./operators"; const log = console.log; let inputInput = addListener("#input", "input"); let inputValue = targetValue(inputInput); let word = forOf("honeycomb"); inputValue((value) => { let result = ""; word((letter) => { if (letter === done) { console.log(result); return } if (value.includes(letter)) { result += letter; } else { result += "*"; } }); }); /* h**e***** honeycomb */
We have outside 'inputValue' to provde value and inside 'word' providing innerValue.
1. Model the behavior for tow broadcasters: "inputValue" & "word".
The logic is "inputValue", inside, it doesn't mapping to a transform function but to another broadcaster: word.
So create a operator called mapBroadcaster:
let mapBroadcaster = createBroadcaster => broadcaster => listener => { broadcaster(value => { let newBroadcaster = createBroadcaster(value) newBroadcaster(listener) }) } mapBroadcaster(value => word)(inputValue)(log)
2. Apply logic to inner broadcaster 'word':
logic we want to apply:
if (value.includes(letter)) { result += letter; } else { result += "*"; }
then:
let mapBroadcaster = (createBroadcaster) => (broadcaster) => (listener) => { broadcaster((value) => { let newBroadcaster = createBroadcaster(value); newBroadcaster(listener); }); }; mapBroadcaster((value) => { return map((letter) => (value.includes(letter) ? letter : "*"))(word); })(inputValue)(log);
Equals to:
mapBroadcaster((value) => { return mapBroadcaster((operator) => operator(word))( map((value) => { return map((letter) => (value.includes(letter) ? letter : "*")); })(inputValue) ); })(log);
Pattern to catch:
inputValue(value => logicFn)(listener) // equals to map(value => logicFn)(inputValue)(listener)
Refect again:
let hangmanLogic = (value) => { return map((letter) => (value.includes(letter) ? letter : "*")); }; mapBroadcaster((value) => { return mapBroadcaster((operator) => operator(word))( map(hangmanLogic)(inputValue) ); })(log);
3. Add helper function 'applyBroadcaster':
Catching this pattern:
mapBroadcaster((operator) => operator(word))
so:
let mapBroadcaster = (createBroadcaster) => (broadcaster) => (listener) => { broadcaster((value) => { let newBroadcaster = createBroadcaster(value); newBroadcaster(listener); }); }; let hangmanLogic = (value) => { return map((letter) => (value.includes(letter) ? letter : "*")); }; let applyOperator = broadcaster => mapBroadcaster(operator => operator(broadcaster)) applyOperator(word)( map(hangmanLogic)(inputValue) )(log);
4. Apply "pipe":
let mapBroadcaster = (createBroadcaster) => (broadcaster) => (listener) => { broadcaster((value) => { let newBroadcaster = createBroadcaster(value); newBroadcaster(listener); }); }; let hangmanLogic = (value) => { return map((letter) => (value.includes(letter) ? letter : "*")); }; let applyOperator = (broadcaster) => mapBroadcaster((operator) => operator(broadcaster)); let hangman = pipe(map(hangmanLogic), applyOperator(word)); hangman(inputValue)(log);
5. Applying the rest of logic:
let mapBroadcaster = (createBroadcaster) => (broadcaster) => (listener) => { broadcaster((value) => { let newBroadcaster = createBroadcaster(value); newBroadcaster(listener); }); }; let hangmanLogic = (value) => { return map((letter) => (value.includes(letter) ? letter : "*")); }; let applyOperator = (broadcaster) => mapBroadcaster((operator) => operator(broadcaster)); let stringConcat = (broadcaster) => (listener) => { let result = ""; broadcaster((value) => { if (value === done) { listener(result); result = ""; return; } result += value; }); }; let hangman = pipe(map(hangmanLogic), applyOperator(word), stringConcat); hangman(inputValue)(log);
Full code:
import { addListener, done, forOf } from "./broadcasters"; import { targetValue, map } from "./operators"; import { pipe } from "lodash/fp"; const log = console.log; let inputInput = addListener("#input", "input"); let inputValue = targetValue(inputInput); let word = forOf("honeycomb"); inputValue((value) => {}); let mapBroadcaster = (createBroadcaster) => (broadcaster) => (listener) => { broadcaster((value) => { let newBroadcaster = createBroadcaster(value); newBroadcaster(listener); }); }; let hangmanLogic = (value) => { return map((letter) => (value.includes(letter) ? letter : "*")); }; let applyOperator = (broadcaster) => mapBroadcaster((operator) => operator(broadcaster)); let stringConcat = (broadcaster) => (listener) => { let result = ""; broadcaster((value) => { if (value === done) { listener(result); result = ""; return; } result += value; }); }; let hangman = pipe(map(hangmanLogic), applyOperator(word), stringConcat); hangman(inputValue)(log);