yucatio@システムエンジニア

趣味で作ったものいろいろ

一時停止、再開機能を実装する (KATAMINOを解くプログラムを作成する)

★前回の記事

yucatio.hatenablog.com

一時停止、再開機能を実装します。 一時停止機能は、solver.solveで実行したsetTimeoutをキャンセル(clearTimeout)することで実現します。

timerの保存

setTimeoutをキャンセルするには、setTimeouotの戻り値が必要になります。solverのプロパティに保存するように変更しましょう。

js/solver.js

const solver = {
  solverStack : [],
  // 追加
  timer: null,

  init : (targetPiece) => {
    const kataminoField = new Array(5).fill().map(() => (
      new Array(targetPiece.length).fill(-1)
    ))

    solver.solverStack  = []
    // 追加
    timer = null

    // 中略
  },

  solve : (options) => {
    // 中略

    if (! solver.isAllEmpty(kataminoField, spin, offset)) {
      // フィールドの外か、すでにピースが置かれている
      console.log("フィールドの外か、すでにピースが置かれている")
      // 変更前
      // setTimeout(() => solver.solve(options), 0)
      // 変更後
      solver.timer = setTimeout(() => solver.solve(options), 0)
      return
    }

    console.log("ピースが置ける")

    // 中略
    
    if (! solver.hasAllFiveTimesCells(nextField, nextEmpty)){
      console.log("フィールドが5の倍数以外で分断されている")
      // 変更前
      // setTimeout(() => solver.solve(options), 300)
      // 変更後
      solver.timer = setTimeout(() => solver.solve(options), 300)
      return
    }

    // 中略

    // 変更前
    // setTimeout(() => solver.solve(options), 300)
    // 変更後
    solver.timer = setTimeout(() => solver.solve(options), 300)
  },

  // 後略
}

timerのキャンセル

timerのキャンセルをする関数をsolverに定義します。clearTimeoutを使用します。

js/solver.js

const solver = {
  // 前略
  // 追加ここから
  stop: () => {
    clearTimeout(solver.timer)
  },
  // 追加ここまで
}

ここまででKATAMINOを解くのをストップさせる準備が整いました。

停止・再開機能のactionへの登録

停止・再開ボタンにactionを登録します。(action.pauseaction.resumeはまだ実装されていません)

js/main.js

// 前略
const initializer = {
  setEvent: () => {
    // 中略
    // 追加ここから
    $("#pause-button").on("click", () => {
      action.pause()
    })

    $("#resume-button").on("click", () => {
      action.resume()
    })
    // 追加ここまで
  },
}

停止・再開actionの登録

停止アクションでは、solverをストップさせ、pauseの状態へ遷移します。 再開アクションでは、solverをスタートさせ、solvingの状態へ遷移します。

js/action.js

const action = {
  // 中略

  // 追加ここから
  pause: () => {
    solver.stop()
    stateManager.setSolverState("pause")
  },

  resume: () => {
    solver.solve({
      onUpdatePieces: (placedPieces) => stateManager.setPlacedPieces(placedPieces),
      onSolved: () => stateManager.setSolverState("solvedSuccess"),
      onNotSolved: () => stateManager.setSolverState("solvedFailed"),
    })
    stateManager.setSolverState("solving")
  },
  // 追加ここまで
}

displayの実装

停止ボタンは画面の状態がsolvingのときのみ押せるようにします。 再開ボタンは画面の状態がpauseのときのみ押せるようにします。

js/display.js

const display = {
  // 前略
  // 追加ここから
  updatePauseResumeButton: ({solverState}) => {
    $("#pause-button").prop("disabled", solverState !== "solving")
    $("#resume-button").prop("disabled", solverState !== "pause")
  },
  // 追加ここまで
}

stateManagerへの登録

solverStateが変わったときにupdatePauseResumeButtonを呼び出します。

js/stateManager.js

const stateManager = {
  setSolverState: (solverState) => {
    state.solverState = solverState

    display.updateDraggablePieces(state)
    display.updateStartButtons(state)
    display.updateResultMessage(state)
    // 追加
    display.updatePauseResumeButton(state)
  },

  // 後略
}

実行結果

実行結果です。 KATAMINOを解いている間は、停止ボタンが有効になり、押すと解いている途中で止まります。再開ボタンを押すと再び動き出します。再開ボタンは停止中のみ有効になっています。

f:id:yucatio:20191017103834p:plain

リファクタリング

actionのなかで、solver.solveを、startSolveresumeで2回同じパラメータで呼び出しているので、1つにまとめましょう。

js/action.js

const action = {
  // 中略
  startSolve: () => {
    solver.init(state.targetPieces)
    // 変更前
    // solver.solve({
    //   onUpdatePieces: (placedPieces) => stateManager.setPlacedPieces(placedPieces),
    //   onSolved: () => stateManager.setSolverState("solvedSuccess"),
    //   onNotSolved: () => stateManager.setSolverState("solvedFailed"),
    // })

    // 変更後
    action.solve()

    stateManager.setSolverState("solving")
  },
  // 中略
  resume: () => {
    // 変更前
    // solver.solve({
    //   onUpdatePieces: (placedPieces) => stateManager.setPlacedPieces(placedPieces),
    //   onSolved: () => stateManager.setSolverState("solvedSuccess"),
    //   onNotSolved: () => stateManager.setSolverState("solvedFailed"),
    // })

    // 変更後
    action.solve()
    stateManager.setSolverState("solving")
  },
  // 追加ここから
  solve: () => {
    solver.solve({
      onUpdatePieces: (placedPieces) => stateManager.setPlacedPieces(placedPieces),
      onSolved: () => stateManager.setSolverState("solvedSuccess"),
      onNotSolved: () => stateManager.setSolverState("solvedFailed"),
    })
  },
  // 追加ここまで
}

以上で一時停止、再開機能が実装できました。


★次回の記事

yucatio.hatenablog.com

★目次

yucatio.hatenablog.com