Skip to content

Commit

Permalink
Mods and players.
Browse files Browse the repository at this point in the history
* port and command to edit player list
* new wheel with more mods
* mod icons displayed on sidebar's bottom
* multiplier mod values now taken into points calculation for consonants
* symbol icons on wheel now displayed straight, and not rotated like
  numbers
* mods now properly expire with turn order
* wildcard consumed on Halt, lets spin again
* wildcard consumed on Bankrupt: turn still lost but points kept
* ignore keyups with alt or ctrl
  • Loading branch information
Krzysztof Zych committed May 30, 2020
1 parent 5d4a19e commit 6509165
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 75 deletions.
4 changes: 4 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
function loadPuzzle(text, category) {
app.ports.setPuzzleText.send([text, category])
}

function setPlayers(...playerNames) {
app.ports.setPlayers.send(playerNames)
}
</script>
</body>
</html>
40 changes: 31 additions & 9 deletions main.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ wof-container .peg {
wof {
position: relative;
top: 24px;
/* display: inline-block; */
/* height: 300px; */
/* left: 50%; */
/* transform: translateX(-50%) */
}

wof #circle {
Expand All @@ -90,17 +86,43 @@ category {
background: white;
}

mods {
position: absolute;
bottom: 0px;
margin-bottom: 12px;
}

mods span {
font-size: 36px;
}

mods .chart-up::after {
content: "📈";
}

mods .chart-down::after {
content: "📉";
}

mods .updown::after {
content: "↕️";
}

mods .shuffled::after {
content: "🔀";
}

mods .malfunction::after {
content: "🔥";
}


letter-grid {
display: grid !important;
height: 320px;
/*
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
*/
grid-template-columns: auto auto auto auto auto auto auto auto auto auto auto auto auto auto;
grid-template-rows: auto auto auto auto;
gap: 5px 5px;
/*background: var(--cadet-blue);*/
background: #f0f;
padding: 20px 10px;
}
Expand Down
129 changes: 81 additions & 48 deletions src/Main.elm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Html.Attributes exposing (class)
import Template exposing (template, render)
import WofGrid exposing (letterGrid)
import Wheel exposing (theWheel)
import Players exposing (modIcons, playerList)
import Players exposing (..)
import Puzzle exposing (acceptConsonant, acceptVowel, reveal, reset, setBoard)
import Json.Decode as Decode
import Random
Expand All @@ -19,9 +19,10 @@ import Task
import Debug exposing (log)


wheelDefinition = { sectors = Array.fromList [ Guess 100, Guess 250, Guess 500, Guess 150, Guess 300, Guess 1500, Bankrupt, Guess 400, Guess 200, Guess 500, Halt, Guess 350, Guess 450, WildCard ],
wheelDefinition = { sectors = Array.fromList [ Guess 100, Guess 250, Guess 500, Stonks, Guess 150, Guess 300, Guess 1500, Bankrupt, Guess 400, Guess 200, Sunks, Guess 500, Halt, Guess 350, Guess 450, WildCard ],
palette = Array.fromList [ ("#729ea1", "dark"), ("#b5bd89", "dark"), ("#dfbe99", "dark"), ("#ec9192", "dark"), ("#db5375", "dark"), ("#3e4e50", "light"), ("#f2aa7e", "dark"), ("#6c756b", "light"), ("#96c5f7", "dark") ] }


initialPlayers = [
initPlayer "Mario",
initPlayer "Peach",
Expand All @@ -40,7 +41,7 @@ init _ = ( { players = initialPlayers,
current = 0,
mods = [],
lettersUsed = Set.empty,
puzzle = [ ".PAN.TADEUSZ..", "....CZYLI.....", "OSTATNI ZAJAZD", "..NA.LITWIE..." ],
puzzle = [ ".PAN.TADEUSZ..", "....CZYLI.....", "OSTATNI.ZAJAZD", "..NA.LITWIE..." ],
category = "Tytuł",
wheel = wheelDefinition,
target = Nothing,
Expand All @@ -60,6 +61,7 @@ replaceNth index new list =

port animationLauncher : () -> Cmd msg
port setPuzzleText : (List String -> msg) -> Sub msg
port setPlayers : (List String -> msg) -> Sub msg

update : Msg -> GameState -> ( GameState, Cmd Msg )
update msg state =
Expand All @@ -81,19 +83,21 @@ update msg state =
Nothing -> ( state, Cmd.none )
Nothing -> ( state, Cmd.none ) -- what just happened?
NextPlayerCommand ->
-- TODO: check if mods need to be dropped
let numPlayers = List.length state.players
nextPlayer = remainderBy numPlayers (state.currentPlayer + 1)
mods = expireMods state.mods nextPlayer
in
( { state | currentPlayer = nextPlayer, playerState = BeforeSpin },
Cmd.none )
( { state | currentPlayer = nextPlayer, mods = mods,
playerState = BeforeSpin }, Cmd.none )
RevealCommand -> ( reveal state, Cmd.none )
SetPuzzleCommand step text category ->
case step of
False -> ( reset state text category,
Process.sleep 1000 |>
Task.perform (always (SetPuzzleCommand True text category)) )
True -> ( setBoard state text category, Cmd.none )
SetPlayersCommand newPlayers ->
( { state | players = newPlayers, currentPlayer = 0, playerState = BeforeSpin }, Cmd.none )
_ -> ( state, Cmd.none )

-- TODO: kick to module?
Expand All @@ -105,34 +109,57 @@ advanceFromSector n state sector =
case (player, sector) of
(Just(_), Guess val) ->
( { landedState | playerState = SpinSuccessConsonant }, Cmd.none )
(Just(_), Halt) ->
-- TODO: also consume wildcard here?
update NextPlayerCommand landedState -- NOTE: TurnLost not set here
(Just(p), Halt) ->
-- with wildcard: consume it, don't lose turn
if p.wildcard then
let playerchanged = { p | wildcard = False }
updatedPlayers = replaceNth state.currentPlayer playerchanged state.players
in
( { landedState | players = updatedPlayers, playerState = BeforeSpin }, Cmd.none )
else
update NextPlayerCommand landedState
(Just(p), Bankrupt) ->
let bankruptPlayer = if p.wildcard then { p | wildcard = False } else { p | score = 0 }
updatedPlayerList = replaceNth state.currentPlayer bankruptPlayer state.players
-- with wildcard: consume it, lose turn, but don't bankrupt.
let playerchanged = if p.wildcard then
{ p | wildcard = False }
else
{ p | score = 0 }
updated = replaceNth state.currentPlayer playerchanged state.players
in
if p.wildcard then
( { landedState | players = updatedPlayerList, playerState = BeforeSpin }, Cmd.none )
else
update NextPlayerCommand { landedState | players = updatedPlayerList }
update NextPlayerCommand { landedState | players = updated }
(Just(_), FreeVowel) -> ( { landedState | playerState = GuessVowel }, Cmd.none )
-- IDEA: new sector type, with a bomb icon. When landed on, a random sector is removed from the wheel and the player spins again.
(Just(p), WildCard) ->
let updatedPlayer = { p | wildcard = True }
updatedPlayerList = replaceNth state.currentPlayer updatedPlayer state.players
let playerchanged = { p | wildcard = True }
updated = replaceNth state.currentPlayer playerchanged state.players
in
( { landedState | playerState = BeforeSpin, players = updatedPlayerList }, Cmd.none )
( { landedState | playerState = BeforeSpin, players = updated }, Cmd.none )
(Just(_), Stonks) ->
let multiplier = Multiplier n ("STONKS", 2.0)
newMods = multiplier :: state.mods
let mul = Multiplier state.currentPlayer ("STONKS", 2.0)
in
( { landedState | mods = newMods, playerState = BeforeSpin }, Cmd.none )
( { landedState | mods = mul :: state.mods, playerState = BeforeSpin }, Cmd.none )
(Just(_), Sunks) ->
let multiplier = Multiplier n ("SUNKS", 0.5)
newMods = multiplier :: state.mods
let mul = Multiplier state.currentPlayer ("SUNKS", 0.5)
in
( { landedState | mods = newMods, playerState = BeforeSpin }, Cmd.none )
(_, _) -> ( state, Cmd.none )
( { landedState | mods = mul :: state.mods, playerState = BeforeSpin }, Cmd.none )
(Nothing, _) -> ( state, Cmd.none )

expirationTurn : Modifier -> Int
expirationTurn mod =
case mod of
Multiplier n _ -> n
UpsideDown n -> n
Shuffled n -> n
Malfunction n -> n

expireMods : List Modifier -> Int -> List Modifier
expireMods mods playernum =
let l_ = log "expireMods mods" mods
k_ = log "expireMods playernum" playernum
keep = \mod -> (expirationTurn mod) /= playernum
in
List.filter keep mods


handleLetterKey : GameState -> Char -> ( GameState, Cmd Msg )
handleLetterKey state char =
Expand Down Expand Up @@ -166,25 +193,25 @@ view state =
]
]

categoryDisplay : String -> Html msg
categoryDisplay cat =
node "category" [class "pure-u-1"] [text cat]

launchSpin : GameState -> (GameState, Cmd Msg)
launchSpin state =
let playerState = log "launchSpin playerstate" state.playerState
let playerState = state.playerState
canSpin = List.member playerState [
BeforeSpin, ChooseAction, SpinOrGuess,
-- allow launching for this state, needed when RNG rolls current value again
Spinning ]
in
-- The first three are obvious, the last one is to retry spins if the rng
-- returns the same value.
if List.member playerState [BeforeSpin, ChooseAction, SpinOrGuess, Spinning] then
if canSpin then
spinRNG state
else
(state, Cmd.none)

spinRNG state =
( { state | playerState = Spinning },
-- how to handle rng generating same sector?
Random.generate SpinTo state.rng )

categoryDisplay : String -> Html msg
categoryDisplay cat =
node "category" [class "pure-u-1"] [text cat]
( { state | playerState = Spinning }, Random.generate SpinTo state.rng )

subscriptions : GameState -> Sub Msg
subscriptions state =
Expand All @@ -193,19 +220,25 @@ subscriptions state =
text :: category :: [] ->
SetPuzzleCommand False text category
_ ->
NoOp)
NoOp),
setPlayers (\l -> SetPlayersCommand (List.map buildNewPlayer l))
]

