Minesweeper reveal nearby tiles function - javascript

I am trying to make the blank or "0" function of ms in a web page.
The goal:
what happens in mine: what should happen:
the red square indicates the button that was clicked, and the green circles indicate its adjacent squares/tiles.
My approach or logic to make this function was:
step1: if the button that was clicked is a 0, reveal its adjacent tiles.
step 2: for every adjacent tile, if THAT tile is 0, reveal THAT TILES' adjacent tiles. and so on until all adjacent tiles of every connected 0 is revealed.
The code of my function: (the parameters are the coordinates of the button that is clicked. e.g.the red square/tile in the picture above has coordinates 3,6)
function RevealNearbyTiles(y,x){
var cordsx; //cordsx and cordsy represent the adjacent tiles of the coordinates (the parameters).
var cordsy;
var coordinates;
for(i=-1; i<2; i++){ //for every adjacent tile:
for(j=-1;j<2;j++){
cordsx = x;
cordsy = y;
if(i === 0 && j === 0){
continue;
}
else{
cordsx += j;
cordsy += i;
//if this ^ offset is within the grid:
if((cordsx >= 0 && cordsx < 10) && (cordsy >= 0 && cordsy < 10)){
//the coordinates of the tile.
coordinates = $("#mstable tr:nth-of-type("+(cordsy+1)+") td:nth-of-type("+(cordsx+1)+") .tiles");
//if it has not been revealed
if(coordinates.parent().attr("data-revealed") === "false"){
//reveal this coordinate.
coordinates.empty().append("<p id='number'>"+coordinates.parent().attr("data-value")+"</p>");
coordinates.parent().attr("data-revealed", "true");
//if this coordinate is 0
if(coordinates.parent().attr("data-value") === " "){
//reveal this coordiantes' nerabytiles
RevealNearbyTiles(cordsy,cordsx);
}
}
}
}
}
}
}
The attribute "data-value" is the number of nearby bombs the tile has.
The attribute "data-revealed" is true if the tile is revealed, or false if it's not. They both work, don't worry too much about them.
The code for every tile button:
$(".tiles").click(function(){
//if this button is clicked, reveal this tile
$(this).empty().append("<p id='number'>"+$(this).parent().attr("data-value")+"</p>");
$(this).parent().attr("data-revealed","true");
//if this tile's value is 0, call the function
if($(this).parent().attr("data-value") === " "){
RevealNearbyTiles($(this).parent().data("index").a,$(this).parent().data("index").b);
}
});
What I think the problem is: The for loop is supposed to run for every adjacent tile of the tile that was clicked, but when it runs the function for the first tile, it forgets about all other adjacent tiles. I need to make it so that the function runs on all adjacent tiles that are 0, and on all of THEIR adjacent tiles that are 0 and so on.
Thanks for your help, its a hard problem to explain =/. I searched many places but could not find an answer. Sorry for the very long and specific problem.

