Permutation of Actionscript 3 Array - actionscript-3

Greetings,
This is my first post and I hope someone out there can help. I am an educator and I designed a quiz using Actionscript 3 (Adobe Flash) that is to determine all the different ways a family can have three children.
I have two buttons that enter either the letter B (for boy) or G (for girl) into an input text field named text_entry. I then have a submit button named enter_btn that checks to see if the entry into the input text is correct. If the input is correct, the timeline moves to the next problem (frame labeled checkmark); if it is incorrect the timeline moves to the end of the quiz (frame 62).
The following code works well for any particular correct single entry (ie: BGB). I need to write code in which all eight correct variations must be entered, but they can be entered in any order (permutation):
ie:
BBB,BBG,BGB,BGG,GBB,GBG,GGB,GGG; or
BGB,GGG,BBG,BBB,GGB,BGB,GGB,BGG; or
GGB,GGG,BBG,BBB,GGB,BGB,BGB,BGG; etc...
there are over 40,000 ways to enter these eight ways of having three children. Help!
baby_B.addEventListener(MouseEvent.CLICK, letterB);
function letterB(event:MouseEvent)
{
text_entry.appendText("B");
}
baby_G.addEventListener(MouseEvent.CLICK, letterG);
function letterG(event:MouseEvent)
{
text_entry.appendText("G");
}
enter_btn.addEventListener(MouseEvent.CLICK, check);
function check(event:MouseEvent):void {
var solution_S:Array=["BBB","BBG","BGB","BGG","GBB","GBG","GGB","GGG "];
if(solution_S.indexOf(text_entry.text)>=0)
{
gotoAndStop("checkmark");
}
else
{
gotoAndPlay(62);
}
}
If you know the correct code, please write it out for me. Thanks!

You will just need to keep a little bit of state to know what the user has entered so far. One possible way of doing that is to have a custom object/dictionary that you initialize outside all your functions, so that it is preserved during the transitions between frames/runs of the functions:
var solutionEntered:Object = {"BBB":false, "BBG":false, /*fill in rest */ };
Then in your function check you can perform an additional check, something like:
function check(event:MouseEvent):void {
var solution_S:Array=["BBB","BBG","BGB","BGG","GBB","GBG","GGB","GGG "];
if(solution_S.indexOf(text_entry.text)>=0) {
// We know the user entered a valid solution, let's check if
// then entered it before
if(solutionEntered[text_entry.text]) {
// yes they entered it before, do whatever you need to do
} else {
// no they haven't entered it, set the status as entered
solutionEntered[text_entry.text] = true;
}
// continue rest of your function
}
// continue the rest of your function
}
Note that this is not necessarily an optimal solution, but it keeps with the code style you already have.

Try this:
import flash.text.TextField;
import flash.events.MouseEvent;
import flash.display.Sprite;
var correctAnswers : Array = [ "BBB", "BBG", "BGB", "GBB", "BGG", "GGB", "GBG", "GGG" ];
var answersSoFar : Array;
var textField : TextField; //on stage
var submitButton : Sprite; //on stage
var correctAnswerCount : int;
//for testing
textField.text = "BBB,BBG,BGB,GBB,BGG,GGB,GBG,GGG";
//textField.text = "BGB,BBB,GGG,BBG,GBB,BGG,GGB,GBG,";
//textField.text = "BBB,BBG, BGB,GBB,BGG, GGB, GBG, GGG";
//textField.text = "BBB,BBG,BGB,GBB,BGG,GGB,GBG";
//textField.text = "BBB,BBG,BGB,GBB,BGG,GGB,GBG,GGG,BBG";
submitButton.addEventListener( MouseEvent.CLICK, onSubmit );
function onSubmit( event : MouseEvent ) : void
{
var answers : Array = getAnswersArray( textField.text );
answersSoFar = [];
correctAnswerCount = 0;
for each ( var answer : String in answers )
if ( answerIsCorrect( answer ) ) correctAnswerCount++;
if ( correctAnswerCount == correctAnswers.length ) trace( "correct" );
else trace( "incorrect" );
}
function getAnswersArray( string : String ) : Array
{
string = removeInvalidCharacters( string );
return string.split( "," );
}
function removeInvalidCharacters( string : String ) : String
{
var result : String = "";
for ( var i : int, len = string.length; i < len; i++ )
if ( string.charAt( i ) == "B" || string.charAt( i ) == "G" || string.charAt( i ) == "," )
result += string.charAt( i );
return result;
}
function answerIsCorrect( answer : String ) : Boolean
{
if ( answerIsADuplicate( answer ) ) return false;
else answersSoFar.push( answer );
if ( answerIsInListOfCorrectAnswers( answer ) ) return true;
return false;
}
function answerIsInListOfCorrectAnswers( answer : String ) : Boolean
{
if ( correctAnswers.indexOf( answer ) == -1 ) return false;
return true;
}
function answerIsADuplicate( answer : String ) : Boolean
{
if ( answersSoFar.indexOf( answer ) == -1 ) return false;
return true;
}

