Experiments in Trivia
Bored, want to see how fast I can develop a simple multiplayer trivia app in my blog environment using Firebase.
Let’s have a QuizBot manage a timer and a state machine that allows others to answer questions in the Chat window that are posed.
Simple Quiz Experiment
For kicks, I’ll use the code from Christmas Presence earlier in this blog to keep track of the number of participants and their locations.
.controller('PresenceController', ['$scope', 'PresenceService',
function($scope, PresenceService) {
$scope.totalViewers = 0;
$scope.allViewers = [];
$scope.$on('onOnlineUser', function() {
$scope.$apply(function() {
$scope.totalViewers = PresenceService.getOnlineUserCount();
$scope.allViewers = PresenceService.getOnlineUserList();
});
});
}
])
.factory('PresenceService', ['$rootScope', '$http',
function($rootScope, $http) {
var onlineUsers = [];
var numOnlineUsers = 0;
var myUserInfo = {};
// Create our references
var listRef = new Firebase('https://doctorbud.firebaseIO.com/simplequizpresence1');
var userRef = listRef.push(); // This creates a unique reference for each user
var presenceRef = new Firebase('https://doctorbud.firebaseIO.com/.info/connected');
var jsonp = $http.jsonp('https://ipinfo.io/');
jsonp.then(
function(response) {
response = response.data;
myUserInfo.city = response.city;
myUserInfo.country = response.country;
myUserInfo.hostname = response.hostname || 'unknown';
myUserInfo.ip = response.ip;
myUserInfo.loc = response.loc;
myUserInfo.org = response.org;
myUserInfo.region = response.region;
});
// Add ourselves to presence list when online.
presenceRef.on('value', function(snap) {
if (snap.val()) {
userRef.set(myUserInfo);
// Remove ourselves when we disconnect.
userRef.onDisconnect().remove();
}
});
// Get the user count and notify the application
listRef.on('value', function(snap) {
numOnlineUsers = snap.numChildren();
onlineUsers = [];
snap.forEach(function(childSnapshot) {
// This code will be called twice.
var name = childSnapshot.name();
var childData = childSnapshot.val();
onlineUsers.push(childData);
});
$rootScope.$broadcast('onOnlineUser');
});
var getOnlineUserCount = function() {
return numOnlineUsers;
}
var getOnlineUserList = function() {
return onlineUsers;
}
return {
getOnlineUserCount: getOnlineUserCount,
getOnlineUserList: getOnlineUserList
}
}
]);
<div scroll-glue class="overflowable" style="font-size:small; color:#1111AA;background-color:#DDDDDD;">
<ul>
<li ng-repeat='viewer in allViewers'>
Geo: {[viewer.city]}, {[viewer.region]}, {[viewer.country]} -- {[viewer.loc]}
<br/>
Net: {[viewer.ip]} {[viewer.hostname]} {[viewer.org]}
</li>
</ul>
</div>
The code for the above is below:
<div>
<script>
angular.module('BlogApp')
.controller('PresenceController', ['$scope', 'PresenceService',
function($scope, PresenceService) {
$scope.totalViewers = 0;
$scope.allViewers = [];
$scope.$on('onOnlineUser', function() {
$scope.$apply(function() {
$scope.totalViewers = PresenceService.getOnlineUserCount();
$scope.allViewers = PresenceService.getOnlineUserList();
});
});
}
])
.factory('PresenceService', ['$rootScope', '$http',
function($rootScope, $http) {
var onlineUsers = [];
var numOnlineUsers = 0;
var myUserInfo = {};
// Create our references
var listRef = new Firebase('https://doctorbud.firebaseIO.com/simplequizpresence1');
var userRef = listRef.push(); // This creates a unique reference for each user
var presenceRef = new Firebase('https://doctorbud.firebaseIO.com/.info/connected');
var jsonp = $http.jsonp('https://ipinfo.io/');
jsonp.then(
function(response) {
myUserInfo.city = response.city;
myUserInfo.country = response.country;
myUserInfo.hostname = response.hostname;
myUserInfo.ip = response.ip;
myUserInfo.loc = response.loc;
myUserInfo.org = response.org;
myUserInfo.region = response.region;
});
// Add ourselves to presence list when online.
presenceRef.on('value', function(snap) {
if (snap.val()) {
userRef.set(myUserInfo);
// Remove ourselves when we disconnect.
userRef.onDisconnect().remove();
}
});
// Get the user count and notify the application
listRef.on('value', function(snap) {
numOnlineUsers = snap.numChildren();
onlineUsers = [];
snap.forEach(function(childSnapshot) {
// This code will be called twice.
var name = childSnapshot.name();
var childData = childSnapshot.val();
onlineUsers.push(childData);
});
$rootScope.$broadcast('onOnlineUser');
});
var getOnlineUserCount = function() {
return numOnlineUsers;
}
var getOnlineUserList = function() {
return onlineUsers;
}
return {
getOnlineUserCount: getOnlineUserCount,
getOnlineUserList: getOnlineUserList
}
}
]);
</script>
<div ng-controller="PresenceController" class="card bg-well p-1" style="padding:20px;max-width:600px;">
<h6>{{totalViewers}} viewers are viewing</h6>
<div scroll-glue class="overflowable" style="font-size:small; color:#1111AA;background-color:#DDDDDD;">
<ul>
<li ng-repeat='viewer in allViewers'>
Geo: {{viewer.city}}, {{viewer.region}}, {{viewer.country}} -- {{viewer.loc}}
<br/>
Net: {{viewer.ip}} {{viewer.hostname}} {{viewer.org}}
</li>
</ul>
</div>
</div>
</div>
The Trivia Game
Here is the main event, a trivia game.
$scope.addMessage = function(e) {
var curState = $scope.state[$scope.state.$getIndex()[0]];
console.log('stateMachine: ', curState);
if (e.keyCode != 13) return;
if ($scope.msg === "#clear")
{
$scope.messages.$set([]);
$scope.answers.$set([]);
}
else
{
if (curState == 'asked')
{
$scope.answers.$add({from: $scope.name, body: $scope.msg});
$scope.messages.$add({from: $scope.name, body: $scope.name + ' has answered.'});
}
else
{
$scope.messages.$add({from: $scope.name, body: $scope.msg});
}
$scope.msg = "";
}
};
$scope.addBotMessage = function(msg) {
$scope.messages.$add({from: "QuizBot", body: msg});
};
$scope.resetGame = function() {
$scope.messages.$set([]);
$scope.answers.$set([]);
$scope.state.$remove();
$scope.state.$add("asking");
$scope.countdown = 10;
$scope.questionNumber = 4;
};
$scope.stateMachine = function() {
var curState = $scope.state[$scope.state.$getIndex()[0]];
console.log('stateMachine: ', curState);
if (curState == 'asking')
{
$scope.addBotMessage('Question... What is ' + $scope.questionNumber + ' + ' + $scope.questionNumber + '?');
$scope.state.$remove();
$scope.state.$add("asked");
}
else if (curState == 'asked')
{
if ($scope.countdown > 0)
{
$scope.addBotMessage('T:' + $scope.countdown);
$scope.countdown = $scope.countdown - 1;
}
else
{
$scope.state.$remove();
$scope.state.$add("scoring");
}
}
else if (curState == 'scoring')
{
$scope.addBotMessage('Scores for Q:' + $scope.questionNumber);
console.log('$scope.answers=', $scope.answers);
var keys = $scope.answers.$getIndex();
keys.forEach(
function(key, i) {
var answer = $scope.answers[key];
console.log($scope.answers[key]);
$scope.addBotMessage(' ' + answer.from + ' answered "' + answer.body + '"');
});
$scope.questionNumber = $scope.questionNumber - 1;
$scope.countdown = 10;
$scope.answers.$remove();
$scope.state.$remove();
$scope.state.$add("asking");
}
};
}
angular
.module('BlogApp')
.controller('GameController', GameController);
<div scroll-glue class="overflowable" id="messagesDiv" style="color:#00AA00;background-color:#000000;height:300px;overflow:scroll;">
<div ng-repeat="msg in messages"><em>{[msg.from]}</em>: {[msg.body]}</div>
</div>
<br/>
<input type="text" ng-model="name" placeholder="Name" size="10">
<input type="text" ng-model="msg" ng-keydown="addMessage($event)" placeholder="Message..." size="50">
<h6>Machine Time: {[ MA.getMachineTime() ]}</h6>
<h6>Machine State: {[ MA.getMachineState() ]}</h6>
<h6>Machine Running: {[ MA.isRunning() ]}</h6>
<h6>Question Number: {[ questionNumber ]}</h6>
<h6>Countdown: {[ countdown ]}</h6>
<h6>Answers: {[ answers ]}</h6>
<h6>State: {[ state ]}</h6>
<mlmmachine
ptr="MA"
ng-init="resetGame()"
reset-fn="resetGame()"
step-fn="stateMachine()"
class="mlm background">
<mlmpanel></mlmpanel>
</mlmmachine>
The code for the above is below:
<div>
<script>
function GameController($scope, $firebase) {
$scope.messages = $firebase(new Firebase('https://doctorbud.firebaseIO.com/simplequizchatmessages'));
$scope.answers = $firebase(new Firebase('https://doctorbud.firebaseIO.com/simplequizchatanswers'));
$scope.state = $firebase(new Firebase('https://doctorbud.firebaseIO.com/simplequizchatstate'));
$scope.countdown = $firebase(new Firebase('https://doctorbud.firebaseIO.com/simplequizchatcountdown'));
$scope.questionNumber = $firebase(new Firebase('https://doctorbud.firebaseIO.com/simplequizchatquestionNumber'));
$scope.addMessage = function(e) {
var curState = $scope.state[$scope.state.$getIndex()[0]];
console.log('stateMachine: ', curState);
if (e.keyCode != 13) return;
if ($scope.msg === "#clear")
{
$scope.messages.$set([]);
$scope.answers.$set([]);
}
else
{
if (curState == 'asked')
{
$scope.answers.$add({from: $scope.name, body: $scope.msg});
$scope.messages.$add({from: $scope.name, body: $scope.name + ' has answered.'});
}
else
{
$scope.messages.$add({from: $scope.name, body: $scope.msg});
}
$scope.msg = "";
}
};
$scope.addBotMessage = function(msg) {
$scope.messages.$add({from: "QuizBot", body: msg});
};
$scope.resetGame = function() {
$scope.messages.$set([]);
$scope.answers.$set([]);
$scope.state.$remove();
$scope.state.$add("asking");
$scope.countdown = 10;
$scope.questionNumber = 4;
};
$scope.stateMachine = function() {
var curState = $scope.state[$scope.state.$getIndex()[0]];
console.log('stateMachine: ', curState);
if (curState == 'asking')
{
$scope.addBotMessage('Question... What is ' + $scope.questionNumber + ' + ' + $scope.questionNumber + '?');
$scope.state.$remove();
$scope.state.$add("asked");
}
else if (curState == 'asked')
{
if ($scope.countdown > 0)
{
$scope.addBotMessage('T:' + $scope.countdown);
$scope.countdown = $scope.countdown - 1;
}
else
{
$scope.state.$remove();
$scope.state.$add("scoring");
}
}
else if (curState == 'scoring')
{
$scope.addBotMessage('Scores for Q:' + $scope.questionNumber);
console.log('$scope.answers=', $scope.answers);
var keys = $scope.answers.$getIndex();
keys.forEach(
function(key, i) {
var answer = $scope.answers[key];
console.log($scope.answers[key]);
$scope.addBotMessage(' ' + answer.from + ' answered "' + answer.body + '"');
});
$scope.questionNumber = $scope.questionNumber - 1;
$scope.countdown = 10;
$scope.answers.$remove();
$scope.state.$remove();
$scope.state.$add("asking");
}
};
}
</script>
<div style="padding:20px;max-width:500px; background-color: aliceblue;" ng-controller="GameController">
<div scroll-glue class="overflowable" id="messagesDiv" style="color:#00AA00;background-color:#000000;">
<div ng-repeat="msg in messages"><em>{{msg.from}}</em>: {{msg.body}}</div>
</div>
<br/>
<input type="text" ng-model="name" placeholder="Name" size="10">
<input type="text" ng-model="msg" ng-keydown="addMessage($event)" placeholder="Message..." size="50">
<h6>Machine Time: {{ MA.getMachineTime() }}</h6>
<h6>Machine State: {{ MA.getMachineState() }}</h6>
<h6>Machine Running: {{ MA.isRunning() }}</h6>
<h6>Question Number: {{ questionNumber }}</h6>
<h6>Countdown: {{ countdown }}</h6>
<h6>Answers: {{ answers }}</h6>
<h6>State: {{ state }}</h6>
<mlmmachine
ptr="MA"
ng-init="resetGame()"
reset-fn="resetGame()"
step-fn="stateMachine()"
class="mlm background">
<mlmpanel></mlmpanel>
</mlmmachine>
</div>
</div>