I have the following code which allows a user to interact with a table to create a game of tic-tac-toe, how do I alter it to replace the 'X' and 'O' text with images instead?
so that when the user clicks on a square an image is displayed instead of plain text ?
<!DOCTYPE html>
<html>
<head>
<title>tic-tac-toe</title>
<style>
td { border: 1px solid black;
width: 2em;
height: 2em;
margin: 0em;
text-align: center;
vertical-align: middle;
}
div.RedBG { background-color: #f00; }
td.BlueBG { background-color: white; }
</style>
</head>
<body>
<center><table id="t1"></table></center>
<div id="m1"></div>
<script>
var board = var board = [[0,0,0],[0,0,0],[0,0,0]];
var free = 9;
var turn = 0;
function numToLetter(num) {
switch (num) {
case 0: return " "
case 1: return "O"
case 2: return "X"
}
}
function clearMessage() {
m1 = document.getElementById("m1");
m1.style.display = "none";
}
function showMessage(message,style) {
m1 = document.getElementById("m1");
m1.innerHTML = message;
m1.style.display = "block";
m1.className = style;
}
...
alter the numToLetter function to return a source for an image contained in td or to assign the source to the image...
i think returning the source would be easier and cleaner.
In that case: in the numToLetter calling code assign the source to the appropriate grid square...
function numToSource(num) {
switch (num) {
case 0: return "path/to/blank.png";
case 1: return "path/to/O.png";
case 2: return "path/to/X.png";
}
}
function assignImageSource(gridId, num){
var element = document.getElementById(gridId);
var source = numberToSource(num);
element.src = source;
}
I'm trying to create a mini-book of images using a clip of code i found online which refreshes a set of predefined images onclick via a basic form button, however I can't figure out why the images i've specified in the array are not appearing in the div below. I've confirmed that the url addresses are correct path - but I must be doing something wrong with the linkage ? ?
<html>
<head>
<title></title>
<script>
var bookImages = new Array();
bookImages[0] = "fsheet1.jpg"
bookImages[1] = "fsheet2.jpg"
bookImages[2] = "fsheet3.jpg"
bookImages[3] = "fsheet4.jpg"
bookImages[4] = "fsheet5.jpg"
bookImages[5] = "fsheet6.jpg"
var i = 0
function updateImg() {
var i = i + 1;
var url = 'url(' + bookImages[i] + ')';
document.getElementById('bookImg').style.backgroundImage = url;
}
</script>
<style type="text/css">
#bookImg {
background-img: url();
background-repeat: no-repeat;
background-color: #CF23FA;
text-align: left;
width: 150px;
height: 200px;
margin-top: 10px;
margin-left: 15px;
}
</style>
</head>
<body>
<div id='bookImg'>
<form>
<input type="button" value=">" onClick="updateImg()">
</form>
</div>
</body>
</html>
The problem:
you are redifining i inside the function updateImg. Technically your function is equivalent to this:
function updateImg() {
var i; // redeclaring i (i is not initialised so the value is undefined)
i = i + 1; // undefined + 1
var url = 'url(' + bookImages[i] + ')'; // url(undefined)
document.getElementById('bookImg').style.backgroundImage = url;
}
The fix:
function updateImg() {
i = i + 1; // don't redeclare i just use the outer one (the one above)
var url = 'url(' + bookImages[i] + ')';
document.getElementById('bookImg').style.backgroundImage = url;
}
Note:
The above code will increment i without checking if it gone beyond the array boundaries (after 6 clicks i will be 7 and there is no image at index 7 in the array). If you want to go back to 0 when i reaches the end then replace i = i + 1; by i = (i + 1) % bookImages.length;!
Complete code:
<html>
<head>
<title></title>
<script>
// array litterals: a better way
var bookImages = [
"http://placehold.it/201x100",
"http://placehold.it/202x100",
"http://placehold.it/203x100",
"http://placehold.it/204x100",
"http://placehold.it/205x100",
"http://placehold.it/206x100"
];
var i = 0;
function updateImg() {
var url = 'url(' + bookImages[i] + ')';
document.getElementById('bookImg').style.backgroundImage = url;
i = (i + 1) % bookImages.length; // increment at the end
}
window.addEventListener("load", updateImg); // call it once the document is ready
</script>
<style type="text/css">
#bookImg {
background-repeat: no-repeat;
background-color: #CF23FA;
text-align: left;
width: 150px;
height: 200px;
margin-top: 10px;
margin-left: 15px;
}
</style>
</head>
<body>
<div id='bookImg'>
<form>
<input type="button" value=">" onClick="updateImg()">
</form>
</div>
</body>
</html>
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..
This code displays all numbers in the array on the left, odd numbers in the middle and the even ones on the right, but the div center does not accept the values. checked over it and debugged for about half an hour and nothing. The code used to input the data for left and right is the same but for center it does not work. when checking through the console center has the values stored just like left and right but it was not able to insert the stored values into the div. Any help would be greatly appreciated.
<style>
#left{
width : 30%;
float: left;
}
#right{
width: 30%;
float: left;
}
#center{
width: 30%;
float: left;
}
</style>
</head>
<body>
<div id = "left"></div>
<div id = "right"></div>
<div id = "center"></div>
<script>
var mya = new Array(50);
var l = document.getElementById("left");
var r = document.getElementById("right");
var c = document.getElementById("center");
var left = '';
var right = '';
var center = '';
for(var c = 0;c<mya.length;c++){
mya[c] = parseInt(Math.random() * 100);
left+="<li>" + mya[c] +"</li>";
if (mya[c]%2){right+="<li>" + mya[c] +"</li>";}
else{center+="<li>" + mya[c] +"</li>";}
}
l.innerHTML+="<ul>" + left + "</ul>";
r.innerHTML+="<ul>" + right + "</ul>";
c.innerHTML+="<ul>" + center + "</ul>";
</script>
</body>
(in addition to the typo I noted in the comments above) You're overwriting c in your loop and that's causing the issue. Change:
var c = document.getElementById("center");
// and
c.inerHTML
to
var ctr = document.getElementById("center");
// and
ctr.innerHTML
jsFiddle example
I'm trying to do a multi-key press combination validation for a "little game" test im doing.
Basically is a character from Mortal Kombat which I move with ASDW keys... but what happens if I want to duck and block at the same time (would be S + K) for example. And I couldn't make it work :S
Now... not only that, if I'm pressing only S, which will duck. Then I press K (while keeping S pushed) it should block while ducking. If I release the K, it should keep ducking. Is this one possible as well?
How to handle for example combinations for doing skills in javascript?
Like if I press S->D->Punch would do a skill (not pressed at the same time but a sequence)
Is there any possible way of doing all this?
This is my entire code inside a simple HTML so you can see it entirely:
P.S: Look at the comment in the preload image part, i mean, the animations are choppy while trying to move the character from one way to another... why is that? what am i doing wrong?
<! DOCTYPE html>
<html>
<head>
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<style>
#reptile_wrapper {
top: 120px;
left: 140px;
position:relative;
}
#container {
margin-top: 240px;
margin-left: 470px;
width: 890px;
height: 301px; /* entire one 547px */
background: url("img/bgs/pit_background.png") no-repeat;
background-size: 100%;
}
#pit {
top: 0px;
position: relative;
width: 890px;
height: 300px;
background: url("img/bgs/pit.png") no-repeat;
background-size: 100%;
}
#pit_chain {
position: absolute;
width: 150px;
height: 340px;
background: url("img/bgs/pit_chains.png") no-repeat;
margin-left: 695;
}
</style>
</head>
<body>
<audio src="sounds/mk3thepit.mp3" preload="auto" loop="true" autoplay="true" autobuffer></audio>
<div id="pit_chain">
</div>
<div id="container">
<div id="pit">
<div id="reptile_wrapper">
<img src="img/reptile_idle.gif"/>
</div>
</div>
</div>
<script>
/* Preload all the images and gifs */
function preloadImages(srcs, imgs, callback) {
var img;
var remaining = srcs.length;
for (var i = 0; i < srcs.length; i++) {
img = new Image();
img.onload = function() {
--remaining;
if (remaining <= 0) {
callback();
}
};
img.src = srcs[i];
imgs.push(img);
}
}
// then to call it, you would use this
var imageSrcs = ["img", "img/bgs"];
var images = [];
preloadImages(imageSrcs, images, startGame());
/* End of preloading images and gifs */
var duckImages = ["d01", "d02"];
var defaultY = '120px';
reptileIdleAnimation();
function startGame() {
var keys = [];
window.addEventListener("keydown",
function(e){
keys[e.keyCode] = e.keyCode;
var keysArray = getNumberArray(keys);
if(keysArray.toString() == "68"){
$("#reptile_wrapper img").attr('src', 'img/reptile_walk_forward.gif')
.parent().css({left: '+=5px'});
}else if(keysArray.toString() == "17,65"){
// document.body.innerHTML += " Select all!"
}
},
false);
window.addEventListener('keyup',
function(e){
keys[e.keyCode] = false;
reptileIdleAnimation();
},
false);
function getNumberArray(arr){
var newArr = new Array();
for(var i = 0; i < arr.length; i++){
if(typeof arr[i] == "number"){
newArr[newArr.length] = arr[i];
}
}
return newArr;
}
}
</script>
</body>
</html>
Thanks.
You don't necessarily need to rely on jQuery for capturing the keys.
Here is a plain javascript solution
var keys = [];
document.body.innerHTML = "Keys currently pressed: "
window.addEventListener("keydown",
function(e){
keys[e.keyCode] = e.keyCode;
var keysArray = getNumberArray(keys);
document.body.innerHTML = "Keys currently pressed:" + keysArray;
if(keysArray.toString() == "17,65"){
document.body.innerHTML += " Select all!"
}
},
false);
window.addEventListener('keyup',
function(e){
keys[e.keyCode] = false;
document.body.innerHTML = "Keys currently pressed: " + getNumberArray(keys);
},
false);
function getNumberArray(arr){
var newArr = new Array();
for(var i = 0; i < arr.length; i++){
if(typeof arr[i] == "number"){
newArr[newArr.length] = arr[i];
}
}
return newArr;
}
As you can see, detecting multiple keypresses and releases at the same time is not a problem, technically speaking.
I think the only browser culprit here (for plain JS) is e.keyCode. If you want to use jQuery you can rewrite the listener definitions so that they use jQuery.
In this question i asked for a proper but plain image preloader.
See the answer supplied; you can use this code to handle the preloading properly.
Hope that helps!