Why is my AI acting strange in a game of tic-tac-toe? - javascript

The organisation of the data for the game of tic-tac-toe is simple.
There is an array that contains a lot of HTML buttons.
The program remembers if a button is used by using the isUsed array.
It checks what image to display on the button using the isX Boolean.
The program contains a list of winning combinations that are available to everyone using freeWins array. When a human picks 5, all the winning combos containing 5 are moved to an array called yourWins. Then, the positions owned by the human will be replaced from yourWins.
Code:
<!DOCTYPE html>
<html>
<title> 1Player </title>
<style>
#stage{
width: 400px;
height: 400px;
padding: 0px;
position: relative;
}
.square{
user-select : none;
-webkit-user-select: none;
-moz-user-select: none;
width: 64px;
height: 64px;
background-color: gray;
position: absolute;
}
</style>
<body>
<div id="stage">
</div>
</body>
<script>
const ROWS = 3;
const COLS = 3;
const GAP = 10;
const SIZE = 64;
var stage = document.querySelector("#stage");
var lotOfButtons = [];
var isUsed = [false,false,false,false,
false,false,false,false,false];
var myPositions = "";
var yourPositions = "";
var youPicked = "";
var iPicked = "";
var isX = true;
var isFirstMove = true;
var myWins = [];
var yourWins = [];
var freeWins = ["123","159","147",
"258",
"369","357",
"456",
"789"];
prepareBoard();
stage.addEventListener("click",clickHandler,false);
function prepareBoard(){ //Prepare the board on which the users will play.
for(var row = 0;row<ROWS;row++){ // Prepare the rows.
for(var col = 0;col < COLS;col++){ //Prepare the columns.
var square = document.createElement("button"); //Prepare the button which represents a square.
square.setAttribute("class","square"); //Make it a square 'officially'.
stage.appendChild(square); //Add the square to the play area..
lotOfButtons.push(square); //Keep a record of squares.
square.style.left = col * (SIZE+GAP) + "px"; //Properly set the horizontal spacing.
square.style.top = row * (SIZE+GAP) + "px"; //Properly set the vertical spacing.
}
}
}
function clickHandler(event){
var index = lotOfButtons.indexOf(event.target);
if(index=== -1 || isX === false){
return;
}
isX = false;
isUsed[index] = true;
event.target.style.backgroundImage = "url(../img/X.png)";
yourMove(index+1);
}
function yourMove(someIndex){
console.log("Finding Human Winning Moves::: ");
yourWins = findWinnindMoves(yourWins,someIndex);
console.log(yourWins);
console.log("Clearing Human Owned Positions::: ");
yourWins = clearOwnedPositions(yourWins,someIndex);
console.log(yourWins + "\n");
myMove();
}
function myMove(){
var isFifthSquareUsed = isFiveUsed();
if(isFifthSquareUsed === false){ //Is fifth square used ?? --- NO!
var selectedSquare = lotOfButtons[4];
selectedSquare.style.backgroundImage = "url(../img/O.PNG)";
isUsed[4] = true;
isFirstMove = false;
isX = true;
console.log("Finding AI Winning Moves::: ");
myWins = findWinnindMoves(myWins,4);
console.log(myWins);
console.log("Clearing AI Owned Positions::: ");
myWins = clearOwnedPositions(myWins,4);
console.log(myWins + "\n");
}else if(isFifthSquareUsed && isFirstMove){ //Is fifth square used ?? --- YES, but it is first move.
//TODO add logic
}else{ //Some random square has already been chosen. Now it is time to use strategy.
//TODO add logic
}
}
function findWinnindMoves(winsArray,someIndex){//using which combination can a user or AI win ?
for(var i = 0;i<freeWins.length;i++){
if(freeWins[i].indexOf(someIndex) != -1){
winsArray.push(freeWins[i]);
i--;
freeWins.splice(i,1);
}
}
return winsArray;
}
function clearOwnedPositions(winsArray,someIndex){ //Reduce the number of squares needed to get triplets.
for(var i=0;i<winsArray.length;i++){
if(winsArray[i].indexOf(someIndex) != -1){
winsArray[i] = winsArray[i].replace(someIndex,"");
}
}
return winsArray;
}
function isFiveUsed(){ //Is AI able to get the center square ?
return isUsed[4];
}
</script>
</html>
Problem
The determination of the winning moves for the user and the AI works properly if I click the second or the third row and not on first row.
Output on Row 2
Finding Human Winning Moves:::
["147", "456"]
Clearing Human Owned Positions:::
17,56
Finding AI Winning Moves:::
["147", "456"]
Clearing AI Owned Positions:::
17,56
Output on Row 1
Finding Human Winning Moves:::
["123", "123", "123", "123", "123", "123", "123", "123"]
Clearing Human Owned Positions:::
23,23,23,23,23,23,23,23
Finding AI Winning Moves:::
[]
Clearing AI Owned Positions:::
Also, after clearing the owned squares the array brackets are gone.
Why are the two problems happening?

To Answer your second question about the Arrays, You cannot initialize a blank Array and at the same time .push() in the same area. Every time you run the JavaScript it re-creates the Array and that means goodbye to the stuff you are putting into it. Maybe you should initialize the Array outside a function and put the rest of the code inside the function. As for your game AI, I am not entirely sure. Hope this helps somewhat.

Related

How do you wait for a button to get pressed a certain amount of times before running another function?

I am trying to build a gambling simulator that lets you gamble with fake money to see if you'd win more often times than not. The game I am sort of trying to replicate is Mines from Roobet. In my game, there are 9 squares laid out in a grid like format. One of the squares is the bomb square, which means if you click it, you lose. The player does not know which square is the bomb square though. You have to click 4 squares, and if all of the ones you have clicked are non-bomb squares, then you win $50. If you click the bomb square, then you lose $50.
What I have been trying to figure out for like a week now is how do you make the game wait until either 4 non-bomb squares have been clicked, or 1 bomb square to have been clicked in order to do certain functions(subtract 50 from gambling amount, restart the game). I have tried while loops, but that crashes the browser. I have also tried if-else statements, but the code doesn't wait until either 4 non-bomb squares or 1 bomb square has been clicked. It just checks it instantly. So it results in it not working right. I also have tried for the function to call itself, and then check for either case, but it just results in an error saying "Maximum call stack size exceeded."
function bombClicked () {
for (let i = 0; i < array.length; i++) { //each square is put into an array named array
array[i].addEventListener("click", () => {
if (array[i].value == "bomb") {
array[i].style.background = "red";
redCounter++;
didLose = true
}
else{
array[i].style.background = "green";
greenCounter++;
didLose = false;
}
})
}
if (greenCounter >= 4 || redCounter >= 1) {
if (didLose == true) {
gamblingAmount.value = parseInt(gamblingAmount.value) - 50;
}
else {
gamblingAmount.value = parseInt(gamblingAmount.value) + 50;
}
reset();
}
}
What a fun little exercise! Here's my quick and dirty jquery solution.
const NO_OF_TRIES_ALLOWED = 4;
const SCORE_WON_LOST = 50;
var score = 0;
var tries = 0;
var bombId;
function restart() {
tries = 0;
$("button").removeClass("ok");
$("button").removeClass("bang");
bombId = Math.floor( Math.random() * 9);
$("#bombLabel").text(bombId);
}
function win() {
window.alert('you win!');
score += SCORE_WON_LOST;
$("#scoreLabel").text(score);
restart();
}
function lose(){
window.alert('you lose!');
score -= SCORE_WON_LOST;
$("#scoreLabel").text(score);
restart();
}
$(document).on("click", "button", function() {
if( !$(this).is(".bang") && !$(this).is(".ok") ) {
let buttonId = $(this).data("id");
if(buttonId === bombId) {
$(this).addClass('bang');
setTimeout(lose, 100); // bit of a delay before the alert
}
else {
$(this).addClass('ok');
tries++;
if(tries === NO_OF_TRIES_ALLOWED) {
setTimeout(win, 100); // bit of a delay before the alert
}
}
}
});
restart();
button{
width: 50px;
height: 50px;
padding: 0;
}
.ok{
background-color: green;
}
.bang{
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button data-id="0"></button>
<button data-id="1"></button>
<button data-id="2"></button><br>
<button data-id="3"></button>
<button data-id="4"></button>
<button data-id="5"></button><br>
<button data-id="6"></button>
<button data-id="7"></button>
<button data-id="8"></button><br>
Score: <span id="scoreLabel"></span>
It looks like you need some sort of state. I don't know what the rest of your code looks like, but maybe creating a helper to keep track of your counts might help:
class Counter {
constructor() {
this.count = 0;
}
inc() {
this.count ++;
}
getCount() {
return this.count
}
}
const counter = new Counter();
counter.inc();
counter.inc();
counter.inc();
counter.getCount(); // 3
Javascript should keep this in memory for you. I might have to see the rest of your code to understand how exactly to fix the issue, but maybe this will put you on the right track!

Subtract an image that is representing a life in a game

Im try to code a mini game.
The game has two boxes you can click on. The boxes are assigned a random number between 1 and 2.
When the game starts, you have 5 lives. These lives are represented as 5 pictures of a small human.
When the player gets 3,5 and 7 correct points you get a extra life. Which i have achieved.
Now im trying to subtract a life everytime the player clicks on the wrong box.
function setRandomNumber() {
randomNumber = Math.floor(Math.random() * 2 + 1);
if (randomNumber === 1) {
numberOfRightAnswersSpan.innerHTML = `${++correctNumber}`;
outputDiv.innerHTML = `Det er riktig svar.`;
if (correctNumber === 3 || correctNumber === 5 || correctNumber === 7) {
numberOfLivesDiv.innerHTML += `<img src="images/person1.jpg"/>`
}
} else {
numberOfWrongAnswersSpan.innerHTML = `${++wrongNumber}`;
outputDiv.innerHTML = `Det er feil svar.`;
}
}
I made a few changes to your code, but tried to keep it fairly similar to your original. I changed your template literals to strings. I changed your .innerHTML modifications to .innerText modifications. I refactored some whitespace to make your if statement more readable. Instead of adding the img element by appending the html string, I used the .appendChild method and createElement method to make the img elements. I also added a class to the element to make it easier to grab later.
function setRandomNumber() {
randomNumber = Math.floor(Math.random() * 2 + 1);
if (randomNumber === 1) {
numberOfRightAnswersSpan.innerText = ++correctNumber;
outputDiv.innerText = "Det er riktig svar.";
if (
correctNumber === 3 ||
correctNumber === 5 ||
correctNumber === 7
) {
const newLife = document.createElement("img");
newLife.src = "images/person1.jpg";
newLife.classList.add("life");
numberOfLivesDiv.appendChild(newLife);
}
} else {
numberOfWrongAnswersSpan.innerText = ++wrongNumber;
outputDiv.innerText = "Det er feil svar.";
const aLife = document.querySelector(".life");
aLife.remove();
}
}
It is important to note at this point there is not logic to check for how many life exists, or what to do when lives run out. Eventually you may hit an error because there is not element with a class of life.
I would re-render the lives every time the number of lives changes.
let number_of_lives = 5;
const lives_container = document.getElementById("user-lives");
function draw_lives(){
lives_container.innerHTML = "";
for(let i = 0; i < number_of_lives; i++){
const img = document.createElement("img");
img.setAttribute("alt", "life.png");
const new_life = img;
lives_container.appendChild(new_life);
};
if(number_of_lives <= 0) lives_container.innerText = number_of_lives;
};
function remove_life() {
number_of_lives--;
draw_lives();
};
function add_life() {
number_of_lives++;
draw_lives();
};
draw_lives();
#user-lives > * {
margin: 10px;
background: #7f7;
}
<div id="user-lives"></div>
<button onclick="add_life();">Add life</button>
<button onclick="remove_life();">Remove life</button>

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..

