.Zzumbong

[DEV] react/typescript로 노노그램 만들어보기 - 2 본문

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. 노노그램을 해본 사람들은 알겠지만 클릭 후 바꿀때는 가로 세로 만 바뀌어야한다. 대각선 불가!

 

다음은 이 문제를 해결하고 좀 더 게임으로 만들어보자

 

 

 

Comments