Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URLs 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 its URL and the proper URL extension.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

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.

+ add another resource

Packages

Add Packages

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.

Behavior

Auto Save

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <body onload="shuffle_wrapper()">
<!--
<button class="btn btn-warning" onclick="reset()">Play again</button>
-->
<br>
<div class="container row" id='tttBox'>
  
  <div class="col-xs-12">
    
    <div class="outer" id='aa'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='ab'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='ac'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
  </div>

  <div class="col-xs-12">
    
    <div class="outer" id='ba'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='bb'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='bc'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>

  </div>
    
  <div class="col-xs-12">
    
    <div class="outer" id='ca'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='cb'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>
    
    <div class="outer" id='cc'>
      <div class="card">
        <div class="front face"></div>
        <div class="back face center"></div>
      </div> 
    </div>

  </div>
  <br>
  <!-- Switch & question -->
  <div class="foot">
    Are you X's or O's?
    <div>
      <label class="switch">
        <input type="checkbox" id="slider">
        <div class="slider" onclick="toggleSwitch()"></div>
      </label>
    </div>
  </div>
  <!--                    -->
    
</div>

</body>
              
            
!

CSS

              
                @font-face {
  font-family: 'GoodDog Plain';
  src: url("https://dl.dropboxusercontent.com/s/0ui52opk4ko9d29/GOODDP__.TTF?dl=0")
}

@media(max-height: 326px) {
  .foot {
    display: block !important;
    font-family: "GoodDog Plain";
    font-size: 25px;
    text-align: right;
    padding-right: 20px;
    bottom: 50px;
    width: 180px !important;
    float: left !important;
    position: relative !important;
  }
  
  .switch {
    float: right !important;
  }
  
  #tttBox {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    -webkit-transform: translate(-50%, -50%);
  }
}

.row {
  text-align: center;
}

.container {
  margin: 0 auto;
  width: 100%;
}

.outer {
  position: relative;
  margin: 2px;
  width: 50px;
  height: 50px;
  display: inline-block;
  float: none;
}