keyDecoder : Decode.Decoder Msg
keyDecoder =
Decode.map toKey (Decode.field "key" Decode.string)

toKey : String -> Msg
toKey inp =
case String.uncons inp of
-- Funky syntax, but useful for pattern matching on single character
Just ( ' ', "" ) -> NextPlayerCommand
Just ( 'E', "nter" ) -> SpinCommand
Just ( '!', "") -> RevealCommand
Just ( char, "" ) -> KeyPressed (Char.toUpper char)
_ -> NoOp
Decode.map3 toKey (Decode.field "key" Decode.string)
(Decode.field "ctrlKey" Decode.bool)
(Decode.field "altKey" Decode.bool)

toKey : String -> Bool -> Bool -> Msg
toKey inp ctrl alt =
if ctrl || alt then
NoOp
else
case String.uncons inp of
-- Funky syntax, but useful for pattern matching on single character
Just ( ' ', "" ) -> NextPlayerCommand
Just ( 'E', "nter" ) -> SpinCommand
Just ( '!', "") -> RevealCommand
Just ( char, "" ) -> KeyPressed (Char.toUpper char)
_ -> NoOp
24 changes: 16 additions & 8 deletions src/Players.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Players exposing (playerList, modIcons)
module Players exposing (playerList, modIcons, buildNewPlayer)