note that in the original code you pasted, you have an extra space in the last element of your correct answer list - "GGG " should be "GGG"
this works
baby_B.addEventListener(MouseEvent.CLICK, letterB);
function letterB(event:MouseEvent) {
text_entry.appendText("B");
}
baby_G.addEventListener(MouseEvent.CLICK, letterG);
function letterG(event:MouseEvent) {
text_entry.appendText("G");
}
var valid:Array = ["BBB","BBG","BGB","BGG","GBB","GBG","GGB","GGG"];
enter_btn.addEventListener(MouseEvent.CLICK, check);
function check(event:MouseEvent):void {
var parts:Array = text_entry.text.split(/,\s*/g); // split the text into component parts
var dup:Array = valid.slice(); // copy the correct list
while(parts.length){ // run through each answer component
var part:String = parts.pop(); // grab the last one
part = part.replace(/(^\s+|\s+$)/g, ''); // strip leading/trailing white space
var pos:int = dup.indexOf(part); // is it in the list of correct answers?
if(pos != -1){ // if it is...
dup.splice(pos, 1); // then remove that answer from the list
}
}
if(dup.length == 0) { // if it's 0, they got all the correct answers
gotoAndStop("checkmark");
} else { // otherwise, they didn't get one or more correct answers
gotoAndPlay(62);
}
}

Related

Script that would find and mark the same words in the paragraph

