ここからはユーザーインターフェースの実装を行っていきます。
ElmではElmアーキテクチャというものに則って実装していきます。
少し難しいのですが、今のところは
- ページはどんなデータを持つのか(Model)
- ページのデータはどう変化させていくのか(Update)
- ページのデータはどう表示されるのか(View)
の3つの仕組みを考えて作れば、後はElmがいい感じにしてくれる、くらいの理解で良いかと思います。
まずは、下記のページで紹介されている範囲で、碁盤の表示を行ってみます。
別途、Wikimedia Commonsにある碁盤と碁石のパーツ(SVG画像)をダウンロードして、assets/images
に配置しておきます。
今回のソースコード
module Main exposing (main)
import Browser
import Html exposing (Html, div, img)
import Html.Attributes exposing (..)
main =
Browser.sandbox { init = init, update = update, view = view }
-- Constants
constants =
{ cellSize = 50 }
-- Type Aliases
-- ★ポイント①
type alias Model =
{ boardSize : Int }
type alias Coord =
( Int, Int )
-- Models
init : Model
init =
{ boardSize = 9 }
-- Updates
update : msg -> Model -> Model
update _ _ =
init
-- Views
view : Model -> Html msg
view model =
goBoardView model.boardSize
goBoardView : Int -> Html msg
goBoardView boardSize =
div
[ style "display" "grid"
, style "grid-template-columns" ("repeat(" ++ (boardSize |> String.fromInt) ++ ", " ++ (constants.cellSize |> String.fromInt) ++ "px)")
, style "grid-auto-rows" ((constants.cellSize |> String.fromInt) ++ "px")
]
(gen2DCoordList boardSize
|> List.map (cellView 9)
)
cellView : Int -> Coord -> Html msg
cellView boardSize coord =
img [ src ("assets/images/Go_" ++ toGoBoardPiece boardSize coord ++ ".svg"), width constants.cellSize ] []
-- View Logics
-- ★ポイント②
gen2DCoordList : Int -> List Coord
gen2DCoordList size =
List.range 1 size
|> List.map
(\r ->
List.range 1 size
|> List.map
(\c ->
( r, c )
)
)
|> List.reverse
|> List.concat
toGoBoardPiece : Int -> Coord -> String
toGoBoardPiece size ( r, c ) =
let
rStr =
if r == 1 then
"d"
else if r == size then
"u"
else
"c"
cStr =
if c == 1 then
"l"
else if c == size then
"r"
else
"c"
in
rStr ++ cStr
以下、ポイントです。
碁盤の大きさ
囲碁には9×9の9路盤、13×13の13路盤、19×19の19路盤(一般的に使われるサイズ)など、様々な大きさの碁盤があります。後々、ユーザーが好きな大きさを選べるようにするために、碁盤の大きさをModelとして定義して、初期値として9を入れておきます。
type alias Model =
{ boardSize : Int }
init : Model
init =
{ boardSize = 9 }
後々、この碁盤の大きさをユーザーが変えられるようにします。
Viewの書き方
普通はWebサイトはHTMLタグを組み合わせて書きますが、 ElmではHTMLタグに対応する関数が用意されているので、それを組み合わせて書きます。
a [ href "https://google.co.jp", target "_blank" ] [ text "Google" ]
-- <a href="https://google.co.jp" target="_blank">Google</a>
HTMLタグがネストされると、大量に[]
が出現するので、読むのに少し慣れが必要です。
div [ style "background-color" "green" ] [ ol [] [ li [] [ text "Item 1" ], li [] [ text "Item 2" ] ] ]
碁盤のレイアウト
碁盤をHTMLでどう構成するかについては、いくつか方法が考えられそうです。今回はCSS Gridを使って碁盤のマスをn×nで並べてみます。
(以下は4×4の碁盤の例)
<div style="display: grid; grid-template-columns: repeat(4, 50px); grid-auto-rows: 50px">
<div>(左上のSVG)</div>
<div>(上のSVG)</div>
<div>(上のSVG)</div>
<div>(右上のSVG)</div>
<div>(左のSVG)</div>
<div>(中央のSVG)</div>
<div>(中央のSVG)</div>
<div>(右のSVG)</div>
<div>(左のSVG)</div>
<div>(中央のSVG)</div>
<div>(中央のSVG)</div>
<div>(右のSVG)</div>
<div>(左下のSVG)</div>
<div>(下のSVG)</div>
<div>(下のSVG)</div>
<div>(右下のSVG)</div>
</div>
碁盤の大きさが決まっていれば上のようにも書けるのですが、後々碁盤の大きさが変わることを念頭に置くと、CSS Gridの中のdiv要素はプログラムで生成する必要があります。
方法としては、一例として
-
(0, 0)
~(n, n)
までの座標リストを生成する -
(r, c)
のr
がn→0
になるように並び替える(左下が(0, 0)
になるようにするためで、これは次のページで説明します) - 座標の位置によって9パターンのSVGのいずれかを使うIMGタグを生成する
でできそうです。
Elmではどう書けば良いか、Listのリファレンスページを見ながらElm REPLを使って検証してみます。
$ elm repl
---- Elm 0.19.1 ----------------------------------------------------------------
Say :help for help and :exit to exit! More at <https://elm-lang.org/0.19.1/repl>
--------------------------------------------------------------------------------
>
> List.range 0 3 |>
| List.map (\i -> List.range 0 3 |> List.map (\j -> (i, j) ) ) |>
| List.reverse
[[(3,0),(3,1),(3,2),(3,3)],[(2,0),(2,1),(2,2),(2,3)],[(1,0),(1,1),(1,2),(1,3)],[(0,0),(0,1),(0,2),(0,3)]]
: List (List ( Int, Int ))
>
> List.range 0 3 |>
| List.map (\i -> List.range 0 3 |> List.map (\j -> (i, j) ) ) |>
| List.reverse |>
| List.concat
|
[(3,0),(3,1),(3,2),(3,3),(2,0),(2,1),(2,2),(2,3),(1,0),(1,1),(1,2),(1,3),(0,0),(0,1),(0,2),(0,3)]
: List ( Int, Int )
>
> List.range 0 3 |>
| List.map (\i -> List.range 0 3 |> List.map (\j -> (i, j) ) ) |>
| List.reverse |>
| List.concat |>
| List.map (\(i, j) -> case (i, j) of
| (3, 0) -> "左上"
| (3, 3) -> "右上"
| (0, 0) -> "左下"
| (0, 3) -> "右下"
| (3, _) -> "上"
| (0, _) -> "下"
| (_, 0) -> "左"
| (_, 3) -> "右"
| (_, _) -> "中央"
| )
|
["左上","上","上","右上","左","中央","中央","右","左","中央","中央","右","左下","下","下","右下"]
: List String
ということで、range、map、reverse、concatあたりを使うことで目的を達成できそうなことがわかりました。
起動してみる
Main.elmをコンパイルしてブラウザで表示してみましょう。
$ $ elm make src/Main.elm
Success!
Main ───> index.html
$ elm reactor --port 3001
Go to http://localhost:3001 to see your project dashboard.
ブラウザを起動し、 http://localhost:3001/index.html にアクセスします。
碁盤が表示されました!
次は、子の碁盤に碁石を表示できるようにしていきます。