Javascript Can Push() and Pop() and Image Replacement Work within an Array - javascript

Can Push() and Pop() and Image Replacement Work within an Array?
8th Gr math teacher attempting to create a slide show of question images that pop() and push() through an image array based on student responses. If the student answers correctly the question is popped, but if they answer incorrectly it is added to the end of the queue. Additionally, since deleting elements in the DOM is bad, I am replacing the current image's src and id with that of the next element in queue. The array is then popped and pushed along, but whenever I enter in the incorrect answer twice the same image appears.
I have moved the global variable that holds the array, domEls, inside of the function retrieveAnsForImage to force it to randomize the images in the array. When I do this, the images change correctly so I believe it is the push() and pop() commands.
I included a snippet that doesn't work here, but works like a champ in Notepad ++. I just took a crash course in Javascript, HTML and CSS last month on Codecademy, I am very new to this. Thank you for reading.
//Jquery
$(document).ready(function() {
$(function() {
$('img.card').on('contextmenu', function(e) {
e.preventDefault();
//alert(this.id);
openPrompt(this.id);
});
});
});
//Provide and Shuffle array function
function shuffleImgs() {
var imgArr = [
"image1",
"image2",
"image3",
"image4",
"image5",
"image6",
"image7",
"image8",
"image9"
];
var currentIndex = imgArr.length, temporaryValue, randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = imgArr[currentIndex];
imgArr[currentIndex] = imgArr[randomIndex];
imgArr[randomIndex] = temporaryValue;
}
alert("shuffle");
return imgArr;
}
function arrStack() {
var imgArr = shuffleImgs();
//Map over the array to create Dom elements
var domElements = imgArr.map(function (imgName, index) {
var cardDiv = document.createElement('div');
var cardImage = document.createElement('img');
//Add img id and class
cardImage.id = imgName;
cardImage.classList.add('card');
//Set img source
cardImage.src = `images/${imgName}.jpg`;
//Put it all together
cardDiv.appendChild(cardImage);
return cardDiv;
});
//this notation to call nested function for Global var stack
this.nDomElements = function () {
stackDomEl = domElements;
return stackDomEl;
}
//Display last element in array
//this notation to call the nested function from outside the function
this.nDisplayLastArr = function displayLastArr() {
var lastImgArr = domElements[domElements.length - 1];
//alert(lastImgArr);
//Append the elements to the DOM
var modal = document.querySelector('div.modal');
modal.appendChild(lastImgArr);
return lastImgArr; //Use brackets when your are returning more than one variable
}
}
//Function called from Jquery to open prompt to answer question
function openPrompt(imageId) {
var userAns = prompt("Please enter your answer below and click OK");
if (userAns == null || userAns == "") {
alert("User cancelled the prompt. Exit and please try again!");
}
else {
/*Vain hope that I can pass imageId from click event through the user prompt
to the answer checking function retrieveAnsForImage*/
retrieveAnsForImage(imageId, userAns); //out of scope?
}
}
//Global variable
func = new arrStack();
window.domEls = func.nDomElements();
//Compare user responses with the question image by use of the click image id
function retrieveAnsForImage(imageId, userAns) {
//Change these variables to the correct answer whenever this website is reused in other assignments
var ansImage1 = "1";
var ansImage2 = "2";
var ansImage3 = "3";
var ansImage4 = "4";
var ansImage5 = "5";
var ansImage6 = "6";
var ansImage7 = "7";
var ansImage8 = "8";
var ansImage9 = "9";
//Give students a second chance to retry a question
//var hintCounter = 0; //include a while statement above the if statements to allow students a retry
/*Compare user response with correct case answer and correct clicked image.
Students may enter the right answer for the wrong image hence the &&.
Images will always be refered to as image1, image2, etc.*/
if (userAns === ansImage1 && imageId === "image1") {
correctAns(imageId);
}
else if (userAns === ansImage2 && imageId === "image2") {
correctAns(imageId);
}
else if (userAns === ansImage3 && imageId === "image3") {
correctAns(imageId);
}
else if (userAns === ansImage4 && imageId === "image4") {
correctAns(imageId);
}
else if (userAns === ansImage5 && imageId === "image5") {
correctAns(imageId);
}
else if (userAns === ansImage6 && imageId === "image6") {
correctAns(imageId);
}
else if (userAns === ansImage7 && imageId === "image7") {
correctAns(imageId);
}
else if (userAns === ansImage8 && imageId === "image8") {
correctAns(imageId);
}
else if (userAns === ansImage9 && imageId === "image9") {
correctAns(imageId);
}
else {
window.alert("Incorrect Answer");
incorrectAns();
}
function correctAns(){
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change im
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
domEls.pop();
//Think about when the array is completely gone
//while domEls.length !== 0;
}
function incorrectAns(){
//Last element in array
var LastElArr = domEls[domEls.length - 1];
//Second to last element in array
var SecLastElArr = domEls[domEls.length - 2];
//Pull image id from second to last element in array
var nextImgId = SecLastElArr.querySelector("div > img").id;
//Pull image id from document
var imgId = document.querySelector("div > img").id;
//Student incorrect answer change image src and id to next element in queue
document.getElementById(imgId).src = `images/${nextImgId}.jpg`;
document.getElementById(imgId).id = nextImgId;
//Remove last element in array
domEls.pop();
//move the last element to the first element in the array for another attempt
domEls.push(LastElArr);
alert(domEls.length);
}
}
function overlay() {
var el = document.getElementById("overlay");
el.style.visibility = (el.style.visibility == "visible") ? "hidden" : "visible";
}
#overlay {
visibility: hidden;
position: absolute;
left: 0px;
top: 0px;
width:100%;
height:100%;
text-align:center;
z-index: 1000;
background-color: rgba(0,191, 255, 0.8);
}
#overlay div {
width:70%;
margin: 10% auto;
background-color: #fff;
border:1px solid #000;
padding:15px;
text-align: center;
}
body {
height:100%;
margin:0;
padding:0;
}
#close-img {
float: right;
clear: right;
width: 30px;
height: 30px;
bottom: 0;
right: 0;
}
<!DOCTYPE html>
<html>
<head>
<title></title>
<span> "Left click to view any questions. Right click (two finger tap) to answer the question and claim the tile. Each player must claim 4 tiles to successfully complete the assignment."</span>
<link href="https://fonts.googleapis.com/css?family=Oswald:300,700|Varela+Round" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="Stack Rnd Temp.css">-->
<script type="text/javascript" src="Stack Rnd Temp.js"></script>
<script src="jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="StackRndTempjq.js"></script>
</head>
<body>
<div class="title">
<h1></h1>
</div>
<div id="gameboard"> <!--Container for all nine divs-->
<a href='#' onclick='overlay()'>Click here to show the overlay</a>
</div>
<div class="modal" id="overlay">
<p> "Right click to answer the question"</p>
<script>
func = new arrStack();
func.nDisplayLastArr();
</script>
<img src="images/close.png" id="close-img" onclick="overlay()">
</div>
</body>
</html>

