Skip to content

Instantly share code, notes, and snippets.

@conartist6
Last active June 1, 2023 15:53
Show Gist options
  • Save conartist6/abfb159d3c4494f4889e3d003558672c to your computer and use it in GitHub Desktop.
Save conartist6/abfb159d3c4494f4889e3d003558672c to your computer and use it in GitHub Desktop.
State machine with generators
/*
Lamps often have on-off switches that twist clockwise, clicking as they do.
Some such switches have four states, even though the lamp has only two (on, and off).
On these lamps clicking the switch will only cause an on/off transition every other click.
Just looking at the lamp you'll know if it's on, but not if the next click will turn it off.
To reliably interact with such a lamp you will use an algorithm, even in real life.
I use the term strategy to refer to an algorithm expressed with generators like this --
the strategy pattern is useful to abstract an algorithm from the representation of the data.
For more about lamps: https://temperaturemaster.com/why-does-it-take-two-clicks-to-turn-on-a-lamp/
*/
function* toggleStrategy(lamp) {
const wasOn = lamp.on;
while (lamp.on === wasOn) {
yield 'click';
}
}
assertEqual(2, useLamp(toggleStrategy, 0b00));
assertEqual(1, useLamp(toggleStrategy, 0b01));
assertEqual(2, useLamp(toggleStrategy, 0b10));
assertEqual(1, useLamp(toggleStrategy, 0b11));
function* onStrategy(lamp) {
while (!lamp.on) {
yield 'click';
}
}
assertEqual(2, useLamp(onStrategy, 0b00));
assertEqual(1, useLamp(onStrategy, 0b01));
assertEqual(0, useLamp(onStrategy, 0b10));
assertEqual(0, useLamp(onStrategy, 0b11));
function useLamp(strategy, initialState) {
const facade = ({ get on() { return state <= 0b10 }});
let state = initialState;
let clicks = 0;
for (const action of strategy(facade)) {
switch(action) {
case 'click':
++clicks;
state = (state + 0b01) & 0b11;
break;
default:
throw new Error(`unknown {action: ${action}}`);
}
}
return clicks;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment