inital commit

This commit is contained in:
Kai Mosebach 2022-08-11 21:43:31 +01:00
commit 3b67975ac9
21 changed files with 3460 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
wav

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# Setup
Wire the Pi according to the game.js pins, then run
```
apt install nodejs npm
npm install
./convertMP3.sh
node game.js
```

5
convertMP3.sh Executable file
View File

@ -0,0 +1,5 @@
for i in `ls mp3`; do
echo Converting $i to wav/`echo $i | sed "s/mp3$/wav/"`
test -e wav/`echo $i | sed "s/mp3$/wav/"` || ffmpeg -loglevel quiet -i mp3/$i wav/`echo $i | sed "s/mp3$/wav/"`
# && rm wav/`echo $i | sed "s/mp3$/wav/"`
done

273
game.js Normal file
View File

@ -0,0 +1,273 @@
const { QLog, undefinedOrNull } = require('quanto-commons');
const player = require('node-wav-player');
const { Telegraf } = require('telegraf')
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
const telegramBotToken = '5472909337:AAGH269wNGa9u99ekJqHqXEHfi0knpV7IFU';
const GlobalLog = QLog.scope('Global');
const TelLog = QLog.scope('Telegram');
GlobalLog.enableLogs(['warn']);
TelLog.enableLogs(['warn']);
GlobalLog.headPadding = 30;
TelLog.headPadding = 30;
const bot = new Telegraf(telegramBotToken);
// debounce der buttons in ms
const bounceMS = 400;
// ist an wenn im kind mode (erw LED is aus)
const childLED = 37;
// ist and wenn im erwachsenen Mode (kind LED ist aus)
const erwLED = 38;
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
const statusLED = 40;
// setze auf HIGH wenn fail (klingel relais)
const failSwitch = 35;
// warte auf spielstart wenn auf HIGH
const startButton = 32;
// beende spiel wenn auf HIGH
// const finishButton = 31;
const finishButton = 22;
// faile spiel wenn auf HIGH
// const failButton = 29;
const failButton = 21;
// wechsle kind/erwachen Modus + LED
const toggleButton = 33;
// wird je nach modus gesetzt und abgespielt wenn start
const backgroundGrown = "northsouth.wav";
const backgroundChild = "northsouth.wav";
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
const stresserSound = "sharks.wav";
// wird bei fail abgespielt (zonk)
const failSound = "zonk.wav";
// hurra wird bei finish abgespielt
const finishSound = "kidscheering.wav";
const GROWN=1;
const CHILD=2;
const STATES = {
STARTED: 'started',
FINISH: 'finshed',
FAIL: 'failed'
};
const best = {
grown: 25,
child: 37
}
const bounce = {};
var STATE = STATES.FINISH;
var MODE = GROWN;
var GAME = {};
var botCTX;
async function sendBotMsg(msg) {
if(botCTX) {
botCTX.reply(msg);
}
}
async function sendBotImg(img) {
if(botCTX) {
botCTX.replyWithPhoto({ source: img});
}
}
async function setup() {
GlobalLog.start('Running');
bot.start((ctx) => {
let message = `Bot aktiv und beobachtet. Benutze /stats um die Bestenliste zu sehen`;
ctx.reply(message);
TelLog.note('Bot started');
botCTX=ctx;
});
bot.command('stats', async (ctx) => {
await ctx.replyWithPhoto({ source: '/root/hotwire-js/img/bestgrown.png' });
await ctx.reply(`Bester Erwachsener mit nur ${best.grown} sek`);
await ctx.replyWithPhoto({ source: '/root/hotwire-js/img/bestchild.png' })
await ctx.reply(`Bestes Kind mit nur ${best.child} sek`);
});
bot.launch();
GlobalLog.note('Setting up GPIOs');
if(process.platform === 'darwin') {
setTimeout(start, 500);
setTimeout(fail, 3500);
setTimeout(start, 4000);
setTimeout(finish, 9000);
setTimeout(fail, 10000);
setTimeout(finish, 11000);
return;
}
gpio.setup(failSwitch, gpio.OUT, () => {
gpio.output(failSwitch, false);
});
gpio.setup(statusLED, gpio.OUT, () => {
gpio.output(statusLED, true);
});
gpio.setup(childLED, gpio.OUT, () => {
gpio.output(childLED, true);
});
gpio.setup(erwLED, gpio.OUT, () => {
gpio.output(erwLED, false);
});
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.on('change', (channel, value) => {
if(bounce[channel] === true) return;
GlobalLog.debug('Change',channel, value);
bounce[channel] = true;
setTimeout(() => { bounce[channel] = false; GlobalLog.debug('Debounced', channel); }, bounceMS);
switch(channel) {
case startButton:
start(value);
break;
case finishButton:
finish(value);
break;
case failButton:
fail(value);
break;
case toggleButton:
toggle(value);
break;
default:
GlobalLog.warn('Alert! Unknown button change',channel);
}
});
GlobalLog.note('Done. Game logic started. Waiting for input');
}
function gameTimer() {
GlobalLog.debug('game timer called');
GAME.events.forEach((el) => {
if(el.start === GAME.runTime) {
GlobalLog.debug('Event :', el);
playSound(el.sound);
}
});
GAME.runTime++;
}
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
function start(value) {
GAME = {
events: [],
runTime: 0
};
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
STATE = STATES.STARTED;
// set all params for next game, then start counter and event timer
if(MODE === GROWN) {
GAME.events.push({ sound: backgroundGrown, start: 0});
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
GAME.mode = GROWN;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
if(MODE === CHILD) {
GAME.events.push({ sound: backgroundChild, start: 0});
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
GAME.mode = CHILD;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
GlobalLog.note('Starting new game', JSON.stringify(GAME, 2, null));
GAME.countTimer = setInterval(gameTimer, 1000);
sendBotMsg('new game startet');
} else {
GlobalLog.warn('START not in state', STATE);
}
}
async function finish(value) {
GlobalLog.debug('Finish');
if(STATE === STATES.STARTED) {
GlobalLog.note('You made it in', GAME.runTime, 'seconds');
playSound(GAME.finish);
STATE = STATES.FINISH;
clearInterval(GAME.countTimer)
GAME.events = [];
await sendBotImg('/tmp/img.png');
await sendBotMsg(`Gewinner in nur ${GAME.runTime} Sekunden!`);
if(MODE === GROWN) {
if(GAME.runTime < best.grown) {
await sendBotMsg(`Neue Bestzeit bei den Erwachsenen!`);
best.grown = GAME.runTime
// todo copy best img
}
} else {
if(GAME.runTime < best.child) {
await sendBotMsg(`Neue Bestzeit bei den Kindern!`);
best.child = GAME.runTime
// todo copy best img
}
}
} else {
GlobalLog.warn('FINISH not in state', STATE);
}
}
function fail(value) {
if(STATE === STATES.STARTED) {
GlobalLog.note('You failed after', GAME.runTime, 'seconds');
playSound(GAME.fail);
STATE = STATES.FAIL;
clearInterval(GAME.countTimer)
GAME.events = [];
} else {
GlobalLog.debug('FAIL not in state', STATE);
}
}
function toggle(value) {
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
GlobalLog.info('Toggle');
if(MODE === CHILD) {
MODE = GROWN;
// set all values for GROWN mode
}
if(MODE === GROWN) {
MODE = CHILD;
// set all values for CHILD mode
}
} else {
GlobalLog.debug('Toggle not in state', STATE);
}
}
function playSound(file){
GlobalLog.info("Play sound : ", file);
if(GAME.playing) {
GlobalLog.debug("Stopping old soundfile", GAME.playing);
player.stop();
}
GAME.playing = file;
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete GAME.playing })
}
setup();

218
game.js-2 Normal file
View File

@ -0,0 +1,218 @@
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
const player = require('node-wav-player');
// debounce der buttons in ms
const bounceMS = 400;
// ist an wenn im kind mode (erw LED is aus)
const childLED = 37;
// ist and wenn im erwachsenen Mode (kind LED ist aus)
const erwLED = 38;
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
const statusLED = 40;
// setze auf HIGH wenn fail (klingel relais)
const failSwitch = 35;
// warte auf spielstart wenn auf HIGH
const startButton = 32;
// beende spiel wenn auf HIGH
const finishButton = 31;
// faile spiel wenn auf HIGH
const failButton = 29;
// wechsle kind/erwachen Modus + LED
const toggleButton = 33;
// wird je nach modus gesetzt und abgespielt wenn start
const backgroundGrown = "jeopardy.wav";
const backgroundChild = "northsouth.wav";
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
const stresserSound = "sharks.wav";
// wird bei fail abgespielt (zonk)
const failSound = "zonk.wav";
// hurra wird bei finish abgespielt
const finishSound = "kidscheering.wav";
const GROWN=1;
const CHILD=2;
const STATES = {
STARTED: 'started',
FINISH: 'finshed',
FAIL: 'failed'
};
const bounce = {};
var STATE = STATES.FINISH;
var MODE = GROWN;
var GAME = {};
function setup() {
console.log('Setting up GPIOs');
if(process.platform === 'darwin') {
setTimeout(start, 500);
setTimeout(fail, 3500);
setTimeout(start, 4000);
setTimeout(finish, 9000);
setTimeout(fail, 10000);
setTimeout(finish, 11000);
return;
}
gpio.setup(failSwitch, gpio.OUT, () => {
gpio.output(failSwitch, false);
});
gpio.setup(statusLED, gpio.OUT, () => {
gpio.output(statusLED, true);
});
gpio.setup(childLED, gpio.OUT, () => {
gpio.output(childLED, true);
});
gpio.setup(erwLED, gpio.OUT, () => {
gpio.output(erwLED, false);
});
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.on('change', (channel, value) => {
if(bounce[channel] === true) return;
console.log('Change',channel, value);
bounce[channel] = true;
setTimeout(() => { bounce[channel] = false; console.log('Debounced', channel); }, bounceMS);
switch(channel) {
case startButton:
start(value);
break;
case finishButton:
finish(value);
break;
case failButton:
fail(value);
break;
case toggleButton:
toggle(value);
break;
default:
console.log('Alert! Unknown button change',channel);
}
});
console.log('Done. Game logic started. Waiting for input');
}
function gameTimer() {
console.log('game timer called');
GAME.events.forEach((el) => {
if(el.start === GAME.runTime) {
console.log('Event :', el);
playSound(el.sound);
}
});
GAME.runTime++;
}
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
function start(value) {
GAME = {
events: [],
runTime: 0
};
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
STATE = STATES.STARTED;
};
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
STATE = STATES.STARTED;
};
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
STATE = STATES.STARTED;
// set all params for next game, then start counter and event timer
if(MODE === GROWN) {
GAME.events.push({ sound: backgroundGrown, start: 0});
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
GAME.mode = GROWN;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
if(MODE === CHILD) {
GAME.events.push({ sound: backgroundChild, start: 0});
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
GAME.mode = CHILD;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
console.log('Starting new game', JSON.stringify(GAME, 2, null));
console.log(GAME);
GAME.countTimer = setInterval(gameTimer, 1000);
} else {
console.log('START not in state', STATE);
}
}
function finish(value) {
console.log('Finish');
if(STATE === STATES.STARTED) {
console.log('You made it in', GAME.runTime, 'seconds');
playSound(GAME.finish);
STATE = STATES.FINISH;
clearInterval(GAME.countTimer)
GAME.events = [];
} else {
console.log('FINISH not in state', STATE);
}
}
function fail(value) {
console.log('Fail');
if(STATE === STATES.STARTED) {
console.log('You failed after', GAME.runTime, 'seconds');
playSound(GAME.fail);
STATE = STATES.FAIL;
clearInterval(GAME.countTimer)
GAME.events = [];
} else {
console.log('FAIL not in state', STATE);
}
}
function toggle(value) {
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
console.log('Toggle');
if(MODE === CHILD) {
MODE = GROWN;
// set all values for GROWN mode
}
if(MODE === GROWN) {
MODE = CHILD;
// set all values for CHILD mode
}
} else {
console.log('Toggle not in state', STATE);
}
}
function playSound(file){
console.log("Play sound : ", file);
if(global.GAME.playing) {
console.log("Stopping old soundfile", global.GAME.playing);
player.stop();
}
global.GAME.playing = file;
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete global.GAME.playing })
}
setup();

209
game.js-global Normal file
View File

@ -0,0 +1,209 @@
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
const player = require('node-wav-player');
// ist an wenn im kind mode (erw LED is aus)
const childLED = 37;
// ist and wenn im erwachsenen Mode (kind LED ist aus)
const erwLED = 38;
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
const statusLED = 40;
// setze auf HIGH wenn fail (klingel relais)
const failSwitch = 35;
// warte auf spielstart wenn auf HIGH
const startButton = 32;
// beende spiel wenn auf HIGH
const finishButton = 31;
// faile spiel wenn auf HIGH
const failButton = 29;
// wechsle kind/erwachen Modus + LED
const toggleButton = 33;
// wird je nach modus gesetzt und abgespielt wenn start
const backgroundGrown = "jeopardy.wav";
const backgroundChild = "northsouth.wav";
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
const stresserSound = "sharks.wav";
// wird bei fail abgespielt (zonk)
const failSound = "zonk.wav";
// hurra wird bei finish abgespielt
const finishSound = "kidscheering.wav";
const GROWN=1;
const CHILD=2;
global.STATES = {
STARTED: 'started',
FINISH: 'finshed',
FAIL: 'failed'
};
const bounce = {};
global.STATE = STATES.FINISH;
global.MODE = GROWN;
global.GAME = {};
function setup() {
console.log('Setting up GPIOs');
if(process.platform === 'darwin') {
setTimeout(start, 500);
setTimeout(fail, 3500);
setTimeout(start, 4000);
setTimeout(finish, 9000);
setTimeout(fail, 10000);
setTimeout(finish, 11000);
return;
}
gpio.setup(failSwitch, gpio.OUT, () => {
gpio.output(failSwitch, false);
});
gpio.setup(statusLED, gpio.OUT, () => {
gpio.output(statusLED, true);
});
gpio.setup(childLED, gpio.OUT, () => {
gpio.output(childLED, true);
});
gpio.setup(erwLED, gpio.OUT, () => {
gpio.output(erwLED, false);
});
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.on('change', (channel, value) => {
if(bounce[value] === true) return;
console.log('Change',channel, value);
bounce[value] = true;
setTimeout(() => { bounce[value] = false; console.log('Debounced', value); }, 1000);
switch(channel) {
case startButton:
start(value);
break;
case finishButton:
finish(value);
break;
case failButton:
fail(value);
break;
case toggleButton:
toggle(value);
break;
default:
console.log('Alert! Unknown button change',channel);
}
});
console.log('Done. Game logic started. Waiting for input');
}
function gameTimer() {
console.log('game timer called');
global.GAME.events.forEach((el) => {
if(el.start === global.GAME.runTime) {
console.log('Event :', el);
playSound(el.sound);
}
});
global.GAME.runTime++;
}
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
function start(value) {
global.GAME = {
events: [],
runTime: 0
};
if(global.STATE === global.STATES.FINISH || global.STATE === STATES.FAIL) {
global.STATE = global.STATES.STARTED;
// set all params for next game, then start counter and event timer
if(global.MODE === GROWN) {
global.GAME.events.push({ sound: backgroundGrown, start: 0});
global.GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
global.GAME.mode = GROWN;
global.GAME.fail = failSound;
global.GAME.finish = finishSound;
global.GAME.runtime = 0;
}
if(global.MODE === CHILD) {
global.GAME.events.push({ sound: backgroundChild, start: 0});
global.GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
global.GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
global.GAME.mode = CHILD;
global.GAME.fail = failSound;
global.GAME.finish = finishSound;
global.GAME.runtime = 0;
}
console.log('Starting new game', JSON.stringify(global.GAME, 2, null));
console.log(global.GAME);
global.GAME.countTimer = setInterval(gameTimer, 1000);
} else {
console.log('START not in state', global.STATE);
}
}
function finish(value) {
console.log('Finish');
if(global.STATE === global.STATES.STARTED) {
console.log('You made it in', global.GAME.runTime, 'seconds');
playSound(global.GAME.finish);
global.STATE = global.STATES.FINISH;
clearInterval(global.GAME.countTimer)
GAME.events = [];
} else {
console.log('FINISH not in state', global.STATE);
}
}
function fail(value) {
console.log('Fail');
if(global.STATE === global.STATES.STARTED) {
console.log('You failed after', global.GAME.runTime, 'seconds');
playSound(global.GAME.fail);
global.STATE = global.STATES.FAIL;
clearInterval(global.GAME.countTimer)
global.GAME.events = [];
} else {
console.log('FAIL not in state', global.STATE);
}
}
function toggle(value) {
if(global.STATE === global.STATES.FINISH || global.STATE === global.STATES.FAIL) {
console.log('Toggle');
if(global.MODE === CHILD) {
global.MODE = GROWN;
// set all values for GROWN mode
}
if(global.MODE === GROWN) {
global.MODE = CHILD;
// set all values for CHILD mode
}
} else {
console.log('Toggle not in state', global.STATE);
}
}
function playSound(file){
console.log("Play sound : ", file);
if(global.GAME.playing) {
console.log("Stopping old soundfile", global.GAME.playing);
player.stop();
}
global.GAME.playing = file;
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete global.GAME.playing })
}
setup();

