STACKERゲームを作ろう その3

nekoroll.hatenablog.com
の続き

せっかく色々教わったので、まずはリファクタから対応した
AB先生のコードを真似しつつ、自分なりに良いと思う方法も混ぜてリファクタしてみた

リファクタ

ellie-app.com
大体はAB先生の真似なので、自分で書いた部分だけ解説

Model

ライトの点灯状態

type LightMode
    = LightOn
    | LightOff

Colorで持ってもいいかな?と考えたけどSTACKERゲームとしては点灯/消灯しかないので
LightModeで管理することにした

m*nのマス目の生成

type alias InitSize =
    { rowSize : Int
    , columnSize : Int
    }

makePointMatrix : InitSize -> List Point
makePointMatrix { rowSize, columnSize } =
    let
        rowNumList =
            List.range 0 (rowSize - 1)

        columnNumList =
            List.range 0 (columnSize - 1)
    in
    rowNumList
        |> List.map
            (\x ->
                columnNumList
                    |> List.map (\y -> { x = x, y = y })
            )
        |> List.concat

let~in記法で行列の番号リストを変数に入れておくようにした
Point生成は結局List.mapをネストさせる方法しか思いつかなかったので、パイプを使って可能な限り括弧を減らして可読性を上げてみた
|>で左から右に読めるようになっているので、少しは読みやすい…はず

Modelの初期化

initialModel : Model
initialModel =
    let
        pointList =
            makePointMatrix { rowSize = 7, columnSize = 10 }
    in
    { boxList = List.map makeBox pointList }

let~in記法でPointの一覧を変数に入れておくようにした

後は大体AB先生のリファクタ後ソースと同じです

今回やること

  • n秒ごとにBoxを点滅させる
    f:id:nekorollykk:20200918071324p:plain

guide.elm-lang.org
を参考にしました。完成品はこちら
ellie-app.com

実装解説

Browser.elementに変更

Subscriptionsを扱いたいのでelementに変更する

update

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        InvertLight { point, lightMode } ->
            let
                invert box =
                    if point == box.point then
                        { box | lightMode = invertLightMode box.lightMode }

                    else
                        box
            in
            ({ model | boxList = List.map invert model.boxList }
            , Cmd.none
            )

返り値型を(Model, Cmd Msg)に変更しCmd.noneを返すようにした

initialModel

initialModel : () -> (Model, Cmd Msg)
initialModel _ =
    let
        pointList =
            makePointMatrix { rowSize = 7, columnSize = 10 }
    in
    ({ boxList = List.map makeBox pointList }
    , Cmd.none
    )

引数にflagsを追加(使わないので_)し、返り値型を(Model, Cmd Msg)に変更しCmd.noneを返すようにした

subscriptionsの実装

import Time

subscriptions : Model -> Sub Msg
subscriptions model =
    Time.every 2000 Tick

2秒毎に更新するsubscriptionsを実装
Timeライブラリが必要だったのでパッケージ追加してインポートした

subscriptionから呼ばれるupdate

Tickのパターンを定義

type Msg
    = InvertLight Box
    | Blinking Time.Posix


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        Blinking _ ->
            (model, Cmd.none)

とりあえず呼び出せるだけ。受け取ったtimeは使わないので_にした

マスを点滅させる処理を実装

        Blinking _ ->
            let
                blinking box =
                    if { x = 0, y = 0 } == box.point then
                        { box | lightMode = invertLightMode box.lightMode }

                    else
                        box
            in
            ( { model | boxList = List.map blinking model.boxList }
            , Cmd.none
            )

まずは仮実装なのでx=0, y=0を点滅させるよう実装

できた🆒
f:id:nekorollykk:20200918070446g:plain

まとめ

  • subscriptionでn秒ごとの制御できるの簡単
  • リファクタしたら行数は増えたけどコードの見通しが良くなった
  • ちょっとパイプ慣れてきた、面白い🐊(>>,<<は除く)

次回は指定した範囲のマスを点滅させるを実装します

おまけ

  • n*mのマス目生成は需要があると思いライブラリを探した package.elm-lang.org
    良さげならライブラリがあった
    どうやって実装しているか気になってソース読んでみたけどわからんかった😇
    qiita.com
    こちらの記事でも紹介されているのでメジャーなライブラリなのかな?