A Pen by Dan Couper on CodePen.
Created
April 5, 2017 09:20
-
-
Save DanCouper/a8de79c3ed6f9120fd7867aee94b12d2 to your computer and use it in GitHub Desktop.
yMrzEB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!--FCC Tic Tac Toe--> | |
<header></header> | |
<main> | |
<button onclick='gameData.reset()'>New Game</button> | |
<section id='gameOverall'> | |
<div id='gameCentral' class='gameCentralDefault'></div> | |
</section> | |
</main> | |
<footer></footer> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//FCC TIC TAC TOE GAME | |
//******************************************************** | |
//******************************************************** | |
//OBJECT TO HOLD STATE OF GAME | |
var gameData = { | |
//data | |
p1: { | |
being: 'Human', | |
shape: 'nought', | |
hasWon: false | |
}, | |
p2: { | |
being: 'Human', | |
shape: 'cross', | |
hasWon: false | |
}, | |
whoseTurn: 'p1', | |
turn: 0, | |
board: [ | |
{name: 'topLeft', shape: 'empty', row: 0, col: 0}, | |
{name: 'topMiddle', shape: 'empty', row: 0, col: 1}, | |
{name: 'topRight', shape: 'empty', row: 0, col: 2}, | |
{name: 'centerLeft', shape: 'empty', row: 1, col: 0}, | |
{name: 'centerMiddle', shape: 'empty' , row: 1, col: 1}, | |
{name: 'centerRight', shape: 'empty', row: 1, col: 2}, | |
{name: 'bottomLeft', shape: 'empty', row: 2, col: 0}, | |
{name: 'bottomMiddle', shape: 'empty', row: 2, col: 1}, | |
{name: 'bottomRight', shape: 'empty', row: 2, col: 2} | |
], | |
stages: [ | |
'choosePlayers', | |
'chooseShape', | |
'playing', | |
'endGame' | |
], | |
currentStage: 0, | |
//methods | |
//expects a str ('p1' or 'p2'). Toggles being of that player. | |
changeBeing: function(player) { | |
if (this[player].being === 'Human') { | |
this[player].being = 'AI'; | |
} | |
else { | |
this[player].being = 'Human'; | |
} | |
}, | |
//expects a str ('p1' or 'p2'). Toggles shape of that player. | |
changeShape: function() { | |
if (this.p1.shape === 'nought') { | |
this.p1.shape = 'cross'; | |
this.p2.shape = 'nought'; | |
} | |
else if (this.p1.shape === 'cross') { | |
this.p1.shape = 'nought'; | |
this.p2.shape = 'cross'; | |
} | |
}, | |
//expects a str ('p1' or 'p2'). Sets hasWon to true for that player. | |
winner: function(player) { | |
this[player].hasWon = true; | |
}, | |
//toggles which players turn it is. | |
changeTurn: function() { | |
if (this.whoseTurn === 'p1') { | |
this.whoseTurn = 'p2'; | |
} | |
else { | |
this.whoseTurn = 'p1'; | |
} | |
}, | |
//expects a str ('p1' or 'p2' & square (eg topLeft), places a shape onto the board. | |
placeShape: function(square) { | |
this.board.forEach(function(element) { | |
if( element.name === square && element.shape === 'empty' ) { | |
element.shape = this[this.whoseTurn].shape; | |
} | |
}, this); | |
}, | |
//resets gameData *** USE THIS SOMEWHERE!!!! | |
reset: function() { | |
this.p1.being = 'Human'; | |
this.p1.shape = 'nought'; | |
this.p1.hasWon = false; | |
this.p2.being = 'Human'; | |
this.p2.shape = 'cross'; | |
this.p2.hasWon = false; | |
this.whoseTurn = 'p1'; | |
this.board.forEach(function(element) { | |
element.shape = 'empty'; | |
}); | |
this.currentStage = 0; | |
doTurn(); | |
}, | |
//progresses game stage | |
nextStage: function() { | |
this.currentStage++; | |
if (this.currentStage > this.stages.length - 1) { | |
this.currentStage = 0; | |
} | |
} | |
}//END OF gameData | |
//******************************************************** | |
//******************************************************** | |
//FUNCTION TO RENDER BOARD | |
function renderGame() { | |
var gameCentral = document.getElementById('gameCentral'); | |
wipe(); | |
switch(gameData.currentStage) { | |
case 0: | |
choosePlayers(); | |
break; | |
case 1: | |
chooseShape(); | |
break; | |
case 2: | |
playing(); | |
break; | |
case 3: | |
endGame(); | |
break; | |
default: | |
console.log('Unexpected stage in switch statement of renderGame()'); | |
} | |
//Put the game board back to all empty squares | |
function wipe() { | |
while (gameCentral.firstChild) { | |
gameCentral.removeChild(gameCentral.firstChild); | |
} | |
gameCentral.className = ''; | |
gameCentral.classList.add('gameCentralDefault'); | |
}//End of wipe() | |
//Choose if players are any combination of AI or Human | |
function choosePlayers() { | |
//Create the message dialog node | |
var msg = document.createElement('p'); | |
var msgContent = document.createTextNode('How do you want to play?'); | |
//Create the next stage button node | |
var nextButton = document.createElement('div'); | |
var nextButtonContent = document.createTextNode('NEXT'); | |
//Create the player one option node | |
var p1Being = document.createElement('div'); | |
var p1BeingMsg = 'P1: ' + gameData.p1.being; | |
var p1BeingMsgContent = document.createTextNode(p1BeingMsg); | |
//Create the player two option node | |
var p2Being = document.createElement('div'); | |
var p2BeingMsg = 'P2: ' + gameData.p2.being; | |
var p2BeingMsgContent = document.createTextNode(p2BeingMsg); | |
//Append message to DOM | |
msg.appendChild(msgContent); | |
gameCentral.appendChild(msg); | |
//Append nextButton to DOM | |
nextButton.appendChild(nextButtonContent); | |
gameCentral.appendChild(nextButton); | |
nextButton.onclick = function() { | |
gameData.nextStage(); | |
doTurn(); | |
}; | |
//Append p1 option node to DOM | |
p1Being.appendChild(p1BeingMsgContent); | |
gameCentral.appendChild(p1Being); | |
p1Being.onclick = function() { | |
gameData.changeBeing('p1'); | |
doTurn(); | |
}; | |
//Append p2 option node to DOM | |
p2Being.appendChild(p2BeingMsgContent); | |
gameCentral.appendChild(p2Being); | |
p2Being.onclick = function() { | |
gameData.changeBeing('p2'); | |
doTurn(); | |
} | |
//Add style classes for newly created nodes | |
gameCentral.classList.add('gameCentralOptions'); | |
msg.classList.add('optionsMsg'); | |
nextButton.classList.add('nextButton'); | |
p1Being.classList.add('chooseOptions'); | |
p2Being.classList.add('chooseOptions'); | |
}//End of choosePlayers() | |
function chooseShape() { | |
//Create message dialog node | |
var msg = document.createElement('p'); | |
var msgContent = document.createTextNode('Should player one be noughts or crosses?'); | |
//Create the next stage button node | |
var nextButton = document.createElement('div'); | |
var nextButtonContent = document.createTextNode('NEXT'); | |
//Create the shape option node | |
var chosenShape = document.createElement('div'); | |
var chosenShapeMsg = gameData.p1.shape; | |
var chosenShapeContent = document.createTextNode(chosenShapeMsg); | |
//Append msg to DOM | |
msg.appendChild(msgContent); | |
gameCentral.appendChild(msg); | |
//Append nextButton to DOM | |
nextButton.appendChild(nextButtonContent); | |
gameCentral.appendChild(nextButton); | |
nextButton.onclick = function() { | |
gameData.nextStage(); | |
doTurn(); | |
}; | |
//Append chosenShape to DOM | |
chosenShape.appendChild(chosenShapeContent); | |
gameCentral.appendChild(chosenShape); | |
chosenShape.onclick = function() { | |
gameData.changeShape(); | |
doTurn(); | |
}; | |
//Style newly created nodes | |
gameCentral.classList.add('gameCentralOptions'); | |
msg.classList.add('optionsMsg'); | |
nextButton.classList.add('nextButton'); | |
chosenShape.classList.add('chooseOptions'); | |
}//End of chooseShape() | |
function playing() { | |
var boardSquare; | |
var boardSquareShape; | |
var boardSquareContent; | |
//smells having state changed by the rendering function but too stupid to think of alternative | |
gameData.turn++; | |
//Create 9 squares on board | |
gameData.board.forEach(function(element) { | |
if (element.shape === 'nought') { | |
boardSquareShape = 'O'; | |
} else if (element.shape === 'cross') { | |
boardSquareShape = 'X'; | |
} else if (element.shape === 'empty') { | |
boardSquareShape = '---'; | |
} | |
boardSquare = document.createElement('div'); | |
boardSquareContent = document.createTextNode(boardSquareShape); | |
boardSquare.appendChild(boardSquareContent); | |
gameCentral.appendChild(boardSquare); | |
boardSquare.id = element.name; | |
//get this logic out of the render function | |
boardSquare.onclick = function() { | |
if (gameData[gameData.whoseTurn].being === 'Human') { | |
gameData.placeShape(element.name); | |
doTurn(); | |
} | |
}; | |
gameCentral.classList.add('gameCentralPlaying'); | |
boardSquare.classList.add('boardSquare'); | |
}); | |
//Populate squares with shape from gameData board | |
}//End of playing() | |
//Not finished *** | |
function endGame() { | |
var player; | |
if (gameData.p1.hasWon === true) { | |
player = 'ONE'; | |
} else if (gameData.p2.hasWon === true) { | |
player = 'TWO'; | |
} | |
console.log('GAME OVER: ' + player + ' WON'); | |
}//End of endGame() | |
}//END OF renderGame() | |
//******************************************************** | |
//******************************************************** | |
//FUNCTION TO CALC IF GAME FINISHED | |
function checkForComplete() { | |
//Avoid lines 57km long | |
var lowRight = gameData.board[8].shape; | |
var midMid = gameData.board[4].shape; | |
var upLeft = gameData.board[0].shape; | |
var lowLeft = gameData.board[6].shape; | |
var upRight = gameData.board[2].shape; | |
//Evaluate to true if all 3 squares of a diagonal containt teh same shape | |
var diagA = (lowRight === midMid ) && (upLeft === midMid); | |
var diagB = (lowLeft === midMid ) && (upRight === midMid) | |
//If an element reaches 3 or -3 then a player has won | |
var victoryConditions = [ | |
0,//top | |
0,//middle row | |
0,//bottom row | |
0,//left col | |
0,//midle col | |
0,//right col | |
0//diag, either | |
] | |
//Check for a win (minus diagonal victory conditions) | |
gameData.board.forEach(function(element) { | |
if (element.shape === gameData.p1.shape) { | |
victoryConditions[element.row]++; | |
victoryConditions[element.col + 3]++; | |
} else if (element.shape === gameData.p2.shape) { | |
victoryConditions[element.row]--; | |
victoryConditions[element.col + 3]--; | |
} | |
}); | |
//Check diagonal victories | |
if ( diagA || diagB ) { | |
console.log('diagonal found'); | |
if (gameData.board[4].shape === gameData.p1.shape) { | |
victoryConditions[6] = 3; | |
} else if (gameData.board[4].shape === gameData.p2.shape) { | |
victoryConditions[6] = -3; | |
} | |
} | |
//Check if either player has won | |
victoryConditions.forEach(function(element) { | |
if (element === 3) { | |
gameData.winner('p1'); | |
} else if (element === -3) { | |
gameData.winner('p2'); | |
} | |
}); | |
//check if draw | |
//game ended, move to next stage | |
if (gameData.p1.hasWon === true || gameData.p2.hasWon === true) { | |
gameData.nextStage(); | |
} | |
}//END OF checkForComplete() | |
//******************************************************** | |
//******************************************************** | |
//FUNCTION TO MOVE AI | |
//jesus christ how do i do this! | |
function AIMove(ai) { | |
console.log('moving ' + ai); | |
} | |
//******************************************************** | |
//******************************************************** | |
//FUNCTION TO DO TURN | |
function doTurn() { | |
if (gameData.turn > 0) { | |
gameData.changeTurn(); | |
} | |
if (gameData.p1.being === 'AI') { | |
AIMove('p1'); | |
} | |
if (gameData.p2.being === 'AI') { | |
AIMove('p2'); | |
} | |
checkForComplete(); | |
renderGame(); | |
} | |
//******************************************************** | |
//******************************************************** | |
//FUNCTION | |
function newGame(){ | |
gameData.reset(); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
:root { | |
--main-bg-color: #999999; | |
--gameOverall-bg-color: #222222; | |
--gameCentral-bg-color: #356634; | |
--shape-color: #ffffff; | |
} | |
* { | |
box-sizing: border-box; | |
border: 2px solid red;/*for dev only*/ | |
} | |
html, body { | |
height: 100%; | |
margin: 0px; | |
} | |
/*LAYOUT ON SCREEN*/ | |
body { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
} | |
main { | |
display: flex; | |
flex-direction: row; | |
justify-content: center; | |
flex-grow: 0; | |
flex-shrink: 0; | |
width: 100%; | |
background-color: var(--main-bg-color); | |
} | |
header, footer { | |
flex-grow: 1; | |
flex-shrink: 1; | |
width: 100%; | |
background-color: var(--main-bg-color); | |
} | |
/*GAME BOARD STYLING*/ | |
#gameOverall { | |
width: 400px; | |
height: 400px; | |
background-color: var(--gameOverall-bg-color); | |
} | |
/*GAME BOARD - DEFAULT STYLING*/ | |
.gameCentralDefault { | |
width: 90%; | |
height: 90%; | |
margin: 5%; | |
background-color: var(--gameCentral-bg-color); | |
} | |
/*GAME BOARD - STAGE=CHOOSING PLAYERS STYLING*/ | |
.gameCentralOptions { | |
display: flex; | |
flex-direction: row; | |
flex-wrap: wrap; | |
align-items: center; | |
justify-content: space-around; | |
} | |
.optionsMsg { | |
flex-grow: 0; | |
flex-shrink: 0; | |
width: 80%; | |
height: 25%; | |
font-size: 30px; | |
text-align: center; | |
margin-top: 5%; | |
} | |
.nextButton { | |
flex-grow: 0; | |
flex-shrink: 0; | |
width: 80%; | |
height: 25%; | |
font-size: 66px; | |
text-align: center; | |
margin-top: -5%; | |
margin-bottom: 5%; | |
} | |
.chooseOptions { | |
flex-grow: 0; | |
flex-shrink: 0; | |
width: 40%; | |
height: 25%; | |
font-size: 36px; | |
text-align: center; | |
} | |
.boardSquare { | |
width: 33.3%; | |
height: 33.3%; | |
font-size: 36px; | |
color: var(--shape-color); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
.gameCentralPlaying { | |
display: flex; | |
flex-direction: row; | |
flex-wrap: wrap; | |
align-items: flex-start; | |
justify-content: flex-start; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment