Почему удаляется содержимое ячеек HTML таблицы при их перемешивании?
В приведенном ниже примере функция shuffleNumbers() должна перемешать числа в таблице случайным образом. Однако, вместо того, чтобы поменять местами пустую ячейку и соседнюю с ней ячейку с числом, функция оставляет пустыми обе ячейки. Проблема решилась добавлением строк, взятых сейчас в комментарий. Я не могу понять причину возникновения проблемы так же, как и ее решение :). Как может повлиять задание несуществующих координат для заменяемой ячейки markedCell = {r:-1, c:-1}; в конце итерации цикла, если в начале следующей итерации происходит задание новых координат?
let markedCell = {r:-1, c:-1}; // ячейка с числом должна меняться местами
let emptyCell = {r:3, c:3}; // с пустой ячейкой
let shuffle = document.querySelector('button[id = shuffle]');
shuffle.addEventListener('click', shuffleNumbers);
function shuffleNumbers(){
while (!(emptyCell.r == 0 && emptyCell.c == 0)){ // перемешивание чисел
let rand = Math.round(Math.random());
let rand1 = Math.round(Math.random());
if(rand == 0){
markedCell.c = emptyCell.c;
if (rand1 == 0){
markedCell.r = emptyCell.r > 0 ? emptyCell.r - 1 : emptyCell.r + 1;
} else {
markedCell.r = emptyCell.r < 3 ? emptyCell.r + 1 : emptyCell.r - 1;
}
} else {
markedCell.r = emptyCell.r;
if (rand1 == 0){
markedCell.c = emptyCell.c > 0 ? emptyCell.c - 1 : emptyCell.c + 1;
} else {
markedCell.c = emptyCell.c < 3 ? emptyCell.c + 1 : emptyCell.c - 1;
}
}
mTab.rows[emptyCell.r].cells[emptyCell.c].innerHTML = mTab.rows[markedCell.r].cells[markedCell.c].innerHTML;
mTab.rows[markedCell.r].cells[markedCell.c].innerHTML = ' ';
emptyCell = markedCell;
// markedCell = {r:-1, c:-1};
}
while(emptyCell.r < 3){ // возврат пустой ячейки в правый нижний угол
markedCell.r = emptyCell.r + 1;
markedCell.c = emptyCell.c;
mTab.rows[emptyCell.r].cells[emptyCell.c].innerHTML = mTab.rows[markedCell.r].cells[markedCell.c].innerHTML;
mTab.rows[markedCell.r].cells[markedCell.c].innerHTML = ' ';
emptyCell = markedCell;
// markedCell = {r:-1, c:-1};
}
while(emptyCell.c < 3){
markedCell.c = emptyCell.c + 1;
markedCell.r = emptyCell.r;
mTab.rows[emptyCell.r].cells[emptyCell.c].innerHTML = mTab.rows[markedCell.r].cells[markedCell.c].innerHTML;
mTab.rows[markedCell.r].cells[markedCell.c].innerHTML = ' ';
emptyCell = markedCell;
// markedCell = {r:-1, c:-1};
}
}
*{
padding:0px; margin:0px; box-sizing:border-box;
}
#mTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 250%; /* Размер шрифта в процентах */
}
#mTab td{
border:solid 1px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#mTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
#resTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 150%; /* Размер шрифта в процентах */
}
#resTab td{
border:solid 0px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#resTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
<!DOCTYPE html>
<html>
<head>
<title>Пятнашки</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<table id="mTab">
<tr>
<td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr>
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr>
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>
<br>
<button id = 'shuffle'>Перемешать </button>
<button id = 'check'>Проверить</button>
</body>
</html>
Ответы (1 шт):
Предлагаю не мешать ячейки, а просто назначать им соответствующий контент.
Функция randomize(min,max) выдает массив чисел от min включительно, до max включительно. При этом все значения случайным образом разбросаны.
Функция shuffle(table) принимает таблицу, перебирает первым делом вызывает randomize() для нашего диапазона (в дальнейшем его можно получать через кол-во ячеек в таблице - 1 => randomize(1,table.querySelectorAll('td').length-1)). Так мы получаем универсальную функцию, заполняющую ячейки случайными номерами.
Затем перебирает строки и ячейки в них, назначая textContent каждой ячейке соответствующее значение из массива. Последней ячейке ничего не достается. Но это работает, хотя можно организовать проверку.
document.getElementById('shuffle').addEventListener('click', () => {shuffle(document.querySelector('table'))});
function randomize(from,to){
const random_order = new Set();
while(random_order.size < (to - from + 1)){
random_order.add(Math.floor(Math.random() * (to - from + 1)) + from);
}
return [...random_order];
}
function shuffle(table){
const rand_nums = randomize(1,15);
const rows = table.querySelectorAll('tr');
rows.forEach((r,i) => {
const cells = r.querySelectorAll('td');
cells.forEach((c,j) => {
c.textContent = rand_nums[i*cells.length + j];
})
})
}
*{
padding:0px; margin:0px; box-sizing:border-box;
}
#mTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 250%; /* Размер шрифта в процентах */
}
#mTab td{
border:solid 1px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#mTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
#resTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 150%; /* Размер шрифта в процентах */
}
#resTab td{
border:solid 0px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#resTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
<!DOCTYPE html>
<html>
<head>
<title>Пятнашки</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<table id="mTab">
<tr>
<td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr>
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr>
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>
<br>
<button id = 'shuffle'>Перемешать </button>
<button id = 'check'>Проверить</button>
</body>
</html>
UPDATE Пришлось немного усложнить)) Но смысл остался прежним в том, что обрабатывается массив с данными, а уже затем результат переносится в таблицу.
getNeibors() возвращает массив индексов возможных соседей пустой клетки без предыдущего сдвинутого соседа (чтобы не зацикливать перемещения). Проверяются границы таблицы в основном.
shuffle(table, move_num) принимает таблицу и количество перемещений со случайным соседом. Формирует базовый массив элементов с номерами в изначальном положении исходя из размеров таблицы.
Задает пустую ячейку со значением ''(empty) объявляет prevдля дальнейшего контроля перемещения.
И в цикле while move_num раз вызывается функция moveToEmpty(), которая получает соседей пустой ячейки, выбирает случайного из них и меняет местами с empty.
После цикла, опять же смещением соседних ячеек пустая ячейка спускается в низ, а затем и вправо.
И только после этого значения ячейкам таблицы назначаются согласно данным в массиве arr.
document.getElementById('shuffle').addEventListener('click', () => {shuffle(document.querySelector('table'), 100)});
function getNeibors(arr, index, row_len, prev){
let neibors = []; // indices!!!
if(index%row_len != 0) neibors.push(index-1); // not the far left cell in row
if(index%row_len != row_len-1) neibors.push(index+1); // not the far right cell in row
if(index >= row_len) neibors.push(index-row_len); // not in top row
if(index < arr.length - row_len) neibors.push(index+row_len); // not in bottom
if(prev) neibors = neibors.filter(i => i != prev);
return neibors;
}
function shuffle(table, move_num){
const row_num = table.querySelectorAll('tr').length;
const row_len = table.querySelector('tr').querySelectorAll('td').length;
const arr_len = row_len*row_num;
const arr = [];
for(let i=1; i < arr_len; i++){ // Initial Array of the table
arr.push(i);
}
let prev = null;
let empty = arr_len-1;
arr[empty] = '';
while(move_num > 0){ // Move move_num times
moveToEmpty();
move_num--;
}
const empty_col = empty%row_len;
for(let i = empty + row_len; i < arr.length; i+=row_len){ // Move empty cell to the bottom
[arr[i], arr[empty]] = [arr[empty], arr[i]];
empty = i;
}
for(let i = empty + 1; i < arr.length; i++){ // Move empty cell to the right
[arr[i], arr[empty]] = [arr[empty], arr[i]];
empty = i;
}
const rows = table.querySelectorAll('tr'); // This part hasn`t changed at all
rows.forEach((r,i) => {
const cells = r.querySelectorAll('td');
cells.forEach((c,j) => {
c.textContent = arr[i*cells.length + j];
})
})
function moveToEmpty(){
const neibors = getNeibors(arr,empty,row_len,prev);
let random_index = (neibors.length > 1)? neibors[(Math.floor(Math.random()*(neibors.length)))] : neibors[0];
[arr[empty],arr[random_index]] = [arr[random_index],arr[empty]];
prev = empty;
empty = random_index;
}
}
*{
padding:0px; margin:0px; box-sizing:border-box;
}
#mTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 250%; /* Размер шрифта в процентах */
}
#mTab td{
border:solid 1px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#mTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
#resTab{
border:solid 5px #666666;
border-collapse:collapse;
font-family: 'Times New Roman', Times, serif; /* Гарнитура текста */
font-size: 150%; /* Размер шрифта в процентах */
}
#resTab td{
border:solid 0px #666666;
width:60px;
height:60px;
max-height:60px;
text-align:center;
overflow:hidden;
}
#resTab td textarea{
display:block;
outline:0px;
border:0px;
width:100%;
height:100%;
}
<table id="mTab">
<tr>
<td>1</td><td>2</td><td>3</td><td>4</td>
</tr>
<tr>
<td>5</td><td>6</td><td>7</td><td>8</td>
</tr>
<tr>
<td>9</td><td>10</td><td>11</td><td>12</td>
</tr>
<tr>
<td>13</td><td>14</td><td>15</td><td> </td>
</tr>
</table>
<br>
<button id = 'shuffle'>Перемешать </button>
<button id = 'check'>Проверить</button>