Week 46 of 2020
Development log of Word Snake
4 items
Show the current score after the game is finished
On by
index e54140c..11f7b9f 100644
--- a/src/Game.elm
+++ b/src/Game.elm
@@ -1,4 +1,15 @@
-port module Game exposing (Area, Model, Msg, init, snakeEscaped, subscriptions, update, view)
+port module Game exposing
+ ( Area
+ , Model
+ , Msg
+ , Outcome(..)
+ , init
+ , outcome
+ , snakeEscaped
+ , subscriptions
+ , update
+ , view
+ )
=
=import Dict exposing (Dict)
=import Html exposing (Html)
@@ -737,6 +748,24 @@ updateLetters wanted snake letters =
=-- game state predicates
=
=
+type Outcome
+ = Won
+ | Lost
+ | InProgress
+
+
+outcome : Model -> Outcome
+outcome model =
+ if snakeEscaped model then
+ Won
+
+ else if model.snake.alive then
+ InProgress
+
+ else
+ Lost
+
+
=passwordCollected : Model -> Bool
=passwordCollected model =
= String.toUpper model.word == String.toUpper model.level.passwordindex 2a894cc..753d685 100644
--- a/src/Level.elm
+++ b/src/Level.elm
@@ -1,4 +1,4 @@
-module Level exposing (Charset, Level)
+module Level exposing (Charset, Level, serialize)
=
=
=type alias Level =
@@ -11,3 +11,19 @@ type alias Level =
=
=type alias Charset =
= List Char
+
+
+serialize : Level -> String
+serialize level =
+ [ "< "
+ , level.charset
+ |> List.map String.fromChar
+ |> String.join " "
+ , " > "
+ , level.intro
+ , "{ "
+ , level.password
+ , " }"
+ , level.outro
+ ]
+ |> String.join ""index d9c1dc5..44abf40 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -1,6 +1,7 @@
=port module Main exposing (Model, Msg, init, main, subscriptions, update, view)
=
=import Browser
+import Dict exposing (Dict)
=import Game
=import Html exposing (Html)
=import Html.Attributes
@@ -29,9 +30,14 @@ main =
=type alias Model =
= { game : Maybe Game.Model
= , levels : WebData (List Level)
+ , outcomes : Outcomes
= }
=
=
+type alias Outcomes =
+ Dict String Game.Outcome
+
+
=
=-- INIT
=
@@ -45,6 +51,7 @@ init : Flags -> ( Model, Cmd Msg )
=init flags =
= ( { game = Nothing
= , levels = RemoteData.Loading
+ , outcomes = Dict.empty
= }
= , fetchLevels GotLevelsResponse flags.levels_url
= )
@@ -84,7 +91,7 @@ view model =
=
= Just game ->
= [ gameView game
- , popupView game
+ , popupView model.outcomes game
= ]
= |> Html.div
= [ Html.Attributes.style "height" "100%"
@@ -156,8 +163,8 @@ gameView game =
= |> Html.map GotGameMsg
=
=
-popupView : Game.Model -> Html Msg
-popupView game =
+popupView : Outcomes -> Game.Model -> Html Msg
+popupView outcomes game =
= let
= popup : Bool -> List (Html Msg) -> Html Msg
= popup show elements =
@@ -203,38 +210,68 @@ popupView game =
= , Html.Attributes.style "transition" "opacity 500ms 1000ms"
= ]
= in
- if Game.snakeEscaped game then
- popup True
- [ Html.h2 [] [ Html.text "🙏" ]
- , Html.p [] [ Html.text "Bravo! The snake is safe." ]
- , Html.button
- [ Html.Events.onClick PlayAgainButtonClicked
- , Html.Attributes.autofocus True
- , Html.Attributes.style "height" "8rem"
- , Html.Attributes.style "width" "8rem"
- , Html.Attributes.style "font-size" "1.2rem"
- , Html.Attributes.style "font-weight" "bold"
+ case Game.outcome game of
+ Game.Won ->
+ popup True
+ [ Html.h2 [] [ Html.text "🙏" ]
+ , Html.p [] [ Html.text "Bravo! The snake is safe." ]
+ , socreView outcomes
+ , Html.button
+ [ Html.Events.onClick PlayAgainButtonClicked
+ , Html.Attributes.autofocus True
+ , Html.Attributes.style "height" "8rem"
+ , Html.Attributes.style "width" "8rem"
+ , Html.Attributes.style "font-size" "1.2rem"
+ , Html.Attributes.style "font-weight" "bold"
+ ]
+ [ Html.text "Play again" ]
= ]
- [ Html.text "Play again" ]
- ]
=
- else if not game.snake.alive then
- popup True
- [ Html.h2 [] [ Html.text "☠️" ]
- , Html.p [] [ Html.text "Oh, no! Your snake is dead." ]
- , Html.button
- [ Html.Events.onClick PlayAgainButtonClicked
- , Html.Attributes.autofocus True
- , Html.Attributes.style "height" "8rem"
- , Html.Attributes.style "width" "8rem"
- , Html.Attributes.style "font-size" "1.2rem"
- , Html.Attributes.style "font-weight" "bold"
+ Game.Lost ->
+ popup True
+ [ Html.h2 [] [ Html.text "☠️" ]
+ , Html.p [] [ Html.text "Oh, no! Your snake is dead." ]
+ , socreView outcomes
+ , Html.button
+ [ Html.Events.onClick PlayAgainButtonClicked
+ , Html.Attributes.autofocus True
+ , Html.Attributes.style "height" "8rem"
+ , Html.Attributes.style "width" "8rem"
+ , Html.Attributes.style "font-size" "1.2rem"
+ , Html.Attributes.style "font-weight" "bold"
+ ]
+ [ Html.text "Play again" ]
= ]
- [ Html.text "Play again" ]
- ]
=
- else
- popup False []
+ Game.InProgress ->
+ popup False []
+
+
+socreView : Outcomes -> Html msg
+socreView outcomes =
+ let
+ { killed, saved } =
+ score outcomes
+ in
+ Html.div
+ [ Html.Attributes.style "display" "flex"
+ , Html.Attributes.style "justify-content" "space-around"
+ , Html.Attributes.style "width" "100%"
+ , Html.Attributes.style "padding" "1em 0"
+ ]
+ [ [ "🐍 "
+ , saved |> String.fromInt
+ ]
+ |> List.map Html.text
+ |> Html.div
+ []
+ , [ "☠️ "
+ , killed |> String.fromInt
+ ]
+ |> List.map Html.text
+ |> Html.div
+ []
+ ]
=
=
=
@@ -280,19 +317,24 @@ update msg model =
=
= Just levels ->
= let
+ outcome =
+ Game.outcome game
+
= trackGoal =
- if won then
- -- TODO: Extract Fathom analytics to own module. Have a union type for goal ids.
- goalTracking ( "MNUMD96E", 0 )
+ case outcome of
+ Game.Won ->
+ -- TODO: Extract Fathom analytics to own module. Have a union type for goal ids.
+ goalTracking ( "MNUMD96E", 0 )
=
- else
- goalTracking ( "QHPNWQCC", 0 )
+ Game.Lost ->
+ goalTracking ( "QHPNWQCC", 0 )
=
- won =
- Game.snakeEscaped game
+ Game.InProgress ->
+ -- TODO: Report an error
+ Cmd.none
=
= rescheduledLevels =
- scheduleLevel 5 won levels
+ scheduleLevel 5 outcome levels
= in
= ( model
= , [ generateRandomGameFlags GotRandomGameFlags rescheduledLevels
@@ -313,6 +355,11 @@ update msg model =
= in
= ( { model
= | game = Just updatedGame
+ , outcomes =
+ Dict.insert
+ (Level.serialize game.level)
+ (Game.outcome game)
+ model.outcomes
= }
= , Cmd.map GotGameMsg gameCmd
= )
@@ -355,14 +402,48 @@ subscriptions model =
=-- HELPERS
=
=
-scheduleLevel : Int -> Bool -> List Level -> List Level
-scheduleLevel push won levels =
+type alias Score =
+ { saved : Int
+ , killed : Int
+ , pending : Int
+ }
+
+
+score : Outcomes -> Score
+score outcomes =
+ let
+ iterator : String -> Game.Outcome -> Score -> Score
+ iterator _ outcome memo =
+ case outcome of
+ Game.Won ->
+ { memo
+ | saved = memo.saved + 1
+ , pending = memo.pending - 1
+ }
+
+ Game.Lost ->
+ { memo
+ | killed = memo.killed + 1
+ , pending = memo.pending - 1
+ }
+
+ Game.InProgress ->
+ memo
+ in
+ Dict.foldl
+ iterator
+ (Score 0 0 (Dict.size outcomes))
+ outcomes
+
+
+scheduleLevel : Int -> Game.Outcome -> List Level -> List Level
+scheduleLevel push outcome levels =
= case levels of
= [] ->
= levels
=
= head :: rest ->
- if won then
+ if outcome == Game.Won then
= -- Move level to the end of the queue
= rest
= |> List.reverseMerge branch 'game-score' into 'master'
On by
Show the current score after the game is finished
See merge request hornbook/word-snake!9
Add 4 sentences
On by
index bff8b8d..ea0332d 100644
--- a/static/levels/dutch-words.snake
+++ b/static/levels/dutch-words.snake
@@ -15,3 +15,7 @@ Het metrostel hangt zo'n tien meter boven het { WATER } op het kunstwerk.
=Morgenochtend wordt het met kranen van { HET } kunstwerk gehaald.
=De berging { BEGINT } niet voor 06.30 uur.
=Dat heeft te { MAKEN } met de wind.
+Onderzoekers bouwden in de dierentuin verschillende tunnels { voor } de apen.
+Een reden hiervoor kan zijn dat de verkeersgeluiden een { beetje } lijken op d geluiden die de apen zelf maken.
+Om te communiceren met elkaar, maken ze hoge sis-, piep- en kwaak-{ geluiden }.
+De Duitse herder Major van Joe Biden is waarschijnlijk de { eerste } asielhond ooit die in het Witte Huis gaat wonen.Update static/levels/dutch-words.snake
On by
index ea0332d..23d8936 100644
--- a/static/levels/dutch-words.snake
+++ b/static/levels/dutch-words.snake
@@ -15,7 +15,7 @@ Het metrostel hangt zo'n tien meter boven het { WATER } op het kunstwerk.
=Morgenochtend wordt het met kranen van { HET } kunstwerk gehaald.
=De berging { BEGINT } niet voor 06.30 uur.
=Dat heeft te { MAKEN } met de wind.
-Onderzoekers bouwden in de dierentuin verschillende tunnels { voor } de apen.
-Een reden hiervoor kan zijn dat de verkeersgeluiden een { beetje } lijken op d geluiden die de apen zelf maken.
-Om te communiceren met elkaar, maken ze hoge sis-, piep- en kwaak-{ geluiden }.
-De Duitse herder Major van Joe Biden is waarschijnlijk de { eerste } asielhond ooit die in het Witte Huis gaat wonen.
+Onderzoekers bouwden in de dierentuin verschillende tunnels { VOOR } de apen.
+Een reden hiervoor kan zijn dat de verkeersgeluiden een { BEETJE } lijken op d geluiden die de apen zelf maken.
+Om te communiceren met elkaar, maken ze hoge sis-, piep- en kwaak-{ GELUIDEN }.
+De Duitse herder Major van Joe Biden is waarschijnlijk de { EERSTE } asielhond ooit die in het Witte Huis gaat wonen.