Can't Get Variable to Add Properly

I have a variable++; in an if/else statement (it being in the else part). It's supposed to add 1 to a score of how many wrong guesses one has.
For some reason it never adds just one, it will also add numbers ranging from 3 to 7 whenever I submit a 'guess'. Can you guys tell me if I'm looping wrong or something? Please try to explain in detail.
EDIT: I realized part of the problem. The tries++; is actually looping once for each letter [var]choice didn't match or equal. For instance if I enter "a" for "apple" tries++; will loop four times because of the four other characters. So how do I get it to only loop only once instead of adding one for each missed character?
This is my code.
// JavaScript Document
var words = new Array("apple","orange","banana","lime","mango","lemon","avacado","pineapple","kiwi","plum","watermelon","peach");
var randomNum;
var word;
var tries = 0;
$('#guess').prop('disabled', true);
$(function(){
$('#start').click(function(){
$('#guess').prop('disabled', false);
word = "";
randomNum = Math.floor(Math.random() * words.length)
for (var i =0; i < words[randomNum].length; i++) {
word += "*";
}
console.log(words[randomNum]);
$('#word').html(word);
});
$('#guess').click(function guess(){
var choice = $('#letter').val().toLowerCase();
for (var i =0; i < word.length; i++) {
if (words[randomNum].charAt(i) == choice) {
word = word.substr(0, i) + choice + word.substr(i + 1);
}
if (words[randomNum].charAt(i) !== choice) {
tries++;
}
}
if (tries < 7) {
$('#tries').html(tries)
} else if (tries >= 7)
$('#tries').html("YOU LOSE");
$('#word').html(word);
$('#' + choice).css("background-color", "red");
});
});
Got it working!
The issue is related to the tries variable, inside the for loop (per letter). In order to see the odd behavior add a console.log(tries); in your code, inside the loop and you will see. At first time it will increase in 1 then the value will change completely (I will suggest some debugging here to understand what's going on with more accuracy since I did this real quick). The solution is increasing the variable out of the for loop context to make it work (I did this in the provided example from the bottom).
By the way, it seems that you are trying to implement a "Hangman" game and to be honest, when implementing those things you need to be really organized.
I fixed your issue, improved the code a lot and also considered other possible game scenarios like:
Play Again
Game Over
Win
Go Back
Please take a look. Just to know, HTML and CSS are just improvisations made for this example, some improvements are needed so just take them as a reference.
Update: What you had put in the EDIT part from your post is correct.
You can run this script at the bottom.
// Game variables
var GAME_WORDS = [ // List of words available when playing
'apple',
'orange',
'banana',
'lime',
'mango',
'lemon',
'avacado',
'pineapple',
'kiwi',
'plum',
'watermelon',
'peach'
],
GAME_MASKED_WORD = '', // Stores the masked word to be discovered
GAME_SELECTED_WORD = '', // Stores the readable word
GAME_PLAYER_ATTEMPTS = 0, // Stores player attempts when failing
GAME_RANDOM_NUMBER = 0, // Random number to pick a word
GAME_MAX_ATTEMPTS = 7, // Max. player attempts before a game over
GAME_UI_COMPONENTS = { // UI components declaration
start: $('#start'),
reset: $('#reset'),
back: $('#back'),
guess: $('#guess'),
msg: $('#msg'),
word: $('#word'),
letter: $('#letter')
},
GAME_UI_SECTIONS = { // UI sections declaration
menu: $('#menu'),
game: $('#game')
};
$(function() {;
var ui = GAME_UI_COMPONENTS;
// Initialize game
init();
// Start button handler
ui.start.on('click', function(e) {
start();
});
// Guess button handler
ui.guess.on('click', function(e) {
guess();
});
// Play Again button handler
ui.reset.on('click', function(e) {
reset();
start();
});
// Go Back button handler
ui.back.on('click', function(e) {
init();
});
});
/**
* Used to initialize the game for first time
*/
function init() {
var sections = GAME_UI_SECTIONS;
sections.menu.show();
sections.game.hide();
reset();
};
/**
* Used to start the game
*/
function start() {
var ui = GAME_UI_COMPONENTS,
sections = GAME_UI_SECTIONS,
words = GAME_WORDS;
sections.menu.hide();
sections.game.show();
GAME_RANDOM_NUMBER = Math.floor(Math.random() * words.length);
for (var i = 0; i < words[GAME_RANDOM_NUMBER].length; ++i) {
GAME_MASKED_WORD += '*';
}
GAME_SELECTED_WORD = words[GAME_RANDOM_NUMBER];
ui.word.html(GAME_MASKED_WORD);
ui.letter.focus();
};
/**
* Guess button handler
*/
function guess() {
var ui = GAME_UI_COMPONENTS,
words = GAME_WORDS,
matches = false,
choice;
// Clean messages each time player do a guess
showMsg('');
if (ui.letter && ui.letter.val()) {
choice = $.trim(ui.letter.val().toLowerCase());
}
if (choice) {
for (var i = 0; i < GAME_MASKED_WORD.length; ++i) {
if (words[GAME_RANDOM_NUMBER].charAt(i) === choice) {
GAME_MASKED_WORD = GAME_MASKED_WORD.substr(0, i) + choice +
GAME_MASKED_WORD.substr(i + 1);
matches = true;
}
}
if (!matches) {
++GAME_PLAYER_ATTEMPTS;
}
} else {
showMsg('Please type a letter.');
}
// Show attempts left if more than zero
if (GAME_PLAYER_ATTEMPTS > 0) {
showMsg('You have ' +
(GAME_MAX_ATTEMPTS - GAME_PLAYER_ATTEMPTS) +
' attempt(s) left.');
}
// Check game status each time doing a guess
if (isGameOver()) {
lose();
} else if (isGameWin()) {
win();
} else {
ui.word.html(GAME_MASKED_WORD);
}
ui.letter.focus();
};
/**
* Used to set all game variables from the scratch
*/
function reset() {
var ui = GAME_UI_COMPONENTS;
GAME_MASKED_WORD = '';
GAME_PLAYER_ATTEMPTS = 0;
GAME_RANDOM_NUMBER = 0;
showMsg('');
ui.guess.show();
ui.letter.val('');
ui.word.html('');
};
/**
* Handler when player lose the game
*/
function lose() {
var ui = GAME_UI_COMPONENTS;
showMsg('You Lose!');
ui.word.html(GAME_SELECTED_WORD);
ui.guess.hide();
};
/**
* Handler when player win the game
*/
function win() {
var ui = GAME_UI_COMPONENTS;
showMsg('You Win!');
ui.word.html(GAME_SELECTED_WORD);
ui.guess.hide();
};
/**
* Use to print UI messages for the player
*/
function showMsg(msg) {
var ui = GAME_UI_COMPONENTS;
ui.msg.html(msg);
};
/**
* Check game status, if player is going to lose the game
* #returns Boolean
*/
function isGameOver() {
return (GAME_PLAYER_ATTEMPTS >= GAME_MAX_ATTEMPTS);
};
/**
* Check game status, if player is going to win the game
* #returns Boolean
*/
function isGameWin() {
return (GAME_MASKED_WORD === GAME_SELECTED_WORD);
};
.btn {
cursor: pointer;
}
span#msg {
color: red;
font-weight: bold;
}
.text {
font-size: 3em;
}
input#letter {
width: 30px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="wrapper">
<div id="menu">
<span class="text">Hangman!</span>
<br><br>
<img src="http://img3.wikia.nocookie.net/__cb20130207191137/scribblenauts/images/0/01/Hangman.png" height="200" width="120"/>
<br><br>
<input type="button" class="btn" id="start" value="Start Game"/>
</div>
<div id="game">
<span id="msg"></span>
<br><br>
Letter: <input type="text" id="letter" value="" maxlength="1"/>
<br><br>
<input type="button" class="btn" id="guess" value="Guess"/>
<input type="button" class="btn" id="reset" value="Play Again"/>
<input type="button" class="btn" id="back" value="Go Back"/>
<br><br>
Word: <div id="word" class="text"></div>
</div>
</div>
Hope this helps.
try this one:
Modify guess click handler like this:
$('#guess').click(function guess(){
var choice = $('#letter').val().toLowerCase(),
found = false;
for (var i =0; i < word.length; i++) {
if (words[randomNum].charAt(i) == choice) {
word = word.substr(0, i) + choice + word.substr(i + 1);
found = true
}
}
if(!found){
tries++;
}
if (tries < 7) {
$('#tries').html(tries)
} else if (tries >= 7){
$('#tries').html("YOU LOSE");
}
$('#word').html(word);
$('#' + choice).css("background-color", "red");
});
Also on start reset the tries:
$('#start').click(function(){
$('#guess').prop('disabled', false);
word = ""; tries = 0;

Categories