Your issue is that pop removes the last element from the array while push adds the element to end of the array.
What you probably want to do is use shift to remove the the first element from the array and pop it back to the end if the answer is wrong.
Alternately, you could pop the last element and use unshift to insert back into the beginning of you want to work in the other direction.
Here's a quick mockup without images.
var currentTest = null;
function getTest() {
$('#answer').html("").hide();
if (tests.length > 0) {
currentTest = tests.shift(); // remove the first question
$('#question').fadeIn(450).html(currentTest.q);
return currentTest;
} else {
$('#answer').html("Finished").fadeIn(500);
$('#btnCorrect').unbind();
$('#btnWrong').unbind();
}
}
var tests = [];
for (var i = 0; i < 5; i++) {
var question = "Question " + i;
var answer = "Answer " + i;
tests.push({
q: question,
a: answer
});
}
$('#btnCorrect').click(function() {
$('#question').hide();
$('#answer').fadeIn(450).html("Correct!");
window.setTimeout(getTest, 750);
});
$('#btnWrong').click(function() {
$('#question').hide();
tests.push(currentTest); // put the question back in the array
$('#answer').fadeIn(450).html("Incorrect!");
window.setTimeout(getTest, 750);
});
$(document).ready(function() {
getTest();
})
* {
font-family: arial;
}
#panel {
height: 50px;
}
#answer {
border: 1px solid #cccccc;
background: #dedede;
width: 400px;
}
#question {
border: 1px solid #999999;
background: #dedede;
width: 400px;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
</head>
<body>
<div id="panel">
<div id="answer"></div>
<div id="question"></div>
</div>
<input id="btnCorrect" value="Mock Correct Answer" type="button">
<input id="btnWrong" value="Mock Wrong Answer" type="button">
</body>
</html>

Related

Getting a position of an element in a 2D array