import Html exposing (Html, text, ul, li, span, div)
import Html.Attributes exposing (class, id)
Expand All @@ -7,18 +7,23 @@ import List exposing (map, indexedMap)
import String exposing (String)
import Types exposing (..)

modText : Modifier -> String
modText : Modifier -> Html msg
modText mod =
let symbol = \t -> span [ class t ] [ ]
in
case mod of
Multiplier tuple (label, mul) -> label
UpsideDown n -> "UPDOWN"
Shuffled n -> "SHUFFLE"
Malfunction n -> "MALFUNCTION"
Multiplier tuple (label, mul) ->
if mul > 1.0 then
symbol "chart-up"
else
symbol "chart-down"
UpsideDown n -> symbol "updown"
Shuffled n -> symbol "shuffled"
Malfunction n -> symbol "malfunction"

modIcons : List Modifier -> Html msg
modIcons mods =
-- TODO
div [] (List.map (\t -> modText t |> text) mods)
Html.node "mods" [] (List.map modText mods)

playerList : List Player -> Int -> PlayerState -> Html Msg
playerList players current state =
Expand Down Expand Up @@ -62,3 +67,6 @@ spinButton = Html.button [ onClick SpinCommand, class "spin" ] [ text "Spin" ]
guessButton = Html.button [ class "guess" ] [ text "Guess" ]
mustGuessButton = Html.button [ class "guess", onClick RevealCommand ] [ text "Guess!" ]
vowelButton = Html.button [ class "vowel" ] [ text "Buy Vowel" ]

