Elm 中的记忆游戏,随机或随机排列的网格

Memory game in Elm, randomized or shuffled grid

我正在开发一款记忆游戏,用户会看到一个 5x5 的正方形网格,其中 9 个突出显示。然后用户必须记住并 select 那 9 个方块。

如何生成 expected/winning 电路板配置?

目前,我有 Board 作为 List (List Bool)

在像 JS 这样的命令式语言中,我会随机遍历网格 9 次 select 对列和行索引进行变异。这在 Elm 中是不可能的,所以我有点卡住了。

想到了几个策略:

策略 1

生成随机网格

策略 2

使用Array并打乱所有坐标,选择前9个。

请注意,我还需要一种方法来 select 磁贴,因此 Board 可能会变成 List (List Tile),其中 Tile{ x: Int, y: Int, selected: Bool}

我已经用 vanilla JS 编写了游戏。您可以在这里尝试一下:http://mruzekw.github.io/recall/

这是我到目前为止编写的代码,在某种程度上实现了策略:

module Main exposing (..)

import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Html.App as App
import Random
import Random.Array
import Array exposing (..)


-- model


type alias Board =
    Array (Array Bool)


type alias Tile =
    { x : Int
    , y : Int
    , selected : Bool
    }


type alias Model =
    { board : Board
    , expectedBoard : Board
    , gameOver : Bool
    , playerWon : Bool
    , turnCount : Int
    }


initModel : ( Model, Cmd Msg )
initModel =
    ( { board = generateSquareMatrix 5 False
      , expectedBoard = Array.fromList []
      , gameOver = False
      , playerWon = False
      , turnCount = 0
      }
    , Cmd.none
    )


generateSquareMatrix : Int -> Bool -> Array (Array Bool)
generateSquareMatrix num value =
    Array.repeat num (Array.repeat num value)



-- update


type Msg
    = SelectTile Bool
    | RestartGame
    | SuffleBoard
    | NewBoard (Array (Array Bool))


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case (Debug.log "msg" msg) of
        SelectTile tile ->
            ( { model
                | turnCount = model.turnCount + 1
              }
            , Cmd.none
            )

        RestartGame ->
            ( { model
                | turnCount = 0
              }
            , Cmd.none
            )

        SuffleBoard ->
            ( model, Random.generate NewBoard (Random.Array.shuffle model.board) )

        NewBoard newBoard ->
            ( { model | board = newBoard }
            , Cmd.none
            )



-- view


view : Model -> Html Msg
view model =
    div [ class "scoreboard" ]
        [ h1 [] [ text "Recall" ]
        , grid model
        , button [ onClick SuffleBoard ] [ text "New Board" ]
        , p [] [ text (toString model) ]
        ]


grid : Model -> Html Msg
grid model =
    div [ class "recall-grid" ]
        (List.map
            (\row ->
                div
                    [ class "recall-grid-row" ]
                    (List.map
                        (\tile ->
                            div [ class "recall-grid-tile", onClick (SelectTile tile) ] []
                        )
                        (Array.toList row)
                    )
            )
            (Array.toList model.board)
        )



-- SUBSCRIPTIONS


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


main : Program Never
main =
    App.program
        { init = initModel
        , view = view
        , update = update
        , subscriptions = subscriptions
        }

策略 2 工作得很好,因为它将保证随机元素的唯一性。

在处理随机性时,最好使用生成器映射函数从简单生成器构建复杂生成器。这使您不必在实际生成器代码中跟踪随机种子。

我们需要的第一个生成器是构建数组索引列表的生成器,我将其描述为 (Int, Int).

的元组
indexGenerator : Int -> Int -> Random.Generator (Array (Int, Int))
indexGenerator edgeSize sampleSize =
    List.map (\i -> List.map2 (,) (List.repeat edgeSize i) [0..(edgeSize-1)]) [0..(edgeSize-1)]
        |> List.concat
        |> Array.fromList
        |> Random.Array.shuffle
        |> Random.map (Array.slice 0 sampleSize)

现在我们可以使用 Random.map 以及您的初始 Falses 矩阵来构建 squareMatrixGenerator:

squareMatrixGenerator : Int -> Int -> Bool -> Random.Generator (Array (Array Bool))
squareMatrixGenerator edgeSize sampleSize value =
    let
        initialMatrix =
            Array.repeat edgeSize (Array.repeat edgeSize value)
        invertPoint (x, y) =
            Array.Extra.update x (Array.Extra.update y not)
        indexes =
            indexGenerator edgeSize sampleSize
    in
        Random.map (Array.foldl invertPoint initialMatrix) indexes

以上代码还依赖于 elm-community/array-extra 包进行数组更新。

您可以在 init 期间通过将种子传递给 Random.step 并在使用 Random.generate 时通过 ShuffleBoard 消息使用它。例如:

ShuffleBoard ->
    ( model, Random.generate NewBoard (squareMatrixGenerator 9 5 False) )