Database.elm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. module Database exposing (Database, decode, decoder, encode, fromCardData, get, getAll)
  2. import Card exposing (CardData, CardDetails, CardPerformanceData, CardType(..), ManaColor(..), Power(..), Rarity(..), parseManaCost, parsePower)
  3. import Dict exposing (Dict)
  4. import Json.Decode as Decode exposing (Decoder, decodeString)
  5. import Json.Decode.Pipeline exposing (custom, optional, required)
  6. import Json.Encode as Encode
  7. import Tuple exposing (pair)
  8. type Database
  9. = Database (Dict String CardData)
  10. fromCardData : List CardData -> Database
  11. fromCardData cards =
  12. List.map (\card -> ( card.details.name, card )) cards
  13. |> Dict.fromList
  14. |> Database
  15. get : String -> Database -> Maybe CardData
  16. get name (Database db) =
  17. Dict.get name db
  18. getAll : Database -> List CardData
  19. getAll (Database db) =
  20. Dict.values db
  21. decode : String -> Result String ( String, Maybe Database )
  22. decode setData =
  23. decodeString decoder setData |> Result.mapError Decode.errorToString
  24. decoder : Decoder ( String, Maybe Database )
  25. decoder =
  26. Decode.map2 pair
  27. (Decode.field "code" Decode.string)
  28. (Decode.field "data"
  29. (Decode.nullable
  30. (Decode.map2 createDatabase
  31. (Decode.field "ratings" decodePerformanceData)
  32. (Decode.field "cards" decodeSetData)
  33. )
  34. )
  35. )
  36. databaseToDict : Database -> Dict String CardData
  37. databaseToDict (Database db) =
  38. db
  39. encode : ( String, Database ) -> Encode.Value
  40. encode ( setCode, Database db ) =
  41. Encode.object
  42. [ ( "code", Encode.string setCode )
  43. , ( "data"
  44. , Encode.object
  45. [ ( "ratings", Encode.list encodeCardPerformance (Dict.values db) )
  46. , ( "cards", Encode.list encodeCardDetails (Dict.values db) )
  47. ]
  48. )
  49. ]
  50. createDatabase : Dict String CardPerformanceData -> Dict String CardDetails -> Database
  51. createDatabase performanceData detailsData =
  52. Dict.merge
  53. (\_ _ cardData -> cardData)
  54. (\name performance details -> Dict.insert name (CardData details performance))
  55. (\_ _ cardData -> cardData)
  56. performanceData
  57. detailsData
  58. Dict.empty
  59. |> Database
  60. decodePerformanceData : Decoder (Dict String CardPerformanceData)
  61. decodePerformanceData =
  62. Decode.list decodeCardPerformance
  63. |> Decode.map Dict.fromList
  64. decodeSetData : Decoder (Dict String CardDetails)
  65. decodeSetData =
  66. Decode.list decodeCardDetails
  67. |> Decode.map Dict.fromList
  68. encodeCardPerformance : CardData -> Encode.Value
  69. encodeCardPerformance d =
  70. Encode.object
  71. [ ( "Name", Encode.string d.details.name )
  72. , ( "# Seen", encodeIntString d.performance.totalTimesSeen )
  73. , ( "# Picked", encodeIntString d.performance.totalTimesPicked )
  74. , ( "ATA", encodeMaybeFloatString d.performance.averagePickPosition )
  75. , ( "ALSA", encodeMaybeFloatString d.performance.averageSeenPosition )
  76. , ( "GIH WR", encodeMaybePercentageString d.performance.gameInHandWinRate )
  77. , ( "IWD"
  78. , Encode.string
  79. (case d.performance.improvementWhenDrawn of
  80. Just i ->
  81. String.fromFloat i ++ "pp"
  82. Nothing ->
  83. ""
  84. )
  85. )
  86. ]
  87. decodeCardPerformance : Decoder ( String, CardPerformanceData )
  88. decodeCardPerformance =
  89. Decode.map2 pair
  90. (Decode.field "Name" Decode.string)
  91. (Decode.succeed CardPerformanceData
  92. |> required "# Seen" decodeIntString
  93. |> required "# Picked" decodeIntString
  94. |> required "ATA" decodeMaybeFloatString
  95. |> required "ALSA" decodeMaybeFloatString
  96. |> required "GIH WR" decodeMaybePercentageString
  97. -- The improvement when drawn changed to improvement in hand in newer set data
  98. -- from 17lands
  99. |> custom decodeIIHOrIWD
  100. )
  101. decodeIIHOrIWD : Decoder (Maybe Float)
  102. decodeIIHOrIWD =
  103. Decode.oneOf
  104. [ Decode.at [ "IIH" ] decodeImprovementWhenDrawn
  105. , Decode.at [ "IWD" ] decodeImprovementWhenDrawn
  106. ]
  107. decodeImprovementWhenDrawn : Decoder (Maybe Float)
  108. decodeImprovementWhenDrawn =
  109. Decode.string
  110. |> Decode.andThen
  111. (\s ->
  112. if s == "" then
  113. Decode.succeed Nothing
  114. else
  115. case String.toFloat (String.replace "pp" "" s) of
  116. Just i ->
  117. Decode.succeed (Just i)
  118. Nothing ->
  119. Decode.succeed Nothing
  120. )
  121. encodeIntString : Int -> Encode.Value
  122. encodeIntString i =
  123. Encode.string (String.fromInt i)
  124. decodeIntString : Decoder Int
  125. decodeIntString =
  126. Decode.string
  127. |> Decode.andThen
  128. (\s ->
  129. case String.toInt s of
  130. Just i ->
  131. Decode.succeed i
  132. Nothing ->
  133. Decode.fail "Invalid integer"
  134. )
  135. encodeMaybeFloatString : Maybe Float -> Encode.Value
  136. encodeMaybeFloatString m =
  137. case m of
  138. Just f ->
  139. Encode.string (String.fromFloat f)
  140. Nothing ->
  141. Encode.string ""
  142. decodeMaybeFloatString : Decoder (Maybe Float)
  143. decodeMaybeFloatString =
  144. Decode.string
  145. |> Decode.andThen
  146. (\s ->
  147. if s == "" then
  148. Decode.succeed Nothing
  149. else
  150. case String.toFloat s of
  151. Just i ->
  152. Decode.succeed (Just i)
  153. Nothing ->
  154. Decode.succeed Nothing
  155. )
  156. encodeMaybePercentageString : Maybe Float -> Encode.Value
  157. encodeMaybePercentageString m =
  158. case m of
  159. Just f ->
  160. Encode.string (String.fromFloat (f * 100) ++ "%")
  161. Nothing ->
  162. Encode.string ""
  163. decodeMaybePercentageString : Decoder (Maybe Float)
  164. decodeMaybePercentageString =
  165. Decode.string
  166. |> Decode.andThen
  167. (\s ->
  168. if s == "" then
  169. Decode.succeed Nothing
  170. else
  171. case String.toFloat (String.replace "%" "" s) of
  172. Just i ->
  173. Decode.succeed (Just (i / 100))
  174. Nothing ->
  175. Decode.succeed Nothing
  176. )
  177. encodeCardDetails : CardData -> Encode.Value
  178. encodeCardDetails card =
  179. Encode.object
  180. [ ( "name", Encode.string card.details.name )
  181. , ( "cmc", Encode.int card.details.cmc )
  182. , ( "type_line", Encode.string card.details.typeLine )
  183. , ( "oracle_text"
  184. , case card.details.oracleText of
  185. Just t ->
  186. Encode.string t
  187. Nothing ->
  188. Encode.null
  189. )
  190. , ( "power", encodePower card.details.power )
  191. , ( "toughness", encodePower card.details.toughness )
  192. , ( "colors", Encode.list encodeManaColor card.details.colors )
  193. , ( "mana_cost", Encode.string card.details.rawManaCost )
  194. , ( "image_uris", Encode.object [ ( "large", Encode.string card.details.imageUrl ) ] )
  195. , ( "rarity", encodeRarity card.details.rarity )
  196. ]
  197. encodeRarity : Rarity -> Encode.Value
  198. encodeRarity r =
  199. case r of
  200. Common ->
  201. Encode.string "common"
  202. Uncommon ->
  203. Encode.string "uncommon"
  204. Rare ->
  205. Encode.string "rare"
  206. Mythic ->
  207. Encode.string "mythic"
  208. NoRarity ->
  209. Encode.string ""
  210. decodeCardDetails : Decoder ( String, CardDetails )
  211. decodeCardDetails =
  212. Decode.map2 pair
  213. (Decode.field "name" Decode.string)
  214. (Decode.succeed CardDetails
  215. |> required "name" Decode.string
  216. |> required "cmc" Decode.int
  217. |> required "type_line" decodeCardType
  218. |> required "type_line" Decode.string
  219. |> optional "oracle_text" (Decode.nullable Decode.string) Nothing
  220. |> optional "power" (Decode.nullable decodePower) Nothing
  221. |> optional "toughness" (Decode.nullable decodePower) Nothing
  222. |> required "colors" (Decode.list decodeManaColor)
  223. |> required "mana_cost" Decode.string
  224. |> required "mana_cost"
  225. (Decode.string
  226. |> Decode.andThen
  227. (\s ->
  228. case parseManaCost s of
  229. Just m ->
  230. Decode.succeed (Just m)
  231. Nothing ->
  232. Decode.succeed Nothing
  233. )
  234. )
  235. |> required "image_uris" (Decode.field "large" Decode.string)
  236. |> optional "rarity" decodeRarity NoRarity
  237. )
  238. encodePower : Maybe Power -> Encode.Value
  239. encodePower p =
  240. case p of
  241. Just (ConstantPower i) ->
  242. Encode.string (String.fromInt i)
  243. Just (ConstantPlusVariablePower i) ->
  244. Encode.string (String.fromInt i ++ "+*")
  245. Just VariablePower ->
  246. Encode.string "*"
  247. Nothing ->
  248. Encode.null
  249. decodePower : Decoder Power
  250. decodePower =
  251. Decode.string
  252. |> Decode.andThen
  253. (\s ->
  254. case parsePower s of
  255. Just p ->
  256. Decode.succeed p
  257. Nothing ->
  258. Decode.fail ("Could not parse power: " ++ s)
  259. )
  260. encodeManaColor : ManaColor -> Encode.Value
  261. encodeManaColor c =
  262. case c of
  263. Red ->
  264. Encode.string "R"
  265. Blue ->
  266. Encode.string "U"
  267. Green ->
  268. Encode.string "G"
  269. White ->
  270. Encode.string "W"
  271. Black ->
  272. Encode.string "B"
  273. Colorless ->
  274. Encode.string "C"
  275. decodeManaColor : Decoder ManaColor
  276. decodeManaColor =
  277. Decode.string
  278. |> Decode.andThen
  279. (\c ->
  280. case c of
  281. "R" ->
  282. Decode.succeed Red
  283. "U" ->
  284. Decode.succeed Blue
  285. "G" ->
  286. Decode.succeed Green
  287. "W" ->
  288. Decode.succeed White
  289. "B" ->
  290. Decode.succeed Black
  291. "C" ->
  292. Decode.succeed Colorless
  293. _ ->
  294. Decode.fail "Invalid mana color"
  295. )
  296. decodeCardType : Decoder CardType
  297. decodeCardType =
  298. Decode.string
  299. |> Decode.map
  300. (\c ->
  301. if String.contains "Creature" c then
  302. Creature
  303. else if String.contains "Instant" c then
  304. Instant
  305. else if String.contains "Sorcery" c then
  306. Sorcery
  307. else if String.contains "Enchantment" c then
  308. Enchantment
  309. else if String.contains "Artifact" c then
  310. Artifact
  311. else if String.contains "Planeswalker" c then
  312. Planeswalker
  313. else if String.contains "Land" c then
  314. Land
  315. else
  316. Other
  317. )
  318. decodeRarity : Decoder Rarity
  319. decodeRarity =
  320. Decode.string
  321. |> Decode.map
  322. (\r ->
  323. if String.contains "mythic" r then
  324. Mythic
  325. else if String.contains "rare" r then
  326. Rare
  327. else if String.contains "uncommon" r then
  328. Uncommon
  329. else if String.contains "common" r then
  330. Common
  331. else
  332. NoRarity
  333. )