.card {
  background-color: #aaf;
  width: 100%;
  height: 100%;
  -webkit-transform-style: preserve-3d;
  transform-style: preserve-3d;
  -webkit-transition: all 1.0s cubic-bezier(0.25, 0.46, 0.45, 0.94);
  transition: all 1.0s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.card:hover {
  background-color: #c3c3ff;
}

.outer.active .card {
  color: #aaa;
  overflow: inherit;
  -webkit-transform: rotateY(540deg);
  transform: rotateY(540deg);
}

.face {
  position: absolute;
  width: 100%;
  height: 100%;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.back {
  display: block;
  color: white;
  text-align: center;
  background-color: #aaa;
  -webkit-transform: rotateY(540deg);
  transform: rotateY(540deg);
}

.o div {
  border-radius: 50%;
  -webkit-border-radius: 50%;
}

.o-border .face {
  border: 13px solid #aaa;
  background-color: white;
}

.x div {
  clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
  -webkit-clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
  overflow: hidden;
}

.foot {
  font-family: "GoodDog Plain";
  position: fixed;
  left: 0px;
  bottom: 50px;
  height: 30px;
  width: 100%;
  font-size: 25px;
}

.vert {
  margin: auto;
  width: 30px;
  height: 34px;
}


/* The switch - the box around the slider */

.switch {
  position: relative;
  display: inline-block;
  width: 60px;
  height: 34px;
}


/* Hide default HTML checkbox */

.switch input {
  display: none;
}


/* The slider */

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: #aaf;
  -webkit-transition: .4s;
  transition: .4s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 26px;
  width: 26px;
  left: 4px;
  bottom: 4px;
  background-color: white;
  -webkit-transition: .4s;
  transition: .4s;
  clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
  -webkit-clip-path: polygon(20% 0%, 0% 20%, 30% 50%, 0% 80%, 20% 100%, 50% 70%, 80% 100%, 100% 80%, 70% 50%, 100% 20%, 80% 0%, 50% 30%);
  overflow: hidden;
}

input:checked + .slider {
  background-color: #aaa;
}

input:focus + .slider {
  box-shadow: 0 0 1px #2196F3;
}

input:checked + .slider:before {
  -webkit-transform: translateX(26px);
  -ms-transform: translateX(26px);
  transform: translateX(26px);
  clip-path: none;
  -webkit-clip-path: none;
  border-radius: 50%;
  -webkit-border-radius: 50%;
  border: 5px solid white;
  background-color: #aaa;
}
              
            
!

JS

              
                /*Each square in the board has an id tag; the variables 'board' and 'wins' refer to these.
  The wins array lists every possible win condition.
  When a square is clicked, the .x or .o css class (determined by the switch) is applied 
  to make it that shape and flip it over. The board is then checked for a player win.
  On the computer's move, first it checks for a win. It takes the first possible win 
  it finds. Next it checks whether the player is about to win; if so, it blocks. If
  neither of those are true, the next unmarked square is taken. 
  When a new game is started, the board array is shuffled. The checks above loop
  through the array and check each square.
  The toggleSwitch flips the values of xo (player) and comp_xo. It is disabled when
  a square is clicked and re-enabled when the board resets.
  The setTimeout function is used to wait for the flip animation to finish.
  The pointerEvents are set to none when it isn't the player's turn.
  TODO: let computer go first; add some gradients; O's don't show up right on some browsers.
*/

var board = ["aa", "ab", "ac", "ba", "bb", "bc", "ca", "cb", "cc"];

var wins = {"aa": [["ab", "ac"], ["bb", "cc"], ["ba", "ca"]],
            "ab": [["aa", "ac"], ["bb", "cb"]],
            "ac": [["aa", "ab"], ["bb", "ca"], ["bc", "cc"]],
            "ba": [["aa", "ca"], ["bb", "bc"]],
            "bb": [["aa", "cc"], ["ca", "ac"], ["ab", "cb"], ["ba", "bc"]],
            "bc": [["ac", "cc"], ["ba", "bb"]],
            "ca": [["aa", "ba"], ["bb", "ac"], ["cb", "cc"]],
            "cb": [["ab", "bb"], ["ca", "cc"]],
            "cc": [["aa", "bb"], ["ca", "cb"], ["ac", "bc"]]
            };

var Reset = false;
var cssTrns = 1000; //square spin duration
var xo = 'x';
var comp_xo = 'o';

$('.outer').on('click', function() {
  Reset = false;
  $("#slider").prop('disabled', true); //disable the x-o switch when you start
  document.getElementById('slider').style.pointerEvents = 'none'; //
  if($(this).hasClass(xo) || $(this).hasClass(comp_xo)) return; //If it's already an x or o, return
  $(this).addClass(xo);
  if(xo == 'o') $(this).addClass('o-border');
  $(this).toggleClass('active');
  document.getElementById($(this).attr("id")).style.pointerEvents = 'none';
  if(check_your_move($(this).attr("id"))) {
    disable();
    setTimeout(function() { //Wait for the your-move spin to stop before alert/reset
      alert("You win.");
      reset();
    }, cssTrns);
    return;
  }
  if(!Reset) {
    //turn pointerEvents off so you can only click one square on your turn
    board.forEach(function(square) {
        document.getElementById(square).style.pointerEvents = 'none';
    });
    //You could also wait for the css animation to end. I thought it was simpler to 
    //do it this way.
    setTimeout(function() { 
      switch(comp_move()) {
        case 1:
          disable();
          setTimeout(function() { //Wait for the computer-move spin to stop before alert/reset
            alert("Computer wins.");          
            reset();
          }, cssTrns);
          break;
        case 2:
          break;
        case 3:
          break;
        case 4:
          break;
        case 5:
          alert("Draw");
          reset();
          break;
      }
    }, cssTrns);
    //turn pointerEvents back on when flip finishes
    setTimeout(function() {
      board.forEach(function(square) {
        if(!$('#'+square).hasClass('x') && !$('#'+square).hasClass('o')) document.getElementById(square).style.pointerEvents = 'auto';
      });
    }, cssTrns);
  }
});

function toggleSwitch() {
  if(document.getElementById('slider').style.pointerEvents == 'none') {
    return;
  }
  if(xo == 'x') {
    xo = 'o';
    comp_xo = 'x';
  } else {
    xo = 'x';
    comp_xo = 'o';
  }
  //console.log(xo);
  //console.log(Reset);
}

//reset board
function reset() {
  board.forEach(function(square) {
    document.getElementById(square).style.pointerEvents = 'auto';
    if($('#'+square).hasClass("x")) $('#'+square).removeClass("x");
    if($('#'+square).hasClass("o")) {
      $('#'+square).removeClass("o");
      $('#'+square).removeClass('o-border');
    }
    if($('.outer').hasClass('active')) $('.outer').removeClass('active');
  });
  Reset = true;
  shuffle_wrapper(); //so the computer's first move will be different next time
  $("#slider").prop('disabled', false); //re-enable x-o switch
  document.getElementById('slider').style.pointerEvents = 'auto'; //
}

function comp_move() {
  var len_board = board.length;
  //Loop through squares to check for winning move.
  //Can't use foreach or for-in because it needs to break/return.
  for(var i = 0; i < len_board; ++i) {
    var len_rows = wins[board[i]].length;
    for(var j = 0; j < len_rows; ++j) {
      //did the computer win?
      if(!$('#'+board[i]).hasClass("x") && 
         !$('#'+board[i]).hasClass("o") && 
         $('#'+wins[board[i]][j][0]).hasClass(comp_xo) && 
         $('#'+wins[board[i]][j][1]).hasClass(comp_xo)) {
        document.getElementById(board[i]).style.pointerEvents = 'none';   
        $('#'+board[i]).addClass(comp_xo);
        if(comp_xo == 'o') $('#'+board[i]).addClass('o-border');
        $('#'+board[i]).toggleClass('active');
          //$('.outer').off("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd");
        return 1;
      }
    }
  }
  
  //loop again to try to block winning move
  for(var i = 0; i < len_board; ++i) {
    var len_rows = wins[board[i]].length;
    for(var j = 0; j < len_rows; ++j) {
      if(!$('#'+board[i]).hasClass("x") && 
         !$('#'+board[i]).hasClass("o") && 
         $('#'+wins[board[i]][j][0]).hasClass(xo) && 
         $('#'+wins[board[i]][j][1]).hasClass(xo)) {
        document.getElementById(board[i]).style.pointerEvents = 'none';   
        $('#'+board[i]).addClass(comp_xo);
        if(comp_xo == 'o') $('#'+board[i]).addClass('o-border');
        $('#'+board[i]).toggleClass('active');
        console.log("true2");
        //$('.outer').off("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd");
        return 2;
      }
    }
  }
  
  //loop again for normal move
  for(var i = 0; i < len_board; ++i) {
    var len_rows = wins[board[i]].length;
    for(var j = 0; j < len_rows; ++j) {
      if(!$('#'+board[i]).hasClass("x") && !$('#'+board[i]).hasClass("o")) {
        document.getElementById(board[i]).style.pointerEvents = 'none';   
        $('#'+board[i]).addClass(comp_xo);
        if(comp_xo == 'o') $('#'+board[i]).addClass('o-border');
        $('#'+board[i]).toggleClass('active');
        
        return 4;
      }
    }
  }
  //draw
  return 5; 
}

//did you win?
function check_your_move(id) {
  var len = wins[id].length;
  for(var i = 0; i < len; ++i) {
    if($('#'+wins[id][i][0]).hasClass(xo) && $('#'+wins[id][i][1]).hasClass(xo)) return true;
  }
  return false;
}

//disable all buttons
function disable() {
  board.forEach(function(square) {
    document.getElementById(square).style.pointerEvents = 'none';
  });
}

function shuffle_wrapper() {
  board = shuffle(board);
}

//shuffle array
function shuffle(arr) {
  var curIndex = arr.length, tempValue, randIndex;
  while (curIndex !== 0) {
    randIndex = Math.floor(Math.random() * curIndex);
    curIndex--;
    tempValue = arr[curIndex];
    arr[curIndex] = arr[randIndex];
    arr[randIndex] = tempValue;
  }
  return arr;
}
              
            
!
999px

Console