I've recently learned about IIFEs, and I'm trying to incorporate them in a Minesweeper game. I'm aware of the Module pattern and how it can return objects. I use it in my addMines function to return an array. I'd like to do something similar with my distanceToMine function. My goal is to return an object that shows the distance to each mine--something that I can access with other functions. The problem I've run into is (I think) related to using Jquery's .each function. I can't access any of the code that I use inside of the .each function.
One possible solution I thought of was maybe replacing .each for a for loop, but then I have difficulty accessing the data attributes that I need to access (var thisCell). On line 130 I console.log() the object that I'd like to return.
What's the best way of going about returning the object on line 130 named obj so that I can access it outside of the function? Is it still possible to use the .each function and access the code within it? If so, how?
$(document).ready(function(){
makeGrid();
//addMines();
detectBombs();
distanceToMine();
})
var makeGrid = (function () {
return function () {
var row = 9;
for (var i=0;i<row;i++){
$(".divTableBody").append("<div class='divTableRow'></div>") }
for (i=0;i<row;i++){
$(".divTableRow").append("<div class='divTableCell'></div>") }
$(".divTableCell").each( function(i) {
$(this).attr('data', (i+1))
// $(this).append(i+1)
});
};
})();
var addMines = (function () {
var mineArray = [];
for (var i = 1; i < 82;i++) {
mineArray.push(i)
}
return {
shuffle: function (array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
},
get: function (){
addMines.shuffle(mineArray);
mineArray = mineArray.splice(1,10)
return mineArray
}
};
})();
var mineArray = addMines.get()
var detectBombs = (function () {
return function () {
// var mineArray = addMines.get()
console.log(mineArray.sort())
$(".divTableCell").on("click", function(){
//console.log($(this).attr("data"))
for (var i=0;i<mineArray.length;i++) {
if ( $(this).attr("data") == mineArray[i] ) {
for (var j = 0;j<82;j++) {
$('*[data="' + mineArray[j] + '"]').html('<i class="fa fa-bomb" aria-hidden="true"></i>')
.css("background-color", "white" )
$('*[data="' + j + '"]').css("background-color", "white" )
}
}
}
})
};
})();
var distanceToMine = (function () {
return function () {
//The following code to find matching array values was taken from this answer:
//https://stackoverflow.com/questions/12433604/how-can-i-find-matching-values-in-two-arrays
Array.prototype.diff = function(arr2) {
var ret = [];
this.sort();
arr2.sort();
for(var i = 0; i < this.length; i += 1) {
if(arr2.indexOf( this[i] ) > -1){
ret.push( this[i] );
}
}
return ret;
};
var arr = [];
$(".divTableCell").each( function(i) {
var thisCell = parseInt($(this).attr("data"));
var up = (thisCell - 9);
var right = (thisCell + 1);
var down = (thisCell + 9);
var left = (thisCell - 1);
var diagonalRightUp = (thisCell - 8);
var diagonalRightDown = (thisCell + 10);
var diagonalLeftUp = (thisCell - 10);
var diagonalLeftDown = (thisCell + 8);
var direction = [up,right,down,left,diagonalRightUp,diagonalRightDown,diagonalLeftUp,diagonalLeftDown];
var adjacentNumbers = direction.filter(function(num){
return num > 0 && num <= 81
})
var mineDistances = mineArray.diff(adjacentNumbers)
arr.push(mineDistances.length)
});
//https://stackoverflow.com/questions/4215737/convert-array-to-object
var obj = arr.reduce(function(acc, cur, i) {
acc[i] = cur;
return acc;
}, {});
console.log(obj)
};
})();
.divTable{
display: table;
width: 50%;
padding: 50px;
}
.divTableRow {
display: table-row;
}
.divTableHeading {
background-color: #EEE;
display: table-header-group;
}
.divTableCell, .divTableHead {
border: 1px solid #999999;
display: table-cell;
padding: 20px 20px;
vertical-align: top;
box-shadow: 1px 1px 1px;
background-color: grey;
border-radius: 5px;
}
.divTableBody {
display: table-row-group;
}
.open {
background-color: white;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>MineSweeper</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="script.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://use.fontawesome.com/eeb147990f.js"></script>
</head>
<body>
<div class="container">
<center>
<h1>Minesweeper</h1>
<div id="container">
<div class="divTable">
<div class="divTableBody">
</div>
</div>
</div>
</center>
</div>
</body>
</html>
Related
I am attempting to display my snake on this dynamic gameboard i made as well as randomly place food
on said board how would i go about turning my js into visual html.
now that i think about it i havent even made a tick function
I've stored the game board and would like to place food randomly within said gameboard
i would assume it would involve toggling a class tied to some styling in css but i do not know how to execute
const gameData = {
getSize(){
let sizeInput = document.querySelector('#sizeInput').value;
sizeInput = parseInt(sizeInput)
console.log(sizeInput)
this.validateBoard(sizeInput)
return sizeInput
},
validateBoard(size){
if(size === NaN || size < 10 || size > 50){
console.log('not good')
return false;
}else if(size === '' || size >= 10 && size <= 50){
console.log('good good')
this.boardSize = size
return true;
}
},
boardSize: 0,
countDownTimer(timer){
if (timer >= 0) {
message.textContent = 'Rendering ' + timer;
timer--
}
return timer;
},
inputMessage(){
let message = document.querySelector('#message')
let validBoard = gameData.validateBoard(gameData.getSize())
console.log(validBoard)
if(validBoard) {
let timer = 5
let countDown = setInterval(function(){
console.log(countDown)
if(timer === 0){
clearInterval(countDown)
generateGameBoard();
}
timer = gameData.countDownTimer(timer)
}, 1000)
} else if(!validBoard){
return message.textContent = 'Invalid entry. Try Again!'
}
},
gameBoard: [],
//currentSnake: [[x,y]],
snakePos: {
x: 4,
y: 4
},
food:[],
//directionFacing: [];
randomfoodSquare(){
let boardSize = this.boardSize
let squareX = Math.floor(Math.random() * (boardSize + 1))
let squareY = Math.floor(Math.random() * (boardSize + 1))
this.food = [squareX, squareY]
}
}
function generateFood(){
randomFoodSquare();
}
let submitSize = document.querySelector('#submit')
function generateGameBoard(){
let sizeInput = gameData.boardSize
let startMenu = document.querySelector('#startMenu')
let table = document.querySelector('#tb')
startMenu.style.display = 'none'
table.style.display = 'flex'
console.log(sizeInput)
for (let i = 0; i < sizeInput; i++) {
console.log(i)
let row = document.createElement('tr');
for(let j = 0; j < sizeInput; j++){
let square = document.createElement('td')
row.appendChild(square)
let x = j
let y = i
gameData.gameBoard.push([x,y])
}
table.appendChild(row)
}
adjustBoardSize();
console.log(gameData.gameBoard)
}
function adjustBoardSize(){
let sizeInput = gameData.boardSize;
let table = document.querySelector('#tb')
let row = document.querySelectorAll('tr')
let square = document.querySelectorAll('td');
let tableWidth = table.offsetWidth;
let tableHeight = table.offsetHeight;
let rowWidth = tableWidth;
row.forEach( rowEl => {
rowEl.style.height = tableHeight/sizeInput + 'px'
})
square.forEach( tdEl=> {
tdEl.style.width = rowWidth/sizeInput + 'px'
})
}
submitSize.addEventListener('click', function(){
gameData.inputMessage();
let sizeInput = document.querySelector('#sizeInput')
sizeInput.value = '';
})
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Document</title>
</head>
<body>
<div id="startMenu" class="hidden">
<h1>Beasteds Snake Game</h1>
<h2 id="message">Choose your board size!</h2>
<input id="sizeInput" type="text" placeholder="ex. 20 will give 20*20 grid">
<input id='submit'type="button">
</div>
<table id="tb">
</table>
<script src="snakeGame.js"></script>
</body>
</html>
```
* {
box-sizing: border-box;
}
body{
background-color: cadetblue;
}
#tb{
display: none;
margin: 0 auto;
flex-wrap: wrap;
text-align: center;
border: black solid 1px;
height: 600px;
width: 600px;
}
#startMenu{
display: block;
justify-content: center;
text-align: center;
margin-top: 200px ;
background-color: cyan;
}
td {
background-color: black;
display: flex;
flex-wrap: wrap;
border: rgb(250, 251, 255) solid 1px;
}
tr {
display: flex;
width: 100%;
}
#snakeHead{
color: darkblue;
}
I am working from the color-swatch chart example in dc.js. My chart (source) renders the size of its given to render data length value. All I want is for it to change on overviewChart filter update.
So no selection:
On selection:
Yet I want is to be much smaller and changed according to overviewChart selection.
Here is my source code:
class dcChart {
constructor(parent, groupByKeyName, valueKeyName, group) {
this._group = null;
this._dimension = null;
this._groupByKeyIdx = groupByKeyName;
this._valueKeyName = valueKeyName;
this._root = d3.select(parent);
dc.registerChart(this, group);
}
cf(data) {
this._cf = data;
return this;
}
dimension(data) {
this._dimension = data;
return this;
}
group(data) {
this._group = data;
return this;
}
render() {
console.log("called once");
this.redraw();
}
redraw() {
this._root.html(this._dimension.hasCurrentFilter() + " " + this._group.all().length)
console.log(this._dimension.hasCurrentFilter())
console.log(this._group.all())
}
}
function loadStockData(stock, callback) {
d3.csv('https://bost.ocks.org/mike/cubism/intro/stocks/' + stock + '.csv').then(function(rows) {
rows = rows.map(function(d) {
return [d3.timeParse(d.Date), +d.Open];
}).filter(function(d) {
return d[1];
}).reverse();
var date = rows[0][0],
compare = rows[400][1],
value = rows[0][1],
values = [],
indices = [];
rows.forEach(function(d, i) {
values.push(value = (d[1] - compare) / compare);
indices.push(i);
});
callback({
'stock': stock,
'values': values,
'indices': indices
});
});
}
var promises = [];
['AAPL', 'GOOG', 'MSFT'].forEach(function(stock) {
promises.push(new Promise(function(resolve, reject) {
var r = loadStockData(stock, resolve);
}));
});
Promise.all(promises).then(function(stocks) {
console.log(stocks);
var data = [];
for (var i = 0; i < stocks.length; i++) {
for (var j = 0; j < stocks[i].indices.length; j++) {
data.push({
'idx': stocks[i].indices[j],
'name': stocks[i].stock,
'value': stocks[i].values[j]
})
}
}
var ndx, runDimension, runGroup, overviewRunDimension, overviewRunGroup;
ndx = crossfilter(data);
console.log(666);
console.log(ndx.groupAll());
runDimension = ndx.dimension(function(d) {
return [d.name, d.idx];
});
runGroup = runDimension.group().reduceSum(function(d) {
return d.value;
});
var runHCDimension = ndx.dimension(function(d) {
return [d.name, d.idx];
});
var runHCGroup = runHCDimension.group().reduceSum(function(d) {
return d.value;
});
var myChart = new dcChart("#test-hc", 1, 2);
myChart.cf(ndx)
.dimension(runHCDimension)
.group(runHCGroup);
var overviewChart = dc.seriesChart("#test-overview");
overviewChart
.width(768)
.height(100)
.chart(function(c) {
return dc.lineChart(c).curve(d3.curveCardinal);
})
.x(d3.scaleLinear().domain([0, 1]))
.brushOn(true)
.xAxisLabel("Values")
.clipPadding(10)
.dimension(runDimension)
.group(runGroup)
.seriesAccessor(function(d) {
return "Stock: " + d.key[0];
})
.keyAccessor(function(d) {
return d.key[1];
})
.valueAccessor(function(d) {
return d.value;
});
dc.renderAll();
});
body {
margin: 0;
padding: 0;
}
.horizon {
border-top: solid 1px #000;
border-bottom: solid 1px #000;
overflow: hidden;
position: relative;
}
.horizon + .horizon {
border-top: none;
}
.horizon canvas {
display: block;
image-rendering: pixelated;
}
.horizon .title,
.horizon .value {
bottom: 0;
line-height: 30px;
margin: 0 6px;
position: absolute;
font-family: sans-serif;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
white-space: nowrap;
}
.horizon .title {
left: 0;
}
.horizon .value {
right: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>dc.js - Custom Chart Example</title>
<meta charset="UTF-8">
<link rel="stylesheet" type="text/css" href="//dc-js.github.io/dc.js/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="//unpkg.com/dc#4/dist/style/dc.css" />
<script src="//d3js.org/d3.v5.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter2/1.4.4/crossfilter.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/dc/3.2.1/dc.min.js"></script>
<script src="//npmcdn.com/d3-horizon-chart/build/d3-horizon-chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>
</head>
<body>
<div class="container">
<div id="test-overview"></div>
<br />
<div id="test-hc"></div>
</div>
</body>
</html>
So how one can get all the dimension data values filtered on another dc.js chart
update?
I am trying to get my cards to display 2 different boxes for the cards. If I hover just right with inspector I can see both cards are there, but I am having difficulty separating the two side by side. I am mostly trying to follow a tutorial but it has you do a lot of the work on your own, and I don't want to advance until I have this completely ready for the next task it gives me. Any help is greatly appreciated
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>
War Cards!
</title>
<style>
.icard
{
position: absolute;
padding: 10px;
height: 200px;
width: 150px;
border: 1px solid black;
border-radius: 15px;
background-color: white;
display: inline-block;
top: 0;
left: 0;
}
.hand
{
position: relative;
}
.players
{
float: left;
width: 49%;
min-height: 300px;
border: 1px solid black;
}
</style>
</head>
<body>
<div id="wrapper">
<div id="start"></div>
<div id="message"></div>
<div id="board">
<div id="player1" class="players">
<div class="score"></div>
<div class="hand"></div>
</div>
<div id="player2">
<div class="score"></div>
<div class="hand"></div>
</div>
</div>
<div id="action">
<button id="btnBattle" type="button" class="btn">
Fight!
</button>
</div>
</div>
<script src="js/jquery-3.3.1.min.js">
</script>
<script>
$('document').ready(function() {
var suits = ["spades", "hearts", "clubs", "diams"];
var cardFace = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
var cards = [];
var players = [[], []];
var firstRun = true;
var gameOver = false;
var fightButton = document.querySelector("#btnBattle");
var p1 = document.querySelector('#player1 .hand');
var p2 = document.querySelector('#player2 .hand');
fightButton.addEventListener('click', battle);
function battle()
{
if (firstRun)
{
firstRun = false;
buildCards();
shuffleArray(cards);
dealCards(cards);
}
attack();
console.log('Works');
}
function attack()
{
if(!gameOver)
{
var card1 = players[0].shift();
var card2 = players[1].shift();
var pot = [card1, card2]
p1.innerHTML = showCard(card1, 0);
p2.innerHTML = showCard(card2, 0)
// Check Winners
// Update Scores
}
}
function showCard(c, p)
{
var move = p * 40;
var bgColor = (c.icon == 'H' || c.icon == 'D') ? 'red' : 'black';
var bCard = '<div class="icard" style="color:'+ bgColor +'">' + c.num + ' &' + c.suit + ';</div>';
console.log(c, move);
return bCard;
}
function buildCards()
{
cards = [];
for (s in suits)
{
var suitNew = suits[s][0].toUpperCase();
for(n in cardFace)
{
var card = {
suit:suits[s],
num:cardFace[n],
cardValue:parseInt(n) +2,
icon:suitNew
}
cards.push(card);
}
}
console.log(cards);
}
function dealCards(array)
{
for(var i = 0; i < array.length; i++)
{
// swaps between remainder 0 and 1, which signifies player[0 OR 1], and then pushes that onto parameter,(array), which
// is cards which is an array, at the index of for loop [i]
var m = i % 2;
players[m].push(array[i])
}
console.log(players)
}
//
// function dealCards(array)
// {
// for(var i = 0; i < array.length; i++)
// {
// if(i % 2 === 0 )
// {
// players[0].push(array[i]);
// }
// else
// {
// players[1].push(array[i]);
// }
//
// }
// console.log(players);
// }
//
function shuffleArray(array)
{
for(var x = array.length -1; x > 0; x--)
{
var ii = Math.floor(Math.random() * (x + 1))
var temp = array[x];
array[x] = array[ii];
array[ii] = temp;
}
console.log(cards);
return array;
}
});
</script>
</body>
</html>
I think you missed to add the players class to the player 2 element.
<div id="player2" class="players">
https://jsfiddle.net/zkw9s4an/
Add css to the player 2 div to move it margin left by the amount you need.
#player2
{
margin-left:10%;
}
Refer this.
Hi I've got this code which is a memory board game. I would like to know if its possible to change the letters and put images instead. I am completely new to javaScript and I am trying to learn by editing and understanding open source code.. any help would be appreciated thanks!
This is the Javscript code:
var memory_array = ['A','A','B','B','C','C','D','D','E','E','F','F','G','G','H','H','I','I','J','J','K','K','L','L'];
var memory_values = [];
var memory_tile_ids = [];
var tiles_flipped = 0;
Array.prototype.memory_tile_shuffle = function(){
var i = this.length, j, temp;
while(--i > 0){
j = Math.floor(Math.random() * (i+1));
temp = this[j];
this[j] = this[i];
this[i] = temp;
}
}
function newBoard(){
tiles_flipped = 0;
var output = '';
memory_array.memory_tile_shuffle();
for(var i = 0; i < memory_array.length; i++){
output += '<div id="tile_'+i+'" onclick="memoryFlipTile(this,\''+memory_array[i]+'\')"></div>';
}
document.getElementById('memory_board').innerHTML = output;
}
function memoryFlipTile(tile,val){
if(tile.innerHTML == "" && memory_values.length < 2){
tile.style.background = '#FFF';
tile.innerHTML = val;
if(memory_values.length == 0){
memory_values.push(val);
memory_tile_ids.push(tile.id);
} else if(memory_values.length == 1){
memory_values.push(val);
memory_tile_ids.push(tile.id);
if(memory_values[0] == memory_values[1]){
tiles_flipped += 2;
// Clear both arrays
memory_values = [];
memory_tile_ids = [];
// Check to see if the whole board is cleared
if(tiles_flipped == memory_array.length){
alert("Board cleared... generating new board");
document.getElementById('memory_board').innerHTML = "";
newBoard();
}
} else {
function flip2Back(){
// Flip the 2 tiles back over
var tile_1 = document.getElementById(memory_tile_ids[0]);
var tile_2 = document.getElementById(memory_tile_ids[1]);
tile_1.style.background = 'url(images/logo.jpg) no-repeat';
tile_1.innerHTML = "";
tile_2.style.background = 'url(images/logo.jpg) no-repeat';
tile_2.innerHTML = "";
// Clear both arrays
memory_values = [];
memory_tile_ids = [];
}
setTimeout(flip2Back, 700);
}
}
}
}
This is the HTML Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The HTML5 Herald</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="script.js"></script>
</head>
<body>
<div id="memory_board"> </div>
<script> newBoard(); </script>
</body>
</html>
And finally this is the CSS code
div#memory_board{
background: #CCC;
border: #999 1px solid;
width: 800px;
height: 540px;
padding: 24px;
margin: 0px auto;
}
div#memory_board > div{
background: url(images/logo.jpg) no-repeat;
border: #000 1px solid;
width: 71px;
height: 71px;
float: left;
margin: 10px;
padding: 20px;
font-size: 64px;
cursor: pointer;
text-align:center;
}
Easy way to change your code to do what you want - instead of
tile.innerHTML = val;
do:
tile.innerHTML = '<img src="' + val + '.png"/>';
which should work if you have A.png, B.png and so on in the same location as index.html.
If you wanted to go more in-depth with it, I wrote a tutorial about doing this with an html5 framework some time ago (apologies for linking to our own website)
I'm trying to implement a single player tic tac toe game in which the computer player never loses (forces a draw or wins every time). After searching around it seems using the minimax strategy is pretty standard for this, yet I can't seem to get my algorithm to work correctly, which causes the computer to pick a really bad move. Can anyone help me find where my code has gone wrong?
'O' is the user (max) and 'X' is the computer (min)
EDIT: I re-posted my code, I've fixed some things within getComputerNextMove and a few minor bugs...now my code is finding better scores, but it doesn't always pick up if someone wins (the functions seem to be alright, I think I'm just not checking in the right spots). It also isn't picking the best move. There are some tester functions at the bottom to look at how minimax works/to test game states (winners/draw).
tictactoe.js:
// Setup
var user = new Player('user', 'O', 'green');
var computer = new Player('computer', 'X', 'pink');
window.players = [user, computer];
var gameBoard = new GameBoard();
gameBoard.initializeBoard();
// Trackers for the board and moves
var wins = ['012', '345', '678', '036', '147', '258', '048', '246'];
var moves = 1;
var currentPlayer = players[0];
var game = true;
function Player(name, selector, color) {
this.name = name;
this.moves = [];
this.selector = selector;
this.color = color;
this.makeMove = function(boxId){
if(!gameBoard.isGameOver() && gameBoard.isValidMove(boxId)){
markBox(this, boxId);
this.updateMoves(boxId);
gameBoard.updateBoard(this.selector, boxId);
setTimeout(function () { nextTurn() }, 75);
}
else{
if(gameBoard.isXWinner())
endGame("Computer");
else if(gameBoard.isOWinner())
endGame("You");
else
endGame();
}
}
this.updateMoves = function(boxId){
moves = moves + 1;
this.moves.push(boxId);
}
}
function Square(mark){
this.mark = mark;
this.position;
this.id;
this.score = 0; //for minimax algorithm
}
function GameBoard(){
this.state = [9];
this.initializeBoard = function(){
for(var space = 0; space < 9; space++){
var square = new Square("-");
this.state[space] = square;
this.state[space].position = space;
this.state[space].id = space;
this.state[space].score = 0;
}
}
this.getAvailableSpaces = function(){
var availableSpaces = [];
for(var space = 0; space < this.state.length; space++){
if(this.state[space].mark === "-")
availableSpaces.push(this.state[space]);
}
return availableSpaces;
}
this.updateBoard = function(selector, position){
this.state[position].mark = selector;
}
this.getPositionOfMarks = function(){
var positionOfMarks = [];
for(var sqr=0; sqr<this.state.length; sqr++){
var positionOfMark = {
'position' : this.state[sqr].position,
'mark' : this.state[sqr].mark
}
positionOfMarks.push(positionOfMark);
}
return positionOfMarks;
}
this.getXMovesAsString = function(){
var positionOfMarks = this.getPositionOfMarks();
var xMoves = "";
for(var pm=0; pm < positionOfMarks.length; pm++){
if(positionOfMarks[pm].mark === "X"){
var m = parseInt(positionOfMarks[pm].position);
xMoves += m;
}
}
return xMoves;
}
this.getOMovesAsString = function(){
var positionOfMarks = this.getPositionOfMarks();
var oMoves = "";
for(var pm=0; pm < positionOfMarks.length; pm++){
if(positionOfMarks[pm].mark === "O"){
var m = parseInt(positionOfMarks[pm].position);
oMoves += m;
}
}
return oMoves;
}
this.isValidMove = function(boxId){
return this.state[boxId].mark === "-"
}
this.isOWinner = function(){
var winner = false;
var oMoves = this.getOMovesAsString();
for(var win=0; win<wins.length; win++){
if(oMoves.search(wins[win]) >= 0)
winner = true
}
return winner;
}
this.isXWinner = function(){
var winner = false;
var xMoves = this.getXMovesAsString();
for(var win=0; win<wins.length; win++){
if(xMoves.search(wins[win]) >= 0)
winner = true;
}
return winner;
}
this.isDraw = function(){
var draw = true;
if(!this.isOWinner() && !this.isXWinner()){
for(var space=0; space < this.state.length; space++){
if(this.state[space].mark === "-"){
draw = false;
}
}
}
return draw;
}
this.isGameOver = function(){
var gameOver = false;
if(gameBoard.isDraw() ||
gameBoard.isOWinner() ||
gameBoard.isXWinner())
gameOver = true;
return gameOver;
}
this.printBoardToConsole = function(){
var row1 = [];
var row2 = [];
var row3 = [];
for(var space=0; space<this.state.length; space++){
if(space < 3)
row1.push((this.state[space].mark));
else if(space >= 3 && space < 6)
row2.push((this.state[space].mark));
else
row3.push((this.state[space].mark));
}
console.log(row1);
console.log(row2);
console.log(row3);
}
GameBoard.prototype.clone = function(){
var clone = new GameBoard();
clone.initializeBoard();
for(var square=0; square < this.state.length; square++){
clone.state[square].mark = this.state[square].mark;
clone.state[square].position = this.state[square].position;
clone.state[square].id = this.state[square].id;
clone.state[square].score = this.state[square].score;
}
return clone;
}
}
// Handle clicks
$(".box").click(function (event) {
if (getCurrentPlayer() === user && game == true){
user.makeMove(event.target.id);
}
else overlay("Wait your turn!");
});
// *** MOVES ***
// With every move, these functions update the state of the board
function getComputerNextMove(board){
var availableSquares = board.getAvailableSpaces();
var prevScore = -100000;
var bestMove;
for(var square = 0; square < availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("X", availableSquares[square].position);
console.log("gameBoardChild: " + gameBoardChild.printBoardToConsole());
console.log("===================");
var currentScore = min(gameBoardChild);
console.log("Child Score: " + currentScore);
if(currentScore > prevScore){
prevScore = currentScore;
bestMove = availableSquares[square].position;
console.log("Best Move: " + bestMove);
}
}
console.log("====================================");
return bestMove;
}
function max(board){
if(board.isOWinner()) return 1;
else if(board.isXWinner()) return -1;
else if(board.isDraw()) return 0;
var availableSquares = board.getAvailableSpaces();
var bestScore = -100000;
for(var square=0; square<availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("O", availableSquares[square].position);
var move = min(gameBoardChild);
if(move > bestScore)
bestScore = move;
}
return bestScore;
}
function min(board){
if(board.isOWinner()) return 1;
else if(board.isXWinner()) return -1;
else if(board.isDraw()) return 0;
var availableSquares = board.getAvailableSpaces();
var bestScore = 100000;
for(var square=0; square<availableSquares.length; square++){
var gameBoardChild = board.clone();
gameBoardChild.updateBoard("X", availableSquares[square].position);
var move = max(gameBoardChild);
if(move < bestScore)
bestScore = move;
}
return bestScore;
}
function markBox(player, boxId) {
$("#" + boxId).text(player.selector).addClass(player.color);
}
// *** SETTERS & GETTERS
function getCurrentPlayer() {
return currentPlayer;
}
function setCurrentPlayer(player) {
currentPlayer = player;
return currentPlayer;
}
function nextTurn() {
if(!gameBoard.isGameOver()){
if (getCurrentPlayer() === user) {
setCurrentPlayer(computer);
computer.makeMove(getComputerNextMove(gameBoard));
}
else setCurrentPlayer(user);
}
else{
if(gameBoard.isOWinner())
endGame("You");
else if(gameBoard.isXWinner())
endGame("Computer");
else
endGame();
}
}
function endGame(winner) {
game = false;
if (winner) {
overlay(winner.name + " wins!");
} else overlay("It's a draw!");
}
//Toggles info box (to avoid using alerts)
function overlay(message) {
$("#info").text(message);
$el = $("#overlay");
$el.toggle();
}
function buildTestBoard(test){
var board = new GameBoard();
board.initializeBoard();
if(test === "minimax"){
board.updateBoard("O", 0);
board.updateBoard("O", 2);
board.updateBoard("O", 4);
board.updateBoard("O", 8);
board.updateBoard("X", 1);
board.updateBoard("X", 6);
board.updateBoard("X", 7);
}
else if(test === "xwin"){
board.updateBoard("X", 6);
board.updateBoard("X", 4);
board.updateBoard("X", 2);
}
board.printBoardToConsole();
return board;
}
function testMinimax(){
var board = buildTestBoard("minimax");
var move = getComputerNextMove(board);
console.log("Best move: " + move);
}
function testWinner(){
var board = buildTestBoard("xwin");
return board.isXWinner();
}
index.html:
<!DOCTYPE html>
<!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title></title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width">
<!-- Place favicon.ico and apple-touch-icon.png in the root directory -->
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/main.css">
<script src="js/vendor/modernizr-2.6.2.min.js"></script>
</head>
<body>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an <strong>outdated</strong> browser. Please
upgrade your browser or
activate Google Chrome Frame
to improve your experience.
</p>
<![endif]-->
<h2 id="title">Single Player Tic Tac Toe</h2>
<div id='0' class='box'></div>
<div id='1' class='box'></div>
<div id='2' class='box'></div>
<br />
<div id='3' class='box'></div>
<div id='4' class='box'></div>
<div id='5' class='box'></div>
<br />
<div id='6' class='box'></div>
<div id='7' class='box'></div>
<div id='8' class='box'></div>
<div id="overlay">
<div>
<p id="info"></p>
</div>
</div>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src='//www.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>
</body>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/vendor/jquery-1.9.1.min.js"><\/script>')</script>
<script src="js/plugins.js"></script>
<script src="js/main.js"></script>
<script src="js/game/tictactoe.js"></script>
</html>
css:
.box {
width:100px;
height:100px;
display:inline-block;
background:#eee;
margin:3px 1px;
text-align:center;
font-size:24px;
font-family:arial;
vertical-align:bottom;
line-height:450%
}
.box:hover {
cursor: pointer;
}
a {
text-decoration:none;
}
.label {
position:relative;
bottom:0;
right:0
}
.green {
background:#90EE90;
}
.pink {
background:#FDCBBB;
}
.credit {
color:#333;
font-family:arial;
font-size:13px;
margin-top:40px;
}
#overlay {
position: absolute;
left: 300px;
top: 0px;
width:20%;
height:20%;
text-align:center;
z-index: 1000;
}
#overlay div {
width:100px;
margin: 100px auto;
background-color: #fff;
border:1px solid #000;
padding:15px;
text-align:center;
align: right;
}