我坐下来思考如何摆脱重复的代码。
2048你知道这个游戏,我正在编写我自己的实现版本。有一个瓷砖数组,每个瓷砖是
const cell = {
row : 1,
col : 1,
value : 32,
merged : false,
node : <HTMLElement>
}
从逻辑上讲,有一个 4x4 瓷砖的运动场。
任务:当玩家移动时,你需要穿过整个场地,并沿着正确的方向移动瓷砖,将具有相同价值的瓷砖合并。
这段代码有效,但看起来很糟糕。它仅使用一组瓦片来实现移动和合并瓦片的逻辑。从技术上讲,DOM 是用另一种方法更新的,瓦片的实际合并也发生在后面,这里只在瓦片上放了一个标签,它将被合并。要删除的图块只需设置为 0。
const size = 4;
function move(direction) {
let moved = false;
if (direction === 'left') {
for (let col = 1; col < size; col++) {
for (let row = 0; row < size; row++) {
const cell = getCell(row, col);
if (cell && !cell.merged) {
let targetCol;
for (targetCol = col - 1; targetCol >= 0; targetCol--) {
const targetCell = getCell(row, targetCol);
if (!targetCell)
continue;
if (targetCell.value == cell.value && !targetCell.merged) {
cell.value = 0;
targetCell.merged = true;
}
break;
}
if (cell.value != 0)
targetCol++;
if (col != targetCol) {
moveCol(cell, targetCol);
moved = true;
}
}
}
}
}
if (direction === 'right') {
for (let col = size - 2; col >= 0; col--) {
for (let row = 0; row < size; row++) {
const cell = getCell(row, col);
if (cell && !cell.merged) {
let targetCol;
for (targetCol = col + 1; targetCol < size; targetCol++) {
const targetCell = getCell(row, targetCol);
if (!targetCell)
continue;
if (targetCell.value == cell.value && !targetCell.merged) {
cell.value = 0;
targetCell.merged = true;
}
break;
}
if (cell.value != 0)
targetCol--;
if (col != targetCol) {
moveCol(cell, targetCol);
moved = true;
}
}
}
}
}
if (direction === 'up') {
for (let row = 1; row < size; row++) {
for (let col = 0; col < size; col++) {
const cell = getCell(row, col);
if (cell && !cell.merged) {
let targetRow;
for (targetRow = row - 1; targetRow >= 0; targetRow--) {
const targetCell = getCell(targetRow, col);
if (!targetCell)
continue;
if (targetCell.value == cell.value && !targetCell.merged) {
cell.value = 0;
targetCell.merged = true;
}
break;
}
if (cell.value != 0)
targetRow++;
if (row != targetRow) {
moveRow(cell, targetRow);
moved = true;
}
}
}
}
}
if (direction === 'down') {
for (let row = size - 2; row >= 0; row--) {
for (let col = 0; col < size; col++) {
const cell = getCell(row, col);
if (cell && !cell.merged) {
let targetRow;
for (targetRow = row + 1; targetRow < size; targetRow++) {
const targetCell = getCell(targetRow, col);
if (!targetCell)
continue;
if (targetCell.value == cell.value && !targetCell.merged) {
cell.value = 0;
targetCell.merged = true;
}
break;
}
if (cell.value != 0)
targetRow--;
if (row != targetRow) {
moveRow(cell, targetRow);
moved = true;
}
}
}
}
}
return moved;
}
我找不到优化额头的方法。也许你需要改变算法,帮我弄清楚。
算法,例如,用于向左(左)移动:
- 我从0开始逐列查看,也就是从左到右
- 如果我找到一个瓷砖,我会检查左边是否有瓷砖
- 如果有,检查它的值
- 如果它匹配并且瓷砖之前没有被冻结,那么我合并
- 我按照上面的条件移动瓦片,也就是要么往合并,要么往左边最远的空闲单元格移动,如果有的话
- 继续下一行
上面代码中调用的方法,供参考
const cells = [];
const cellSize = 100 / size;
function moveRow(cell, row) {
cell.row = row;
cell.node.style.top = (row * cellSize) + '%';
}
function moveCol(cell, col) {
cell.col = col;
cell.node.style.left = (col * cellSize) + '%';
}
function getCell(row, col) {
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
if (cell.row == row && cell.col == col && cell.value != 0)
return cell;
}
}
如果你不关心动画,那么最简单的方法就是通过表格。存储长数字也没有多大意义。您只能存储二的幂。
Проверку на выигрыш или проигрыш не делал.
Чуть позже мне пришла идея, хранить состояние в одной строке, (один символ - одна фишка), если кодировать в шестнадцатеричной системе. Или если делать большие поля (5х5 или 10х10), то большие числа также можно хранить вплоть до 36-ричной системы.
Переводы между строками и числами можно делать так:
Чуть позже напишу комментариев к коду. Но если будут вопросы - спрашивайте, отвечу как увижу.
您可以旋转比赛场地,以便始终从左到右执行移动,然后移动单元格并将场地旋转回来。
您可以将此坐标变换算法转换为函数
getCell
:Несложно заметить, что при хранении элементов в плоском массиве разные варианты перебора столбцов и строк отличаются только стартовой позицией и смещениями при переходе к следующим элементам. Соответственно имеет смысл не хардкодить эти величины, а вынести их в отдельную С++style табличку и обращаться к ней при входе в функцию. Также для практической реализации, если надо делать анимации перескакивания и схлопывания ячеек, то имеет смыл добавить в функцию третий аргумент, принимающий функцию обратного вызова для соотв. оповещений.
Решил немного расширить ответ. А новый код вместе со старым ответом в один ответ не вместился. Потому выложу отдельным ответом.
Прикрутил к демке выбор размера поля, две кнопки перехода по истории вперед-назад, индикатор набранных очков, обработчики touch-событий для управления с мобильных гаджетов и еще несколько неважных улучшений.
В целом, демка получилась неплохая. Над анимацией еще поработать надо.
Будут вопросы - задавайте. Код может работать некорректно. Если заметили баг, напишите, исправим.