Elmでじゃんけん

追記: コメントでelmらしく書こうというアドバイスを頂いたので記事をかなり書き換えました
もしも書き換え前の記事を参考にした方がいたらごめんなさい

##はじめに
Elm歴は[Elm Guide](https://guide.elm-lang.jp/)や「基礎からわかるElm」を一通り読んだ程度
HTML, CSS JavaScirptは基本的な構文すら勉強してない

そんな奴が書いた記事読みたくないよ～って人はブラウザバック推奨ということで・・・


##ひな形を書く
```elm
module Main exposing (..)

import Browser
import Html exposing (..)

--MAIN--
main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }

--MODEL--
type alias Model =
    ()

init : () -> ( Model, Cmd Msg )
init _ =
    ( (), Cmd.none )

--UPDATE--
type alias Msg =
    Never

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    ( model, Cmd.none )

--SUBSCRIPTIONS--
subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none

--VIEW--
view : Model -> Html Msg
view model =
    div [] []
```
elm reactorで見てみると真っ白な画面が表示される
ひな型に追加していく形で進めていく

## viewを書く
```elm
--VIEW--
view : Model -> Html Msg
view model =
    div []
        [ h1 [] [ text "Rock-paper-scissors" ]
        , h2 [] [ text "Choice your hand" ]
        , button [] [ text "Rock" ]
        , button [] [ text "Paper" ]
        , button [] [ text "Scissors" ]
        , h2 [] [ text "Result: " ]
        ]
```
画像のように表示される
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431281/aa492117-ac6f-a22e-3d55-cb428a31a979.png)


##Hand型を作る
```elm
type Hand
    = Rock
    | Paper
    | Scissors
```

##モデルを書く
先ほど作ったHand型で相手の手と自分の手を定義する

```elm
--MODEL--
type alias Model =
    { opponentHand : Hand
    , myHand : Hand
    }

init : () -> ( Model, Cmd Msg )
init _ =
    ( Model Rock Rock
    , Cmd.none
    )
```

##RandomとHtml.Eventsのインポート

```elm
import Html.Events exposing (..)
import Random
```
Randomは`elm install  elm/random`でインストール

##1~3のランダムな手を生成するgenHandを作る
```elm
--UPDATE--
genHand : Cmd Msg
genHand =
    Random.generate NewHand (Random.uniform Rock [Paper, Scissors])
```

##updateを書く
```elm
--UPDATE--
type Msg
    = ChoiceRock
    | ChoicePaper
    | ChoiceScissors
    | NewNumber Hand

update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
    case msg of
        ChoiceRock ->
            ( { model | myHand = Rock }
            , genHand
            )
        
        ChoicePaper ->
            ( { model | myHand = Paper }
            , genHand
            )
        
        ChoiceScissors ->
            ( { model | myHand = Scissors }
            , genHand
            )

        NewHand newHand ->
            ( { model | opponentHand = newHand }
            , Cmd.none
            )
```


##ボタンをクリックするとメッセージが送られるようにする
```elm
--VIEW--
//... 略
        , button [ onClick ChoiceRock ] [ text "Rock" ]
        , button [ onClick ChoicePaper ] [ text "Paper" ]
        , button [ onClick ChoiceScissors ] [ text "Scissors" ]
//... 略
```

##勝敗結果を表示する
```elm
createResult : Model -> String
createResult model =
    case (model.myHand, model.opponentHand) of
        (Rock, Rock) ->
            "Draw"

        (Rock, Paper) ->
            "Lose"

        (Rock, Scissors) ->
            "Win"
        
        (Paper, Rock) ->
            "Win"
        
        (Paper, Paper) ->
            "Draw"

        (Paper, Scissors) ->
            "Lose"
        
        (Scissors, Rock) ->
            "Lose"

        (Scissors, Paper) ->
            "Win"
        
        (Scissors, Scissors) ->
            "Draw"
```

またVIEWを以下のように書き換える

```elm
--VIEW--
//... 略
h2 [] [ text "Result: " ]
↓
h2 [] [ text ("Result: " ++ (createResult model)) ]
//...　略
```

##完成したコード
```elm:rock-paper-scissors.elm
module Main exposing (..)

import Browser
import Html exposing (..)
import Html.Events exposing (..)
import Random


--MAIN--

main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , subscriptions = subscriptions
        , view = view
        }


--MODEL--

type Hand
    = Rock
    | Paper
    | Scissors

type alias Model =
    { opponentHand : Hand
    , myHand : Hand
    }


init : () -> ( Model, Cmd Msg )
init _ =
    ( Model Rock Rock
    , Cmd.none
    )


--UPDATE--

genHand : Cmd Msg
genHand =
    Random.generate NewHand (Random.uniform Rock [Paper, Scissors])

type Msg
    = ChoiceRock
    | ChoicePaper
    | ChoiceScissors
    | NewHand Hand

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        ChoiceRock ->
            ( { model | myHand = Rock }
            , genHand
            )
        
        ChoicePaper ->
            ( { model | myHand = Paper }
            , genHand
            )
        
        ChoiceScissors ->
            ( { model | myHand = Scissors }
            , genHand
            )

        NewHand newHand ->
            ( { model | opponentHand = newHand }
            , Cmd.none
            )


--subscriptions--

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.none


--VIEW--

view : Model -> Html Msg
view model =
    div []
        [ h1 [] [ text "Rock-paper-scissors" ]
        , h2 [] [ text "Choice Hand" ]
        , button [ onClick ChoiceRock ] [ text "Rock" ]
        , button [ onClick ChoicePaper ] [ text "Paper" ]
        , button [ onClick ChoiceScissors ] [ text "Scissors" ]
        , h2 [] [ text ("Result: " ++ (createResult model)) ]
        ]

createResult : Model -> String
createResult model =
    case (model.myHand, model.opponentHand) of
        (Rock, Rock) ->
            "Draw"

        (Rock, Paper) ->
            "Lose"

        (Rock, Scissors) ->
            "Win"
        
        (Paper, Rock) ->
            "Win"
        
        (Paper, Paper) ->
            "Draw"

        (Paper, Scissors) ->
            "Lose"
        
        (Scissors, Rock) ->
            "Lose"

        (Scissors, Paper) ->
            "Win"
        
        (Scissors, Scissors) ->
            "Draw"
```


##動作確認
`elm make src/rock-paper-scissors.elm --debug`でindex.htmlが出力される
index.htmlを開いて右下にあるExplore Historyを開く
するとmodelの中身が表示されるのでボタンをクリックし中身が変化することを確認する


##まとめ
~~勝敗判定の関数はもっといいアイデアがあるはずなので考えてみたい(丸々書き直しになりそうだが)
初めて記事を書いたので構成がめちゃめちゃな気がするので直したい~~

マサカリ大歓迎なのでよろしくお願いします