JSFiddle - React, Tailwind, and code Playground

by jcubed111

HTML

<canvas id="overlay" width=500 height=500></canvas>
<div class="row" id="r7">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div class="cell" id="c7"></div>
</div>
<div class="row" id="r6">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div class="cell" id="c7"></div>
</div>
<div class="row" id="r5">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div class="cell" id="c7"></div>
</div>
<div class="row" id="r4">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div class="cell" id="c7"></div>
</div>
<div class="row" id="r3">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div class="cell" id="c7"></div>
</div>
<div class="row" id="r2">
    <div class="cell" id="c0"></div>
    <div class="cell" id="c1"></div>
    <div class="cell" id="c2"></div>
    <div class="cell" id="c3"></div>
    <div class="cell" id="c4"></div>
    <div class="cell" id="c5"></div>
    <div class="cell" id="c6"></div>
    <div...

SCSS

#overlay{
    position: absolute;
    left: 8px;
    top: 8px;
    pointer-events: none;
    z-index: 5;
}

.row{
    display: flex;
}

.cell{
    box-sizing: border-box;
    width: 50px;
    height: 50px;
    background: #eee;
    text-align: center;
    padding-top: 4px;
}

.row:nth-child(2n+1) {
    .cell:nth-child(2n) {
        background: #ddd;
    }
}
.row:nth-child(2n) {
    .cell:nth-child(2n+1) {
        background: #ddd;
    }
}

.cell.goal{
    box-shadow: inset 0 0 0 2px #0af;
}

.cell.current{
    font-size: 40px;
    font-family: sans-serif;
}

.cell.option{
    cursor: pointer;
    box-shadow: inset 0 0 0 2px #3d3;
}

.cell.void{
    background: #555 !important;
}

#pieces{
    margin-top: 5px;
    font-size: 40px;
    font-family: sans-serif;
    display: flex;
}

.piece{
    width: 50px;
    height: 50px;
    background: #eee;
    text-align: center;
    padding-top: 4px;
    box-sizing: border-box;
    
    &.active{
        background: #fff;
    }
}

JavaScript

const ctx = document.getElementById('overlay').getContext('2d');

function getCell(x, y) {
	return document.querySelector(`#r${y} #c${x}`);
}

function strBoardToBin(boardString) {
	const rows = boardString.trim().split('\n').reverse();
    return rows.map(row => {
    	let rowValue = 0;
        row.trim().split(' ').reverse().forEach(cell => {
        	if(cell == '1') rowValue++;
            rowValue *= 2;
        });
        return rowValue >> 1;
    });
}





class BoardPos{
	constructor(x, y) {
    	this.x = x; 
        this.y = y;
    }
    
    isValid() {
    	return this.x >= 0 && this.x < 8 && this.y >= 0 && this.y < 8;
    }
    
    cell() {
    	return getCell(this.x, this.y);
    }
    
    plus(dx, dy) {
    	return new BoardPos(this.x + dx, this.y + dy);
    }
}

class Level{
	constructor(board, pieces, start, goal) {
    	this.board = board;
        this.pieces = pieces.split('');
        this.start = start;
        this.goal = goal;
    }
}

class GameState{
	constructor() {
        this.boardCells = [[],[],[],[],[],[],[],[]];
        this.pieces = [];
        this.moveNum = 0;
        this.pastSpots = [];
        this.goalPos = null;
        this.moveOptions = [];
    }
    
    isNotVoid(pos) {
    	return pos.isValid() && this.boardCells[pos.x][pos.y];
    }
    
	loadLevel(level) {
        // load board
        for(let y=0; y<8; y++) {
            let row = level.board[y];
            for(let x=0; x<8; x++) {
                this.boardCells[x][y] = row & 1;
                row >>= 1;
            }
        }
        
        // load goal
        this.goalPos = level.goal;
        
        // load pieces
        this.pieces = level.pieces.slice(0);
        
        // load start pos
        this.pastSpots = [level.start];
        this.moveNum = 0;
        
        // update currnet position
        this.calculateMoveOptions();
        this.render();
    }
    
    isAtGoal() {
    	const current = this.pastSpots[this.moveNum];
        return...