I'm a fiction writer and I used to do my writing in MS Word. I've written some macros to help me edit the fiction text and one of them check the paragraph and marks (red) the duplicate (or triplicate words, etc). Example:
"I came **home**. And while at **home** I did this and that."
Word "home" is used twice and worth checking if I really can't change the sentence.
Now I mostly use google documents for writing, but I still have to do my editing in MS Word, mostly just because of this macro - I am not able to program it in the google script.
function PobarvajBesede() {
var doc = DocumentApp.getActiveDocument();
var cursor = DocumentApp.getActiveDocument().getCursor();
var surroundingText = cursor.getSurroundingText().getText();
var WordsString = WORDS(surroundingText);
Logger.log(WordsString);
//so far, so good. But this doesn't work:
var SortedWordsString = SORT(WordsString[1],1,False);
// and I'm lost.
}
function WORDS(input) {
var input = input.toString();
var inputSplit = input.split(" ");
// Logger.log(inputSplit);
inputSplit = inputSplit.toString();
var punctuationless = inputSplit.replace(/[.,\/#!$%\?^&\*;:{}=\-_`~()]/g," ");
var finalString = punctuationless.replace(/\s{2,}/g," ");
finalString = finalString.toLowerCase();
return finalString.split(" ") ;
}
If I could only get a list of words (in uppercase, longer than 3 characters), sorted by the number of their appearances in the logger, it would help me a lot:
HOME (2)
AND (1)
...
Thank you.
Flow:
Transform the string to upper case and sanitize the string of all non ascii characters
After splitting the string to word array, reduce the array to a object of word:count
Map the reduced object to a 2D array [[word,count of this word],[..],...] and sort the array by the inner array's count.
Snippet:
function wordCount(str) {
str = str || 'I came **home**. And while at **home** I did this and that.';
var countObj = str
.toUpperCase() //'I CAME **HOME**...'
.replace(/[^A-Z ]/g, '') //'I CAME HOME...'
.split(' ') //['I', 'CAME',..]
.reduce(function(obj, word) {
if (word.length >= 3) {
obj[word] = obj[word] ? ++obj[word] : 1;
}
return obj;
}, {}); //{HOME:2,DID:1}
return Object.keys(countObj)
.map(function(word) {
return [word, countObj[word]];
}) //[['HOME',2],['CAME',1],...]
.sort(function(a, b) {
return b[1] - a[1];
});
}
console.info(wordCount());
To read and practice:
Object
Array methods
This is a combination of TheMaster answer and some of my work. I need to learn more about the way he did it so I spent some learning time today. This function eliminates some problems I was having the carriage returns and it also removes items that only appear once. You should probably pick TheMasters solution as I couldn't have done it without his work.
function getDuplicateWords() {
var str=DocumentApp.getActiveDocument().getBody().getText();
var countObj = str
.toUpperCase()
.replace(/\n/g,' ')
.replace(/[^A-Z ]/g, '')
.split(' ')
.reduce(function(obj, word) {
if (word.length >= 2) {
obj[word] = obj[word] ? ++obj[word] : 1;
}
return obj;
}, {});
var oA=Object.keys(countObj).map(function(word){return [word, countObj[word]];}).filter(function(elem){return elem[1]>1;}).sort(function(a,b){return b[1]-a[1]});
var userInterface=HtmlService.createHtmlOutput(oA.join("<br />"));
DocumentApp.getUi().showSidebar(userInterface);
}
function onOpen() {
DocumentApp.getUi().createMenu('MyMenu')
.addItem('Get Duplicates','getDuplicateWords' )
.addToUi();
}
And yes I was having problems with get the results to change in my last solution.

AS3 many buttons with boolean function - less verbose?

I have twenty eight instances of a two-frame MovieClip (frame1 = off - frame 2 = on) to select PDFs to send. The following code works fine, but I am looking to tighten it up and make it less verbose and easier to read. I include only one reference to an instance for space and sanity sake.
function PDFClick(e:MouseEvent):void {
targetPDF = e.target.ID;
trace("targetPDF " +targetPDF);
if (targetPDF == "PDF1")
if (pdf.pcconnectionPDF1.currentFrame == 1)
{
pdf.pcconnectionPDF1.gotoAndPlay(2);
PDF1 = 1;
trace("PDF1 is "+PDF1);
}else{
pdf.pcconnectionPDF1.gotoAndPlay(1);
PDF1 = 0;
trace("PDF1 is "+PDF1);
}
Thanks! trying to learn
You'll want to generalize your calls to your ID, that way you don't need special code for each condition.
function PDFClick(e:MouseEvent):void {
var ID:String = e.target.ID;
var mc = pdf["pcconnection" + ID];
if (mc.currentframe == 1) {
mc.gotoAndPlay(2);
this[ID] = 1;
} else {
mc.gotoAndPlay(1);
this[ID] = 0;
}
}
How about this:
function PDFClick(e:MouseEvent):void {
targetPDF = e.target.ID;
trace("targetPDF " +targetPDF);
if (targetPDF == "PDF1") {
var frame:int = pdf.pconnectionPDF1.currentFrame;
pdf.pconnectionPDF1.gotoAndPlay( frame == 1 ? (PDF1 = 1)+1 : (PDF1 = 0)+1 );
}
}
I think that's about what you are looking for.

ScriptDB object size calculation

I'm trying to estimate the limits of my current GAS project. I use ScriptDB to chunk out processing to get around the 6 min execution limit. If I have an object like
var userObj{
id: //user email address
count: //integer 1-1000
trigger: //trigger ID
label: //string ~30 char or less
folder: //Google Drive folder ID
sendto: //'true' or 'false'
shareto: //'true' or 'false'
}
How would I calculate the size that this object takes up in the DB? I would like to project how many of these objects can exist concurrently before I reach the 200MB limit for our domain.
Whenever you've got a question about google-apps-script that isn't about the API, try searching for javascript questions first. In this case, I found JavaScript object size, and tried out the accepted answer in apps-script. (Actually, the "improved" accepted answer.) I've made no changes at all, but have reproduced it here with a test function so you can just cut & paste to try it out.
Here's what I got with the test stud object, in the debugger.
Now, it's not perfect - for instance, it doesn't factor in the size of the keys you'll use in ScriptDB. Another answer took a stab at that. But since your object contains some potentially huge values, such as an email address which can be 256 characters long, the key lengths may be of little concern.
// https://stackoverflow.com/questions/1248302/javascript-object-size/11900218#11900218
function roughSizeOfObject( object ) {
var objectList = [];
var stack = [ object ];
var bytes = 0;
while ( stack.length ) {
var value = stack.pop();
if ( typeof value === 'boolean' ) {
bytes += 4;
}
else if ( typeof value === 'string' ) {
bytes += value.length * 2;
}
else if ( typeof value === 'number' ) {
bytes += 8;
}
else if
(
typeof value === 'object'
&& objectList.indexOf( value ) === -1
)
{
objectList.push( value );
for( i in value ) {
stack.push( value[ i ] );
}
}
}
return bytes;
}
function Marks()
{
this.maxMarks = 100;
}
function Student()
{
this.firstName = "firstName";
this.lastName = "lastName";
this.marks = new Marks();
}
function test () {
var stud = new Student();
var studSize = roughSizeOfObject(stud);
debugger;
}

how to compare two array collection using action script

how to compare two arraycollection
collectionArray1 = ({first: 'Dave', last: 'Matthews'},...........n values
collectionArray = ({first: 'Dave', last: 'Matthews'},...........n values
how to compare..if equal just alert nochange if not alert chaged
If you just want to know if they are different from each other, meaning by length, order or individual items, you can do the following, which first checks to see if the lengths are different, then checks to see if the individual elements are different. This isn't terribly reusable, it's left as an exercise for the reader to split this apart into cleaner chunks :)
public function foo(coll1:ArrayCollection, coll2:ArrayCollection):void {
if (coll1.length == coll2.length) {
for (var i:int = 0; i < coll1.length; i++) {
if (coll1[i].first != coll2[i].first || coll1[i].last != coll2[i].last) {
Alert.show("Different");
return;
}
}
}
Alert.show("Same");
}
/* elements need to implement valueOf
public function valueOf():Object{}
*/
public static function equalsByValueOf(
first:ArrayCollection,
seconde:ArrayCollection):Boolean{
if((first==null) != (seconde==null) ){
return false;
}else if(!first && !seconde){
return false;
}
if(first.length!=seconde.length){
return false;
}
var commonLength:int = first.length;
var dictionary:Dictionary = new Dictionary();
for(var i:int=0;i<commonLength;i++){
var item1:Object = first.getItemAt(i);
var item2:Object = seconde.getItemAt(i);
dictionary[item1.valueOf()]=i;
dictionary[item2.valueOf()]=i;
}
var count:int = 0;
for (var key:Object in dictionary)
{
count++;
}
return count==commonLength;
}
/* valueOf sample
* something like javaObject.hashCode()
* use non changing fields(recommended)
*/
public function valueOf():Object{
return "_"+nonChangeField1+"_"+nonChangeField2+"...";
}
I was going to say this.
if(collectionArray === collectionArray1)
But that wont work (not triple = signs). As === is used to see classes.
I would write a function called check if object exists in array.
Create an array to hold elements that are not found. eg notFound
in Collection1 go through all the element and see if they exist in Collection2, if an element does not exist, add it to the notFound array. Use the function your created in step1
Now check Collection2, if an element is not found add it to the notFound array.
There is no 5.
Dude, use the mx.utils.ObjectUtil... the creators of actionscript have already thought about this.
ObjectUtil.compare(collection1, collection2) == 0;

AS3: indexOf() sub-array in a multi-dimensional array

var asdf:Array = [ [1,1] ];
trace( asdf.indexOf( [1,1] ) ); // -1
Why can't indexOf() find the [1,1] array?
Here is a little function I wrote a while ago that works great. I included a lot of comments and an example search/function to output the results.
// set up a multidimensional array that contains some data
var myArray:Array = new Array();
myArray.push(["granola","people... are great"," 4 ","10"]);
myArray.push(["bill","orangutan","buster","keaton"]);
myArray.push(["steve","gates","24","yes, sometimes"]);
myArray.push(["help","dave","jobs","hal"]);
// here we set up some properties on the array object to hold our search string and our results
myArray.myTarget = "steve";
myArray.myResults = [];
// now we call the search
myArray.forEach(multiSearch);
// this is the function that does all the heavy lifting....
function multiSearch(element:*, index:int, array:Array)
{
// see if we have a match in this array and pass back its index
for(var i:* in element)
{
if( element[i].indexOf( array.myTarget ) > -1 )
{
var tempArray:Array = array.myResults;
tempArray.push([index,i]);
array.myResults = tempArray;
}
}
}
// -------------------------------------------------------------------------------
// all the code below is OPTIONAL... it is just to show our results
// in the output window in Flash so you know it worked....
var printArray:Array = myArray.myResults;
for(var i:* in printArray)
{
trace("TARGET FOUND #: "+printArray[i][0]+", "+printArray[i][1]+" = "+myArray[ printArray[i][0] ][ printArray[i][1] ]);
}
// -------------------------------------------------------------------------------
It fails because when you do a [x,y] you are creating a new array, adsf contains one array and indexOf search for another one.
try:
trace([1,1] == [1,1]);
You will see that it prints false, since array are compare by reference.
One quick indexOf function, arrange it to suit your needs:
function isElmEquals(e1:*, e2:*):Boolean {
return (e1==e2);
}
function isArrayEquals(a1:Array, a2:Array):Boolean {
if (a1==a2)
return true;
if ((a1==null) || (a2==null)) {
return false;
}
if (a1.length!=a2.length)
return false;
for (var i:int=0;i<a1.length;i++){
if (!isElmEquals(a1[i], a2[i]))
return false;
}
return true;
}
function indexOf(value:Array, into:Array):int{
var i:int = -1;
into.some(
function(item:*, index:int, array:Array):Boolean {
if (isArrayEquals(item as Array, value)) {
i = index;
return true;
}
return false;
}
);
return i;
}
var i:int=indexOf([1,1], [[-1,1], [0,1], [1,1], [1,-1]]);
trace(i);
var j:int=indexOf([1,2], [[-1,1], [0,1], [1,1], [1,-1]]);
trace(j);
this works. probably because the inner array is typed.
var qwer:Array = [1,1];
var asdf:Array = [qwer];
trace( asdf.indexOf( qwer ) ); // 0