<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.7/css/all.css">

<div class="chess-board">
  
<div class="digits">
  <div class="digit">8</div>
  <div class="digit">7</div>
  <div class="digit">6</div>
  <div class="digit">5</div>
  <div class="digit">4</div>
  <div class="digit">3</div>
  <div class="digit">2</div>
  <div class="digit">1</div>
</div>
  
<div class="main-board" id="main-board">
</div>
  
<div>
</div>
  
<div class="letters">
  <div class="letter">a</div>
  <div class="letter">b</div>
  <div class="letter">c</div>
  <div class="letter">d</div>
  <div class="letter">e</div>
  <div class="letter">f</div>
  <div class="letter">g</div>
  <div class="letter">h</div>
</div>
  
</div>


<div class="play-again">
  <button onclick="playAgain()">Play again</button>
  <br>
  (uses randomized backtracking, to find another solution)
</div>

<div class="story" id="story">
</div>
.chess-board {
  display: grid;
  grid-template-columns: 20px auto;
  grid-template-rows: auto auto;
  margin-top: 40px;
  margin-left: 40px;
  width: 450px;
  float: left;
  margin-bottom: 50px;
}

.digits {
  display: grid;
  grid-template-columns: 30px;
  grid-template-rows: 50px 50px 50px 50px 50px 50px 50px 50px;
}

.digit {
  color: seagreen;
  font-weight: bold;
  padding-top: 15px;
  font-size: 16px;
}

.letters {
  display: grid;
  grid-template-columns: 50px 50px 50px 50px 50px 50px 50px 50px;
  grid-template-rows: 30px;
}

.letter {
  color: seagreen;
  font-weight: bold;
  text-align: center;
  padding-top: 6px;
  font-size: 18px;
  font-family: Consolas;
}

.main-board {
  display: grid;
  grid-template-columns: 50px 50px 50px 50px 50px 50px 50px 50px;
  grid-template-rows: 50px 50px 50px 50px 50px 50px 50px 50px;
  width: 400px;
  border: solid 2px seagreen;
}

.square {
  font-size: 30px;
  text-align: center;
  padding-top: 8px;
  color: crimson;
}

.white-square {
  background: lavenderblush;
}

.dark-square {
  background: seagreen;
}

.play-again {
  float: left;
  font-family: Verdana;
  font-size: 10px;
  margin-top: 90px;
  margin-left: 50px;
  color: midnightblue;
}

.play-again > button {
  width: 120px;
  height: 50px;
  margin-bottom: 10px;
  cursor: pointer;
  font-weight: bold;
  color: midnightblue;
}



.story {
  clear: left;
  margin-left: 50px;
  font-family: Verdana;
  font-size: 12px;
  color: #555555;
  line-height: 150%;
  letter-spacing: .25px;
  max-width: 600px;
  padding: 20px;
  box-shadow: 0px 0px 40px grey;
}

.story > h3 {
  color: midnightblue;
  margin-top: 10px;
  margin-bottom: 5px;
}

code {
  font-family: Consolas,"courier new";
  color: crimson;
  background-color: #f1f1f1;
  font-size: small;
}

class Chess {
  constructor() {
    this.board = new Board();
    this.solution = [];
  }
  
  
  solvePuzzle() {
    return this.placeQueen(0);
  }
  
  
  placeQueen(row) {
    if(row > 7) {
      return true;
    }
    
    let options = this.shuffle( [0, 1, 2, 3, 4, 5, 6, 7] );
    
    for(let i = 0; i < options.length; i++) {
      let col = options[i];
      
      if(this.isSquareThreatened(row, col)) {
        continue;
      }
      
      this.board.squares[row][col].hasQueen = true;
      
      if(this.placeQueen(row + 1)) {
        this.solution.push(this.board.squares[row][col].id);
        return true;
      }
      
      this.board.squares[row][col].hasQueen = false;
    }
    
    return false;
  }
  
  
  isSquareThreatened(row, col) {
    return this.isColumnThreatened(row, col) ||
            this.isDiagonalThreatenedNW(row, col) ||
            this.isDiagonalThreatenedNE(row, col);
  }
  
  
  isColumnThreatened(row, col) {
    for(let r = row - 1; r >= 0; r--) {
      if(this.board.squares[r][col].hasQueen) {
        return true;
      }
    }
    
    return false;
  }
  
  
  isDiagonalThreatenedNW(row, col) {
    for(let r = row - 1, c = col - 1; r >= 0 && c >= 0; r--, c--) {
      if(this.board.squares[r][c].hasQueen) {
        return true;
      }
    }
    
    return false;
  }
  
  
  isDiagonalThreatenedNE(row, col) {
    for(let r = row - 1, c = col + 1; r >= 0 && c < 8; r--, c++) {
      if(this.board.squares[r][c].hasQueen) {
        return true;
      }
    }
    
    return false;
  }
  
  
  shuffle(a) {
    let a2 = [];
    
    while(a.length > 0) {
      let i = Math.floor( Math.random() * a.length );
      a2.push(...a.splice(i, 1));
    }
    
    return a2;
  }
}