So I'm building a a turn based board game which needs to contain 2 player that can move across the map. I'm stuck at getting the position of the player(which is just a simple div element) inside of that 2D array. I've tried using indexOf, but even tho it's placed inside an onclick function, always returns 0.
The html code contains just of few div's with col classes:
And here is the JavaScript code (btw it contains some unnecessary stuff that I've just added for test purposes) :
let row = document.querySelector('.row');
let fields = document.getElementsByClassName('col-md-2')
let fieldsArr = Array.from(fields);
let header = document.getElementById("clicked");
let cols = header.getElementsByClassName("col-md-2");
let player = document.getElementById('player');
let player2 = document.getElementById('player2');
let blockedField = document.getElementsByClassName('blocked');
fieldsArr.sort(function() {
return 0.5 - Math.random();
}).forEach(function(el) {
row.appendChild(el);
});
// ADD AN EVENT LISTENER AND LISTEN FOR COLS ID
function replyClick(e) {
e = e || window.event;
e = e.target || e.srcElement;
if (e.nodeName === 'DIV') {
let changable = e.id;
//console.log(changable);
}
}
// CREATE A 2D ARRAY (MAP)
var map = [];
while (fieldsArr.length) map.push(fieldsArr.splice(0, 6));
// ON CLICK ADD A CLASS OF ACTIVE
for (var i = 0; i < cols.length; i++) {
cols[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
// MOVE PLAYER ONE ACROSS THE MAP
function movePlayer(multWidth, multHeight) {
$(".active").append(player);
if ((row).click > multWidth) {
alert(1)
}
}
// MOVE PLAYER 2 ACROSS THE MAP
function movePlayer2() {
$(".active").append(player2);
}
// MAKE GRAYED OUT FIELD UNAVALIABLE AND SHOW AN ALERT
$(blockedField).css("pointer-events", "none");
// APPEND PLAYER1(2) TO THE FIRST(LAST) FIELD ON THE MAP
map[0][0].appendChild(player);
map[5][5].appendChild(player2);
// GET PLAYERS CURRENT POSITION
$(row).click(function() {
let current = player.offsetTop;
});
const widthAllowed = 3 * 156;
const heightAllowed = 3 * 146;
// LIMIT PLAYER MOVES
let player1Moves = 3;
player2Moves = 3;
$(row).click(function() {
movePlayer();
let remainingMoves = player1Moves -= 1;
if (remainingMoves === 0) {
alert("You don't have any more moves. Player's 2 turn.");
$(player).css("pointer-events", "none");
$(row).click(movePlayer2);
}
})
for (var x = 0; x < map.length; x++) {
for (var y = 0; y < map[x].length; y++) {
console.log(x, y);
}
}
console.log(map);
console.log(map[2][5]);
console.log(map[5][0]);
You should take some beginner jquery/javascript course, since your question is very simple and you will find the whole programming thing way easier with a few basic concepts (like selectors, events and callbacks)
That said, here is a basic example of how to return the div element that contains the player element and how to use event attachment instead of inline events.
let row = $('.row');
row.on('click', replyClick);
function replyClick(e) {
var targetRow = $(e.target);
$('.row > div.active').removeClass('active');
targetRow.addClass('active');
var player = $('.row div.player');
alert(player.parent().attr('id'));
};
.player {
width: 20px;
height: 20px;
background: red;
}
.row > div {
padding: 10px;
width: 20px;
height: 20px;
border: 1px solid red;
}
.row > div.active {
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div id="col1" class="col-md-2">
<div class="player"></div>
</div>
<div id="col2" class="col-md-2 blocked"></div>
<div id="col3" class="col-md-2 active"></div>
<div id="col4" class="col-md-2"></div>
</div>
</div>

How should I refer back to the an element before (in a different function), and modify it in a new function?

How do I make reference to the specific dashes I created, and add a
specific letter to them. Read the comments and code to get a better
context. Thanks for any help in advance!!
<!Doctype html>
<html lang="en">
<head>
<style>
ul {
display: inline;
list-style-type: none;
}
.boxes {
font-size:1.6em;
text-align:center;
width: 10px;
border-bottom: 3px solid black;
margin: 5px;
padding: 10px;
display: inline;
}
.hidden {
visibility: hidden;
}
.visible {
visibility: visible;
}
</style>
</head>
<body>
<script>
var possibleWord = ["COW", "BETTER", "HARDER", "JUSTIFY", "CONDEMN",
"CONTROL", "HELLO", "UNDERSTAND", "LIFE", "INSIGHT","DATE",
"RIGHTEOUSNESS"];
var hangmanWord = possibleWord[Math.floor(Math.random() *
possibleWord.length)];
var underlineHelp;
var space;
var guess;
var guesses = [];
var placement;
var underscores = [];
var character = [];
var textNodes = [];
window.onload = function () {
placement = document.getElementById('hold');
underlineHelp = document.createElement('ul');
placement.appendChild(underlineHelp);
for (i = 0; i < hangmanWord.length; i++) {
underscores = document.createElement('li');
underscores.setAttribute('class', 'boxes');
guesses.push(underscores);
underlineHelp.appendChild(underscores);
character = document.createElement('span');
character.appendChild(document.createTextNode(hangmanWord[i]));
character.classList.add('hidden');
underscores.appendChild(character);
}
This is the area I want to refer to later.
for(x=1;x<=26;x++){
document.getElementById("test").innerHTML = hangmanWord;
var btn = document.createElement("BUTTON");
var myP = document.createElement("br");
var letter = String.fromCharCode(x+64);
var t = document.createTextNode(letter);
btn.appendChild(t);
btn.id = letter;
Just creating buttons. This is important when I say 'this.id' down below.
btn.addEventListener("click", checkLetter);
document.body.appendChild(btn);
//add a line break 'myP' after 3 buttons
if (x%10==0) {
document.body.appendChild(myP);
}
}
}
function checkLetter(){
//this refers to the object that called this function
document.getElementById("p1").innerHTML += this.id;
for (i = 0; i < hangmanWord.length; i++) {
guess = hangmanWord[i];
if (this.id == guess) {
character[i] = hangmanWord[i];
character.appendChild(document.createTextNode(hangmanWord[i]));
character.classList.add('visible');
}
}
}
Here is where I am in trouble. If I do this (the code I wrote after the if statement) the letters will be added on the end of the string. I Want to have it on the specific dashes that I created earlier. How can I manage to do that? Do I have to do something called "object passing." I am relatively new to js (high school student) and I am keen on any insight! Once again thanks for the help in advance!
</script>
</head>
<body>
<p>Click the button to make a BUTTON element with text.</p>
<div id = "contents">
<div id = "hold"></div>
</div>
<p id ="p1"> Letters picked: </p>
<div id= "picBox"></div>
<div id = "test"></div>
</div>
</body>
You could store the guessing word and the guessed characters in an object and just output the replaced result of such instead. Seems easier to me.
<html>
<head>
<script>
//The hangman object
var Hangman = {
wordToGuess: 'RIGHTEOUSNESS', //The word to guess. Just one for my example
guessedLetters: [] //The guessed characters
};
window.onload = function(){
//Creating the buttons
for(var tF=document.createDocumentFragment(), x=1; x<=26; x++){
var tLetter = String.fromCharCode(x+64),
tButton = tF.appendChild(document.createElement("button"));
tButton.id = tLetter;
tButton.addEventListener("click", checkLetter);
tButton.appendChild(document.createTextNode(tLetter));
(x%10 === 0) && tF.appendChild(document.createElement('br'))
};
document.body.appendChild(tF);
startTheGame()
};
//Starts a game of hangman
function startTheGame(){
var tWord = Hangman.wordToGuess,
tPlacement = document.getElementById('hold');
document.getElementById('test').innerHTML = tWord;
//Resetting the guesses
Hangman.guessedLetters = [];
//Creating dashes for all letters in our word
for(var i=0, j=tWord.length; i<j; i++){
tPlacement.appendChild(document.createTextNode('_'))
}
}
function checkLetter(){
var tButton = this,
tLetter = tButton.id,
tWord = Hangman.wordToGuess,
tPlacement = document.getElementById('hold');
//Make a guess
document.getElementById('p1').innerHTML += tLetter;
(Hangman.guessedLetters.indexOf(tLetter) === -1) && Hangman.guessedLetters.push(tLetter.toLowerCase());
//Clear the current word
while(tPlacement.firstChild) tPlacement.removeChild(tPlacement.firstChild);
//Now we reverse replace the hangman word by the guessed characters
for(var i=0, j=tWord.length; i<j; i++){
tPlacement.appendChild(document.createTextNode(
(Hangman.guessedLetters.indexOf(tWord[i].toLowerCase()) === -1) ? '_' : tWord[i]
))
}
}
</script>
</head>
<body>
<p>Click the button to make a BUTTON element with text.</p>
<div id = 'contents'>
<div id = 'hold'></div>
</div>
<p id = 'p1'> Letters picked: </p>
<div id= "picBox"></div>
<div id = "test"></div>
<!-- This one seems to be too much
</div>
-->
</body>
</html>
https://jsfiddle.net/zk0uhetz/
Update
Made a version with propert object handling, separating the actual hangman logic from the interface.
<html>
<head>
<style>
button{
margin: 1px;
min-width: 30px
}
button[disabled]{
background: #777;
color: #fff
}
</style>
<script>
//Handles the hangman game itself
;(function(ns){
'use strict';
var _listOfWord = ['COW', 'BETTER', 'HARDER', 'JUSTIFY', 'CONDEMN', 'CONTROL', 'HELLO', 'UNDERSTAND', 'LIFE', 'INSIGHT','DATE', 'RIGHTEOUSNESS'],
_wordToGuess = null, //The word to guess. Just one for my example
_guessedLetters = [] //The guessed characters
ns.Hangman = {
//Returns the current obstructed word
getCurrentObstructedWord: function(){
var tWord = []; //The current state of the game
if(_wordToGuess){
//Now we reverse replace the hangman word by the guessed characters
for(var i=0, j=_wordToGuess.length; i<j; i++){
tWord.push(
(_guessedLetters.indexOf(_wordToGuess[i].toLowerCase()) === -1) ? '_' : _wordToGuess[i]
)
}
};
return tWord
},
//Make a guess at the current game
Guess: function(letter){
//Add the guess to the list of guesses, unless already guessed
(_guessedLetters.indexOf(letter) === -1) && _guessedLetters.push(letter.toLowerCase());
return this.getCurrentObstructedWord()
},
isComplete: function(){
return !!(this.getCurrentObstructedWord().indexOf('_') === -1)
},
//Starts a new game
Start: function(){
_guessedLetters = []; //Resetting the guesses
_wordToGuess = _listOfWord[Math.floor(Math.random() * _listOfWord.length)];
return _wordToGuess
}
}
}(window._ = window._ || {}));
//Creates the buttons for hangman
function createButtons(){
var tContainer = document.querySelector('#buttons');
while(tContainer.firstChild) tContainer.removeChild(tContainer.firstChild);
//Creating the buttons
for(var tF=document.createDocumentFragment(), x=1; x<=26; x++){
var tLetter = String.fromCharCode(x+64),
tButton = tF.appendChild(document.createElement('button'));
tButton.id = tLetter;
tButton.addEventListener('click', makeAGuess);
tButton.appendChild(document.createTextNode(tLetter));
(x%10 === 0) && tF.appendChild(document.createElement('br'))
};
tContainer.appendChild(tF)
};
//Makes a guess
function makeAGuess(){
var tButton = this,
tLetter = tButton.id;
//Disable the button
tButton.setAttribute('disabled', 'disabled');
//Make a guess
var tElement = document.getElementById('hold');
tElement.textContent = _.Hangman.Guess(tLetter).join('');
//Check if finished
if(_.Hangman.isComplete()){
tElement.style.color = 'limegreen'
}
};
//Starts a new game of hangman
function Reset(){
createButtons(); //Recreated the buttons
_.Hangman.Start(); //Starting a game of hangman
var tElement = document.getElementById('hold');
tElement.textContent = _.Hangman.getCurrentObstructedWord().join('');
tElement.style.color = ''
};
window.onload = function(){
Reset()
};
</script>
</head>
<body>
<div id = 'hold'></div>
<p id = 'p1'>Make a guess:</p>
<div id = 'buttons'></div>
<br /><br />
<button onclick = 'Reset()'>Start a new game</button>
</body>
</html>
https://jsfiddle.net/mm6pLfze/

Checking function for sliding puzzle javascript

I created a sliding puzzle with different formats like: 3x3, 3x4, 4x3 and 4x4. When you run my code you can see on the right side a selection box where you can choose the 4 formats. The slidingpuzzle is almost done. But I need a function which checks after every move if the puzzle is solved and if that is the case it should give out a line like "Congrantulations you solved it!" or "You won!". Any idea how to make that work?
In the javascript code you can see the first function loadFunc() is to replace every piece with the blank one and the functions after that are to select a format and change the format into it. The function Shiftpuzzlepieces makes it so that you can move each piece into the blank space. Function shuffle randomizes every pieces position. If you have any more question or understanding issues just feel free to ask in the comments. Many thanks in advance.
Since I don't have enough reputation I will post a link to the images here: http://imgur.com/a/2nMlt . These images are just placeholders right now.
Here is the jsfiddle:
http://jsfiddle.net/Cuttingtheaces/vkyxgwo6/19/
As always, there is a "hacky", easy way to do this, and then there is more elegant but one that requires significant changes to your code.
Hacky way
To accomplish this as fast and dirty as possible, I would go with parsing id-s of pieces to check if they are in correct order, because they have this handy pattern "position" + it's expected index or "blank":
function isFinished() {
var puzzleEl = document.getElementById('slidingpuzzleContainer').children[0];
// convert a live list of child elements into regular array
var pieces = [].slice.call(puzzleEl.children);
return pieces
.map(function (piece) {
return piece.id.substr(8); // strip "position" prefix
})
.every(function (id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == parseInt(id);
});
}
Now we need to check it somewhere, and naturally the best place would be after each move, so shiftPuzzlepieces() should be updated to call isFinished() function, and show the finishing message if it returns true:
function shiftPuzzlepieces(el) {
// ...
if (isFinished()) {
alert("You won!");
}
}
And voilĂ : live version.
How would I implement this game
For me, the proper way of implementing this would be to track current positions of pieces in some data structure and check it in similar way, but without traversing DOM or checking node's id-s. Also, it would allow to implement something like React.js application: onclick handler would mutate current game's state and then just render it into the DOM.
Here how I would implement the game:
/**
* Provides an initial state of the game
* with default size 4x4
*/
function initialState() {
return {
x: 4,
y: 4,
started: false,
finished: false
};
}
/**
* Inits a game
*/
function initGame() {
var gameContainer = document.querySelector("#slidingpuzzleContainer");
var gameState = initialState();
initFormatControl(gameContainer, gameState);
initGameControls(gameContainer, gameState);
// kick-off rendering
render(gameContainer, gameState);
}
/**
* Handles clicks on the container element
*/
function initGameControls(gameContainer, gameState) {
gameContainer.addEventListener("click", function hanldeClick(event) {
if (!gameState.started || gameState.finished) {
// game didn't started yet or already finished, ignore clicks
return;
}
if (event.target.className.indexOf("piece") == -1) {
// click somewhere not on the piece (like, margins between them)
return;
}
// try to move piece somewhere
movePiece(gameState, parseInt(event.target.dataset.index));
// check if we're done here
checkFinish(gameState);
// render the state of game
render(gameContainer, gameState);
event.stopPropagation();
return false;
});
}
/**
* Checks whether game is finished
*/
function checkFinish(gameState) {
gameState.finished = gameState.pieces.every(function(id, index, arr) {
if (arr.length - 1 == index) {
// last peace, check if it's blank
return id == "blank";
}
// check that every piece has an index that matches its expected position
return index == id;
});
}
/**
* Moves target piece around if there's blank somewhere near it
*/
function movePiece(gameState, targetIndex) {
if (isBlank(targetIndex)) {
// ignore clicks on the "blank" piece
return;
}
var blankPiece = findBlankAround();
if (blankPiece == null) {
// nowhere to go :(
return;
}
swap(targetIndex, blankPiece);
function findBlankAround() {
var up = targetIndex - gameState.x;
if (targetIndex >= gameState.x && isBlank(up)) {
return up;
}
var down = targetIndex + gameState.x;
if (targetIndex < ((gameState.y - 1) * gameState.x) && isBlank(down)) {
return down;
}
var left = targetIndex - 1;
if ((targetIndex % gameState.x) > 0 && isBlank(left)) {
return left;
}
var right = targetIndex + 1;
if ((targetIndex % gameState.x) < (gameState.x - 1) && isBlank(right)) {
return right;
}
}
function isBlank(index) {
return gameState.pieces[index] == "blank";
}
function swap(i1, i2) {
var t = gameState.pieces[i1];
gameState.pieces[i1] = gameState.pieces[i2];
gameState.pieces[i2] = t;
}
}
/**
* Handles form for selecting and starting the game
*/
function initFormatControl(gameContainer, state) {
var formatContainer = document.querySelector("#formatContainer");
var formatSelect = formatContainer.querySelector("select");
var formatApply = formatContainer.querySelector("button");
formatSelect.addEventListener("change", function(event) {
formatApply.disabled = false;
});
formatContainer.addEventListener("submit", function(event) {
var rawValue = event.target.format.value;
var value = rawValue.split("x");
// update state
state.x = parseInt(value[0], 10);
state.y = parseInt(value[1], 10);
state.started = true;
state.pieces = generatePuzzle(state.x * state.y);
// render game
render(gameContainer, state);
event.preventDefault();
return false;
});
}
/**
* Renders game's state into container element
*/
function render(container, state) {
var numberOfPieces = state.x * state.y;
updateClass(container, state.x, state.y);
clear(container);
var containerHTML = "";
if (!state.started) {
for (var i = 0; i < numberOfPieces; i++) {
containerHTML += renderPiece("", i) + "\n";
}
} else if (state.finished) {
containerHTML = "<div class='congratulation'><h2 >You won!</h2><p>Press 'Play!' to start again.</p></div>";
} else {
containerHTML = state.pieces.map(renderPiece).join("\n");
}
container.innerHTML = containerHTML;
function renderPiece(id, index) {
return "<div class='piece' data-index='" + index + "'>" + id + "</div>";
}
function updateClass(container, x, y) {
container.className = "slidingpuzzleContainer" + x + "x" + y;
}
function clear(container) {
container.innerHTML = "";
}
}
/**
* Generates a shuffled array of id-s ready to be rendered
*/
function generatePuzzle(n) {
var pieces = ["blank"];
for (var i = 0; i < n - 1; i++) {
pieces.push(i);
}
return shuffleArray(pieces);
function shuffleArray(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
}
body {
font-family: "Lucida Grande", "Lucida Sans Unicode", Verdana, Helvetica, Arial, sans-serif;
font-size: 12px;
color: #000;
}
#formatContainer {
position: absolute;
top: 50px;
left: 500px;
}
#formatContainer label {
display: inline-block;
max-width: 100%;
margin-bottom: 5px;
}
#formatContainer select {
display: block;
width: 100%;
margin-top: 10px;
margin-bottom: 10px;
}
#formatContainer button {
display: inline-block;
width: 100%;
}
.piece {
width: 96px;
height: 96px;
margin: 1px;
float: left;
border: 1px solid black;
}
.slidingpuzzleContainer3x3,
.slidingpuzzleContainer3x4,
.slidingpuzzleContainer4x3,
.slidingpuzzleContainer4x4 {
position: absolute;
top: 50px;
left: 50px;
border: 10px solid black;
}
.slidingpuzzleContainer3x3 {
width: 300px;
height: 300px;
}
.slidingpuzzleContainer3x4 {
width: 300px;
height: 400px;
}
.slidingpuzzleContainer4x3 {
width: 400px;
height: 300px;
}
.slidingpuzzleContainer4x4 {
width: 400px;
height: 400px;
}
.congratulation {
margin: 10px;
}
}
<body onload="initGame();">
<div id="slidingpuzzleContainer"></div>
<form id="formatContainer">
<label for="format">select format:</label>
<select name="format" id="format" size="1">
<option value="" selected="true" disabled="true"></option>
<option value="3x3">Format 3 x 3</option>
<option value="3x4">Format 3 x 4</option>
<option value="4x3">Format 4 x 3</option>
<option value="4x4">Format 4 x 4</option>
</select>
<button type="submit" disabled="true">Play!</button>
</form>
</body>
Here we have the initGame() function that starts everything. When called it will create an initial state of the game (we have default size and state properties to care about there), add listeners on the controls and call render() function with the current state.
initGameControls() sets up a listener for clicks on the field that will 1) call movePiece() which will try to move clicked piece on the blank spot if the former is somewhere around, 2) check if after move game is finished with checkFinish(), 3) call render() with updated state.
Now render() is a pretty simple function: it just gets the state and updates the DOM on the page accordingly.
Utility function initFormatControl() handles clicks and updates on the form for field size selection, and when the 'Play!' button is pressed will generate initial order of the pieces on the field and call render() with new state.
The main benefit of this approach is that almost all functions are decoupled from one another: you can tweak logic for finding blank space around target piece, to allow, for example, to swap pieces with adjacent ids, and even then functions for rendering, initialization and click handling will stay the same.
$(document).on('click','.puzzlepiece', function(){
var count = 0;
var imgarray = [];
var test =[0,1,2,3,4,5,6,7,8,'blank']
$('#slidingpuzzleContainer img').each(function(i){
var imgalt = $(this).attr('alt');
imgarray[i] = imgalt;
count++;
});
var is_same = (imgarray.length == test.length) && imgarray.every(function(element, index) {
return element === array2[index];
});
console.log(is_same); ///it will true if two array is same
});
try this... this is for only 3*3.. you pass the parameter and makethe array value as dynamically..

