|
|
@@ -0,0 +1,202 @@
|
|
|
+module Database exposing (Database, decode)
|
|
|
+
|
|
|
+import Dict exposing (Dict)
|
|
|
+import Json.Decode as Decode exposing (Decoder, decodeString)
|
|
|
+import Json.Decode.Pipeline exposing (optional, required)
|
|
|
+import Tuple exposing (pair)
|
|
|
+
|
|
|
+
|
|
|
+type Database
|
|
|
+ = Database (Dict String CardData)
|
|
|
+
|
|
|
+
|
|
|
+type ManaColor
|
|
|
+ = Red
|
|
|
+ | Blue
|
|
|
+ | Green
|
|
|
+ | White
|
|
|
+ | Black
|
|
|
+ | Colorless
|
|
|
+
|
|
|
+
|
|
|
+type CardType
|
|
|
+ = Creature
|
|
|
+ | Instant
|
|
|
+ | Sorcery
|
|
|
+ | Enchantment
|
|
|
+ | Artifact
|
|
|
+ | Planeswalker
|
|
|
+ | Land
|
|
|
+ | Other
|
|
|
+
|
|
|
+
|
|
|
+type Power
|
|
|
+ = ConstantPower Int
|
|
|
+ | VariablePower
|
|
|
+
|
|
|
+
|
|
|
+type alias CardData =
|
|
|
+ { details : CardDetails
|
|
|
+ , performance : CardPerformanceData
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+type alias CardDetails =
|
|
|
+ { cmc : Int
|
|
|
+ , cardType : CardType
|
|
|
+ , typeLine : String
|
|
|
+ , oracleText : String
|
|
|
+ , power : Maybe Power
|
|
|
+ , toughness : Maybe Power
|
|
|
+ , colors : List ManaColor
|
|
|
+
|
|
|
+ --, manaCost : List ManaColor
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+type alias CardPerformanceData =
|
|
|
+ { totalTimesSeen : Int
|
|
|
+ , totalTimesPicked : Int
|
|
|
+ , averagePickPosition : Maybe Float
|
|
|
+ , averageSeenPosition : Maybe Float
|
|
|
+ , gameInHandWinRate : Maybe Float
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+decode : String -> String -> Result String Database
|
|
|
+decode setData draftData =
|
|
|
+ case ( decodeString decodeSetData setData, decodeString decodePerformanceData draftData ) of
|
|
|
+ ( Ok set, Ok draft ) ->
|
|
|
+ Ok (createDatabase draft set |> Database)
|
|
|
+
|
|
|
+ ( Err err, _ ) ->
|
|
|
+ Err <| Decode.errorToString err
|
|
|
+
|
|
|
+ ( _, Err err ) ->
|
|
|
+ Err <| Decode.errorToString err
|
|
|
+
|
|
|
+
|
|
|
+createDatabase : Dict String CardPerformanceData -> Dict String CardDetails -> Dict String CardData
|
|
|
+createDatabase performanceData detailsData =
|
|
|
+ Dict.merge
|
|
|
+ (\_ _ cardData -> cardData)
|
|
|
+ (\name performance details -> Dict.insert name (CardData details performance))
|
|
|
+ (\_ _ cardData -> cardData)
|
|
|
+ performanceData
|
|
|
+ detailsData
|
|
|
+ Dict.empty
|
|
|
+
|
|
|
+
|
|
|
+decodePerformanceData : Decoder (Dict String CardPerformanceData)
|
|
|
+decodePerformanceData =
|
|
|
+ Decode.field "card_performance_data"
|
|
|
+ (Decode.dict decodeCardPerformance)
|
|
|
+
|
|
|
+
|
|
|
+decodeSetData : Decoder (Dict String CardDetails)
|
|
|
+decodeSetData =
|
|
|
+ Decode.list decodeCardDetails
|
|
|
+ |> Decode.map Dict.fromList
|
|
|
+
|
|
|
+
|
|
|
+decodeCardPerformance : Decoder CardPerformanceData
|
|
|
+decodeCardPerformance =
|
|
|
+ Decode.succeed CardPerformanceData
|
|
|
+ |> required "total_times_seen" Decode.int
|
|
|
+ |> required "total_times_picked" Decode.int
|
|
|
+ |> required "avg_pick_position" (Decode.nullable Decode.float)
|
|
|
+ |> required "avg_seen_position" (Decode.nullable Decode.float)
|
|
|
+ |> required "game_in_hand_win_rate" (Decode.nullable Decode.float)
|
|
|
+
|
|
|
+
|
|
|
+decodeCardDetails : Decoder ( String, CardDetails )
|
|
|
+decodeCardDetails =
|
|
|
+ Decode.map2 pair
|
|
|
+ (Decode.field "name" Decode.string)
|
|
|
+ (Decode.succeed CardDetails
|
|
|
+ |> required "cmc" Decode.int
|
|
|
+ |> required "type_line" decodeCardType
|
|
|
+ |> required "type_line" Decode.string
|
|
|
+ |> required "oracle_text" Decode.string
|
|
|
+ |> optional "power" (Decode.nullable decodePower) Nothing
|
|
|
+ |> optional "toughness" (Decode.nullable decodePower) Nothing
|
|
|
+ |> required "colors" (Decode.list decodeManaColor)
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+decodePower : Decoder Power
|
|
|
+decodePower =
|
|
|
+ Decode.string
|
|
|
+ |> Decode.andThen
|
|
|
+ (\s ->
|
|
|
+ if s == "*" then
|
|
|
+ Decode.succeed VariablePower
|
|
|
+
|
|
|
+ else
|
|
|
+ case String.toInt s of
|
|
|
+ Just i ->
|
|
|
+ Decode.succeed (ConstantPower i)
|
|
|
+
|
|
|
+ Nothing ->
|
|
|
+ Decode.fail "Invalid integer"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+decodeManaColor : Decoder ManaColor
|
|
|
+decodeManaColor =
|
|
|
+ Decode.string
|
|
|
+ |> Decode.andThen
|
|
|
+ (\c ->
|
|
|
+ case c of
|
|
|
+ "R" ->
|
|
|
+ Decode.succeed Red
|
|
|
+
|
|
|
+ "U" ->
|
|
|
+ Decode.succeed Blue
|
|
|
+
|
|
|
+ "G" ->
|
|
|
+ Decode.succeed Green
|
|
|
+
|
|
|
+ "W" ->
|
|
|
+ Decode.succeed White
|
|
|
+
|
|
|
+ "B" ->
|
|
|
+ Decode.succeed Black
|
|
|
+
|
|
|
+ "C" ->
|
|
|
+ Decode.succeed Colorless
|
|
|
+
|
|
|
+ _ ->
|
|
|
+ Decode.fail "Invalid mana color"
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+decodeCardType : Decoder CardType
|
|
|
+decodeCardType =
|
|
|
+ Decode.string
|
|
|
+ |> Decode.map
|
|
|
+ (\c ->
|
|
|
+ if String.contains "Creature" c then
|
|
|
+ Creature
|
|
|
+
|
|
|
+ else if String.contains "Instant" c then
|
|
|
+ Instant
|
|
|
+
|
|
|
+ else if String.contains "Sorcery" c then
|
|
|
+ Sorcery
|
|
|
+
|
|
|
+ else if String.contains "Enchantment" c then
|
|
|
+ Enchantment
|
|
|
+
|
|
|
+ else if String.contains "Artifact" c then
|
|
|
+ Artifact
|
|
|
+
|
|
|
+ else if String.contains "Planeswalker" c then
|
|
|
+ Planeswalker
|
|
|
+
|
|
|
+ else if String.contains "Land" c then
|
|
|
+ Land
|
|
|
+
|
|
|
+ else
|
|
|
+ Other
|
|
|
+ )
|