Week 50 of 2020
Development log of Word Snake
7 items
- Merge branch 'dutch-words' into 'master'
- Move the popup slide from the bottom of the screen
- Animate the position of the game area when game is over
- Implement 5 second countdown before the game begins
- Display dark overlay when the game is over
- Merge branch 'countdown' into 'master'
- Fix quotation in Artful Dodger question
Merge branch 'dutch-words' into 'master'
On by
Add 4 sentences
See merge request hornbook/word-snake!10
Move the popup slide from the bottom of the screen
On by
Score is always visible.
The password element drops shadow, so it looks good when the snake escapes to the north.
index 11f7b9f..ce0ae77 100644
--- a/src/Game.elm
+++ b/src/Game.elm
@@ -3,8 +3,10 @@ port module Game exposing
= , Model
= , Msg
= , Outcome(..)
+ , areaView
= , init
= , outcome
+ , passwordView
= , snakeEscaped
= , subscriptions
= , update
@@ -143,47 +145,52 @@ view model =
= , Touch.onMove TouchMoved
= , Touch.onCancel TouchCanceled
= ]
- [ Html.h1
- [ Html.Attributes.style "text-align" "center"
- , Html.Attributes.style "font-size" "1rem"
- ]
- [ Html.text model.level.intro
- , Html.span
- [ Html.Attributes.style "background" <|
- if passwordCollected model then
- "lightgreen"
+ [ passwordView model
+ , areaView model
+ ]
=
- else
- "lightyellow"
- , Html.Attributes.style "padding" "0.2em"
- ]
- [ if String.isEmpty model.word then
- Html.text "..."
=
- else
- Html.text model.word
- ]
- , Html.text model.level.outro
+passwordView : Model -> Html msg
+passwordView model =
+ Html.h1
+ [ Html.Attributes.style "text-align" "center"
+ , Html.Attributes.style "font-size" "1rem"
+ ]
+ [ Html.text model.level.intro
+ , Html.span
+ [ Html.Attributes.style "background" <|
+ if passwordCollected model then
+ "lightgreen"
+
+ else
+ "lightyellow"
+ , Html.Attributes.style "padding" "0.2em"
+ ]
+ [ if String.isEmpty model.word then
+ Html.text "..."
+
+ else
+ Html.text model.word
= ]
- , areaView model.area model.letters model.snake
+ , Html.text model.level.outro
= ]
=
=
-areaView : Area -> Letters -> Snake -> Html Msg
-areaView area letters snake =
+areaView : Model -> Html Msg
+areaView model =
= let
= viewbox : String
= viewbox =
= [ -2 * scale
= , -2 * scale
- , (area.width + 4) * scale
- , (area.height + 4) * scale
+ , (model.area.width + 4) * scale
+ , (model.area.height + 4) * scale
= ]
= |> List.map String.fromInt
= |> String.join " "
= in
- [ lettersView letters
- , snakeView snake
+ [ lettersView model.letters
+ , snakeView model.snake
= ]
= |> Svg.svg
= [ Svg.Attributes.viewBox viewboxindex 44abf40..8ee7a1f 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -90,13 +90,16 @@ view model =
= introView
=
= Just game ->
- [ gameView game
+ [ passwordView game
+ , gameView game
= , popupView model.outcomes game
= ]
= |> Html.div
= [ Html.Attributes.style "height" "100%"
= , Html.Attributes.style "width" "100%"
= , Html.Attributes.style "aling-self" "center"
+ , Html.Attributes.style "display" "flex"
+ , Html.Attributes.style "flex-direction" "column"
= ]
=
=
@@ -156,11 +159,28 @@ introView =
= ]
=
=
+passwordView : Game.Model -> Html Msg
+passwordView game =
+ game
+ |> Game.passwordView
+ |> Html.map GotGameMsg
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "background" "hsl(0, 0%, 86%)"
+ , Html.Attributes.style "box-shadow" "0px 0px 6px 2px hsla(0, 0%, 0%, 20%)"
+ ]
+
+
=gameView : Game.Model -> Html Msg
=gameView game =
= game
- |> Game.view
+ |> Game.areaView
= |> Html.map GotGameMsg
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "height" "0"
+ , Html.Attributes.style "flex" "auto"
+ ]
=
=
=popupView : Outcomes -> Game.Model -> Html Msg
@@ -169,53 +189,39 @@ popupView outcomes game =
= popup : Bool -> List (Html Msg) -> Html Msg
= popup show elements =
= elements
+ -- |> Html.div
+ -- [ Html.Attributes.style "width" "100%"
+ -- ]
+ -- |> List.singleton
= |> Html.div
= [ Html.Attributes.style "text-align" "center"
- , Html.Attributes.style "padding" "4rem"
- , Html.Attributes.style "background" "hsl(0, 0%, 86%)"
- , Html.Attributes.style "border-radius" "10px"
- , Html.Attributes.style "box-shadow" "-2px -2px 6px 0 hsla(0, 0%, 0%, 20%)"
- ]
- |> List.singleton
- |> Html.div
- [ Html.Attributes.style "position" "absolute"
- , Html.Attributes.style "top" "0"
- , Html.Attributes.style "left" "0"
+ , Html.Attributes.style "padding" "0.2em 0"
+ , Html.Attributes.style "position" "absolute"
= , Html.Attributes.style "bottom" "0"
- , Html.Attributes.style "right" "0"
- , Html.Attributes.style "align-items" "center"
- , Html.Attributes.style "justify-content" "center"
- , Html.Attributes.style "opacity"
- (if show then
- "1"
-
- else
- "0"
- )
- , Html.Attributes.style "display" "flex"
- , Html.Attributes.style "pointer-events"
- (if show then
- "auto"
-
- else
- "none"
- )
- , Html.Attributes.style "visibility"
+ , Html.Attributes.style "width" "100%"
+ , Html.Attributes.style "background" "hsl(0, 0%, 86%)"
+ , Html.Attributes.style "box-shadow" "0px 0px 6px 2px hsla(0, 0%, 0%, 20%)"
+ , Html.Attributes.style "transition" "max-height 1500ms 500ms"
+ , Html.Attributes.style "max-height"
= (if show then
- "visible"
+ "18rem"
=
= else
- "hidden"
+ "3rem"
= )
- , Html.Attributes.style "transition" "opacity 500ms 1000ms"
+ ]
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "height" "3rem"
+ , Html.Attributes.style "position" "relative"
= ]
= in
= case Game.outcome game of
= Game.Won ->
= popup True
- [ Html.h2 [] [ Html.text "🙏" ]
+ [ socreView outcomes
+ , 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
@@ -229,9 +235,9 @@ popupView outcomes game =
=
= Game.Lost ->
= popup True
- [ Html.h2 [] [ Html.text "☠️" ]
+ [ socreView outcomes
+ , 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
@@ -244,7 +250,7 @@ popupView outcomes game =
= ]
=
= Game.InProgress ->
- popup False []
+ popup False [ socreView outcomes ]
=
=
=socreView : Outcomes -> Html msgAnimate the position of the game area when game is over
On by
index 8ee7a1f..b1f2083 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -168,6 +168,7 @@ passwordView game =
= |> Html.div
= [ Html.Attributes.style "background" "hsl(0, 0%, 86%)"
= , Html.Attributes.style "box-shadow" "0px 0px 6px 2px hsla(0, 0%, 0%, 20%)"
+ , Html.Attributes.style "z-index" "1"
= ]
=
=
@@ -180,6 +181,13 @@ gameView game =
= |> Html.div
= [ Html.Attributes.style "height" "0"
= , Html.Attributes.style "flex" "auto"
+ , Html.Attributes.style "transition" "transform 1500ms 500ms"
+ , Html.Attributes.style "transform" <|
+ if Game.outcome game == Game.InProgress then
+ "translateY(0)"
+
+ else
+ "translateY(-9rem)"
= ]
=
=
@@ -201,8 +209,8 @@ popupView outcomes game =
= , Html.Attributes.style "width" "100%"
= , Html.Attributes.style "background" "hsl(0, 0%, 86%)"
= , Html.Attributes.style "box-shadow" "0px 0px 6px 2px hsla(0, 0%, 0%, 20%)"
- , Html.Attributes.style "transition" "max-height 1500ms 500ms"
- , Html.Attributes.style "max-height"
+ , Html.Attributes.style "transition" "height 1500ms 500ms"
+ , Html.Attributes.style "height"
= (if show then
= "18rem"
=Implement 5 second countdown before the game begins
On by
So that player can read the sentence and think about the password.
index ce0ae77..dc4d68c 100644
--- a/src/Game.elm
+++ b/src/Game.elm
@@ -101,7 +101,9 @@ type alias Flags =
=init : Flags -> Model
=init flags =
= { area = flags.area
- , letters = Dict.empty
+ , letters =
+ Dict.empty
+ |> burnTheEdge flags.area True
= , snake =
= { head = ( 0, 0 )
= , body = List.repeat 5 ( 0, 0 )
@@ -140,10 +142,6 @@ view model =
= , Html.Attributes.style "height" "100%"
= , Html.Attributes.style "display" "flex"
= , Html.Attributes.style "flex-direction" "column"
- , Touch.onStart TouchStarted
- , Touch.onEnd TouchEnded
- , Touch.onMove TouchMoved
- , Touch.onCancel TouchCanceled
= ]
= [ passwordView model
= , areaView model
@@ -196,6 +194,10 @@ areaView model =
= [ Svg.Attributes.viewBox viewbox
= , Html.Attributes.style "width" "100%"
= , Html.Attributes.style "height" "100%"
+ , Touch.onStart TouchStarted
+ , Touch.onEnd TouchEnded
+ , Touch.onMove TouchMoved
+ , Touch.onCancel TouchCanceled
= ]
=
=index b1f2083..7e7c3ad 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -12,6 +12,7 @@ import Parser
=import Parser.Custom as Parser
=import Random
=import RemoteData exposing (WebData)
+import Time
=
=
=port goalTracking : ( String, Int ) -> Cmd msg
@@ -31,6 +32,7 @@ type alias Model =
= { game : Maybe Game.Model
= , levels : WebData (List Level)
= , outcomes : Outcomes
+ , countdown : Int
= }
=
=
@@ -52,6 +54,7 @@ init flags =
= ( { game = Nothing
= , levels = RemoteData.Loading
= , outcomes = Dict.empty
+ , countdown = 0
= }
= , fetchLevels GotLevelsResponse flags.levels_url
= )
@@ -93,6 +96,7 @@ view model =
= [ passwordView game
= , gameView game
= , popupView model.outcomes game
+ , countdownView model.countdown
= ]
= |> Html.div
= [ Html.Attributes.style "height" "100%"
@@ -103,6 +107,38 @@ view model =
= ]
=
=
+countdownView : Int -> Html Msg
+countdownView countdown =
+ if countdown == 0 then
+ Html.div [] []
+
+ else
+ countdown
+ |> String.fromInt
+ |> Html.text
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "font-size" "10rem"
+ , Html.Attributes.style "font-weight" "bold"
+ , Html.Attributes.style "color" "white"
+ , Html.Attributes.style "animation-name" "countdown"
+ , Html.Attributes.style "animation-duration" "1s"
+ , Html.Attributes.style "animation-iteration-count" "infinite"
+ ]
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "position" "absolute"
+ , Html.Attributes.style "top" "0"
+ , Html.Attributes.style "bottom" "0"
+ , Html.Attributes.style "left" "0"
+ , Html.Attributes.style "right" "0"
+ , Html.Attributes.style "display" "flex"
+ , Html.Attributes.style "align-items" "center"
+ , Html.Attributes.style "justify-content" "center"
+ , Html.Attributes.style "background" "hsla(0,0%,0%,80%)"
+ ]
+
+
=introView : Html Msg
=introView =
= Html.div
@@ -197,10 +233,6 @@ popupView outcomes game =
= popup : Bool -> List (Html Msg) -> Html Msg
= popup show elements =
= elements
- -- |> Html.div
- -- [ Html.Attributes.style "width" "100%"
- -- ]
- -- |> List.singleton
= |> Html.div
= [ Html.Attributes.style "text-align" "center"
= , Html.Attributes.style "padding" "0.2em 0"
@@ -222,6 +254,7 @@ popupView outcomes game =
= |> Html.div
= [ Html.Attributes.style "height" "3rem"
= , Html.Attributes.style "position" "relative"
+ , Html.Attributes.style "z-index" "1"
= ]
= in
= case Game.outcome game of
@@ -295,6 +328,7 @@ socreView outcomes =
=type Msg
= = GotLevelsResponse (Result Http.Error (List Level))
= | StartButtonClicked
+ | CountdownClockTick Time.Posix
= | GotGameMsg Game.Msg
= | GotRandomGameFlags (List Level) Random.Seed
= | PlayAgainButtonClicked
@@ -315,6 +349,11 @@ update msg model =
= |> RemoteData.withDefault Cmd.none
= )
=
+ CountdownClockTick _ ->
+ ( { model | countdown = model.countdown - 1 }
+ , Cmd.none
+ )
+
= PlayAgainButtonClicked ->
= case model.game of
= Nothing ->
@@ -388,6 +427,7 @@ update msg model =
= level :: _ ->
= ( { model
= | levels = RemoteData.Success levels
+ , countdown = 5
= , game =
= { level = level
= , randomness = randomness
@@ -406,10 +446,18 @@ update msg model =
=
=subscriptions : Model -> Sub Msg
=subscriptions model =
- model.game
- |> Maybe.map Game.subscriptions
- |> Maybe.map (Sub.map GotGameMsg)
- |> Maybe.withDefault Sub.none
+ case model.game of
+ Nothing ->
+ Sub.none
+
+ Just game ->
+ if model.countdown == 0 then
+ game
+ |> Game.subscriptions
+ |> Sub.map GotGameMsg
+
+ else
+ Time.every 1000 CountdownClockTick
=
=
=index 9771568..5d927ee 100644
--- a/src/index.html
+++ b/src/index.html
@@ -51,6 +51,18 @@
= #preload {
= display: none;
= }
+
+ @keyframes countdown {
+ from {
+ transform: scale(1);
+ opacity: 100%;
+ }
+
+ to {
+ transform: scale(3);
+ opacity: 0%;
+ }
+ }
= </style>
=
= </head>Display dark overlay when the game is over
On by
Move the text and emoji from popup to this new overlay (called curtainView). The contents will zoom and fade in.
index 7e7c3ad..b0893b3 100644
--- a/src/Main.elm
+++ b/src/Main.elm
@@ -96,7 +96,7 @@ view model =
= [ passwordView game
= , gameView game
= , popupView model.outcomes game
- , countdownView model.countdown
+ , curtainView model.countdown game
= ]
= |> Html.div
= [ Html.Attributes.style "height" "100%"
@@ -107,38 +107,6 @@ view model =
= ]
=
=
-countdownView : Int -> Html Msg
-countdownView countdown =
- if countdown == 0 then
- Html.div [] []
-
- else
- countdown
- |> String.fromInt
- |> Html.text
- |> List.singleton
- |> Html.div
- [ Html.Attributes.style "font-size" "10rem"
- , Html.Attributes.style "font-weight" "bold"
- , Html.Attributes.style "color" "white"
- , Html.Attributes.style "animation-name" "countdown"
- , Html.Attributes.style "animation-duration" "1s"
- , Html.Attributes.style "animation-iteration-count" "infinite"
- ]
- |> List.singleton
- |> Html.div
- [ Html.Attributes.style "position" "absolute"
- , Html.Attributes.style "top" "0"
- , Html.Attributes.style "bottom" "0"
- , Html.Attributes.style "left" "0"
- , Html.Attributes.style "right" "0"
- , Html.Attributes.style "display" "flex"
- , Html.Attributes.style "align-items" "center"
- , Html.Attributes.style "justify-content" "center"
- , Html.Attributes.style "background" "hsla(0,0%,0%,80%)"
- ]
-
-
=introView : Html Msg
=introView =
= Html.div
@@ -261,8 +229,6 @@ popupView outcomes game =
= Game.Won ->
= popup True
= [ socreView outcomes
- , Html.h2 [] [ Html.text "🙏" ]
- , Html.p [] [ Html.text "Bravo! The snake is safe." ]
= , Html.button
= [ Html.Events.onClick PlayAgainButtonClicked
= , Html.Attributes.autofocus True
@@ -277,8 +243,6 @@ popupView outcomes game =
= Game.Lost ->
= popup True
= [ socreView outcomes
- , Html.h2 [] [ Html.text "☠️" ]
- , Html.p [] [ Html.text "Oh, no! Your snake is dead." ]
= , Html.button
= [ Html.Events.onClick PlayAgainButtonClicked
= , Html.Attributes.autofocus True
@@ -294,6 +258,87 @@ popupView outcomes game =
= popup False [ socreView outcomes ]
=
=
+curtainView : Int -> Game.Model -> Html Msg
+curtainView countdown game =
+ let
+ overlay elements =
+ elements
+ |> Html.div
+ [ Html.Attributes.style "position" "absolute"
+ , Html.Attributes.style "top" "0"
+ , Html.Attributes.style "bottom" "0"
+ , Html.Attributes.style "left" "0"
+ , Html.Attributes.style "right" "0"
+ , Html.Attributes.style "display" "flex"
+ , Html.Attributes.style "align-items" "center"
+ , Html.Attributes.style "justify-content" "center"
+ , Html.Attributes.style "background" "hsla(0,0%,0%,80%)"
+ ]
+ in
+ case Game.outcome game of
+ Game.InProgress ->
+ if countdown == 0 then
+ Html.div [] []
+
+ else
+ countdown
+ |> String.fromInt
+ |> Html.text
+ |> List.singleton
+ |> Html.div
+ [ Html.Attributes.style "font-size" "10rem"
+ , Html.Attributes.style "font-weight" "bold"
+ , Html.Attributes.style "color" "white"
+ , Html.Attributes.style "animation-name" "countdown"
+ , Html.Attributes.style "animation-duration" "1s"
+ , Html.Attributes.style "animation-iteration-count" "infinite"
+ ]
+ |> List.singleton
+ |> overlay
+
+ Game.Lost ->
+ [ Html.h2
+ [ Html.Attributes.style "font-size" "2em"
+ ]
+ [ Html.text "☠️" ]
+ , Html.p [] [ Html.text "Oh, no! Your snake is dead." ]
+ ]
+ |> Html.div
+ [ Html.Attributes.style "font-size" "2rem"
+ , Html.Attributes.style "font-weight" "bold"
+ , Html.Attributes.style "color" "white"
+ , Html.Attributes.style "animation-name" "outcome"
+ , Html.Attributes.style "animation-duration" "1s"
+ , Html.Attributes.style "animation-delay" "1s"
+ , Html.Attributes.style "animation-iteration-count" "1"
+ , Html.Attributes.style "animation-fill-mode" "both"
+ , Html.Attributes.style "text-align" "center"
+ ]
+ |> List.singleton
+ |> overlay
+
+ Game.Won ->
+ [ Html.h2
+ [ Html.Attributes.style "font-size" "2em"
+ ]
+ [ Html.text "🙏" ]
+ , Html.p [] [ Html.text "Bravo! The snake is safe." ]
+ ]
+ |> Html.div
+ [ Html.Attributes.style "font-size" "2rem"
+ , Html.Attributes.style "font-weight" "bold"
+ , Html.Attributes.style "color" "white"
+ , Html.Attributes.style "animation-name" "outcome"
+ , Html.Attributes.style "animation-duration" "1s"
+ , Html.Attributes.style "animation-delay" "1s"
+ , Html.Attributes.style "animation-iteration-count" "1"
+ , Html.Attributes.style "animation-fill-mode" "both"
+ , Html.Attributes.style "text-align" "center"
+ ]
+ |> List.singleton
+ |> overlay
+
+
=socreView : Outcomes -> Html msg
=socreView outcomes =
= letindex 5d927ee..875d949 100644
--- a/src/index.html
+++ b/src/index.html
@@ -63,6 +63,18 @@
= opacity: 0%;
= }
= }
+
+ @keyframes outcome {
+ from {
+ transform: scale(4);
+ opacity: 0%;
+ }
+
+ to {
+ transform: scale(1);
+ opacity: 100%;
+ }
+ }
= </style>
=
= </head>Merge branch 'countdown' into 'master'
On by
Countdown and game over overlay
See merge request hornbook/word-snake!11
Fix quotation in Artful Dodger question
On by
index 3d4e180..d2e9706 100644
--- a/static/levels/default.snake
+++ b/static/levels/default.snake
@@ -4,7 +4,7 @@ Tibia is a name of one of the { BONES } in the leg.
=Dr. Dre is an American { RAPPER }.
=Somewhat surprisingly, a person can get { SUNBURNED } on a cloudy day.
=Wales is a country inside the United Kingdom that does not appear on its flag, the Union { JACK }.
-The Artful Dodger is a nickname of Jack Dawkins, a character in \"Oliver Twist\", a novel by Charles { DICKENS }.
+The Artful Dodger is a nickname of Jack Dawkins, a character in "Oliver Twist", a novel by Charles { DICKENS }.
=Pokemon Go is a location-based augmented { REALITY } game developed by company called Niantic.
=In 1960 Michael Woodruff { PERFORMED } the first successful kidney transplant in the United Kingdom.
=To bypass US Munitions Export Laws, the creator of the PGP { PUBLISHED } all the source code in book form.