<div class='body-frame'>
<div class='container'>
<div class='player-options'>
<div class='player-heading raylan-givens'>
<span>Player 1</span>
<span>Player 2</span>
</div>
<div class='icon-menu raylan-givens'>
<select id='p1select' name="icons-p1">
<option value="X">X</option>
<option value="♠">♠</option>
<option value="♣">♣</option>
<option value="☀">☀</option>
<option value="☠">☠</option>
<option value="♀">♀</option>
<option value="≒">☺</option>
<option value="⚽">⚽</option>
<option value="❁">❁</option>
<option value="❄">❄</option>
<option value="☕">☕</option>
</select>
<select id='p2select' name="icons-p2">
<option value="O">O</option>
<option value="♥">♥</option>
<option value="♦">♦</option>
<option value="☁">☁</option>
<option value="☤">☤</option>
<option value="♂">♂</option>
<option value="☺">☺</option>
<option value="⚾">⚾</option>
<option value="✵">✵</option>
<option value="✵">✵</option>
<option value="⚡">⚡</option>
</select>
</div>
<div class='radio-bar raylan-givens'>
<input id='p1c' type="radio" name="ai-player" value="player1">P1 AI</input>
<input type="radio" name="ai-player" value="none" checked>NONE</input>
<input id='p2c' type="radio" name="ai-player" value="player2">P2 AI</input>
</div>
</div>
<div class='button-bar'>
<button class='game-button' id='play'>Play</button>
</div>
<div class='game-board'>
<div class='square corner' id='A1'></div>
<div class='square side' id='A2'></div>
<div class='square corner' id='A3'></div>
<div class='square side' id='B1'></div>
<div class='square center' id='B2'></div>
<div class='square side' id='B3'></div>
<div class='square corner' id='C1'></div>
<div class='square side' id='C2'></div>
<div class='square corner' id='C3'></div>
</div>
<div class='message-board'>
<div class='marquee' id='game-speak'>Select game-token & AI player (None to play with a friend). Press Play to start!</div>
</div>
<div class='button-bar'>
<button class='game-button' id='reset'>Reset</button>
</div>
</div>
</div>
body{
background: #000000;
color: #FFFFFF;
font-family: 'Bangers', cursive;
line-height: 1.6em;
}
button{
width: 100%;
height: 26px;
margin-left: 3px;
font-size: 1em;
font-weight: bold;
background: #686868;
border: 2px none #686868;
box-shadow: -2px -2px 3px 0px #161616 inset, 2px 2px 3px 0px #848484 inset;
border-radius: 5px;
}
button:active{
box-shadow: -2px -2px 3px 0px #848484 inset, 2px 2px 3px 0px #161616 inset;
padding-left: 15px;
padding-top: 5px;
}
select{
width: 30%;
font-size: 1em;
text-align: center;
}
.body-frame{
width: 312px;
margin: auto;
}
.container{
}
.right{
float: right;
}
.left{
float: left;
}
.player-options{
padding-left: 8px;
padding-right: 8px;
}
.raylan-givens{
width: 100%;
height: 1.6em;
margin-bottom: 5px;
color: #07efe4;
letter-spacing: 2px;
font-size: 1.2em;
line-height: 1.8em;
display: flex;
justify-content: space-between;
}
.radio-bar{
text-indent: -2.2em;
}
.button-bar{
width: 100%;
height: 32px;
display: flex;
flex-wrap: wrap;
}
.game-board{
width: 100%;
height: 300px;
display: flex;
flex-wrap: wrap;
}
.square{
width: 98px;
height: 96px;
margin-left: 5px;
margin-bottom: 5px;
text-align: center;
color: #F4D742;
line-height: 1.5em;
background: #212121;
font-family: 'Luckiest Guy', cursive;
border-radius: 10px;
box-shadow: -2px -2px 3px 0px #161616 inset, 2px 2px 3px 0px #666666 inset;
font-size: 5em;
}
.marquee {
width: 300px;
height: 2em;
margin: 0 auto;
padding-top: 0.2em;
color: #0bcde2;
letter-spacing: 2px;
font-size: 1.2em;
overflow: hidden;
white-space: nowrap;
box-sizing: border-box;
animation: marquee 15s linear infinite;
}
@keyframes marquee {
0% { text-indent: 23em }
100% { text-indent: -50em }
}
.clearfix{
}
/*
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ///////////////////////////////
∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞ TIC-TAC-TOKEN: TRY NOT TO LOSE, IN STYLE! ∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞
/////////////////////////////// \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
*/
// GLOBALS
var TTTGames = [];
var game = null;
var turnCount = 0;
var recOneDrawTwo = [0,0,0];
// LIST OF WIN COMBINATIONS
var winCheckLists = {
'A1': [['A2','A3'],['B2','C3'],['B1','C1']],
'A2': [['A1','A3'],['B2','C2']],
'A3': [['A1','A2'],['B2','C1'],['B3','C3']],
'B1': [['A1','C1'],['B2','B3']],
'B2': [['A1','C3'],['A2','C2'],['A3','C1'],['B1','B3']],
'B3': [['A3','C3'],['B1','B2']],
'C1': [['A1','B1'],['A3','B2'],['C2','C3']],
'C2': [['A2','B2'],['C1','C3']],
'C3': [['A3','B3'],['A1','B2'],['C1','C2']]
};
/*
===============================
= GLOBAL FUNCTIONS =
===============================
*/
// START NEW SET OF GAMES - CALLED WHEN 'PLAY' BUTTON IS PRESSED
function nuGame(){
console.log('INITIALZING GAME');
TTTGames.unshift(new GameBoard());
game = TTTGames[0];
if (TTTGames.length > 1){
TTTGames.pop();
recOneDrawTwo = [0,0,0];
turnCount = 0;
game.squares.forEach(function(mame_square){
mame_square.removeEventListener('click', turnCycle, false);
})
}
turnCount = 0;
game.init();
document.getElementById('game-speak').innerHTML = '';
}
// ORDER OF OPERATIONS FOR EACH TURN (COMPUTER TURN SUBSEQUENT TO PLAYER)
function turnCycle(){
console.log();
if (this.innerHTML == ''){
game.playerTakeTurn(this);
}
if ((game.ai1 || game.ai2) && turnCount<9){
game.aiPlay();
if (game.ai1 && game.turn){
game.turn = false;
} else {
game.turn = true;
}
}
if (game.win){
game.winDisplay();
} else if (turnCount >= 9){
game.drawDisplay();
}
}
/*
===============================
= BOARD CLASS =
===============================
*/
class GameBoard {
// INITIAL VALUES FOR GAMEBOARD
constructor(){
this.player1comp = document.getElementById('p1c').checked;
this.player2comp = document.getElementById('p2c').checked;
this.player1icon = document.getElementById('p1select').value;
this.player2icon = document.getElementById('p2select').value;
this.player1own = [];
this.player2own = [];
this.player1Turn = true;
this.squares = [
document.getElementById('A1'),
document.getElementById('A2'),
document.getElementById('A3'),
document.getElementById('B1'),
document.getElementById('B2'),
document.getElementById('B3'),
document.getElementById('C1'),
document.getElementById('C2'),
document.getElementById('C3')];
this.aiIcon = null;
this.aiOwn = null;
this.plOwn = null;
this.winGame = false;
this.winSquares = [];
}
// MAKE SQUARES CLICKABLE
turnOnSquares(){
this.squares.forEach(function(clicked_square){
clicked_square.addEventListener('click', turnCycle);
})
}
// MAKE SQUARES NON-CLICKABLE
turnOffSquares(){
this.squares.forEach(function(mame_square){
mame_square.removeEventListener('click', turnCycle, false);
})
}
// REMOVE PLAYER MARKERS AND RESET IN-GAME VALUES
clearBoard(){
for (var i in this.squares){
this.squares[i].innerHTML = '';
}
this.player1own.splice(0);
this.player2own.splice(0);
this.winGame = false;
this.winSquares = [];
console.log('CLEARED BOARD');
}
// INITIALIZATION OF BOARD FOR EACH GAME
init(){
var self = this;
self.clearBoard.apply(self);
if (this.player1comp){
this.aiIcon = this.player1icon;
this.aiOwn = this.player1own;
this.plOwn = this.player2own;
} else if (this.player2comp){
this.aiIcon = this.player2icon;
this.aiOwn = this.player2own;
this.plOwn = this.player1own;
}
if (this.player1comp && this.player1Turn){
self.aiPlay();
this.player1Turn = false;
}
if (this.player2comp && !this.player1Turn){
self.aiPlay();
this.player1Turn = true;
}
this.turnOnSquares();
console.log('INITIALIZATION SUCCESSFUL');
}
// GET/ SET FUNCTIONS FOR EXTERNAL REFERENCES
get turn(){
return this.player1Turn;
}
set turn(val){
this.player1Turn = val;
}
get ai1(){
return this.player1comp;
}
get ai2(){
return this.player2comp;
}
get win(){
return this.winGame;
}
// CHECK FOR WIN CONDITION
winCheck(square, plist){
var winCombo = null;
var winList = winCheckLists[square.id];
for (var h in winList){
if (plist.indexOf(winList[h][0])> -1 && plist.indexOf(winList[h][1])> -1){
winCombo = [square.id, winList[h][0], winList[h][1]];
break;
}
}
return winCombo;
}
// FUNCTION CALLED WHEN PLAYER SELECTS A SQUARE
playerTakeTurn(square){
if (this.player1Turn){
square.innerHTML = this.player1icon;
this.player1own.push(square.id);
if (this.winCheck(square, this.player1own)){
this.winGame = true;
this.winSquares = this.winCheck(square, this.player1own);
}
this.player1Turn = false;
} else {
square.innerHTML = this.player2icon;
this.player2own.push(square.id);
if (this.winCheck(square, this.player2own)){
this.winGame = true;
this.winSquares = this.winCheck(square, this.player2own);
}
this.player1Turn = true;
}
turnCount ++;
console.log('PLAYER TURN');
}
// LOGIC FOR AI SQUARE SELECTION
aiPlay(){
console.log('COMPUTER TAKING TURN', this.aiOwn, this.plOwn);
var aiChoice = null;
var emptySpaces = this.squares.filter(function(box){
return box.innerHTML == '';
});
var mockTrial = [];
var potentialBoxes = [];
if (!aiChoice){
console.log('aiChoice-1');
for (var j in emptySpaces){
if (this.winCheck(emptySpaces[j], this.aiOwn)){
aiChoice = emptySpaces[j];
}
}
}
if (!aiChoice) {
console.log('aiChoice-2');
for (var k in emptySpaces){
if (this.winCheck(emptySpaces[k], this.plOwn)){
aiChoice = emptySpaces[k];
}
}
}
if (!aiChoice) {
console.log('aiChoice-3');
for (var l in emptySpaces){
potentialBoxes = [];
mockTrial = this.aiOwn.slice();
mockTrial.push(emptySpaces[l].id);
for (var m in emptySpaces){
if (this.winCheck(emptySpaces[m], mockTrial)){
potentialBoxes.push(emptySpaces[m].id);
}
}
console.log('AI trees: ', potentialBoxes.length);
if (potentialBoxes.length > 1){
aiChoice = emptySpaces[l];
break;
}
}
}
if (!aiChoice && this.aiOwn.length==1 && this.aiOwn[0]=='B2'){
if ((this.plOwn.find(function(a){return a=='A1'}) &&
this.plOwn.find(function(a){return a=='C3'})) ||
(this.plOwn.find(function(a){return a=='A3'}) &&
this.plOwn.find(function(a){return a=='C1'}))){
aiChoice = this.squares[[1,3,5,7][Math.floor(Math.random()*4)]];
}
}
if (!aiChoice){
console.log('aiChoice-4');
for (var n in emptySpaces){
potentialBoxes = [];
mockTrial = this.plOwn.slice();
mockTrial.push(emptySpaces[n].id);
for (var o in emptySpaces){
if (this.winCheck(emptySpaces[o], mockTrial)){
potentialBoxes.push(emptySpaces[o].id);
}
}
console.log('Player trees: ', potentialBoxes.length);
if (potentialBoxes.length > 1){
aiChoice = emptySpaces[n];
break;
}
}
}
if (!aiChoice){
console.log('aiChoice-5');
if (this.aiOwn.length == 0 && this.plOwn.length ==1){
if (this.squares[4].innerHTML == '') {
aiChoice = this.squares[4];
} else {
aiChoice = this.squares[[0,2,6,8][Math.floor(Math.random()*4)]];
}
} else {
console.log('aiChoice-5b');
potentialBoxes = [];
for (var p in emptySpaces){
if (emptySpaces[p].className.search('corner')> -1){
potentialBoxes.push(emptySpaces[p]);
if (emptySpaces[p].id == 'A1' &&
this.plOwn.find(function(elem){return elem=='C3';})){
aiChoice = emptySpaces[p];
break;
}
if (emptySpaces[p].id == 'A3' &&
this.plOwn.find(function(elem){return elem=='C1';})){
aiChoice = emptySpaces[p];
break;
}
if (emptySpaces[p].id == 'C1' &&
this.plOwn.find(function(elem){return elem=='A3';})){
aiChoice = emptySpaces[p];
break;
}
if (emptySpaces[p].id == 'C3' &&
this.plOwn.find(function(elem){return elem=='A1';})){
aiChoice = emptySpaces[p];
break;
}
}
}
if (!aiChoice && potentialBoxes.length > 0) {
console.log('aiChoice-6');
aiChoice = potentialBoxes[0];
}
}
}
if (!aiChoice && emptySpaces.length > 0) {
console.log('aiChoice-7');
aiChoice = emptySpaces[0];
}
aiChoice.innerHTML = this.aiIcon;
this.aiOwn.push(aiChoice.id);
if (this.winCheck(aiChoice, this.aiOwn)){
this.winGame = true;
this.winSquares = this.winCheck(aiChoice, this.aiOwn);
}
turnCount ++;
console.log('COMPUTER TURN COMPLETE');
}
// CALLED FOR MARQUEE MESSAGE AT END OF EACH GAME
scoreString(){
return ' - P1: ' + recOneDrawTwo[0] +
' - DRAW: ' + recOneDrawTwo[1] +
' - P2: ' + recOneDrawTwo[2] ;
}
// WIN SEQUENCE
winDisplay(){
this.turnOffSquares();
for (var q in this.squares){
if (this.winSquares.indexOf(this.squares[q].id)< 0){
this.squares[q].innerHTML = '';
}
}
if (this.player1Turn){
recOneDrawTwo[2] ++;
document.getElementById('game-speak').innerHTML = 'Player 2 wins!' +
this.scoreString() + '.....Press Reset for a rematch!';
} else {
recOneDrawTwo[0] ++;
document.getElementById('game-speak').innerHTML = 'Player 1 wins!' +
this.scoreString() + '.....Press Reset for a rematch!';
}
}
// DRAW SEQUENCE
drawDisplay(){
this.turnOffSquares();
recOneDrawTwo[1] ++;
document.getElementById('game-speak').innerHTML = 'TIE! ' +
this.scoreString() + '.....Press Reset for a rematch!';
}
}
/*
===============================
= LISTEN FOR GAME START/RESET =
===============================
*/
document.addEventListener('DOMContentLoaded', function(){
// MAKE 'PLAY' BUTTON CLICKABLE
document.getElementById('play').addEventListener('click', nuGame);
// MAKE 'RESET' BUTTON CLICKABLE
document.getElementById('reset').addEventListener('click', function(res){
if (game){
res.preventDefault();
console.log('RESETING GAME');
turnCount = 0;
game.init();
document.getElementById('game-speak').innerHTML = game.scoreString();
}
});
})
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.