JSFiddle - React, Tailwind, and code Playground

HTML

<table id="puzzle">
    <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td></td>
        <td></td>
    </tr>
</table>

CSS

#puzzle{
    border-spacing:5px;
    table-layout:fixed;
    margin: 100px auto;
}
th, td{
    border-right:2px solid #000;
    border-bottom:2px solid #000;
    border-left:2px solid #8e8e8e;
    border-top:2px solid #8e8e8e;
    width:60px;
    height:60px;
    text-align:center;
    vertical-align:middle;

}
td:hover{
    background-color: #ccc;
    cursor:pointer;
}
#empty:hover{    
    background-color:#fff;
    cursor:default;
}

JavaScript

var auxiliary = [];
var nums = [];
var table = document.getElementById('puzzle');
var cells = table.getElementsByTagName('td');

// забиваем цифры подрям
for (var i = 1; i <= 16; i++) {
    auxiliary.push(i);
    };

// перемешиваем
shuffle(auxiliary);

// добавляем в двумерный массив
for (var i = 0; i < 4; i++) {    
    nums[i] = new Array();
    for (var j = 0; j < 4; j++) {
        if(i == 3 && j == 3) break;
        nums[i][j] = auxiliary.pop();
        };
    };
auxiliary = 0;

//добавляем в таблицу
for (var j = 0; j < 4; j++) {
    for (var k = 0; k < 4; k++) {
        if(j == 3 && k == 3) break;
        cells[auxiliary].innerHTML = nums[j][k];
        auxiliary++;
        };
    };

//последнюю ячейку делаем пустой
cells[cells.length-1].setAttribute('id','empty');

//jQuery...
$('td').click(function(){
    var y, x;
    var thisNum = parseInt(this.innerHTML);
    // находим позицию элемента в двумерном массиве
    label: for (var i = 0; i < 4; i++) {
        for (var j = 0; j < 4; j++) {
            if(nums[i][j] == thisNum){
                y = i;
                x = j;
                break label;
                }
            }
        };
    var emptyCell;
    // проверяем наичие пустой чейки снизу
    if(nums[y+1][x] == undefined){
        nums[y+1][x] = nums[y][x];
        emptyCell = document.getElementById('empty');
        emptyCell.innerHTML = nums[y+1][x];
        //
        nums[y][x] = undefined;
        this.innerHTML = '';
        this.setAttribute('id','empty');
        emptyCell.removeAttribute('id');
        }
    // проверяем наичие пустой чейки сверху
    else if(nums[y-1][x] == undefined){
        nums[y-1][x] = nums[y][x];
        emptyCell = document.getElementById('empty');
        emptyCell.innerHTML = nums[y-1][x];
        //
        nums[y][x] = undefined;
        this.innerHTML = '';
        this.setAttribute('id','empty');
        emptyCell.removeAttribute('id');
        }
    });

function shuffle(arr) {        
   ...