슬라이딩 퍼즐 제작기

이영욱·2023년 8월 2일

토이프로젝트

목록 보기
1/7
post-thumbnail

플레이 링크

https://sliding-puzzle-gamma.vercel.app/

크롬 웹스토어 링크

https://chrome.google.com/webstore/detail/sliding-puzzle/olfemikngkfdngieiokahplpghddjjam/related?hl=ko

야놀자 프론트엔드 부트캠프 과정중 HTML, CSS, JS를 복습하고자
간단한 슬라이딩 퍼즐 게임을 구현하기로 결정했다.

이전에 자바스크립트를 사용했지만 ES6에서 나온 class를 써본적이 없어
내부 요소들을 모듈로 나누어 작성하는것에 집중했다.
index.js 에서 각 모듈, Block.js와 Timer.js를 임포트하는 구조로 작성했다.

제일 신경썼던 로직은, 각 퍼즐을 클릭시 자동으로 빈 공간을 찾아가고,
클릭할때 마다 퍼즐을 완성했는지 확인하는 알고리즘 이였다.

이전에 몇번 풀어본 BFS 문제들을 떠올리니 쉽게 구현할 수 있었다.

// Block.js 내용 일부
...
checkAnswer() {
  let isAnswer = true
  let number = 1
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (number === 9) {
        break
      }
      if (this.BOARD[i][j] === null || this.BOARD[i][j].number !== number++) {
        isAnswer = false
        break
      }
    }
    if (!isAnswer) break
  }
}
...
click() {
  // 주변 블럭을 확인하여 빈 칸이 있을 경우 이동
  const dx = [1, -1, 0, 0]
  const dy = [0, 0, 1, -1]
  let isMoved = false
  for (let i = 0; i < 4; i++) {
    let nx = this.x + dx[i]
    let ny = this.y + dy[i]
    if (nx < 0 || nx > 2 || ny < 0 || ny > 2) {
      continue
    } else {
      if (this.BOARD[nx][ny] === null) {
        this.BOARD[nx][ny] = this.BOARD[this.x][this.y]
        this.BOARD[this.x][this.y] = null
        this.x = nx
        this.y = ny
        this.move()
        isMoved = true
        break
      }
    }
  }
  if (this.GAMESTARTED) this.checkAnswer()
}
...

타이머의 경우에는 피드백에서 탭 비활성화시 동작하지 않는 버그를 다른분이 제보해주셔서
처음으로 웹 워커를 이용해 메인 쓰레드에서 타이머 로직을 분리하여 작성할 수 있었다.
웹 워커로 타이머 구현하기

// worker.js
onmessage = function (e) {
  if (e.data === "start") {
    this.min = 0
    this.sec = 0
    this.msec = 0
    this.intervalId = this.setInterval(() => {
      this.msec += 10
      if (this.msec === 1000) {
        this.sec += 1
        this.msec = 0
      }
      if (this.sec === 60) {
        this.min += 1
        this.sec = 0
      }
      const result = [this.min, this.sec, this.msec]
      postMessage(result)
    }, 10)
  }
  
  if (e.data === "stop") {
    clearInterval(this.intervalId)
  }
}

// Timer.js
const worker = new Worker('worker.js')

class Timer {
  constructor() {
    this.min = 0
    this.sec = 0
    this.msec = 0
  }

  start() {
    worker.postMessage("start")
    worker.onmessage = (e) => {
      let [min, sec, msec] = e.data
      this.min = min
      this.sec = sec
      this.msec = msec
      this.render()
    }
  }

  stop() {
    worker.postMessage("stop")
  }
...

그리고, 심심할때마다 하고싶은데 사이트로 접속하기가 귀찮아서, chromeExtension 폴더를 따로 만들어
CSS를 조금 변경한 뒤 처음으로 크롬 웹스토어에 신청도 넣어보았다.

다음날 확인해보니 등록 성공!

처음엔 단순히 간단한 게임을 만들어 보고자 진행한 토이프로젝트지만,
진행하면서 생각보다 더 많은것을 배울 수 있었다!

profile
다양한 경험을 통해 성장하는 개발자, 이영욱 입니다.

0개의 댓글