<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>
`;
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.