class Board {
  constructor() {
    this.squares = [];
    
    for(let r = 0; r < 8; r++) {
      let row = [];
      
      for(let c = 0; c < 8; c++) {
        let square = new Square(r, c);
        row.push(square);
      }
      
      this.squares.push(row);
    }
  }
}


class Square {
  constructor(row, col) {
    this.row = row;
    this.col = col;
    
    this.hasQueen = false;
    this.id = `${row}, ${col}`;
  }
}


let chess = null;


window.onload = () => {
  drawMainBoard();
  playAgain();
  
  displayStory();
}


function drawMainBoard() {
  let board = document.getElementById("main-board"),
      classes = ["white-square", "dark-square"];
  
  for(let row = 0; row < 8; row++) {
    let c = row % 2;

    for(let col = 0; col < 8; col++) {
      let square = document.createElement("div");
      
      square.className = classes[c] + " " + "square";
      square.id = `${row}, ${col}`;
      
      board.appendChild(square);
      c = (c + 1) % 2;
    }
  }
}


function displaySolution() {
  chess.solution.forEach(id => {
    let square = document.getElementById(id);
    square.innerHTML = '<i class="fas fa-chess-queen"></i>';
  });
}


function clearBoard() {
  if(chess == null) {
    return;
  }
  
  chess.solution.forEach(id => {
    let square = document.getElementById(id);
    square.innerHTML = '';
  });
}


function playAgain() {
  clearBoard();
  chess = new Chess();
  
  if(!chess.solvePuzzle()) {
    alert("Could not find a solution!");
    return;
  }
  
  displaySolution();
}


function displayStory() {
  let story = document.getElementById("story");
  story.innerHTML = getStoryHtml();
}


function getStoryHtml() {
  return `
<h3>Challenge</h3>
<p>
This week we propose another straightforward challenge, which will delight the chess lovers among us… And also those who’ve followed the new Netflix original series <a href="https://www.netflix.com/title/80234304" target="_blank"><i>"The Queen’s Gambit"</i></a> (<b>hint</b>: highly recommended!)
</p>

<p>
So without further suspense, here it goes:
</p>

<br>
<p><b><i>
Place eight queens on a chess-board, in such a way that no two queens attack or threaten each other.
</i></b></p>
<br>
  
<p>
This is known as the Eight Queens problem, and if you’ve followed our <a href="https://codepen.io/ccodreanu/pen/mdPwOva" target="_blank">maze-traversal</a> or <a href="https://codepen.io/ccodreanu/pen/pobOrxv" target="_blank">Sudoku</a> puzzles…
You’ll realize that it’s the same class of algorithm (<b>hint</b>: backtracking) – only a tad easier.
</p>
<br>

<h3>Implementation hints</h3>

<ul>
<li>Use JavaScript for the program, and HTML + CSS for the UX</li>
<li>Be creative with the UX, try to paint the chess-board and the pieces realistically</li>
  <li>Use any HTML technique you like (<code>div</code>, <code>table</code>, <code>grid</code> layout, even <code>canvas</code> – anything is fair game)</li>
  <li>Feel free to revisit the <a href="https://codepen.io/ccodreanu/pen/pobOrxv" target="_blank">Sudoku challenge</a>, in case you’d like a refresher on backtracking</li>
  <li>If you want a bit of extra challenge, try to find all the solutions to this problem (<b>hint</b>: there are <a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle" target="_blank">92 of them</a>!)</li>
</ul>

<br>
<p>
<span style="color: navy; font-weight: bold;";>Good luck, and have fun!</span>
</p>
`;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.