I think the issue is with your two for loops, they use global vars called i and j which are the same for each call to RevealNearbyTiles(). You can fix this with the following code...
for(var i=-1; i<2; i++){ //for every adjacent tile:
for(var j=-1;j<2;j++){
I think what happened was that your algorithm ran until it hit the case where it had revealed all adjacent tiles (e.g. i = 1, j = 1), it then exited up the call chain with those values and exited each loop rather than keep executing them.

Related

Remove multiple layers on leaflet draw

I use leaflet draw for drawing polygons on a map.
At the moment, I'm only able to delete the clicked polygons.
This is an example of what I have right now:
http://leaflet.github.io/Leaflet.draw/docs/examples-0.7.x/full.html
Let's say that you draw 3 polygons. If you want to delete the first 2, you have to click on the trash icon, click on the first 2, and click save. What I want to achieve is to not have to click on the second one. I actually have the id of the second but I can't manage to add it to the removingLayers array that keeps the clicked layers in.
What I have atm:
function deleteSubPolygons(e) {
var layersToRemove = [];
if (e.layer && e.layer._originalPoints != null && e.layer._latlngs != null && e.layer.id != null && Number.isInteger(e.layer.id)) {
var polygonChildren = getPoligonChildren(e.layer.id);
for (var l in map._layers) {
if (polygonChildren.indexOf(map._layers[l].id) > -1) {
polygonsToDelete.push(map._layers[l].id);
layersToRemove.push(map._layers[l]);
}
}
for (var i = 0; i < layersToRemove.length; i++) {
map.removeLayer(layersToRemove[i]);
}
}
}
I managed to remove them from the view manually, but the revert option doesn't work well anymore.I'm pretty sure I should just push the layer I want to remove to a list of leaflet-draw.js as they are doing:
this._deletableLayers.removeLayer(e),
this._deletedLayers.addLayer(e)
My question would be: How can I acces the _deletedLayers from outside?
I solved this by firing the click event on the polygons I want to delete when I'm in delete mode:
for (var l in map._layers) {
if (polygonChildren.indexOf(map._layers[l].id) > -1) {
map._layers[l].fireEvent('click');
}
}

Adding and deleting content between two points on a document

I currently have a document, with help from StackOverflow users already, that randomly generates questions, adds it to the end of a document, and then has the ability to delete all the questions posted. This is based on deleting everything under a horizontal rule.
Link to GDrive containing example document & code: LINK TO GDRIVE
You can also see what it currently does here: https://imgur.com/QVrOZKu
However, I now want to only want to add content after a certain point in the document, as well as only delete content between two certain points. You can see the two horizontal rules in an image below in which I want to add/delete
content.
The first horizontal rule in the picture is the third horizontal rule in the document.
Has anyone got any ideas how I can delete and add content between those two points? I've tried using child index's but failed miserably.
This is similar to Deleting all content down from the second horizontal line in a document so I adapt the solutions. First function deletes the paragraphs between the 3rd and 4th line. It counts horizontal lines as we loop through paragraphs. When the count reaches 3, start deleting subsequent paragraphs. When it exceeds 3, stop the loop.
function deleteFrom3to4() {
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('');
var para = body.getParagraphs();
var ruleCount = 0;
for (var i = 0; i < para.length - 1; i++) {
if (para[i].findElement(DocumentApp.ElementType.HORIZONTAL_RULE)) {
ruleCount++;
}
else if (ruleCount == 3) {
body.removeChild(para[i]);
}
if (ruleCount > 3) {
break;
}
}
}
And this one inserts a paragraph after the 3rd horizontal line. Again, it loops until the 3rd line is found; inserts a paragraph after it (expressed by body.getChildIndex(para[i]) + 1 child index) and stops.
function insertAfter3() {
var body = DocumentApp.getActiveDocument().getBody();
body.appendParagraph('');
var para = body.getParagraphs();
var ruleCount = 0;
for (var i = 0; i < para.length - 1; i++) {
if (para[i].findElement(DocumentApp.ElementType.HORIZONTAL_RULE)) {
ruleCount++;
}
if (ruleCount == 3) {
body.insertParagraph(body.getChildIndex(para[i]) + 1, "Here is a new paragraph");
break;
}
}
}

Making "next" button work

I am creating a simple numeracy game where by there is a grid populated with numbers. The numbers are hidden and when the game is run the grid space is highlighted. A div on the side produces a sum to help the user get the correct answer. The user then clicks the corresponding numbers that animate into position and signal whether they are right or wrong.
I have but in a next button ('.minibutton'), so that if the user gets the answer wrong 3 times they have a chance to move to the next question. The button also has a .trigger('click') function so that when a word is completed correctly it moves on automatically and keeps the game flowing.
My problem is the button has stopped working and I am clueless as to why. Here is the ".minibutton" function...
$('.minibutton').click(function() {
var sum = $('#answerlist li[data-answer="' + answer + '"]').data('sum');
$(right).val('');
$(wrong).val('');
$('td').removeClass('spellanswer');
score.wrong = 0;
var r = rndanswer;
while (r == rndanswer) {
rndanswer = Math.floor(Math.random() * (listOfanswers.length));
}
when I added this statement the button stopped working
//for (var x = 0; x < listOfanswers.length; x++) {
//if (eval(sum.replace("=", "").replace("x", "*")) == listOfanswers[x].name) {
// rndanswer = x;
// }
//}
$('td[data-answer="' + listOfanswers[rndanswer].name + '"]').addClass('spellanswer');
$('td[data-answer=' + answer + ']').removeClass('answerglow').removeClass('answerglow4').removeClass('answerglow3').css('color', 'transparent');
var noExist = $('td[data-answer=' + listOfanswers[rndanswer].name + ']').hasClass('answerglow2');
if (noExist) {
$('.minibutton').prop('disabled', false);
} else {
$('.sumstyle').text(sum);
sum.hide();
}
}).trigger("click");
http://jsfiddle.net/ZAfVZ/28/
Running the jsFiddle gives this error:
Object 1 + 2 = has no method 'hide'
You're trying to hide a string, which is obviously wrong. It's causing the script to stop executing after that point. Remove/comment out sum.hide(), and the 'next' button appears after three wrong guesses.
I've edited the JSFiddle to define sum (a text string containing the sum that the player is trying to answer) and seumElem (the HTML element containing the sum) at the top of the function: http://jsfiddle.net/ZAfVZ/30/

"Bucket Fill" algorithm in Javascript or coffeescript

i'm writing a little coffeescript/js app that allows user to design icons ( 16x16 pixels or 32X32 pixels ).
The icon is actually a 2 dimensional array with color cells. A cell can have a color or be empty.
I want the user to be able to fill blank cells with a "bucket paint" tool.
It means that
if the user clicks on a blank cell , all the cells that are blank next to the clicked cell with be filled with the choosen color , until it reaches a colored cell
if the user clicks on a colored cell , all the cells that are next to the clicked cell and share the same color will be filled , but not the blank ones nor the colored ones ( with another color ).
The app already allows the user to fill cells one by one with a chosen color , or delete colored cells with a pen tool.
Any suggestions ?
(ps : i'm not using html canvas to draw )
Since this is only 16x16 or 32x32 you can use a recursive solution:
Say your starting point is to change pixel x/y from color A to color B (A or B can be empty).
In pseudo code:
function floodfill(x,y,A,B) {
if ((x<0) || (x>15) || (y<0) || (y>15)) return;
if (get_color(x,y)!=A) return;
set_color(x,y,B);
floodfill(x-1,y-1,A,B);
floodfill(x-1,y,A,B);
floodfill(x-1,y+1,A,B);
floodfill(x,y-1,A,B);
floodfill(x,y+1,A,B);
floodfill(x+1,y-1,A,B);
floodfill(x+1,y,A,B);
floodfill(x+1,y+1,A,B);
}
I'm not entirely sure what type of suggestions you're looking for, but you should take a look at flood fill algorithms.
Here it is on Wikipedia: http://en.wikipedia.org/wiki/Flood_fill
Allright , here is how i solve my problem in coffeescript.
It is an exemple using canvas.
the script should run on any page where there is a canvas element with a id of canvas , i had to create my own stack because of stackoverflow issues.
log = ->
console.log arguments
class Point
constructor:(#x,#y)->
class BucketFiller
MAXITERATION:100000
factor:1
fill : (ctx,pixel, colCible, colRep)->
P = []
max = #MAXITERATION
if #getColorAtPixel(ctx,pixel)!=colCible then return null
P.push(pixel)
while P.length > 0 and max >=0
--max
currentpixel = P.pop()
#fillRect(ctx,currentpixel.x,currentpixel.y,#factor,#factor,colRep)
if #isInCanvas(ctx,currentpixel)
if #getColorAtPixel(ctx,#up(currentpixel)) == colCible then P.push(#up(currentpixel))
if #getColorAtPixel(ctx,#down(currentpixel)) == colCible then P.push(#down(currentpixel))
if #getColorAtPixel(ctx,#right(currentpixel)) == colCible then P.push(#right(currentpixel))
if #getColorAtPixel(ctx,#left(currentpixel)) == colCible then P.push(#left(currentpixel))
return
fillRect:(ctx,x,y,width,height,color)->
ctx.fillStyle = color
ctx.fillRect(x,y,width,height)
return
down :(pixel)->
return {x:pixel.x,y:pixel.y-#factor}
up:(pixel)->
return {x:pixel.x,y:pixel.y+#factor}
right :(pixel)->
return {x:pixel.x+#factor,y:pixel.y}
left :(pixel)->
return {x:pixel.x-#factor,y:pixel.y}
getColorAtPixel:(ctx,pixel)->
try
imageData = ctx.getImageData(pixel.x,pixel.y,1,1)
catch e
return null
return #rgbArrayToCssColorString(imageData.data)
rgbArrayToCssColorString:(array)->
result = "rgb(#{array[0]},#{array[1]},#{array[2]})"
return result
isInCanvas : (ctx,pixel)->
result = ((0 <= pixel.x <= ctx.canvas.width) and (0 <= pixel.y <= ctx.canvas.height))
return result
main=->
buckfiller = new BucketFiller()
log("start")
canvas = document.getElementById("canvas")
ctx = canvas.getContext("2d")
penPosition = new Point(2,10)
fillColor = "rgb(255,0,0)"
colCible = buckfiller.getColorAtPixel(ctx,penPosition)
try
buckfiller.fill(ctx,penPosition,colCible,fillColor)
catch e
log e
window.onload=->
main()

Weird behaviour with for loop in JavaScript

I have this image table that has two columns and 20 rows. When executing this for loop, all the rows are working okay except for the first row, which only displays the first image on the left. It's really weird; is there something wrong with the order of the execution?
var image= [];
var rows=5;
for (var i = 0; i < test.length; i++) {
var avatar = test[i].image; // The profile image
if(i % 2 === 0){
image[i]= Titanium.UI.createImageView({
top:row,
image:avatar
align:right
});
win.add(image[i]);
//trying to increase the image
row =row+200;
} else if(i % 2 === 1) {
image[i]= Titanium.UI.createImageView({
top:row,
image:avatar
align:left
});
win.add(image[i]);
}
}
i=0, i%2=0, show the image (supposed to be right), row+=200;
i=1, i%2=1, show the image (left side), row stays same
i=2, i%2=0, show the image (right side), row+=200
0%2 = 0, and that represents your right side image, and then it goes to the next line. Just need to play around with where you increment row and which side your loop starts with.

Categories