Skip to content

Commit

Permalink
Refactor guesses out of Cypher, add filtering of text to remove Twitt…
Browse files Browse the repository at this point in the history
…er noise, use enyo.Repeater for cells
  • Loading branch information
unwiredben committed Jun 14, 2012
1 parent b74af9b commit a184228
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 107 deletions.
25 changes: 17 additions & 8 deletions apps/cryptotweets/source/App.js
Expand Up @@ -25,7 +25,7 @@

enyo.kind({
name: "PickLetterPopup",
kind: "onyx.Popup",
kind: onyx.Popup,
centered: true,
modal: true,
floating: true,
Expand Down Expand Up @@ -78,17 +78,16 @@ enyo.kind({

enyo.kind({
name: "App",
kind: "Control",
classes: "onyx",
handlers: {
onStartGuess: "startGuess",
onFinishGuess: "finishGuess"
},
components: [
{ kind: "enyo.Signals",
{ kind: enyo.Signals,
onkeypress: "handleKeyPress",
onkeydown: "handleKeyDown" },
{ kind: "onyx.Toolbar", components: [
{ kind: onyx.Toolbar, components: [
//{kind: onyx.Grabber},
{content: "CryptoTweets", style: "padding-right: 30px"},
{kind: enyo.Group, highlander: false, components: [
Expand All @@ -101,8 +100,8 @@ enyo.kind({
{kind: onyx.Button, content: "Fetch News", onclick: "fetchNews"}
]}
]},
{ kind: "Cryptogram" },
{ name: "popup", kind: "PickLetterPopup" }
{ kind: Cryptogram },
{ name: "popup", kind: PickLetterPopup }
],
create: function() {
this.inherited(arguments);
Expand All @@ -114,7 +113,12 @@ enyo.kind({
this.nextTweetIndex = 0;
//this.nextTweet();
},
fetchTweets: function() {
fetchTweets: function(inSender) {
// disable button for 30 seconds to reduce API usage
if (inSender) {
inSender.setDisabled(true);
setTimeout(function() { inSender.setDisabled(false); }, 30000);
}
var request = new enyo.JsonpRequest({
url: "http://search.twitter.com/search.json",
callbackName: "callback"
Expand All @@ -124,7 +128,12 @@ enyo.kind({
q: "from:TopTweets -filter:links"
});
},
fetchNews: function() {
fetchNews: function(inSender) {
// disable button for 30 seconds to reduce API usage
if (inSender) {
inSender.setDisabled(true);
setTimeout(function() { inSender.setDisabled(false); }, 30000);
}
var request = new enyo.JsonpRequest({
url: "http://api.usatoday.com/open/articles/topnews",
callbackName: "jsoncallbackmethod"
Expand Down
38 changes: 23 additions & 15 deletions apps/cryptotweets/source/Cell.js
Expand Up @@ -7,7 +7,7 @@ enyo.kind({
top: "",
bottom: "",
mutable: true,
isLetter: false
encrypted: false
},
events: {
onHoverCell: ""
Expand All @@ -26,33 +26,39 @@ enyo.kind({
},
create: function () {
this.inherited(arguments);
this.addRemoveClass("letter", this.isLetter);
this.originalGuess = this.top;
this.encryptedChanged();
this.topChanged();
this.mutableChanged();
this.bottomChanged();
},
reset: function () {
// go back to "unguessed" state
if (this.isLetter) {
if (this.encrypted) {
this.setMutable(true);
}
this.setTop(this.originalGuess);
this.setTop("");
},
topChanged: function() {
this.$.top.setContent(this.top || Unicode.nbsp);
},
bottomChanged: function() {
this.$.bottom.setContent(this.bottom || Unicode.nbsp);
},
mutableChanged: function() {
if (this.isLetter && this.mutable) {
updateMiddle: function() {
if (this.encrypted && this.mutable) {
this.$.middle.setContent(Unicode.mdash);
}
else {
this.$.middle.setContent(Unicode.nbsp);
}
},
encryptedChanged: function() {
this.addRemoveClass("letter", this.encrypted);
this.updateMiddle();
},
mutableChanged: function() {
this.updateMiddle();
},
topChanged: function() {
this.$.top.setContent(this.top || Unicode.nbsp);
},
bottomChanged: function() {
this.$.bottom.setContent(this.bottom || Unicode.nbsp);
},
guess: function(inSender, inEvent) {
if (this.mutable) {
if (inEvent.cypher === this.bottom) {
Expand All @@ -75,7 +81,7 @@ enyo.kind({
},
// notify owner about mouse enter/leaves
hoverStart: function() {
if (this.mutable) {
if (this.encrypted && this.mutable) {
this.doHoverCell({ cypher: this.bottom });
}
},
Expand All @@ -84,6 +90,8 @@ enyo.kind({
},
// handle event sent down the tree to change my visible hover state
updateHoverState: function(inSender, inEvent) {
this.addRemoveClass("selectedCell", this.bottom === inEvent.cypher);
if (this.encrypted && this.mutable) {
this.addRemoveClass("selectedCell", this.bottom === inEvent.cypher);
}
}
});
122 changes: 76 additions & 46 deletions apps/cryptotweets/source/Cryptogram.js
Expand Up @@ -5,67 +5,97 @@ enyo.kind({
allowHtml: true,
classes: "cryptogram",
components: [
{ name: "lines" },
{ name: "cells", kind: enyo.Repeater, onSetupItem: "setupCell",
components: [ { kind: Cell } ] },
{ tag: "br", attributes: { clear: "all" } },
{ kind: "Distribution", name: "distribution" }
{ kind: Distribution }
],
handlers: {
onHoverCell: "broadcastHover"
},
published: {
//* required, should be capital letter in range "A" to "Z"
text: ""
},
handlers: {
onHoverCell: "broadcastHover"
},
// private properties
// hash mapping clear text letters to guessed letter
guesses: null,
resetCypher: function(text) {
this.cypher = new Cypher();
this.$.distribution.setCypher(this.cypher);
},
setGuess: function(clearLetter, guessLetter) {
// clear out any old guess with the same letter
forEachLetter(this, function(ch) {
if (this.guesses[ch] === guessLetter) {
this.guesses[ch] = null;
}
});
// set the new guess
this.guesses[clearLetter] = guessLetter;
},
create: function() {
this.inherited(arguments);
this.resetCypher();
this.textChanged();
},
textChanged: function() {
this.text = this.text.toUpperCase();
this.$.distribution.setText(this.text);

var words = this.text.split(/ +/);
this.$.lines.destroyClientControls();
enyo.forEach(words, this.outputWord, this);
this.render();
markUnencrypted: function(regex) {
var result;
regex.lastIndex = 0;
while (result = regex.exec(this.text)) {
for (var i = result.index; i < regex.lastIndex; ++i) {
this.isEncrypted[i] = false;
}
}
},
addLetter: function(ch) {
this.createComponent({
kind: "Cell",
container: this.$.lines,
top: this.cypher.clearToGuess(ch),
bottom: this.cypher.clearToCypher(ch),
mutable: true,
isLetter: true
});
findUnencryptedRuns: function() {
// put all characters into cells with default isEncrypted value
this.isEncrypted = [];
for (var i = 0; i < this.text.length; ++i) {
this.isEncrypted[i] = (this.text[i] >= 'A') && (this.text[i] <= 'Z');
}
// find specific strings to "unencrypt" - URLs, RT, hashtags, @names
this.markUnencrypted(/^RT\s/g);
this.markUnencrypted(/HTTPS?:\/\/\S*/g);
this.markUnencrypted(/@\w+/g);
this.markUnencrypted(/#\w+/g);
},
addNonLetter: function(ch) {
this.createComponent({
kind: "Cell",
container: this.$.lines,
top: ch,
bottom: ch,
mutable: false,
isLetter: false
generateDistribution: function() {
var distribution = {};
forEachLetter(this, function(ch) {
distribution[ch] = 0;
});
},
outputWord: function(word) {
enyo.forEach(word, function(ch) {
if (ch >= "A" && ch <= "Z") {
this.addLetter(ch);
for (var i = this.text.length - 1; i >= 0; --i) {
var ch = this.text[i];
if (this.isEncrypted[i]) {
distribution[ch]++;
}
else {
this.addNonLetter(ch);
}
}, this);

// FIXME: need better after-word separator or wordwrap method
this.addNonLetter(Unicode.nbsp);
}
this.$.distribution.setDistribution(distribution);
},
textChanged: function() {
// start by forcing uppercase and trimming white space
this.text = this.text.toUpperCase();
this.text = this.text.replace(/^\s+|\s+$/g,'');
this.text = this.text.replace(/\s+/g, Unicode.nbsp);
this.findUnencryptedRuns();
this.generateDistribution();
this.guesses = {};
this.$.cells.setCount(this.text.length);
},
setupCell: function(inSender, inEvent) {
var ch = this.text[inEvent.index];
var isEncrypted = this.isEncrypted[inEvent.index];
var cell = inEvent.item.$.cell;
cell.setEncrypted(isEncrypted);
cell.setMutable(isEncrypted);
if (isEncrypted) {
cell.setTop(Unicode.nbsp);
cell.setBottom(this.cypher.clearToCypher(ch));
}
else {
cell.setTop(ch);
cell.setBottom(ch);
}
},
// give a hint using the given encrypted letter
giveHint: function(pick) {
Expand All @@ -76,7 +106,7 @@ enyo.kind({
// if no argument, pick a unguessed letter to reveal
var unguessed = [];
forEachLetter(this, function(ch) {
if (!this.cypher.clearToGuess(ch) && this.$.distribution.getCount(ch) > 0) {
if (!this.guesses[ch] && this.$.distribution.getCount(ch) > 0) {
unguessed.push(ch);
}
});
Expand All @@ -90,15 +120,15 @@ enyo.kind({
},
guessLetter: function(cypher, guess, hint) {
var clear = this.cypher.cypherToClear(cypher);
this.cypher.setGuess(clear, guess);
this.setGuess(clear, guess);
this.waterfall("onGuess", {
cypher: cypher,
guess: guess,
hint: hint
});
},
restart: function() {
this.cypher.resetGuesses();
this.guesses = {};
this.waterfall("onResetGuess");
},
broadcastHover: function(inSender, inEvent) {
Expand Down
22 changes: 3 additions & 19 deletions apps/cryptotweets/source/Cypher.js
@@ -1,12 +1,13 @@
/* CryptoTweets, a game built using Enyo 2.0 */

/** This kind encapsulates the substitution cypher needed for the game,
but doesn't include cyphertext management. */
enyo.kind({
name: "Cypher",
kind: enyo.Object,
shuffleAlphabet: function() {
var alpha = generateCypherAlphabet();

// take the shuffled alphabet and assign it to our hashes
var alpha = generateCypherAlphabet();
this.cypher = {};
this.reverseCypher = {};
enyo.forEach(alpha, function(val,idx,arr) {
Expand All @@ -15,31 +16,14 @@ enyo.kind({
this.reverseCypher[val] = key;
}, this);
},
resetGuesses: function() {
this.guesses = {};
},
constructor: function() {
this.inherited(arguments);
this.shuffleAlphabet();
this.resetGuesses();
},
clearToCypher: function(clearLetter) {
return this.cypher[clearLetter];
},
cypherToClear: function(cypherLetter) {
return this.reverseCypher[cypherLetter];
},
clearToGuess: function(clearLetter) {
return this.guesses[clearLetter];
},
setGuess: function(clearLetter, guessLetter) {
// clear out any old guess with the same letter
forEachLetter(this, function(ch) {
if (this.guesses[ch] === guessLetter) {
this.guesses[ch] = null;
}
});
// set the new guess
this.guesses[clearLetter] = guessLetter;
}
});

0 comments on commit a184228

Please sign in to comment.