Boolean Randomness - Recursion... or not? - javascript

Trying to create a random (0/1) boolean operation. I have a multidimensional array set up. So far, everything works. Am I on the right path? I currently use recursion; is there a more efficient way to do this?
function randomMove(){
var cs=b.length,
cr=Math.floor((Math.random()*cs)+1)-1,
cc=Math.floor((Math.random()*cs)+1)-1;
if(b[cr][cc]===""){ // THIS IS DEPENDENT ON EXISTING CODE. VIEW FIDDLE. //
// DO STUFF //
} else { randomMove(); }
}
Is this considered a good way to create a random move for 2 players? Or is recursion the wrong process here?
Fiddle

Select the cells that can possibly be selected, then choose a random one by index.
http://jsfiddle.net/Ehqka/1/
$.fn.random = function(){
return this.eq(Math.floor(Math.random()*this.length));
};
function randomMove(){
$("#board td").not(".o,.comp").random().addClass('comp');
testWin();
}
It selects all cells in the board, drops the ones that have a class of o or comp from the selection, generates a random number between 0 and the number of remaining cells, then selects the cell at that index to add a class to.
Note, however, your method will be "faster" than the above on the first move due to the fact that on the first move, every move is a valid one therefore it will never iterate past the first time. Mine can be slightly optimized to be more efficient on the first by removing the .not(), but your's should still be "faster" because it doesn't have to go to the DOM.
"faster" is relative, my method will grow less efficient as the table grows than yours will on the first move. As more and more moves happen, your method will become less and less efficient since more and more of the possible moves are invalid.

Related

.forEach only moving half of array elements

Firstly, I'm new to JavaScript and in an effort to gain a better understanding of OOP I've decided to build solitaire (the three-card version mind you). In solitaire--as I'm sure is well known-- you deal out the cards in the whatever pattern and the remaining cards are placed off to the side; I'm calling these the 'reserve' deck. Then you move the cards you dealt around following the rules of the game (can only place a red 6 on a black 7, etc.) and when you run out of options you can pull three cards off the top of 'reserve' and see if you can use the third card to continue playing. If not, you pull three more cards, so on and so forth until you run out of cards in the 'reserve' deck. At this point, all the cards you pulled off the top of 'reserve' deck can then get placed back into the 'reserve' deck and then reused in the same manner.
And this is where my javascript is breaking. So I've created a Table object with the properties playSlots, winSlots, reserve and pick. playSlots is an array of arrays in which cards get dealt into, reserve is an array of the cards existent within the 'reserve' deck, winSlots isn't relative to this question and pick is an array that becomes populated with cards from reserve as it becomes necessary throughout game play. Pretty self-explanatory. When reserve becomes empty and you want to put the pick cards back into it, my code for this is currently:
if(!Array.isArray(newGame.reserve) || !newGame.reserve.length){
newGame.pick.forEach(function(cur, i, arr){
newGame.reserve.push(arr.shift());
});
}
The problem with this is that this only iterates through half of the cards in the pick array. I went through it in the debugger and it is, in fact adding the cards back into the reserve deck one at a time but once it gets to the 13th card it breaks out of the loop (28 cards cards get dealt out in the solitaire pattern, leaving 24 in the reserve deck...).
I was pretty sure this issue has something to do with the !newGame.reserve.length condition. That condition is something I found here:
Check if array is empty / does not exist. JS
and although the answer-er is pretty clear in his his explanation of this, I'm still kinda fuzzy on what it means. Thing is, I also tried this for a condition:
if(newGame.reserve[0] === undefined)
and had the exact same result. The .forEach loop is only iterating over half of the array elements.... What am I missing here?
You can achieve this easier using Array splice
Note: this reverses pick to the end of reserve, whereas your code does not
If you do not need it reversed, simply remove the .reverse()
It's not clear if you want pick reversed into reserve or not
var newGame = {
pick: [1,2,3,4],
reserve: [10,11,12]
};
newGame.reserve.push(...newGame.pick.splice(0, newGame.pick.length).reverse());
console.log(newGame);
in "old school" javascript, this can be written using .push.apply
var newGame = {
pick: [1,2,3,4],
reserve: [10,11,12]
};
newGame.reserve.push.apply(newGame.reserve, newGame.pick.splice(0, newGame.pick.length).reverse());
console.log(newGame);