209
game.js~ Normal file
View File

@ -0,0 +1,209 @@
if(process.platform !== 'darwin') var gpio = require('rpi-gpio');
const player = require('node-wav-player');
// ist an wenn im kind mode (erw LED is aus)
const childLED = 37;
// ist and wenn im erwachsenen Mode (kind LED ist aus)
const erwLED = 38;
// status : ist an wenn in ruhe position, blinkt, wenn gestartet, ist aus bei fail
const statusLED = 40;
// setze auf HIGH wenn fail (klingel relais)
const failSwitch = 35;
// warte auf spielstart wenn auf HIGH
const startButton = 32;
// beende spiel wenn auf HIGH
const finishButton = 31;
// faile spiel wenn auf HIGH
const failButton = 29;
// wechsle kind/erwachen Modus + LED
const toggleButton = 33;
// wird je nach modus gesetzt und abgespielt wenn start
const backgroundGrown = "jeopardy.wav";
const backgroundChild = "northsouth.wav";
// wird zufallig zu zufaelligen momenten abgespielt (erw mode)
const erschreckSounds = [ "ziege.wav", "scream.wav", "lough.wav" ];
// wird nach fester zeit + random oder spaeter nach der besten zeit abgespielt (erw mode)
const stresserSound = "sharks.wav";
// wird bei fail abgespielt (zonk)
const failSound = "zonk.wav";
// hurra wird bei finish abgespielt
const finishSound = "kidscheering.wav";
const GROWN=1;
const CHILD=2;
const STATES = {
STARTED: 'started',
FINISH: 'finshed',
FAIL: 'failed'
};
const bounce = {};
var STATE = STATES.FINISH;
var MODE = GROWN;
var GAME = {};
function setup() {
console.log('Setting up GPIOs');
if(process.platform === 'darwin') {
setTimeout(start, 500);
setTimeout(fail, 3500);
setTimeout(start, 4000);
setTimeout(finish, 9000);
setTimeout(fail, 10000);
setTimeout(finish, 11000);
return;
}
gpio.setup(failSwitch, gpio.OUT, () => {
gpio.output(failSwitch, false);
});
gpio.setup(statusLED, gpio.OUT, () => {
gpio.output(statusLED, true);
});
gpio.setup(childLED, gpio.OUT, () => {
gpio.output(childLED, true);
});
gpio.setup(erwLED, gpio.OUT, () => {
gpio.output(erwLED, false);
});
gpio.setup(finishButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(toggleButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(startButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.setup(failButton, gpio.DIR_IN, gpio.EDGE_RISING);
gpio.on('change', (channel, value) => {
if(bounce[channel] === true) return;
console.log('Change',channel, value);
bounce[channel] = true;
setTimeout(() => { bounce[channel] = false; console.log('Debounced', channel); }, 500);
switch(channel) {
case startButton:
start(value);
break;
case finishButton:
finish(value);
break;
case failButton:
fail(value);
break;
case toggleButton:
toggle(value);
break;
default:
console.log('Alert! Unknown button change',channel);
}
});
console.log('Done. Game logic started. Waiting for input');
}
function gameTimer() {
console.log('game timer called');
GAME.events.forEach((el) => {
if(el.start === GAME.runTime) {
console.log('Event :', el);
playSound(el.sound);
}
});
GAME.runTime++;
}
function getRandomInt(max) {
return Math.floor(Math.random() * max);
}
function start(value) {
GAME = {
events: [],
runTime: 0
};
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
STATE = STATES.STARTED;
// set all params for next game, then start counter and event timer
if(MODE === GROWN) {
GAME.events.push({ sound: backgroundGrown, start: 0});
GAME.events.push({ sound: stresserSound, start: 30+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10) });
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 20+getRandomInt(10) });
GAME.mode = GROWN;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
if(MODE === CHILD) {
GAME.events.push({ sound: backgroundChild, start: 0});
GAME.events.push({ sound: stresserSound, start: 40+getRandomInt(10)});
GAME.events.push({ sound: erschreckSounds[getRandomInt(2)], start: 10+getRandomInt(10)});
GAME.mode = CHILD;
GAME.fail = failSound;
GAME.finish = finishSound;
GAME.runtime = 0;
}
console.log('Starting new game', JSON.stringify(GAME, 2, null));
console.log(GAME);
GAME.countTimer = setInterval(gameTimer, 1000);
} else {
console.log('START not in state', STATE);
}
}
function finish(value) {
console.log('Finish');
if(STATE === STATES.STARTED) {
console.log('You made it in', GAME.runTime, 'seconds');
playSound(GAME.finish);
STATE = STATES.FINISH;
clearInterval(GAME.countTimer)
GAME.events = [];
} else {
console.log('FINISH not in state', STATE);
}
}
function fail(value) {
console.log('Fail');
if(STATE === STATES.STARTED) {
console.log('You failed after', GAME.runTime, 'seconds');
playSound(GAME.fail);
STATE = STATES.FAIL;
clearInterval(GAME.countTimer)
GAME.events = [];
} else {
console.log('FAIL not in state', STATE);
}
}
function toggle(value) {
if(STATE === STATES.FINISH || STATE === STATES.FAIL) {
console.log('Toggle');
if(MODE === CHILD) {
MODE = GROWN;
// set all values for GROWN mode
}
if(MODE === GROWN) {
MODE = CHILD;
// set all values for CHILD mode
}
} else {
console.log('Toggle not in state', STATE);
}
}
function playSound(file){
console.log("Play sound : ", file);
if(GAME.playing) {
console.log("Stopping old soundfile", GAME.playing);
player.stop();
}
GAME.playing = file;
player.play({ path: `wav/${file}`, sync: true }).then(() => { delete GAME.playing })
}
setup();

BIN
img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
img/bestchild.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
img/bestgrown.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

BIN
mp3/._northsouth.mp3 Normal file

Binary file not shown.

BIN
mp3/._zonk.mp3 Normal file

Binary file not shown.

BIN
mp3/jeopardy.mp3 Normal file

Binary file not shown.

BIN
mp3/kidscheering.mp3 Normal file

Binary file not shown.

BIN
mp3/northsouth.mp3 Normal file

Binary file not shown.

BIN
mp3/scream.mp3 Normal file

Binary file not shown.

BIN
mp3/sharks.mp3 Normal file

Binary file not shown.

BIN
mp3/ziege.mp3 Normal file

Binary file not shown.

BIN
mp3/zonk.mp3 Normal file

Binary file not shown.

2525
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

9
package.json Normal file
View File

@ -0,0 +1,9 @@
{
"dependencies": {
"node-telegram-bot-api": "^0.30.0",
"node-wav-player": "^0.2.0",
"quanto-commons": "^1.0.46",
"rpi-gpio": "^2.1.7",
"telegraf": "^4.8.6"
}
}