HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URL's added here will be added as <link>
s in order, and before the CSS in the editor. You can use the CSS from another Pen by using it's URL and the proper URL extention.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
You can also link to another Pen here (use the .css
URL Extension) and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, use the appropriate URL Extension and we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<div class="wrapper container-fluid">
<!--THIS contains the top row - X or 0 -->
<div id="header">
<h1 class="text-center">Tic Tac Toe</h1>
</div>
<div class="row" id="choices">
<div class="ultra-xs col-xs-3 col-xs-offset-2 col-sm-3 col-sm-offset-3 col-md-3 col-md-offset-3 col-lg-2 col-lg-offset-4" id="ex">
<div class="input-holder">
<!--<input type="text" id="xInp" class="player-choice" placeholder="X">-->
<button class="waves-effect waves-light btn player-choice valign-wrapper" id="xInp">X
<span id="xScore" class="score valign">-</span>
</button>
<!--<span id="xScore" class="score">-</span>-->
</div>
</div>
<div class="ultra-xs col-xs-3 col-xs-offset-2 col-sm-offset-1 col-sm-3 col-md-3 col-lg-2 col-lg-offset-1" id="zero">
<div class="input-holder">
<!--<input type="text" id="oInp" class="player-choice" placeholder="O">-->
<button class="waves-effect waves-light btn player-choice" id="oInp">O
<span id="oScore" class="score">-</span>
</button>
<!--<span id="oScore" class="score">-</span>-->
</div>
</div>
</div>
<div id="feedback-container">
<p id="feedback">Start game or select player</p>
</div>
<!--<div id="table-div">-->
<div id="table-div">
<table class="table table-bordered" id="app">
<tr>
<td id="one" class="cell"></td>
<td id="two" class="cell"></td>
<td id="three" class="cell"></td>
</tr>
<tr>
<td id="four" class="cell"></td>
<td id="five" class="cell"></td>
<td id="six" class="cell"></td>
</tr>
<tr>
<td id="seven" class="cell"></td>
<td id="eight" class="cell"></td>
<td id="nine" class="cell"></td>
</tr>
</table>
</div>
<!--</div>-->
<div class="button-holder">
<button type="button" class="btn btn-lg btn-block btn-custom" id="restart">
<span class="glyphicon glyphicon-repeat" aria-hidden="true"></span>
Restart</button>
<button type="button" class="btn btn-lg btn-block btn-custom" id="reset">
<span class="glyphicon glyphicon-remove" aria-hidden="true"></span>
Reset</button>
</div>
<div class="push">
<!-- this element for implementing sticky footer-->
</div>
</div>
<footer class="footer">
<span>© 2017 Developed by Manish Giri for FreeCodeCamp</span>
</footer>
@import url('https://fonts.googleapis.com/css?family=Mirza|Ravi+Prakash|Roboto');
html, body {
height: 100%;
margin: 0;
}
body {
background-color: #F8F8F8;
}
#header {
margin-left: auto;
margin-right: auto;
margin-bottom: 10px;
}
#header h1 {
font-family: 'Ravi Prakash', cursive;
font-size: 60px;
color: #14BDAC;
}
#choices {
margin-top: 10px;
margin-bottom: 20px;
}
input[type="text"] {
padding: 20px;
height: 35px;
width: 190px;
font-family: sans-serif;
font-size: 18px;
appearance: none;
border: solid 1px #707070;
transition: box-shadow 0.3s, border 0.3s;
}
input[type="text"]:focus {
outline: none;
border: none;
border-bottom: solid 3px #14BDAC;
box-shadow: 0 0 5px 1px #969696;
}
#feedback-container {
width: 80%;
margin-left: auto;
margin-right: auto;
margin-top: 20px;
font-family: 'Roboto', sans-serif;
}
#feedback {
text-align: center;
font-size: 18px;
/*text-transform: uppercase;*/
}
table {
table-layout: fixed !important;
}
#table-div {
padding: 20px;
min-width: 400px;
max-width: 600px;
margin: 0px auto;
}
#app {
background-color: #14BDAC;
width: 65%;
margin: 0 auto;
height: 300px;
/*table-layout: fixed !important;*/
}
#app tr td {
height: 85px !important;
border: 4px solid #0DA192;
padding: 0 !important;
padding-top: 20px !important;
}
.cell {
font-size: 40px;
text-align: center;
font-family: 'Roboto', sans-serif;
line-height: 85px;
vertical-align: center;
font-weight: 900;
}
/* make cursor as pointer on hover, focus and active states */
.cell:hover, .cell:focus, .cell:active {
cursor: pointer;
}
.wrapper {
padding: 20px;
min-height: 100% !important;
margin: 0 auto -50px !important;
padding-top: 0;
min-width: 320px;
}
/*
footer {
position: fixed;
bottom: 0;
text-align: center;
width: 100%;
margin: 20px auto;
}
*/
.footer,
.push {
height: 50px;
}
.footer {
/*background-color: black;*/
margin-top: 10px;
position: relative;
}
/*footer p {
font-family: 'Mirza', cursive;
text-align: center;
}*/
.footer span {
position: absolute;
bottom: 2px;
right: 0;
left: 0;
text-align: center;
font-family: 'Mirza', cursive;
color: #0DA192;
font-weight: bold;
font-size: 16px;
}
.button-holder {
min-width: 200px;
max-width: 370px;
margin: 10px auto;
}
.btn, .btn-custom {
color: white;
background-color: #0DA192;
font-family: 'Roboto', sans-serif !important;
font-size: 17px;
}
.clicked {
background-color: #0DA192;
outline: 0;
}
.btn-custom:hover {
color: white;
background-color: red !important;
outline: 0;
}
.btn-custom:active, .btn-custom:focus {
color: white;
background-color: #0DA192;
outline: 0;
}
.input-holder {
display: inline-block;
}
.score {
position: absolute;
top: -2px;
right: 20px;
}
/* styles for second button - below */
.button-holder button {
display: inline-block;
width: 45%;
}
#reset {
margin-top: 0;
margin-left: 33px;
}
#restart:hover {
background-color: green !important;
}
button span {
margin-right: 10px;
margin-top: 2px;
position: absolute;
}
/* styles for player choice buttons */
#xInp, #oInp {
width: 150px;
position: relative;
}
/*Media Queries for responsiveness*/
/* small sizes-ultraxs */
@media screen and (max-width: 508px) {
.ultra-xs {
display: block;
width: 100%;
margin: 10px auto !important;
text-align: center;
}
.input-holder {
margin: 0 auto;
}
}
/* reduce margin on the left for large sizes 1200px-1400px */
@media screen and (min-width: 1200px) and (max-width: 1300px) {
#ex {
margin-left: 350px;
}
#zero {
margin-left: 170px;
}
}
@media screen and (min-width: 768px) and (max-width: 875px) {
#ex {
margin-left: 150px;
}
#zero {
margin-left: 90px;
}
}
/* fix for bottom two buttons misalignment on <425px */
@media screen and (max-width: 425px){
.button-holder button {
display: block;
margin: 20px auto;
width: 170px;
}
#reset {
margin: 10px auto;
}
#table-div table {
margin-left: 40px;
/*margin-right: 0;*/
}
}
@media screen and (max-width: 370px){
#table-div table {
margin-left: 10px;
margin-right: 0;
}
}
/**
* Created by manishgiri on 12/20/16.
*/
/**
* Created by manishgiri on 12/10/16.
*/
/**
* Created by manishgiri on 11/24/16.
*/
$(document).ready(function () {
//------------------------------------
//global variables
//reference to board cells
var $tile1 = $("#one");
var $tile2 = $("#two");
var $tile3 = $("#three");
var $tile4 = $("#four");
var $tile5 = $("#five");
var $tile6 = $("#six");
var $tile7 = $("#seven");
var $tile8 = $("#eight");
var $tile9 = $("#nine");
//2D array -internal representation of board - 0 is empty, 1 is occupied
var board = [
[0,0,0],
[0,0,0],
[0,0,0]
];
//object to relate cell position and array number
var cellToMoves = {
one: 0,
two: 1,
three: 2,
four: 3,
five: 4,
six: 5,
seven: 6,
eight: 7,
nine: 8
};
//reference to array which will determine number of moves left
/* var movesLeft = new Array(9);
movesLeft.fill(0)*/
//reference to array of cells
var cellsLeft = Object.keys(cellToMoves);
console.log("cells left array - ")
console.log(cellsLeft);
//reference to x and o player box
var $xInp = $("#xInp");
var $oInp = $("#oInp");
var $feedback = $("#feedback");
//current player
var isXSelected = false;
var isPlayerSelected = false;
var playerChoice = '';
var aiChoice = '';
var currentTurn = '';
var playerScore = 0;
var computerScore = 0;
//function to determine current player based on input selection
function selectPlayer() {
$(".player-choice").click(function () {
console.log("inside select player");
if(this.id == 'xInp') {
$($oInp).prop("disabled", true);
console.log("Player X selected");
isXSelected = true;
isPlayerSelected = true;
playerChoice = 'X';
setMessage(`Turn ${playerChoice}`);
}
else if(this.id == 'oInp') {
$($xInp).prop("disabled", true);
console.log("Player O selected");
isXSelected = false;
isPlayerSelected = true;
playerChoice = 'O';
setMessage(`Turn ${playerChoice}`);
}
currentTurn = playerChoice;
});
}
//function to set message on top
function setMessage(message) {
$feedback.html(message);
}
//function to detect a player win
function detectWin(player) {
console.log(`Inside detect win, current player = ${player}`);
//check a player's win in all cases
if(
($tile1.html() === player && $tile4.html() === player && $tile7.html() === player) ||
($tile1.html() === player && $tile2.html() === player && $tile3.html() === player) ||
($tile1.html() === player && $tile5.html() === player && $tile9.html() === player) ||
($tile4.html() === player && $tile5.html() === player && $tile6.html() === player) ||
($tile7.html() === player && $tile8.html() === player && $tile9.html() === player) ||
($tile3.html() === player && $tile5.html() === player && $tile7.html() === player) ||
($tile2.html() === player && $tile5.html() === player && $tile8.html() === player) ||
($tile3.html() === player && $tile6.html() === player && $tile9.html() === player)
) {
//set message
setMessage(`Player ${player} wins!`);
return true;
}
else {
return false;
}
}
//function to remove a cell from the playing board
function removeCell(cells, cell) {
//cells is the array, cell is the id of the cell clicled
let cellPos = cells.indexOf(cell);
if(cellPos !== -1) {
cells.splice(cellPos, 1);
}
}
//function to make the moves by the player
function playerMove() {
//if player selects X or 0
selectPlayer();
//check sequence of playermove execution
let count = 1;
console.log(`Player Move started = ${count}`);
count++;
//player clicks on a td cell
$(".cell").click(function () {
//let $this = $(this);
//test unbinding of click events
//$(".cell").off("click");
$(".cell").css("pointer-events", "none");
//check if click occured on an occupied cell
if($(this).html() !== "") {
//SHOW an alert
swal({
title: "Duh!",
text: "That cell is filled.",
type: "warning"
});
restart();
return;
}
console.log("Callback running");
let $this = $(this);
let cellID = this.id;
console.log(`clicked on cell with id ${cellID}`);
//if player didn't select anything
if (!isPlayerSelected) {
//if a player has not been selected, select X by default
isXSelected = true;
isPlayerSelected = true;
playerChoice = 'X';
}
currentTurn = playerChoice;
let player = playerChoice;
setMessage(`Turn ${player}`);
$this.css("color", "rgb(84,84,84)");
$this.html(player);
//lookup cell position using id of current cell and remove it from movesLeft array
let selectedCellPos = cellToMoves[cellID];
console.log(`position of selected cell on board = ${selectedCellPos}`);
//remove current cell from cellsLeft array
removeCell(cellsLeft, cellID);
//verify selected cell was removed
console.log("After player click,cells left array - ")
console.log(cellsLeft);
//change message on top to indicate turn of ai - test
let nextTurn = currentTurn === 'X' ? 'O' : 'X';
setMessage(`Turn ${nextTurn}`);
//test aiMove() here
//aiMove();
//test a delay before AI move
setTimeout(() => {
aiMove()
}, 1000);
//$(".cell").bind("click");
//$(".cell").css("pointer-events", "auto");
});
}
//test gameAlert here
gameAlert();
playerMove();
function aiMove() {
$(".cell").css("pointer-events", "auto");
console.log("AI Move running");
//test for player win from previous run - works for player win
if(currentTurn !== "" && detectWin(currentTurn)) {
playerScore++;
console.log(`Player score is ${playerScore}`);
console.log("Inside aimove - 1");
//code for custom alerts - sweetalert library
//alert(`Player ${currentTurn} won!!!`);
swal("Good job!", `Player ${currentTurn} won!!!`, "success");
let currentPlayerScore = $("#xScore").html();
console.log(`Current player score = ${currentPlayerScore}`);
//bug - always increments player X's score, instead of the current player
//$("#xScore").html(playerScore);
//fix - check which player has 'currentTurn', and increment accordingly
if(currentTurn === "X") {
//increment player X's score - get the current score first
//for first run, score is -
let currentXScore = $("#xScore").html();
if (currentXScore == '-') {
currentXScore = 0;
}
else {
currentXScore = parseInt(currentXScore);
}
currentXScore++;
$("#xScore").html(currentXScore);
}
else {
//increment player O's score
//for first run, score is -
let currentOScore = $("#oScore").html();
if(currentOScore == '-') {
currentOScore = 0;
}
else {
currentOScore = parseInt(currentOScore);
}
currentOScore++;
$("#oScore").html(currentOScore);
}
//call restart
restart();
//test draw implementation
//draw();
// game resets
// reset();
return;
}
//detemine if AI is using X or O
let computerChoice = (playerChoice === 'X') ? 'O' : 'X';
console.log(`AI Choice = ${computerChoice}`);
currentTurn = computerChoice;
//set message on top
setMessage(`Turn ${computerChoice}`);
//AI choice is computerChoice
//pick a random cell from the cellsleft array
let selectedCellPos = Math.floor(Math.random() * cellsLeft.length);
let selectedCell = cellsLeft[selectedCellPos];
console.log(`selected cell id = ${selectedCell}`);
//place an O in this cell
let selectedCellID = ('#'+selectedCell);
$(selectedCellID).css("color", "rgb(242,235,211)");
$(selectedCellID).html(computerChoice);
//remove this cell from array of available cells
removeCell(cellsLeft, selectedCell);
//verify selected cell was removed
console.log("After AI click,cells left array - ")
console.log(cellsLeft);
//detect AI win here
//test for player win from previous run - works for player win
if(currentTurn !== "" && detectWin(currentTurn)) {
console.log("Inside aimove - 2");
console.log(selectedCellID);
//ai move
$(selectedCellID).css("color", "rgb(242,235,211)");
$(selectedCellID).html(computerChoice);
//print contents of winning cell for test
let winAICell = $(selectedCellID).html();
console.log(`Contents of ${selectedCellID} = ${winAICell}`);
//increment ai score
computerScore++;
//create a delay so AI move can be drawn on screen
setTimeout(function () {
//test out swal alert here
swal({
title: "Uh-Oh.",
text: "Yikes, Computer Won!!!",
type: "warning",
//showCancelButton: true,
//confirmButtonColor: "#DD6B55",
//confirmButtonText: "Yes, delete it!",
//closeOnConfirm: false
});
//alert(`Player ${currentTurn} won!!!`);
//draw ai score
//computerScore++;
console.log(`AI score is ${computerScore}`);
$("#oScore").html(computerScore);
//call restart
restart();
// resets by default
// reset();
return;
}, 1000);
$(".cell").css("pointer-events", "auto");
// alert(`Player ${currentTurn} won!!!`);
// reset();
// return;
}
//set message on top to indicate turn of next player
let nextTurn = computerChoice === 'X' ? 'O' : 'X';
setMessage(`Turn ${nextTurn}`);
//a draw will happen when all cells on the board are filled and code has not yet "returned"
if(isGameDrawn()) {
draw();
}
}
//aiMove();
function commonReset() {
//do reset procedures common to both reset and restart
//reset cellsLeft array
cellsLeft = Object.keys(cellToMoves);
//verify cellsLeft was reset
// console.log("After game restart,cells left array - ")
console.log(cellsLeft);
//clear the board
$(".cell").each(function () {
$(this).html("");
});
//set message on top
setMessage("Start game or select player");
//if a player choice button was selected at the start, the other button would be disabled
//enable both buttons
$("#xInp").prop("disabled", false);
$("#oInp").prop("disabled", false);
//reset global variables
isXSelected = false;
isPlayerSelected = false;
playerChoice = '';
aiChoice = '';
currentTurn = '';
//test if cells are clickable again - works
$(".cell").css("pointer-events", "auto");
}
function reset() {
console.log("-----GAME RESET-------");
playerScore = 0;
computerScore = 0;
//call common reset function
commonReset();
//reset player scores to -
//test
$("#xScore").html("-");
$("#oScore").html("-");
}
//function to restart game
function restart() {
console.log("-----GAME RESTART-------");
//call common reset function
commonReset();
}
//function to check if game ended in draw
function isGameDrawn() {
//loop through the cells and check if all cells are filled
//clear the board
let gameDrawn = true;
$(".cell").each(function () {
if($(this).html() === "") {
gameDrawn = false;
}
});
return gameDrawn;
}
//function to show if game ends in draw
function draw() {
//create the alert for fraw
swal({
title: "Meh!",
text: "Well, that's a draw.",
//test image hosted on dropbox
imageUrl: "https://dl.dropbox.com/s/xsd5cv1fuozvvz8/handshake.png?dl=0"
//imageUrl: "handshake.png"
});
//common reset
commonReset();
}
//hook up restart function to restart button
$("#restart").click(restart);
//hook up reset function to reset button
$("#reset").click(reset);
//an alert at the beginning with instructions?
function gameAlert() {
swal({
title: "Tips and Tricks!",
text: "<ul><li>Select X or O, or directly begin the game as X.</li>" +
"<li>Player makes the first move.</li> " +
"<li>Next, the computer makes it's move after a second.</li>" +
"<li>Restart or Reset a game at any time.</li>" +
"<li>NOTE - Restarting or Resetting will clear the current game state.</li></ul>",
html: true,
timer: 20000,
showConfirmButton: true,
type: "warning",
confirmButtonColor: "#009933"
});
}
})
Also see: Tab Triggers