How to bring an element to the front and put it back later in d3

I'm working on a graph in d3, that performs multitude of animations on different events. For one of those, I had to bring selected element to the front, which I did using following bit:
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
Thing is, after all the code I needed it in the front is performed, I want to put that element back exactly where I took it from. So, If an element had an index of 1 (out of 7 elements available), then I moved it to front (which changed the index to 6), now I want to set the index back to 1.
I tried putting it at the bottom of index array already (asigning it index 0), but it does not suit my needs.
Also, I'm willing to get rid of moveToFront() prototype, if there's any better way to accomplish what I want, that's just the best way I could find for now.

How do I check my condition on an if statement to see if it is working correctly?

I'm trying to make a type of circular display which cycles through a series of values as well as moving text elements within an svg file. It uses the hammer.js library and uses the drag event. The values can go in either direction. I have it working to some degree. As the last value is shown from an array, it goes back to the beginning of the array to get the first values. Or vice-versa.
var keyArray = ["C","C#","Db","D","D#","Eb","E","F","F#","Gb","G","G#","Ab","A","A#","Bb","B"];
This is my array. Here is how I wrap it past the end of the array and back to the beginning.
** As per the request of a few commenters and the suggested solution by Nina, I have modified the code below to reflect their suggestions. I have also added a few more lines for clarity of what is happening overall.**
var delta = keyArray.length - 5; // can be constant, it is always positive
for(i=0;i<5;i++){
//5 svg text element containing 5 musical keys
keys = document.getElementById("keys"+i);
//ev.deltaX is the change received from the hammer pan event
//text element moves relative to its original starting X
keys.setAttribute("x",startingSpots[i]+ev.deltaX%150);
currentX=keys.getAttribute("x");
currentEdgeIndex=keyArray.indexOf(keys.textContent);
//This if is what appears to be failing.
if (keys.getAttribute("x")>=565){
keys.setAttribute("x",currentX-150);
keys.textContent = keyArray[(currentEdgeIndex + delta) % keyArray.length];
}
}
With the suggested changes, I removed the Number() calls as well as implementing the modulus for the wrapper. The behavior is still erratic. On the example below, if you pan to the right, as the first text element reaches 565, it meets the condition for the if, is moved back to the left by 150.
What it should do next is to change the textContent to the next appropriate value in the array. However, this is where it becomes erratic, it is no longer past 565 so it does not meet the condition of the if statement, but the text changes at every increment of the pan event as if it were.
I am sure I am not seeing something simple that is causing the trouble but not sure what it is.
The array does appear to be circling correctly, though I'm still not sure "How can I check to see if the if statement is being correctly evaluated and met?"
The project can be viewed here. http://codepen.io/cmgdesignstudios/pen/zrmQaE?editors=1010
* Edit with solution *
Nina suggested the problem lie in the handling of the touch event. After further investigation, I found she was correct. I had originally been moving the object relative to its starting position and the deltaX from the touch event. I then was trying to change the current position by simply moving it back to the left rather than adjusting the starting position.
So I replaced the
keys.setAttribute("x",currentX-150);
with
startingSpots[i]-=150;
This kept the movement relative to the starting position and the deltaX of the touch event.
Please delete the Number(...) casting where it's not necessary. ...length returns always number and the result of calculation is a number too.
Your actual key feature is to move 5 entries down, and this can be achieved wit a simple addition and a modulo, to keep the value in a specific range, like
keys.textContent = keyArray[(keyArray.length + currentEdgeIndex - 5) % keyArray.length];
Further simplified can this calculation then lead to just add a positive number of
delta = keyArray.length - 5; // can be constant, it is always positive
keys.textContent = keyArray[(currentEdgeIndex + delta) % keyArray.length];
and make the modulo out of it.

