Dev
[DEV] react/typescript로 노노그램 만들어보기 - 2
쭘봉
2024. 3. 24. 20:38
노노그램 토이프로젝트는 기획이나 정리 없이 머릿 속 순서대로 하고 있다!
정신 없이 흘러가기도 한다.
1편에 이어서 노노그램 게임을 만들어보자~
1. 정답지를 받고 내가 푸는 노노 그램을 일단 만들자
const Nonogram: React.FC<NonogramProps> = ({ answer }) => {
const [nonogram, setNonogram] = useState<Array<Array<number>>>(
Array(answer.length).fill(Array(answer.length).fill(0))
);
return (
<NonoBox>
{nonogram &&
nonogram.map((row, indexY) => (
<Row key={indexY}>
{row.map((cell, indexX) => (
<NonoItem
key={indexX}
type={cell}/>
))}
</Row>
))}
</NonoBox>
);
};
빈 노노 그램 박스가 완성된다.
2. 클릭 이벤트 만들기
노노그램 클릭은 2가지로 정했다.
- 왼쪽 클릭 시 체크 되고 체크된 cell을 클릭 시 다시 풀린다.
만약 X를 클릭시 체크된다. - 오른쪽 클릭 시 X로 표기하고 다시 클릭 시 빈칸으로 돌아간다.
만약 Cecked 상태라면 X로 바꾼다.
const updateNoNogram = (y: number, x: number, value: number) => {
setNonogram((nonogram) => {
const tempNonogram = nonogram.map((row) => [...row]);
tempNonogram[y][x] = value;
return tempNonogram;
});
};
const onclick = (y: number, x: number) => {
updateNoNogram(y, x, nonogram[y][x] === 1 ? 0 : 1);
};
const onRightClick = (e: MouseEvent, y: number, x: number) => {
e.preventDefault();
updateNoNogram(y, x, nonogram[y][x] === 2 ? 0 : 2);
};
//를 추가하고
// 아래처럼 이벤트를 받는다.
return (
<NonoBox>
{nonogram &&
nonogram.map((row, indexY) => (
<Row key={indexY}>
{row.map((cell, indexX) => (
<NonoItem
key={indexX}
type={cell}
onClick={() => onclick(indexY, indexX)}
onContextMenu={(e) =>
onRightClick(e, indexY, indexX)
}></NonoItem>
))}
</Row>
))}
</NonoBox>
);
보다보니 0, 1, 2처럼 나만 알아볼 수 있는 숫자보단 상수로 정의해보자
// 상수 정의
const CELL_STATE = {
NONE: 0, // 셀이 체크되지 않은 상태
CHECK: 1, // 셀이 체크된 상태
CROSS: 2 // 셀이 크로스된 상태
};
3. 드래그 이벤트 만들기
노노그램에서는 CHECK, CROSS, NONE을 선택하고 드래그하면 그 state로 바꾸는 기능이 있다.
없으면 매우 불편.
이런 기능을 만들려면 2가지 이벤트가 필요할 것 같다.
mouseDown, mouseEnter 정도 쓰면 될 것 같은데
다운 시 이벤트를 트리거해서 enter된 cell에서 좌표를 받아서 스테이트를 바꾸는 작업을 할 수 있을 것 같다.
const CELL_STATE = {
NONE: 0, // 셀이 체크되지 않은 상태
CHECK: 1, // 셀이 체크된 상태
CROSS: 2, // 셀이 크로스된 상태
} as const;
type CellState = (typeof CELL_STATE)[keyof typeof CELL_STATE];
const Nonogram: React.FC<NonogramProps> = ({ answer }) => {
const [nonogram, setNonogram] = useState<Array<Array<number>>>(
Array(answer.length).fill(Array(answer.length).fill(0))
);
const [dragState, setDragState] = useState<CellState>(0);
const [isDrag, setIsDrag] = useState<boolean>(false);
useEffect(() => {
if (nonogram.flat().join('') === answer.flat().join('')) {
console.log('정답이야.');
}
document.addEventListener('mouseup', onMouseUp);
return () => {
document.removeEventListener('mouseup', onMouseUp);
};
}, [nonogram, answer]);
const updateNoNogram = (y: number, x: number, value: number) => {
setNonogram((nonogram) => {
const tempNonogram = nonogram.map((row) => [...row]);
tempNonogram[y][x] = value;
return tempNonogram;
});
};
const onclick = (y: number, x: number) => {
updateNoNogram(
y,
x,
nonogram[y][x] === CELL_STATE.CHECK ? CELL_STATE.NONE : CELL_STATE.CHECK
);
};
const onRightClick = (e: MouseEvent, y: number, x: number) => {
e.preventDefault();
updateNoNogram(
y,
x,
nonogram[y][x] === CELL_STATE.CROSS ? CELL_STATE.NONE : CELL_STATE.CROSS
);
};
const onMouseDown = () => {
setIsDrag(true);
};
const onMouseUp = () => {
setIsDrag(false);
};
const onMouseEnter = (y: number, x: number) => {
if (isDrag) updateNoNogram(y, x, dragState);
};
return (
<div>
<NonoBox>
{nonogram &&
nonogram.map((row, indexY) => (
<Row key={indexY}>
{row.map((cell, indexX) => (
<NonoItem
key={indexX}
state={cell}
onMouseDown={() => onMouseDown()}
onClick={() => onclick(indexY, indexX)}
onContextMenu={(e) => onRightClick(e, indexY, indexX)}
onMouseEnter={() => onMouseEnter(indexY, indexX)}
/>
))}
</Row>
))}
</NonoBox>
current isDrag : {JSON.stringify(isDrag)} <br />
current drag state : {dragState} <br />
<button onClick={() => setDragState(CELL_STATE.NONE)}>None</button>
<button onClick={() => setDragState(CELL_STATE.CHECK)}>Check</button>
<button onClick={() => setDragState(CELL_STATE.CROSS)}>Cross</button>
</div>
);
};
export default Nonogram;
MouseDown 시 Enter된 셀에서 state를 바꾸도록 이벤트를 추가하고 실행
문제 1. 이미 엔터된 셀은 이벤트가 일어나지 않는다.
문제 2. 노노그램을 해본 사람들은 알겠지만 클릭 후 바꿀때는 가로 세로 만 바뀌어야한다. 대각선 불가!
다음은 이 문제를 해결하고 좀 더 게임으로 만들어보자