Random number into div and then let delete divs in sequence. How?

So, i want to make game for my child. Have low experience in JS.
Scenario:
Have for example 4 square divs with blank bg. After refresh (or win) i want to:
Generate random numbers into div (1...4). And show them in them.
Then let player delete those divs by clicking on them, but in sequence how divs are numbered.
*For example after refresh divs have those numbers 2 3 1 4. So, user has to have rights to delete first div numbered 1 (2 3 _ 4) and so on.* If he clicks on 2 it get error , div stays in place, and user can try again delete right one.
It game for learning numbers. I have the begining.
Index.html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css.css">
<script src="http://code.jquery.com/jquery-latest.min.js"
type="text/javascript"></script>
</head>
<body>
<div class="grid">
<div id="Uleft"></div>
<div id="Uright"></div>
<div id="Dleft"></div>
<div id="Dright"></div>
</div>
<script>
$(".grid").children( "div" ).on("click", function(){
$(this).css("visibility", "hidden");
});
</script>
</body>
</html>
css.css
.grid {
margin: 0 auto;
width: 430px;
}
#Uleft, #Uright, #Dleft, #Dright {
border: 1px solid black;
width: 200px;
height: 200px;
margin: 5px;
}
#Uright {
float: right;
background-color: red;
}
#Uleft {
float: left;
background-color: blue;
}
#Dleft {
float: left;
background-color: green;
}
#Dright {
float: right;
background-color: yellow;
}
So, i guess i have use jQuery as well, but i dont know how to make it dynamic and different after refresh of page. Please help :)
http://jsfiddle.net/bNa8Z/
There are a few things you have to do. First you have to create a random array which you use sort and Math.random() to do then, you need insert the text in the squares. Find the min of the visible squares and then remove/alert depending if its the min value.
// sort by random
var rnd = [1,2,3,4].sort(function() {
return .5 - Math.random();
});
// map over each div in the grid
$('.grid div').each(function(ii, div) {
$(div).text(rnd[ii]); // set the text to the ii'th rnd
});
function minVisible() {
var min = 1e10; // a big number
$('.grid div').each(function(ii, div) {
// if not visible ignore
if ($(div).css('visibility') === "hidden" ){
return;
}
// if new min, store
var curFloatValue = parseFloat($(div).text());
console.log(curFloatValue);
if (curFloatValue < min) {
min = curFloatValue;
}
});
return min;
}
$(".grid").children( "div" ).on("click", function(){
var clickedFloatValue = parseFloat($(this).text());
if (clickedFloatValue == minVisible()) {
$(this).css("visibility", "hidden");
} else {
alert("sorry little tike");
}
});
Updated jsfiddle http://jsfiddle.net/bNa8Z/2/
Roughly this is what it would look like:
var selected = {};
$('.grid div').each(function(idx){
var is_done = false;
do{
var rand = Math.floor((Math.random()*4)+1);
if( selected[rand] == undefined ){
$(this).html(rand);
selected[rand] = 1;
is_done = true;
}
}while(!is_done);
});
alert("Start the game");
var clicked = [];
$('.grid').on('click', 'div.block', function(){
var num = $(this).html();
if( num == clicked.length + 1 ){
//alert(num + " is correct!");
clicked.push(num);
$(this).addClass("hide");
}else{
alert("Failed!");
}
if( clicked.length == 4 ){
alert("You Won!");
}
});
HTML:
<div class="grid">
<div class="block" id="Uleft"></div>
<div class="block" id="Uright"></div>
<div class="block" id="Dleft"></div>
<div class="block" id="Dright"></div>
</div>
Added CSS:
#Uleft, #Uright, #Dleft, #Dright {
position:absolute;
...
}
#Uright {
left:220px;
top:0px;
background-color: red;
}
#Uleft {
left:0px;
top:0px;
background-color: blue;
}
#Dleft {
left:0px;
top:220px;
background-color: green;
}
#Dright {
left:220px;
top:220px;
background-color: yellow;
}
.hide {
display: none;
}
See the working version at
JSFiddle
You will need to re-"run" the fiddle per game.
please try it. I think that It will help you.
var generated_random_number_sequesce = function(){
var number_array = [];
var number_string = '';
var is_true = true;
while(is_true){
var ran_num = Math.round(1 + Math.random()*3);
if(number_string.indexOf(ran_num) == -1 && ran_num < 5){
number_array[number_array.length] = ran_num;
number_string = number_string + ran_num;
if(number_array.length == 4){is_true = false;}
}
}
return number_array;
}
var set_number_on_divs = function(){
var number_array = generated_random_number_sequesce();
$(".grid").children().each(function(index, element){
$(this).attr('data-div_number' , number_array[index]);
});
}
set_number_on_divs()
var clicked = 0;
$(".grid").children( "div" ).on("click", function(){
clicked += 1;
var current_div_number = $(this).attr('data-div_number');
if( parseInt(current_div_number) == clicked){
$(this).css("visibility", "hidden");
} else{
clicked -= 1;
alert('error');
}
});