InDesign extendScript: How do I transform the entire selection?

By using the simple array app.selection[x], you can apply a transformation to any object in the selection, independently. But how do I apply a transformation to the entire selection together?
For example: inside InDesign, I can select two side-by-side objects and flip them horizontally, causing them to switch places and flip.
Inside a script, I can target each object in the selection, but they will not switch places; they will remain in the same place and flip.
for ( var x = 0; x < app.selection.length; x++ ){
app.selection[x].absoluteFlip = Flip.HORIZONTAL;
}
I could possibly group the selection, apply a transformation, then ungroup when finished, but this seems like unnecessary bulk that could slow down the code. I can easily do it manually inside InDesign, so it should follow that there's some way to access app.selection as a single object instead of an array. Does such an object exist?
Not really a solution, but it's worth noting that I don't think absoluteFlip is the action being performed, but a state indicating if the item has ben flipped. It's writable so you can set the state, but I think what's happening when using the menu to flip is flipItem: http://jongware.mit.edu/idcs6js/pc_PageItem.html#flipItem,
in which you can set "around" coordinates.
Now getting the origin of the selection box isn't straightforward for some reason (or maybe it is but I don't know how), but you can either use first object coordinates to set the origin so you can flip it around different coordinates depending on order of selection. Or you can sort the array to find left most coordinates (or whichever is needed), like this:
var selection_array = app.selection;
selection_array.sort(function(a, b){return a.visibleBounds[1]-b.visibleBounds[1]})
var flip_origin = [selection_array[0].visibleBounds[1],selection_array[0].visibleBounds[0]]
for(i=0;i<app.selection.length;i++){
app.selection[i].flipItem(Flip.HORIZONTAL, flip_origin);
}
Not sure it's easier or faster than grouping and ungrouping though.
Consider resize. It has a "individual/global" parameter :
void resize (in: varies, from: varies, by: ResizeMethods, values: Array of varies[, resizeIndividually: bool=true][, consideringRulerUnits: bool=false])
Resize the page item.

Determine direction of change in array

Given a sorted array, eg. [1,2,3,4,5] and a variation of this array, where one of the items got moved to a different position (so no longer sorted) eg. [1,2,5,3,4] or [2,3,4,1,5] I need to reliably determine the "direction" of the move - left or right depending on which way that one element got moved
(5 got moved left in first example, 1 got moved right in a second example)
At the moment I have a solution that walks (from the left) over arrays and compares their values at index i - as soon as they are not the same (to determine point of discrepancy), it then compares values at i+1. if old[i+1] > new[i+1] then left, otherwise right
//pseudocode:
function(old, now) {
for(var i in old) {
if (old[i] !== now[i]) {
return old[i+1] > now[i+1] ? "left" : "right"
}
}
}
EDIT
is there a more reliable (and potentially more efficient) solution to that problem? at the moment it gives "false positives" in certain situations. Do we event need the first array? its always sorted and sequential.
There is not really any room for improvements in your solution - You won't find a way to solve your problem without going trough all members of the array and checking them against the member in the other array.
Your current time complexity is O(n), which is actually not too bad. Therefore you should not worry about this function and if your application is working too slow, you should look for other fields to improve on.
Not really an answer, more discussion of the algorithm.
If you want guaranteed iteration from 0 and to ignore inherited enumerable properties, replace for..in with a for, while or do loop.
Given iteration from index 0, the algorigthm you have doesn't work. e.g. in the following:
var old = [1,2,3];
var now = [2,1,3];
value 1 has been moved right. The algorithm will find a miss-match at index 0, then compare the values at index 1. old[1] is greater than now[1] so "left" is returned.
It's also impossible to determine the direction of some moves, e.g. in the above was 1 moved right or 2 moved left? Another example:
var old = [1,2,3,4,5];
var new = [1,2,3,5,4];
Did 4 move right or 5 move left? A rule is required to determine which takes precidence.

Categories

Resources