개발/개발로그

[React] 간단한 성냥퍼즐 웹 서비스 만들기 9일차 (정답 검증 구현)

seungho-dev 2024. 12. 9. 19:10

오늘은 드디어 핵심기능 중 마지막인 정답 검증을 구현하는 날이다

다음 주부터는 회원가입과 로그인을 구현해 볼 생각이다.

고고~!!

개인적인 공부를 위해 작성하는 블로그입니다. 혹시라도 잘못되거나 부족한 부분이 있다면 댓글로 알려주시면 감사하겠습니다.

 

💡 핵심 기능 구현

  • 성냥개비 드래그 & 드롭 (완료)
  • 성냥개비 회전 (완료)
  • Match 컴포넌트 분리, History 기능 (완료)
  • Undo, Redo, Remove (완료)
  • 게임타입 및 횟수 제한 (완료)
  • 퍼즐 정답 검증 시스템 (오늘 할 것)

 

💁🏻 유사도 비교 함수

유사도 함수를 만들려고 생각해보니까 문제가 있었다. 현재는 이렇게 gameData.json을 불러올 때 정답까지 같이 가져오고 있는데 커닝이 가능하기 때문에 추후에 서버에서 정답을 조회해서 체크 후 결과를 받아오는 방식으로 변경해 봐야겠다 ㅎㅎ

 

중요한 점은 gameType이 "move"일 때는 성냥개비가 id와 순서 상관없이 하나씩 x, y, angle 을 비교해 모든 성냥개비가 정해진 threshold값 이하에 매치된다면 정답으로 인정하고 gameType이 "remove" 일 경우는 단순히 id값만 비교해 일치한다면 정답으로 인정하기로 하였다. 

  const checkRemoveSimilarity = (moveCounts, solution) => {
    // 이동 횟수 확인
    if (Object.keys(moveCounts).length !== limit) return false

    // 삭제된 성냥개비의 id 가져오기
    const removeIds = Object.keys(moveCounts);
    
    // 삭제된 성냥이 solution과 정확히 일치하는지 확인
    return !solution.some(stick => {
      return removeIds.includes(stick.id)
    })
  }
  
const checkMoveSimilarity = (currentState, solution, threshold = 30) => {
	// 수정 횟수 확인
    if (currentState.length !== solution.length) return false
	
    // position, angle 오차 비교
    return currentState.every((currentStick) => {
      return solution.some((solutionStick) => {
        const positionMatch =
        Math.abs(currentStick.relativeX - solutionStick.relativeX) <= threshold &&
        Math.abs(currentStick.relativeY - solutionStick.relativeY) <= threshold;
        const angleMatch =
          Math.abs(currentStick.angle) - Math.abs(solutionStick.angle) < threshold
        return positionMatch && angleMatch
      })
    })
  }

 

다음은 check 버튼을 눌렀을때 게임모드에 따라 정답을 확인하고 모달창을 띄우는 함수를 만들었다.

  const handleCheckAnswer = () => {
    const { solution } = gameData
    let isCorrect = false;
    
    if (gameType === "move") {
      isCorrect = checkMoveSimilarity(matchsticks, solution, 10)
    } else if (gameType === "remove") {
      isCorrect = checkRemoveSimilarity(moveCounts, solution)
    }
    if (isCorrect) {
      setModalContent({
        message: "정답입니다!",
        buttons: [
          { label: "확인", onClick: () => setIsModalOpen(false) }
        ],
      });
    } else {
      setModalContent({
        message: "오답입니다.",
        buttons: [
          { label: "확인", onClick: () => setIsModalOpen(false) }
        ],
      });
    }
    setIsModalOpen(true)
  }