사용자가 요청한 대로, 한 번의 방향키 입력에 모든 타일이 최대 한 칸씩만 움직이도록 수정해야 합니다. 즉, 기존에는 빈 칸이 여러 개 있을 때 끝까지 밀리는 방식이었는데, 이제는 한 번 누를 때마다 한 칸씩만 이동하고, 인접한 동일 숫자 타일이 있을 때만 병합되도록 해야 합니다.
이건 게임의 느낌을 완전히 바꾸는 변경입니다. 예를 들어:
기존: [2,0,0,2] 왼쪽 → [4,0,0,0] (2개가 2칸 이동 후 병합)
변경: [2,0,0,2] 왼쪽 → [2,0,2,0] (마지막 2가 한 칸만 이동)
그리고 한 번 더 왼쪽 → [2,2,0,0] (두 번째 2가 한 칸 이동)
그리고 한 번 더 왼쪽 → [4,0,0,0] (병합)
이렇게 수정해야 합니다.
`slideLine` 함수를 수정해야 합니다. 현재 로직:
1. 0이 아닌 값들만 필터링
2. 인접한 동일 값들 병합
3. 나머지를 0으로 채움
이걸 한 칸씩만 이동하는 로직으로 변경:
각 위치에서 방향으로 한 칸 앞을 보고, 그 칸이:
- 비어있으면: 한 칸 이동 (그리고 병합 체크는 하지 않음)
- 같은 숫자면: 병합
- 다른 숫자면: 이동 안 함
왼쪽 이동 예시로 생각해보면 (index 0부터 시작):
- i=0: 왼쪽 끝이므로 이동 불가
- i=1: i=0이 비어있으면 이동, 같은 숫자면 병합, 다른 숫자면 그대로
- i=2: i=1이 비어있으면 이동, 같은 숫자면 병합, 다른 숫자면 그대로
- i=3: i=2가 비어있으면 이동, 같은 숫자면 병합, 다른 숫자면 그대로
그런데 이렇게 하면 한 번의 이동에서 여러 타일이 연쇄적으로 움직이지 않아야 합니다. 즉, 각 타일은 독립적으로 바로 앞 칸만 본다는 뜻입니다.
근데 문제는: 같은 열에서 앞 타일이 이동한 후 그 자리가 비게 되면, 그 뒤 타일이 그 빈 자리로 이동할 수 있게 되는 연쇄 이동을 허용할지 말지 입니다.
사용자가 "최대 한 칸씩만"이라고 했으므로, 연쇄 이동은 허용하지 않는 것이 맞습니다. 각 타일은 현재 상태를 기준으로 바로 앞 칸만 보고 이동합니다.
또한 병합은 한 번 일어나면 그 타일은 이번 턴에 더 이상 병합되지 않아야 합니다.
구현 방식을 생각해보겠습니다:
왼쪽 이동의 경우:
- 2차원으로 각 행을 처리
- merged 배열로 이번 턴에 병합된 위치를 추적
- 각 행에서 i=1부터 3까지 순회 (오른쪽에서 왼쪽으로 볼 수도 있고...)
사실 왼쪽 이동은 왼쪽부터 처리하는 게 맞습니다:
- i=1: 왼쪽(i=0)이 비어있으면 이동, 같으면 병합
- i=2: 왼쪽(i=1)이 비어있으면 이동, 같으면 병합
- i=3: 왼쪽(i=2)이 비어있으면 이동, 같으면 병합
그런데 i=1에서 왼쪽으로 이동한 후 i=2가 오면, i=1의 원래 자리가 비어있는 걸 보고 i=2가 이동할 수 있습니다. 이건 "연쇄 이동"이죠.
사용자가 "최대 한 칸씩만"이라고 했으니, 연쇄 이동을 막으려면 이동 전 상태를 스냅샷으로 저장하고 그걸 기준으로 판단해야 합니다.
구현:
```javascript
function moveLineLeft(line) {
const newLine = [...line];
const merged = [false, false, false, false];
for (let i = 1; i < 4; i++) {
if (newLine[i] === 0) continue;
const leftIdx = i - 1;
if (newLine[leftIdx] === 0) {
// 빈 칸이면 이동
newLine[leftIdx] = newLine[i];
newLine[i] = 0;
} else if (newLine[leftIdx] === newLine[i] && !merged[leftIdx] && !merged[i]) {
// 같은 숫자면 병합
newLine[leftIdx] *= 2;
newLine[i] = 0;
merged[leftIdx] = true;
}
// 다른 숫자면 그대로
}
return newLine;
}
```
이렇게 하면 각 타일이 최대 한 칸만 이동하고, 연쇄 이동은 발생하지 않습니다.
오른쪽 이동은 오른쪽부터:
```javascript
function moveLineRight(line) {
const newLine = [...line];
const merged = [false, false, false, false];
for (let i = 2; i >= 0; i--) {
if (newLine[i] === 0) continue;
const rightIdx = i + 1;
if (newLine[rightIdx] === 0) {
newLine[rightIdx] = newLine[i];
newLine[i] = 0;
} else if (newLine[rightIdx] === newLine[i] && !merged[rightIdx] && !merged[i]) {
newLine[rightIdx] *= 2;
newLine[i] = 0;
merged[rightIdx] = true;
}
}
return newLine;
}
```
위쪽 이동은 각 열을 위에서 아래로:
```javascript
function moveLineUp(col) {
const newCol = [...col];
const merged = [false, false, false, false];
for (let i = 1; i < 4; i++) {
if (newCol[i] === 0) continue;
const upIdx = i - 1;
if (newCol[upIdx] === 0) {
newCol[upIdx] = newCol[i];
newCol[i] = 0;
} else if (newCol[upIdx] === newCol[i] && !merged[upIdx] && !merged[i]) {
newCol[upIdx] *= 2;
newCol[i] = 0;
merged[upIdx] = true;
}
}
return newCol;
}
```
아래쪽 이동은 각 열을 아래에서 위로:
```javascript
function moveLineDown(col) {
const newCol = [...col];
const merged = [false, false, false, false];
for (let i = 2; i >= 0; i--) {
if (newCol[i] === 0) continue;
const downIdx = i + 1;
if (newCol[downIdx] === 0) {
newCol[downIdx] = newCol[i];
newCol[i] = 0;
} else if (newCol[downIdx] === newCol[i] && !merged[downIdx] && !merged[i]) {
newCol[downIdx] *= 2;
newCol[i] = 0;
merged[downIdx] = true;
}
}
return newCol;
}
```
이제 `slideLine`을 `moveLineLeft`와 `moveLineRight`로 분리하고, 열 단위 작업도 `moveColumnUp`, `moveColumnDown`으로 만들어야 합니다. 기존 코드에서는 `slideLine` 하나로 모든 방향을 처리하기 위해 그리드를 회전시키는 방식을 썼는데, 이제는 각 방향별로 다른 함수가 필요합니다.
그런데 사실 왼쪽과 위쪽은 동일한 로직(앞에서 뒤로), 오른쪽과 아래쪽은 동일한 로직(뒤에서 앞으로)이므로, 두 함수로도 충분합니다.
기존 `move` 함수를 수정해야 합니다.
원래 코드에서는 `slideLine`을 이용해 행을 슬라이드하고, 방향에 따라 그리드를 회전시켰습니다. 이걸 새로운 로직으로 변경하겠습니다.
점수 계산도 병합된 값만 합산해야 합니다. moveLineLeft 등에서 병합 시 점수를 반환하도록 할 수 있습니다.
전체적인 변경 사항을 생각해보면:
1. `slideLine` → `moveLineForward` (왼쪽/위쪽용), `moveLineBackward` (오른쪽/아래쪽용)
2. `move` 함수에서 각 방향별로 적절히 처리
수정할 부분이 꽤 많으니 전체 파일을 읽고 정확히 수정하겠습니다.
현재 `move` 함수를 보면:
```javascript
const move = useCallback((dir) => {
const rotated = rotateGrid(grid, dir);
let moved = false;
let gained = 0;
const newGrid = rotated.map((row) => {
const { line: newRow, changed, score } = slideLine(row);
if (changed) moved = true;
gained += score;
return newRow;
});
if (!moved) return;
const unrotated = rotateGrid(newGrid, (4 - dir) % 4);
addRandomTile(unrotated);
setGrid(unrotated);
setScore((s) => {
const ns = s + gained;
if (ns > bestScore) setBestScore(ns);
return ns;
});
}, [grid, bestScore]);
```
회전 방식으로 모든 방향을 처리하고 있습니다. 이걸 각 방향별로 개별 함수를 쓰도록 변경하겠습니다.
```javascript
const move = useCallback((dir) => {
let moved = false;
let gained = 0;
const newGrid = grid.map(row => [...row]);
if (dir === 0) { // up
for (let c = 0; c < 4; c++) {
const col = [newGrid[0][c], newGrid[1][c], newGrid[2][c], newGrid[3][c]];
const { line: newCol, changed, score } = moveLineForward(col);
if (changed) moved = true;
gained += score;
for (let r = 0; r < 4; r++) newGrid[r][c] = newCol[r];
}
} else if (dir === 1) { // right
for (let r = 0; r < 4; r++) {
const row = newGrid[r];
const { line: newRow, changed, score } = moveLineBackward(row);
if (changed) moved = true;
gained += score;
newGrid[r] = newRow;
}
} else if (dir === 2) { // down
for (let c = 0; c < 4; c++) {
const col = [newGrid[0][c], newGrid[1][c], newGrid[2][c], newGrid[3][c]];
const { line: newCol, changed, score } = moveLineBackward(col);
if (changed) moved = true;
gained += score;
for (let r = 0; r < 4; r++) newGrid[r][c] = newCol[r];
}
} else if (dir === 3) { // left
for (let r = 0; r < 4; r++) {
const row = newGrid[r];
const { line: newRow, changed, score } = moveLineForward(row);
if (changed) moved = true;
gained += score;
newGrid[r] = newRow;
}
}
if (!moved) return;
addRandomTile(newGrid);
setGrid(newGrid);
setScore((s) => {
const ns = s + gained;
if (ns > bestScore) setBestScore(ns);
return ns;
});
}, [grid, bestScore]);
```
이제 `moveLineForward`와 `moveLineBackward`, 그리고 `rotateGrid` 함수와 `slideLine` 함수를 정리해야 합니다.
`rotateGrid`는 더 이상 필요 없으므로 제거해도 됩니다.
자, 이제 실제 코드 수정을 해보겠습니다. 전체 파일을 읽고 두 부분을 수정해야 합니다:
1. `slideLine` 함수 → `moveLineForward`, `moveLineBackward`로 교체
2. `move` 함수 수정
3. `rotateGrid` 함수 제거
먼저 파일을 다시 읽고 정확히 수정하겠습니다.
기존 `slideLine`이 빈 칸을 건너뛰며 끝까지 밀어버리기 때문입니다. 한 번에 한 칸씩만 이동하고, 바로 인접한 칸과만 병합되도록 수정하겠습니다.
✅파일 읽기web/src/App.jsx
이제 코드를 다 읽었습니다. 수정해야 할 주요 부분:
1. `slideLine` 함수 → `moveLineForward` (왼쪽/위쪽용, 한 칸씩만), `moveLineBackward` (오른쪽/아래쪽용) 로 교체
2. `moveBoard` 함수 → 각 방향별로 행/열을 적절히 처리
3. `rotate` 함수 제거
한 칸씩만 이동하는 로직으로 변경합니다. 각 타일은 이동 방향의 바로 인접 칸만 확인하고, 비어있으면 한 칸 이동, 같으면 병합, 다르면 그대로.
수정할 코드:
1. `slideLine` → `moveLineForward`와 `moveLineBackward`
2. `moveBoard` → 회전 대신 각 방향별로 처리
이제 수정해보겠습니다.
```javascript
// 한 줄 왼쪽/위쪽으로 한 칸씩만 밀기
function moveLineForward(line) {
const arr = [...line];
const merged = [false, false, false, false];
let score = 0;
for (let i = 1; i < GRID_SIZE; i++) {
if (arr[i] === 0) continue;
const prev = i - 1;
if (arr[prev] === 0) {
arr[prev] = arr[i];
arr[i] = 0;
} else if (arr[prev] === arr[i] && !merged[prev] && !merged[i]) {
arr[prev] *= 2;
score += arr[prev];
arr[i] = 0;
merged[prev] = true;
}
}
return { line: arr, score, moved: arr.join(",") !== line.join(",") };
}
// 한 줄 오른쪽/아래쪽으로 한 칸씩만 밀기
function moveLineBackward(line) {
const arr = [...line];
const merged = [false, false, false, false];
let score = 0;
for (let i = GRID_SIZE - 2; i >= 0; i--) {
if (arr[i] === 0) continue;
const next = i + 1;
if (arr[next] === 0) {
arr[next] = arr[i];
arr[i] = 0;
} else if (arr[next] === arr[i] && !merged[next] && !merged[i]) {
arr[next] *= 2;
score += arr[next];
arr[i] = 0;
merged[next] = true;
}
}
return { line: arr, score, moved: arr.join(",") !== line.join(",") };
}
```
그리고 `moveBoard` 함수:
```javascript
function moveBoard(board, direction) {
const newBoard = board.map((row) => [...row]);
let totalScore = 0;
let moved = false;
if (direction === "left") {
for (let r = 0; r < GRID_SIZE; r++) {
const { line, score, moved: lineMoved } = moveLineForward(newBoard[r]);
if (lineMoved) moved = true;
totalScore += score;
newBoard[r] = line;
}
} else if (direction === "right") {
for (let r = 0; r < GRID_SIZE; r++) {
const { line, score, moved: lineMoved } = moveLineBackward(newBoard[r]);
if (lineMoved) moved = true;
totalScore += score;
newBoard[r] = line;
}
} else if (direction === "up") {
for (let c = 0; c < GRID_SIZE; c++) {
const col = [newBoard[0][c], newBoard[1][c], newBoard[2][c], newBoard[3][c]];
const { line, score, moved: lineMoved } = moveLineForward(col);
if (lineMoved) moved = true;
totalScore += score;
for (let r = 0; r < GRID_SIZE; r++) newBoard[r][c] = line[r];
}
} else if (direction === "down") {
for (let c = 0; c < GRID_SIZE; c++) {
const col = [newBoard[0][c], newBoard[1][c], newBoard[2][c], newBoard[3][c]];
const { line, score, moved: lineMoved } = moveLineBackward(col);
if (lineMoved) moved = true;
totalScore += score;
for (let r = 0; r < GRID_SIZE; r++) newBoard[r][c] = line[r];
}
}
return { board: newBoard, score: totalScore, moved };
}
```
이제 edit_file로 수정하겠습니다. old_string에 정확히 매칭되도록 해야 합니다.
먼저 `slideLine` 함수 전체를 교체합니다.
`slideLine`을 한 칸만 이동하는 `moveLineForward`/`moveLineBackward`로 바꾸고, `moveBoard`도 회전 없이 각 방향별로 처리하도록 수정합니다.
❌파일 수정web/src/App.jsx