Javascript run a function at the same time with different vars

Sorry about the confusing title, I'll explain better.
I have a 20x20 grid of div's, so its 400 of them each with an id, going from 0 to 399.
Each div is given one of three random values - red, green or blue - and when a div is clicked, a function is run to check if the div to the left, right, over and under are of the same value, if it is of the same value it will be simulated a click and the same function will run again.
The problem, is that the function sets vars, so if it finds that the div below has the same value, it will overwrite the vars set by the first click, hence never click any of the others.
JSfiddle - http://jsfiddle.net/5e52s/
Here is what I've got:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<title>untiteled</title>
<style>
body {
width: 420px;
}
.box {
width: 19px;
height: 19px;
border: 1px solid #fafafa;
float: left;
}
.box:hover {
border: 1px solid #333;
}
.clicked {
background: #bada55 !important;
}
</style>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$().ready(function(){
var colors = ['red', 'green', 'blue'];
var i = 0;
while(i<400){
var color = colors[Math.floor(Math.random() * colors.length)];
$('.test').append('<div class="box" id="'+i+'" value="'+color+'" style="background:'+color+';">'+i+'</div>');
i++;
}
$('.box').click(function(){
var t = $(this);
t.addClass('clicked');
id = t.attr('id');
val = t.attr('value');
//Set color
up = parseInt(id) - 20;
right = parseInt(id) + 1;
down = parseInt(id) + 20;
left = parseInt(id) - 1;
clickup = false;
clickdown = false;
if($('#'+down).attr('value') === val){
clickdown = true;
}
if(up > -1 && ($('#'+up).attr('value') === val)){
clickup = true;
}
if(clickdown == true){
$('#'+down).click();
}
if(clickup == true){
$('#'+up).click();
}
});
});
</script>
</head>
<body>
<div class="test">
</div>
</body>
I think the biggest root cause of your problem is you don't check if it already has class 'clicked' or not. That could make the infinite recursive. For example, if you click on the div#2 then the div#1 receives a simulated click, and div#2 receives a simulated click from div#1.
$('.box').click(function(){
var t = $(this);
if(t.hasClass('clicked')) {
return;
}
t.addClass('clicked');
var id = t.attr('id');
var val = t.attr('value');
//Set color
var up = parseInt(id) - 20;
var right = (id%20 != 19) ? ((0|id) + 1) : 'nothing' ;
var down = parseInt(id) + 20;
var left = (id%20 != 0) ? ((0|id) - 1) : 'nothing';
console.log(up, right, down, left);
if($('#'+down).attr('value') === val) {
$('#'+down).click();
}
if($('#'+right).attr('value') === val) {
$('#'+right).click();
}
if($('#'+up).attr('value') === val) {
$('#'+up).click();
}
if($('#'+left).attr('value') === val) {
$('#'+left).click();
}
});
You can schedule the clicks onto the event loop instead of calling them directly, eg:
if(clickdown == true){
setTimeout(function () {
$('#'+down).click();
});
}
I don't think that's your root cause though, it's probably a combination of global vars and scope issues. Try reformatting as such:
$('.box').click(function (event){
var t = $(this), id, val, up, right, down, left, clickup, clickdown;
//...
Your variables id and val are not in a var statement, thus are implicitly created as members of the window object instead of being scoped to the local function. Change the semicolon on the line before each to a comma so that they become part of the var statement, and your code should begin working.

Categories