<style>
#table {
border-collapse: collapse;
user-select: none;
/* 드래그 막기 */
}
#table td {
border: 10px solid #bbada0;
width: 116px;
height: 128px;
font-size: 50px;
font-weight: bold;
text-align: center;
}
#score {
user-select: none;
}
.color-2 {
background-color: #eee4da;
color: #776e65;
}
.color-4 {
background-color: #eee1c9;
color: #776e65;
}
.color-8 {
background-color: #f3b27a;
color: white;
}
.color-16 {
background-color: #f69664;
color: white;
}
.color-32 {
background-color: #f77c5f;
color: white;
}
.color-64 {
background-color: #f75f3b;
color: white;
}
.color-128 {
background-color: #edd073;
color: #776e65;
}
.color-256 {
background-color: #edcc62;
color: #776e65;
}
.color-512 {
background-color: #edc950;
color: #776e65;
}
.color-1024 {
background-color: #edc53f;
color: #776e65;
}
.color-2048 {
background-color: #edc22e;
color: #776e65;
}
</style>
<body>
<table id="table"></table>
<div id="score">0</div>
<button id="back">되돌리기</button>
<script>
const $table = document.getElementById('table');
const $score = document.getElementById('score');
const $back = document.getElementById('back');
let data = [];
const history = [];
$back.addEventListener('click', () => {
const prevData = history.pop();
if (!prevData) return; //되돌릴게 없으면 종료
$score.textContent = prevData.score;
data = prevData.table;
draw();
});
function startGame() {
//fragment 메모리에 저장함 성능이 좋아진다.
const $fragment = document.createDocumentFragment();
[1, 2, 3, 4].forEach((function () {
const rowData = []; //2차원배열 만들어줌
data.push(rowData);
const $tr = document.createElement('tr');
[1, 2, 3, 4].forEach(() => {
rowData.push(0);
const $td = document.createElement('td');
$tr.appendChild($td);
});
$fragment.appendChild($tr);
}));
$table.appendChild($fragment);
put2ToRandomCell();
draw();
}
function put2ToRandomCell() {
const emptyCells = []; // [i1, j1], [i2,j2], [i3,j3]
data.forEach(function (rowData, i) {
rowData.forEach(function (cellData, j) {
if (!cellData) {
emptyCells.push([i, j]);
}
});
});
//randomCell === [i,j]
const randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];
data[randomCell[0]][randomCell[1]] = 2;
}
function draw() {
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
const $target = $table.children[i].children[j];
if (cellData > 0) {
$target.textContent = cellData;
$target.className = 'color-' + cellData;
} else {
$target.textContent = '';
$target.className = '';
}
})
})
}
startGame();
//dummy data
// data = [
// [32, 2, 4, 2],
// [64, 4, 8, 4],
// [2, 1024, 1024, 32],
// [32, 16, 64, 4],
// ];
// draw();
function moveCells(direction) {
history.push({
table: JSON.parse(JSON.stringify(data)), //깊은복사
score: $score.textContent,
});
switch (direction) {
case 'left': { //case문 안에서 변수 선언했을때 괄호 {} 써주기
const newData = [[], [], [], []];
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
if (cellData) {
const currentRow = newData[i]
const prevData = currentRow[currentRow.length - 1];
if (prevData === cellData) { //이전 값과 지금 값이 같으면
const score = parseInt($score.textContent);
$score.textContent = score + currentRow[currentRow.length - 1] * 2;
currentRow[currentRow.length - 1] *= -2;
} else {
newData[i].push(cellData);
}
}
});
});
console.log(newData);
[1, 2, 3, 4].forEach((rowData, i) => {
[1, 2, 3, 4].forEach((cellData, j) => {
data[i][j] = Math.abs(newData[i][j]) || 0; //-2 곱해준거 절대값만들기
});
});
break;
}
case 'right': {
const newData = [[], [], [], []];
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
if (rowData[3 - j]) {
const currentRow = newData[i]
const prevData = currentRow[currentRow.length - 1];
if (prevData === rowData[3 - j]) {
const score = parseInt($score.textContent);
$score.textContent = score + currentRow[currentRow.length - 1] * 2;
currentRow[currentRow.length - 1] *= -2;
currentRow[currentRow.length - 1] *= -2;
} else {
newData[i].push(rowData[3 - j]);
}
}
});
});
console.log(newData);
[1, 2, 3, 4].forEach((rowData, i) => {
[1, 2, 3, 4].forEach((cellData, j) => {
data[i][3 - j] = Math.abs(newData[i][j]) || 0;
});
});
break;
}
case 'up': {
const newData = [[], [], [], []];
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
if (cellData) {
const currentRow = newData[j]
const prevData = currentRow[currentRow.length - 1];
if (prevData === cellData) {
const score = parseInt($score.textContent);
$score.textContent = score + currentRow[currentRow.length - 1] * 2;
currentRow[currentRow.length - 1] *= -2;
currentRow[currentRow.length - 1] *= -2;
} else {
newData[j].push(cellData);
}
}
});
});
console.log(newData);
[1, 2, 3, 4].forEach((cellData, i) => {
[1, 2, 3, 4].forEach((rowData, j) => {
data[j][i] = Math.abs(newData[i][j]) || 0;
});
});
break;
}
case 'down': {
const newData = [[], [], [], []];
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
if (data[3 - i][j]) {
const currentRow = newData[j]
const prevData = currentRow[currentRow.length - 1];
if (prevData === data[3 - i][j]) {
const score = parseInt($score.textContent);
$score.textContent = score + currentRow[currentRow.length - 1] * 2;
currentRow[currentRow.length - 1] *= -2;
currentRow[currentRow.length - 1] *= -2;
} else {
newData[j].push(data[3 - i][j]);
}
}
});
});
console.log(newData);
[1, 2, 3, 4].forEach((cellData, i) => {
[1, 2, 3, 4].forEach((rowData, j) => {
data[3 - j][i] = Math.abs(newData[i][j]) || 0;
});
});
break;
}
}
if (data.flat().includes(2048)) { //승리
draw();
setTimeout(() => {
alert('축하합니다. 2048을 만들었습니다!');
}, 500);
} else if (!data.flat().includes(0)) { //빈칸이 없으면 패배
alert(`패배했습니다...${$score.textContent}점`);
} else {
put2ToRandomCell();
draw();
}
}
window.addEventListener('keyup', (event) => {
if (event.key === 'ArrowUp') {
moveCells('up');
} else if (event.key === 'ArrowDown') {
moveCells('down');
} else if (event.key === 'ArrowLeft') {
moveCells('left');
} else if (event.key === 'ArrowRight') {
moveCells('right');
}
});
let startCoord;
window.addEventListener('mousedown', (event) => {
startCoord = [event.clientX, event.clientY];
});
window.addEventListener('mouseup', (event) => {
const endCoord = [event.clientX, event.clientY];
const diffX = endCoord[0] - startCoord[0];
const diffY = endCoord[1] - startCoord[1];
if (diffX < 0 && Math.abs(diffX) > Math.abs(diffY)) {
moveCells('left');
} else if (diffX > 0 && Math.aabs(diffX) > Math.abs(diffY)) {
moveCells('right');
} else if (diffY > 0 && Math.abs(diffX) <= Math.abs(diffY)) {
moveCells('down');
} else if (diffY < 0 && Math.abs(diffX) <= Math.abs(diffY)) {
moveCells('up');
}
//Math.abs = 절대값
});
</script>
</body>
Fragment => 메모리에 저장되어 성능을 좋게 한다.
const $fragment = document.createDocumentFragment();
깊은 복사 - 데이터를 저장할때는 깊은 복사를 사용한다.
history.push({
table: JSON.parse(JSON.stringify(data)), //깊은복사
score: $score.textContent,
});
case문 안에서 변수를 선언했을 때는 전체를 괄호로 묶어준다.
const는 스코프 문이기 때문에 스코프에 가둬서 제어한다.
case 'left': { //case문 안에서 변수 선언했을때 괄호 {} 써주기
const newData = [[], [], [], []];
data.forEach((rowData, i) => {
rowData.forEach((cellData, j) => {
if (cellData) {
const currentRow = newData[i]
const prevData = currentRow[currentRow.length - 1];
if (prevData === cellData) { //이전 값과 지금 값이 같으면
const score = parseInt($score.textContent);
$score.textContent = score + currentRow[currentRow.length - 1] * 2;
currentRow[currentRow.length - 1] *= -2;
} else {
newData[i].push(cellData);
}
}
});
});
console.log(newData);
[1, 2, 3, 4].forEach((rowData, i) => {
[1, 2, 3, 4].forEach((cellData, j) => {
data[i][j] = Math.abs(newData[i][j]) || 0; //-2 곱해준거 절대값만들기
});
});
break;
}
2048 이렇게 하는거 아닌데 조금 불편하네요.