buildNewPlayer : String -> Player
buildNewPlayer name = { name = name, score = 0, wildcard = False }
6 changes: 4 additions & 2 deletions src/Puzzle.elm
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ acceptConsonant letter state =
{ state |
playerState = ChooseAction,
lettersUsed = Set.union (Set.singleton letter) state.lettersUsed,
players = updateScore state.players state.currentPlayer letterScore
players = updateScore state.players state.currentPlayer letterScore state.mods
}

acceptVowel : Char -> GameState -> GameState
Expand All @@ -41,7 +41,9 @@ acceptVowel letter state =
else
{state | playerState = SpinOrGuess,
lettersUsed = Set.union (Set.singleton letter) state.lettersUsed,
players = updateScore state.players state.currentPlayer -vowelCost
-- mods do not apply to vowels
-- TODO: freeVowel
players = updateScore state.players state.currentPlayer -vowelCost []
}

allLetters : List String -> Set Char
Expand Down
18 changes: 14 additions & 4 deletions src/Scoring.elm
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import List exposing (indexedMap, foldl, filter, length, map)
import String exposing (toList)
import Debug exposing (log)

updateScore: List Player -> Int -> Int -> List Player
updateScore players index points =
let modPoints = \i player -> if i == index then
{ player | score = player.score + points }
updateScore: List Player -> Int -> Int -> List Modifier -> List Player
updateScore players index points mods =
let multiplier = calculateMultiplier mods
won = round (toFloat points * multiplier)
modPoints = \i player -> if i == index then
{ player | score = player.score + won }
else
player
in
Expand All @@ -35,3 +37,11 @@ calculateScore letter puzzle sector =
in
perLetter * occurrences

calculateMultiplier : List Modifier -> Float
calculateMultiplier mods =
let modvalue = \mod -> case mod of
Multiplier _ (_, mul) -> mul
-- non-multipliers don't contribute
_ -> 1.0
in
List.product (List.map modvalue mods)
1 change: 1 addition & 0 deletions src/Types.elm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type Msg = NoOp
| SpinComplete
| RevealCommand
| SetPuzzleCommand Bool String String
| SetPlayersCommand (List Player)

type WheelSector = Guess Int
| Halt
Expand Down
Loading

0 comments on commit 6509165

Please sign in to comment.