Added - hobbit live draft (and support for drafting from shared pool and hobbit past games evaluator for draft bots)

This commit is contained in:
jakub.salavec
2025-04-23 16:56:45 +02:00
parent 6b8271953e
commit dc40798641
5 changed files with 769 additions and 22 deletions

View File

@@ -0,0 +1,434 @@
{
"name": "Hobbit Booster Draft",
"code": "hobbit_table_draft",
"format": "limited_hobbit",
"max-players": 4,
"starting-collection": [
{
"size": 68,
"card-list": [
"30_1" , "30_2" , "30_3" , "30_4" , "30_5" , "30_6" , "30_7" , "30_8" ,
"30_9" , "30_10", "30_11", "30_12", "30_13", "30_14", "30_15", "30_16",
"30_17", "30_18", "30_19", "30_20", "30_21", "30_22", "30_23", "30_24",
"30_29", "30_30", "30_31", "30_47", "30_48", "30_32", "30_32", "30_32",
"30_33", "30_33", "30_34", "30_34", "30_35", "30_35", "30_35", "30_35",
"30_36", "30_36", "30_37", "30_37", "30_38", "30_38", "30_39", "30_39",
"30_39", "30_40", "30_40", "30_40", "30_41", "30_41", "30_42", "30_42",
"30_42", "30_42", "30_66", "30_49", "30_50", "30_51", "30_52", "30_53",
"30_54", "30_55", "30_56", "30_57"
]
},
{ "size": 1, "card-list": ["30_25", "30_26", "30_27", "30_28"] },
{ "size": 1, "card-list": ["30_43", "30_44", "30_45", "30_46"] }
],
"card-sets": [
{
"set-name": "free-peoples-pack",
"card-set": [
"30_1" , "30_1" , "30_3" , "30_3" , "30_4" , "30_4" , "30_13", "30_13",
"30_14", "30_14", "30_20", "30_20", "30_23", "30_23", "30_24", "30_24",
"30_30", "30_30", "30_31", "30_31", "30_58", "30_59", "30_60", "30_61",
"30_62", "30_63", "30_64", "30_65"
]
},
{
"set-name": "swarm-pack-1",
"card-set": [
"31_30", "31_30", "31_30", "31_30", "31_31", "31_31", "31_31", "31_31",
"31_32", "31_32", "31_32", "31_32", "31_34", "31_34", "31_35", "31_35",
"31_35", "31_35", "31_36", "31_36", "31_49", "31_49", "31_49", "31_51",
"31_51", "31_51", "31_51", "31_52", "31_52", "31_52", "31_53", "31_53",
"31_53", "31_53", "31_54", "31_54", "31_55", "31_55", "31_55", "31_55"
]
},
{
"set-name": "swarm-pack-2",
"card-set": [
"32_27", "32_27", "32_27", "32_27", "32_28", "32_28", "32_28", "32_28",
"32_31", "32_31", "32_31", "32_32", "32_32", "32_32", "32_33", "32_33",
"32_33", "32_34", "32_34", "32_34", "32_36", "32_36", "32_36", "32_38",
"32_38", "32_40", "32_40", "32_40", "32_41", "32_41", "32_41", "32_41",
"32_42", "32_42", "32_42", "32_42", "32_43", "32_43", "32_43", "32_43"
]
},
{
"set-name": "swarm-pack-3",
"card-set": [
"33_38", "33_38", "33_38", "33_38", "33_39", "33_39", "33_39", "33_39",
"33_40", "33_40", "33_40", "33_41", "33_41", "33_41", "33_43", "33_43",
"33_45", "33_45", "33_45", "33_45", "33_46", "33_46", "33_46", "33_47",
"33_47", "33_47", "33_47", "33_48", "33_48", "33_48", "33_49", "33_49",
"33_49", "33_49", "33_50", "33_50", "33_50", "33_51", "33_51", "33_51"
]
},
{
"set-name": "beatdown-pack-1",
"card-set": [
"31_19", "31_19", "31_19", "31_19", "31_20", "31_20", "31_20", "31_21",
"31_21", "31_21", "31_23", "31_23", "31_23", "31_24", "31_24", "31_26",
"31_26", "31_26", "31_27", "31_27", "31_27", "31_27", "31_28", "31_28",
"31_28", "31_56", "31_56", "31_56", "31_58", "31_58", "31_58", "31_59",
"31_59", "31_59", "31_60", "31_60", "31_60", "31_61", "31_61", "31_61"
]
},
{
"set-name": "beatdown-pack-2",
"card-set": [
"32_37", "32_37", "32_37", "32_37", "32_44", "32_44", "32_45", "32_45",
"32_45", "32_45", "32_50", "32_50", "32_51", "32_51", "32_51", "32_51",
"32_52", "32_52", "32_52", "32_52", "32_54", "32_54", "32_54", "32_55",
"32_55", "32_56", "32_56", "32_56", "32_58", "32_58", "32_58", "32_59",
"32_60", "32_61", "32_62", "32_63", "32_64", "32_65", "32_66", "32_67"
]
},
{
"set-name": "beatdown-pack-3",
"card-set": [
"33_24", "33_24", "33_24", "33_25", "33_25", "33_25", "33_26", "33_26",
"33_26", "33_27", "33_27", "33_27", "33_28", "33_28", "33_28", "33_29",
"33_29", "33_29", "33_30", "33_30", "33_30", "33_31", "33_31", "33_31",
"33_32", "33_32", "33_32", "33_34", "33_34", "33_34", "33_35", "33_35",
"33_35", "33_42", "33_42", "33_42", "33_42", "33_44", "33_44", "33_44"
]
},
{
"set-name": "pack-a-bilbo",
"card-set": [
"31_38", "31_38", "31_38", "31_39", "31_39", "31_40", "31_40", "31_41",
"31_41", "31_41", "31_42", "31_42", "31_42", "31_43", "31_43", "31_22",
"31_22", "31_22", "31_57", "31_57", "31_45", "31_45"
]
},
{
"set-name": "pack-b-dain",
"card-set": [
"32_1" , "32_1" , "32_1" , "32_2" , "32_2" , "32_2" , "32_4" , "32_4" ,
"32_5" , "32_5" , "32_6" , "32_6" , "32_8" , "32_8" , "32_8" , "32_26",
"32_26", "32_30", "32_30", "32_30", "32_49", "32_49"
]
},
{
"set-name": "pack-c-esgaroth",
"card-set": [
"32_15", "32_15", "32_15", "32_16", "32_16", "32_17", "32_17", "32_18",
"32_18", "32_19", "32_19", "32_19", "32_20", "32_20", "32_20", "32_25",
"32_25", "32_25", "32_53", "32_53", "32_47", "32_47"
]
},
{
"set-name": "pack-d-mirkwood",
"card-set": [
"31_3" , "31_3" , "31_3" , "31_8" , "31_8" , "31_9" , "31_9" , "31_10",
"31_10", "31_11", "31_11", "31_11", "31_12", "31_12", "31_12", "31_29",
"31_29", "31_37", "31_37", "31_37", "31_46", "31_46"
]
},
{
"set-name": "pack-e-thorin",
"card-set": [
"31_1" , "31_1" , "31_2" , "31_2" , "31_2" , "31_4" , "31_4" , "31_5" ,
"31_5" , "31_5" , "31_6" , "31_6" , "31_7" , "31_7" , "31_7" , "31_33",
"31_33", "31_33", "31_48", "31_48", "31_47", "31_47"
]
},
{
"set-name": "pack-f-thrain",
"card-set": [
"32_3" , "32_3" , "32_3" , "32_7" , "32_7" , "32_9" , "32_9" , "32_9" ,
"32_10", "32_10", "32_11", "32_11", "32_11", "32_12", "32_12", "32_29",
"32_29", "32_35", "32_35", "32_35", "32_48", "32_48"
]
},
{
"set-name": "pack-g-white-council",
"card-set": [
"32_13", "32_13", "32_13", "32_14", "32_14", "32_21", "32_21", "32_22",
"32_22", "32_23", "32_23", "32_23", "32_24", "32_24", "32_24", "32_39",
"32_39", "32_57", "32_57", "32_57", "32_46", "32_46"
]
},
{
"set-name": "pack-h-wizard",
"card-set": [
"31_13", "31_13", "31_13", "31_14", "31_14", "31_15", "31_15", "31_16",
"31_16", "31_17", "31_17", "31_17", "31_18", "31_18", "31_18", "31_25",
"31_25", "31_25", "31_50", "31_50", "31_44", "31_44"
]
},
{
"set-name": "pack-i-balin",
"card-set": [
"33_1" , "33_1" , "33_3" , "33_3" , "33_3" , "33_4" , "33_4" , "33_10",
"33_10", "33_10", "33_11", "33_11", "33_11", "33_12", "33_12", "33_36",
"33_36", "33_36", "33_59", "33_59", "33_56", "33_56"
]
},
{
"set-name": "pack-j-blue-mountains",
"card-set": [
"33_2" , "33_2" , "33_5" , "33_5" , "33_5" , "33_6" , "33_6" , "33_6" ,
"33_7" , "33_7" , "33_8" , "33_8" , "33_9" , "33_9" , "33_9" , "33_37",
"33_37", "33_37", "33_60", "33_60", "33_58", "33_58"
]
},
{
"set-name": "pack-k-rivendell",
"card-set": [
"33_13", "33_13", "33_13", "33_14", "33_14", "33_14", "33_15", "33_15",
"33_16", "33_16", "33_17", "33_17", "33_17", "33_18", "33_18", "33_33",
"33_33", "33_33", "33_61", "33_61", "33_57", "33_57"
]
},
{
"set-name": "pack-l-travellers",
"card-set": [
"33_19", "33_19", "33_20", "33_20", "33_21", "33_21", "33_52", "33_52",
"33_52", "33_53", "33_53", "33_53", "33_54", "33_54", "33_54", "33_22",
"33_22", "33_23", "33_23", "33_23", "33_55", "33_55"
]
},
{
"set-name": "chosen-swarm-pack",
"choose": 1,
"set-set": ["swarm-pack-1", "swarm-pack-2", "swarm-pack-3"]
},
{
"set-name": "chosen-beatdown-pack",
"choose": 1,
"set-set": ["beatdown-pack-1", "beatdown-pack-2", "beatdown-pack-3"]
},
{
"set-name": "chosen-burglar-pack",
"choose": 1,
"set-set": ["pack-a-bilbo", "pack-i-balin", "pack-l-travellers"]
},
{
"set-name": "chosen-arming-pack",
"choose": 1,
"set-set": ["pack-e-thorin", "pack-f-thrain", "pack-j-blue-mountains"]
},
{
"set-name": "chosen-allies-pack",
"choose": 1,
"set-set": ["pack-b-dain", "pack-c-esgaroth", "pack-d-mirkwood"]
},
{
"set-name": "chosen-wises-pack",
"choose": 1,
"set-set": ["pack-g-white-council", "pack-h-wizard", "pack-k-rivendell"]
},
{
"set-name": "chosen-supplementary-packs",
"choose": 2,
"set-set": [
"chosen-burglar-pack", "chosen-arming-pack" , "chosen-allies-pack" ,
"chosen-wises-pack"
]
}
],
"pools": [
{
"pool-name": "draft-pool",
"set-set": [
"chosen-supplementary-packs",
"chosen-swarm-pack" ,
"chosen-beatdown-pack" ,
"free-peoples-pack" ,
"free-peoples-pack"
]
}
],
"boosters": [
{
"rounds": [ 1, 2, 3, 4, 5 ],
"cards" : [ {"count": 9, "set-name": "draft-pool"} ]
}
],
"card-values": [
{"card": "33_7" , "value": 1.0 },
{"card": "33_44", "value": 0.8333787095017696 },
{"card": "32_67", "value": 0.8203103729921045 },
{"card": "30_31", "value": 0.7296487884563028 },
{"card": "32_18", "value": 0.6634903348761231 },
{"card": "31_6" , "value": 0.6602232507487068 },
{"card": "31_38", "value": 0.6471549142390417 },
{"card": "31_5" , "value": 0.6471549142390417 },
{"card": "33_42", "value": 0.5867138578818405 },
{"card": "32_24", "value": 0.5513204465014974 },
{"card": "31_20", "value": 0.5360740539068881 },
{"card": "32_13", "value": 0.5317179417369997 },
{"card": "32_40", "value": 0.5273618295671113 },
{"card": "32_59", "value": 0.5262728015246393 },
{"card": "32_60", "value": 0.5262728015246393 },
{"card": "31_1" , "value": 0.5262728015246393 },
{"card": "31_48", "value": 0.5132044650149742 },
{"card": "33_37", "value": 0.5099373808875578 },
{"card": "33_35", "value": 0.5012251565477811 },
{"card": "33_39", "value": 0.4911516471549142 },
{"card": "32_23", "value": 0.49033487612306015 },
{"card": "32_66", "value": 0.4838007078682276 },
{"card": "33_41", "value": 0.4827116798257555 },
{"card": "31_26", "value": 0.47944459569833925 },
{"card": "32_54", "value": 0.47944459569833925 },
{"card": "32_4" , "value": 0.47399945548597877 },
{"card": "32_36", "value": 0.4674652872311462 },
{"card": "32_50", "value": 0.46419820310372994 },
{"card": "32_43", "value": 0.4486795534985026 },
{"card": "33_21", "value": 0.4478627824666485 },
{"card": "32_45", "value": 0.44377892730737817 },
{"card": "31_24", "value": 0.44132861421181596 },
{"card": "31_22", "value": 0.4402395861693439 },
{"card": "33_45", "value": 0.43887830111625376 },
{"card": "33_38", "value": 0.43642798802069155 },
{"card": "31_13", "value": 0.43152736182956714 },
{"card": "31_19", "value": 0.4290770487340049 },
{"card": "30_3" , "value": 0.42145385243670025 },
{"card": "31_21", "value": 0.418459025319902 },
{"card": "32_61", "value": 0.418459025319902 },
{"card": "31_10", "value": 0.4151919411924857 },
{"card": "31_37", "value": 0.41410291315001363 },
{"card": "33_52", "value": 0.41410291315001363 },
{"card": "33_40", "value": 0.4053906888102369 },
{"card": "31_18", "value": 0.4010345766403485 },
{"card": "31_23", "value": 0.3966784644704601 },
{"card": "31_58", "value": 0.39341138034304385 },
{"card": "30_1" , "value": 0.3920500952899537 },
{"card": "31_61", "value": 0.39123332425809965 },
{"card": "31_28", "value": 0.38905526817315544 },
{"card": "32_53", "value": 0.38578818404573917 },
{"card": "32_56", "value": 0.3825210999183229 },
{"card": "32_51", "value": 0.37598693166349034 },
{"card": "32_39", "value": 0.37598693166349034 },
{"card": "32_52", "value": 0.37271984753607407 },
{"card": "32_9" , "value": 0.37054179145112986 },
{"card": "31_9" , "value": 0.3694527634086578 },
{"card": "30_23", "value": 0.36836373536618566 },
{"card": "32_42", "value": 0.3645521372175334 },
{"card": "32_7" , "value": 0.35638442689899263 },
{"card": "32_58", "value": 0.35311734277157636 },
{"card": "32_41", "value": 0.35066702967601415 },
{"card": "31_29", "value": 0.3465831745167438 },
{"card": "33_20", "value": 0.3465831745167438 },
{"card": "32_32", "value": 0.3422270623468554 },
{"card": "31_8" , "value": 0.336781922134495 },
{"card": "32_55", "value": 0.32861421181595424 },
{"card": "33_23", "value": 0.3269806697522461 },
{"card": "30_13", "value": 0.32534712768853796 },
{"card": "32_63", "value": 0.3237135856248298 },
{"card": "31_12", "value": 0.313912333242581 },
{"card": "31_30", "value": 0.30737816498774845 },
{"card": "33_19", "value": 0.3008439967329159 },
{"card": "32_30", "value": 0.2986659406479717 },
{"card": "33_12", "value": 0.2943098284780833 },
{"card": "31_57", "value": 0.2943098284780833 },
{"card": "32_31", "value": 0.28886468826572287 },
{"card": "31_36", "value": 0.28777566022325074 },
{"card": "32_37", "value": 0.2861421181595426 },
{"card": "33_43", "value": 0.28450857609583446 },
{"card": "33_60", "value": 0.2779744078410019 },
{"card": "31_56", "value": 0.2768853797985298 },
{"card": "33_17", "value": 0.27470732371358564 },
{"card": "31_31", "value": 0.2730737816498775 },
{"card": "32_64", "value": 0.27144023958616936 },
{"card": "31_59", "value": 0.26708412741628096 },
{"card": "33_59", "value": 0.26490607133133676 },
{"card": "32_5" , "value": 0.26490607133133676 },
{"card": "33_22", "value": 0.26490607133133676 },
{"card": "32_29", "value": 0.26490607133133676 },
{"card": "33_54", "value": 0.2638170432888647 },
{"card": "32_34", "value": 0.2627280152463926 },
{"card": "33_53", "value": 0.25728287503403213 },
{"card": "33_47", "value": 0.25183773482167165 },
{"card": "32_65", "value": 0.25183773482167165 },
{"card": "31_41", "value": 0.2507487067791996 },
{"card": "32_38", "value": 0.2502041927579635 },
{"card": "30_30", "value": 0.2488429077048734 },
{"card": "32_62", "value": 0.24857065069425538 },
{"card": "31_14", "value": 0.2453035665668391 },
{"card": "32_44", "value": 0.2453035665668391 },
{"card": "31_17", "value": 0.244214538524367 },
{"card": "32_35", "value": 0.24203648243942283 },
{"card": "32_2" , "value": 0.23768037026953445 },
{"card": "33_61", "value": 0.23550231418459025 },
{"card": "31_60", "value": 0.23441328614211815 },
{"card": "32_28", "value": 0.23060168799346584 },
{"card": "31_35", "value": 0.22733460386604956 },
{"card": "32_11", "value": 0.2267900898448135 },
{"card": "32_17", "value": 0.22570106180234142 },
{"card": "32_16", "value": 0.22570106180234142 },
{"card": "33_36", "value": 0.22461203375986932 },
{"card": "33_32", "value": 0.22134494963245305 },
{"card": "33_50", "value": 0.21807786550503674 },
{"card": "31_27", "value": 0.21671658045194664 },
{"card": "31_16", "value": 0.20936564116526002 },
{"card": "33_10", "value": 0.20936564116526002 },
{"card": "32_15", "value": 0.20936564116526002 },
{"card": "32_20", "value": 0.20936564116526002 },
{"card": "33_34", "value": 0.20609855703784372 },
{"card": "31_43", "value": 0.20609855703784372 },
{"card": "30_4" , "value": 0.20528178600598965 },
{"card": "33_27", "value": 0.20500952899537164 },
{"card": "31_25", "value": 0.20283147291042744 },
{"card": "33_3" , "value": 0.20065341682548327 },
{"card": "33_49", "value": 0.20038115981486523 },
{"card": "31_52", "value": 0.19738633269806696 },
{"card": "31_49", "value": 0.19303022052817861 },
{"card": "32_26", "value": 0.1897631364007623 },
{"card": "33_4" , "value": 0.18649605227334604 },
{"card": "32_10", "value": 0.18322896814592976 },
{"card": "32_57", "value": 0.18105091206098556 },
{"card": "33_8" , "value": 0.17996188401851348 },
{"card": "31_42", "value": 0.17887285597604138 },
{"card": "32_22", "value": 0.1734277157636809 },
{"card": "31_53", "value": 0.1734277157636809 },
{"card": "32_1" , "value": 0.17016063163626463 },
{"card": "32_25", "value": 0.17016063163626463 },
{"card": "33_9" , "value": 0.16798257555132046 },
{"card": "32_19", "value": 0.16798257555132046 },
{"card": "32_6" , "value": 0.16689354750884836 },
{"card": "33_48", "value": 0.16471549142390415 },
{"card": "33_5" , "value": 0.16362646338143208 },
{"card": "33_46", "value": 0.16362646338143208 },
{"card": "31_15", "value": 0.16362646338143208 },
{"card": "32_8" , "value": 0.16362646338143208 },
{"card": "31_2" , "value": 0.16362646338143208 },
{"card": "33_6" , "value": 0.16144840729648788 },
{"card": "31_32", "value": 0.16117615028586987 },
{"card": "33_29", "value": 0.1603593792540158 },
{"card": "31_51", "value": 0.15790906615845357 },
{"card": "31_3" , "value": 0.1570922951265995 },
{"card": "33_31", "value": 0.15491423904165533 },
{"card": "31_34", "value": 0.1521916689354751 },
{"card": "32_27", "value": 0.15055812687176695 },
{"card": "32_14", "value": 0.15055812687176695 },
{"card": "32_21", "value": 0.14729104274435068 },
{"card": "31_4" , "value": 0.1407568744895181 },
{"card": "31_7" , "value": 0.13531173427715765 },
{"card": "33_2" , "value": 0.13422270623468555 },
{"card": "33_30", "value": 0.13095562210726927 },
{"card": "31_33", "value": 0.1265995099373809 },
{"card": "33_14", "value": 0.1265995099373809 },
{"card": "31_55", "value": 0.12605499591614483 },
{"card": "33_33", "value": 0.1244214538524367 },
{"card": "30_14", "value": 0.12060985570378437 },
{"card": "33_25", "value": 0.12006534168254833 },
{"card": "32_33", "value": 0.11570922951265995 },
{"card": "31_39", "value": 0.11462020147018785 },
{"card": "31_40", "value": 0.11135311734277158 },
{"card": "31_54", "value": 0.11135311734277158 },
{"card": "31_11", "value": 0.10917506125782739 },
{"card": "33_28", "value": 0.10917506125782739 },
{"card": "31_50", "value": 0.1080860332153553 },
{"card": "33_51", "value": 0.10590797713041111 },
{"card": "33_13", "value": 0.10264089300299482 },
{"card": "33_18", "value": 0.10155186496052274 },
{"card": "33_26", "value": 0.10046283691805064 },
{"card": "33_15", "value": 0.09501769670569017 },
{"card": "33_24", "value": 0.0917506125782739 },
{"card": "33_1" , "value": 0.08194936019602504 },
{"card": "32_12", "value": 0.08194936019602504 },
{"card": "33_11", "value": 0.08086033215355296 },
{"card": "30_24", "value": 0.015790906615845358},
{"card": "30_20", "value": 0.010890280424720936}
]
}

View File

@@ -18,6 +18,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TableDraftDefinitionBuilder {
private static final int HIGH_ENOUGH_PRIME_NUMBER = 8963;
@@ -45,7 +47,8 @@ public class TableDraftDefinitionBuilder {
int maxPlayers = ((Number) definition.get("max-players")).intValue();
StartingCollectionProducer startingCollectionProducer = buildStartingCollectionProducer((JSONArray) definition.get("starting-collection"));
BoosterProducer boosterProducer = buildBoosterProducer((JSONArray) definition.get("card-sets"), (JSONArray) definition.get("boosters"));
// test if booster producer can be made from this json
BoosterProducer testBoosterProducer = buildBoosterProducer((JSONArray) definition.get("card-sets"), (JSONArray) definition.get("boosters"), (JSONArray) definition.get("pools"));
Map<String, Double> cardValues = buildCardValues((JSONArray) definition.get("card-values"));
@@ -53,6 +56,7 @@ public class TableDraftDefinitionBuilder {
return new TableDraftDefinition() {
@Override
public TableDraft getTableDraft(CollectionsManager collectionsManager, CollectionType collectionType, DraftTimer draftTimer) {
BoosterProducer boosterProducer = buildBoosterProducer((JSONArray) definition.get("card-sets"), (JSONArray) definition.get("boosters"), (JSONArray) definition.get("pools"));
return new TableDraftClassic(collectionsManager, collectionType, startingCollectionProducer, boosterProducer, maxPlayers, boosterProducer.getMaxRound(), draftTimer, cardValues);
}
@@ -90,13 +94,43 @@ public class TableDraftDefinitionBuilder {
return tbr;
}
private static BoosterProducer buildBoosterProducer(JSONArray cardSets, JSONArray boosters) {
private static BoosterProducer buildBoosterProducer(JSONArray cardSets, JSONArray boosters, JSONArray pools) {
Map<String, List<String>> cardPools = new HashMap<>();
Map<String, List<String>> setPools = new HashMap<>();
Map<String, List<String>> draftPools = new HashMap<>();
for (JSONObject cardSet : (Iterable<JSONObject>) cardSets) {
String name = (String) cardSet.get("set-name");
List<String> cards = (List<String>) cardSet.get("card-set");
cardPools.put(name, cards);
if (cardSet.containsKey("card-set")) {
List<String> cards = (List<String>) cardSet.get("card-set");
cardPools.put(name, cards);
} else if (cardSet.containsKey("set-set") && cardSet.containsKey("choose")) {
List<String> setNames = (List<String>) cardSet.get("set-set");
Collections.shuffle(setNames);
int howMany = ((Number) cardSet.get("choose")).intValue();
List<String> chosenSetNames = new ArrayList<>();
if (howMany <= setNames.size()) {
for (int i = 0; i < howMany; i++) {
chosenSetNames.add(setNames.get(i));
}
setPools.put(name, chosenSetNames);
} else {
throw new IllegalArgumentException("Invalid JSON content");
}
} else {
throw new IllegalArgumentException("Invalid JSON content");
}
}
if (pools != null) {
for (JSONObject cardSet : (Iterable<JSONObject>) pools) {
String name = (String) cardSet.get("pool-name");
draftPools.put(name, new ArrayList<>());
List<String> sets = (List<String>) cardSet.get("set-set");
for (String set : sets) {
addSetToPool(cardPools, setPools, draftPools, name, set);
}
}
}
Map<Integer, List<Pair<String, Integer>>> boosterPlan = new HashMap<>();
@@ -132,10 +166,25 @@ public class TableDraftDefinitionBuilder {
}
return new BoosterProducer() {
Lock poolLock = new ReentrantLock();
@Override
public Booster getBooster(int round) {
List<String> pickedCards = new ArrayList<>();
boosterPlan.get(round).forEach(pair -> pickedCards.addAll(pickRandom(cardPools.get(pair.getLeft()), pair.getRight())));
boosterPlan.get(round).forEach(pair -> {
if (draftPools.get(pair.getLeft()) == null) {
// opening boosters
pickedCards.addAll(pickRandom(cardPools.get(pair.getLeft()), pair.getRight(), false));
} else {
// opening cards from draft pool
poolLock.lock();
try {
pickedCards.addAll(pickRandom(draftPools.get(pair.getLeft()), pair.getRight(), true));
} finally {
poolLock.unlock();
}
}
});
return new Booster(pickedCards);
}
@@ -144,7 +193,7 @@ public class TableDraftDefinitionBuilder {
return boosterPlan.keySet().stream().max(Comparator.naturalOrder()).orElse(-1);
}
private List<String> pickRandom(List<String> source, int count) {
private List<String> pickRandom(List<String> source, int count, boolean remove) {
if (source.isEmpty() || count <= 0) return Collections.emptyList();
int size = source.size();
@@ -154,10 +203,18 @@ public class TableDraftDefinitionBuilder {
List<String> result = new ArrayList<>();
Random rand = new Random();
while (selectedIndexes.size() < count) {
int randomIndex = rand.nextInt(size);
if (selectedIndexes.add(randomIndex)) {
result.add(source.get(randomIndex));
while (result.size() < count) {
int chosenIndex;
if (size > 0) {
chosenIndex = rand.nextInt(size);
} else {
chosenIndex = 0;
}
if (remove) {
result.add(source.remove(chosenIndex));
size--;
} else if (selectedIndexes.add(chosenIndex)) {
result.add(source.get(chosenIndex));
}
}
return result;
@@ -165,6 +222,18 @@ public class TableDraftDefinitionBuilder {
};
}
private static void addSetToPool(Map<String, List<String>> cardPools, Map<String, List<String>> setPools, Map<String, List<String>> draftPools, String poolName, String setToAdd) {
if (cardPools.containsKey(setToAdd)) {
draftPools.get(poolName).addAll(cardPools.get(setToAdd));
} else if (setPools.containsKey(setToAdd)) {
for (String setName : setPools.get(setToAdd)) {
addSetToPool(cardPools, setPools, draftPools, poolName, setName);
}
} else {
throw new IllegalArgumentException("Invalid JSON content");
}
}
private static StartingCollectionProducer buildStartingCollectionProducer(JSONArray jsonArray) {
return new StartingCollectionProducer() {

View File

@@ -29,7 +29,7 @@ public abstract class AbstractDraftCardEvaluator {
// This is what bots pick according to
public final Map<String, Double> getValuesMap(Map<String, Integer> winningMap, Map<String, Integer> losingMap, int gamesAnalyzed) {
public Map<String, Double> getValuesMap(Map<String, Integer> winningMap, Map<String, Integer> losingMap, int gamesAnalyzed) {
// Merge win and lose maps (they are kept separate for potential wr info)
Map<String, Integer> mergedMap = mergeMaps(winningMap, losingMap);
@@ -54,7 +54,7 @@ public abstract class AbstractDraftCardEvaluator {
return shiftedMap;
}
private static Map<String, Integer> mergeMaps(Map<String, Integer> map1, Map<String, Integer> map2) {
static Map<String, Integer> mergeMaps(Map<String, Integer> map1, Map<String, Integer> map2) {
Map<String, Integer> result = new HashMap<>(map1);
map2.forEach((key, value) -> result.merge(key, value, Integer::sum));
@@ -62,7 +62,7 @@ public abstract class AbstractDraftCardEvaluator {
return result;
}
private Map<String, Double> normalizeCountByDraftPackChance(Map<String, Integer> map, int gamesAnalyzed) {
Map<String, Double> normalizeCountByDraftPackChance(Map<String, Integer> map, int gamesAnalyzed) {
Map<String, Double> normalizedMap = new HashMap<>();
map.forEach((key, value) -> {
@@ -155,7 +155,7 @@ public abstract class AbstractDraftCardEvaluator {
return fpInflatedMap;
}
private Map<String, Double> shift(Map<String, Double> map) {
Map<String, Double> shift(Map<String, Double> map) {
double min = map.values().stream().min(Double::compareTo).orElse(0.0);
double positiveShift;

View File

@@ -1,6 +1,7 @@
package com.gempukku.lotro.draft3.evaluator;
import com.gempukku.lotro.common.AppConfig;
import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import com.gempukku.lotro.game.ReplayMetadata;
import com.gempukku.util.JsonUtils;
@@ -46,6 +47,7 @@ public class Evaluator {
// Define what we care for
assignmentList.add(new Assignment("Limited - FOTR", "draft"));
assignmentList.add(new Assignment("Limited - TTT", "draft"));
assignmentList.add(new Assignment("Limited - Hobbit", "draft"));
System.out.println("Game history reading started at " + new SimpleDateFormat("HH.mm.ss").format(new Date()));
// Load decks from the past
@@ -60,29 +62,39 @@ public class Evaluator {
FotrDraftCardEvaluator fotrDraftCardEvaluator = new FotrDraftCardEvaluator(library);
TttDraftCardEvaluator tttDraftCardEvaluator = new TttDraftCardEvaluator(library);
HobbitDraftCardEvaluator hobbitDraftCardEvaluator = new HobbitDraftCardEvaluator(library);
// Send the loaded data to card evaluators -> they convert card counts to values from 0.0 to 1.0
// This is what bots pick according to in FotR drafts
var fotrValues = fotrDraftCardEvaluator.getValuesMap(assignmentList.getFirst().winningMap, assignmentList.get(0).losingMap, assignmentList.getFirst().gamesAnalyzed);
// Those are values to base fotr draft pools cuts on
var fotrPlayRates = fotrDraftCardEvaluator.getPlayRateMap(assignmentList.getFirst().deckCount, assignmentList.getFirst().gamesAnalyzed);
// This is what bots pick according to in TTT drafts
var tttValues = tttDraftCardEvaluator.getValuesMap(assignmentList.get(1).winningMap, assignmentList.get(1).losingMap, assignmentList.get(1).gamesAnalyzed);
// Those are values to base TTT draft pools cuts on
var tttPlayRates = tttDraftCardEvaluator.getPlayRateMap(assignmentList.get(1).deckCount, assignmentList.get(1).gamesAnalyzed);
var hobbitValues = hobbitDraftCardEvaluator.getValuesMap(assignmentList.get(2).winningMap, assignmentList.get(2).losingMap, assignmentList.get(2).gamesAnalyzed);
// Data from solo drafts do not contain rare one ring cards, don't forget to add some value manually after
System.out.println("===FOTR VALUES");
System.out.println("Games analyzes: " + assignmentList.get(0).gamesAnalyzed);
printJsonStyle(fotrValues); // For json file
printMapStyle(fotrValues); // For cached maps in evaluators (not being used right now)
System.out.println("===FOTR PLAY RATES");
// Regular draft - all rares included, U/C with play rate lower than 0.01 are cut
// Power draft - R with play rate lower than 0.02 and U/C with play rate lower than 0.05 are cut
printMapStyle(fotrPlayRates);
printHumanReadable(fotrPlayRates, library);
System.out.println("===TTT VALUES");
System.out.println("Games analyzes: " + assignmentList.get(1).gamesAnalyzed);
printJsonStyle(tttValues);
printMapStyle(tttValues);
System.out.println("===TTT PLAY RATES");
printMapStyle(tttPlayRates);
printHumanReadable(tttPlayRates, library);
System.out.println("===HOBBIT VALUES");
System.out.println("Games analyzes: " + assignmentList.get(2).gamesAnalyzed);
printJsonStyle(hobbitValues);
printHumanReadable(hobbitValues, library);
}
private static void analyzeSummary(Path jsonFile, List<Assignment> assignmentList) {
@@ -102,21 +114,29 @@ public class Evaluator {
assignment.gamesAnalyzed++;
assignment.deckCount.merge(winningDeck.RingBearer.replace("*", "").replace("T", ""), 1, Integer::sum);
assignment.deckCount.merge(winningDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
if (winningDeck.Ring != null) {
assignment.deckCount.merge(winningDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
}
Set<String> winningCardSet = new HashSet<>(winningDeck.DrawDeck);
winningCardSet.forEach(card -> assignment.deckCount.merge(card.replace("*", "").replace("T", ""), 1, Integer::sum));
assignment.deckCount.merge(losingDeck.RingBearer.replace("*", "").replace("T", ""), 1, Integer::sum);
assignment.deckCount.merge(losingDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
if (losingDeck.Ring != null) {
assignment.deckCount.merge(losingDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
}
Set<String> losingCardSet = new HashSet<>(losingDeck.DrawDeck);
losingCardSet.forEach(card -> assignment.deckCount.merge(card.replace("*", "").replace("T", ""), 1, Integer::sum));
assignment.winningMap.merge(winningDeck.RingBearer.replace("*", "").replace("T", ""), 1, Integer::sum);
assignment.winningMap.merge(winningDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
if (winningDeck.Ring != null) {
assignment.winningMap.merge(winningDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
}
winningDeck.DrawDeck.forEach(card -> assignment.winningMap.merge(card.replace("*", "").replace("T", ""), 1, Integer::sum));
assignment.losingMap.merge(losingDeck.RingBearer.replace("*", "").replace("T", ""), 1, Integer::sum);
assignment.losingMap.merge(losingDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
if (losingDeck.Ring != null) {
assignment.losingMap.merge(losingDeck.Ring.replace("*", "").replace("T", ""), 1, Integer::sum);
}
losingDeck.DrawDeck.forEach(card -> assignment.losingMap.merge(card.replace("*", "").replace("T", ""), 1, Integer::sum));
}
}
@@ -153,4 +173,20 @@ public class Evaluator {
System.out.println(s);
});
}
private static void printHumanReadable(Map<String, Double> toPrint, LotroCardBlueprintLibrary library) {
// 33_44 Lavastone Giant: 0.8333787095017696
toPrint.entrySet().stream()
.sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue()))
.forEach(stringDoubleEntry -> {
try {
String s = stringDoubleEntry.getKey() + " "
+ library.getLotroCardBlueprint(stringDoubleEntry.getKey()).getFullName() + ": " +
stringDoubleEntry.getValue();
System.out.println(s);
} catch (CardNotFoundException ignore) {
}
});
}
}

View File

@@ -0,0 +1,208 @@
package com.gempukku.lotro.draft3.evaluator;
import com.gempukku.lotro.game.LotroCardBlueprintLibrary;
import org.apache.commons.collections4.map.UnmodifiableMap;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HobbitDraftCardEvaluator extends AbstractDraftCardEvaluator {
private static final Map<String, Double> AVERAGE_STARTING_COUNT_MAP = new HashMap<>();
private static final List<String> freePeoplesPack = List.of(
"30_1", "30_1", "30_3", "30_3", "30_4", "30_4", "30_13", "30_13",
"30_14", "30_14", "30_20", "30_20", "30_23", "30_23", "30_24", "30_24",
"30_30", "30_30", "30_31", "30_31", "30_58", "30_59", "30_60", "30_61",
"30_62", "30_63", "30_64", "30_65");
private static final List<String> swarmPack1 = List.of(
"31_30", "31_30", "31_30", "31_30", "31_31", "31_31", "31_31", "31_31",
"31_32", "31_32", "31_32", "31_32", "31_34", "31_34", "31_35", "31_35",
"31_35", "31_35", "31_36", "31_36", "31_49", "31_49", "31_49", "31_51",
"31_51", "31_51", "31_51", "31_52", "31_52", "31_52", "31_53", "31_53",
"31_53", "31_53", "31_54", "31_54", "31_55", "31_55", "31_55", "31_55");
private static final List<String> swarmPack2 = List.of(
"32_27", "32_27", "32_27", "32_27", "32_28", "32_28", "32_28", "32_28",
"32_31", "32_31", "32_31", "32_32", "32_32", "32_32", "32_33", "32_33",
"32_33", "32_34", "32_34", "32_34", "32_36", "32_36", "32_36", "32_38",
"32_38", "32_40", "32_40", "32_40", "32_41", "32_41", "32_41", "32_41",
"32_42", "32_42", "32_42", "32_42", "32_43", "32_43", "32_43", "32_43");
private static final List<String> swarmPack3 = List.of(
"33_38", "33_38", "33_38", "33_38", "33_39", "33_39", "33_39", "33_39",
"33_40", "33_40", "33_40", "33_41", "33_41", "33_41", "33_43", "33_43",
"33_45", "33_45", "33_45", "33_45", "33_46", "33_46", "33_46", "33_47",
"33_47", "33_47", "33_47", "33_48", "33_48", "33_48", "33_49", "33_49",
"33_49", "33_49", "33_50", "33_50", "33_50", "33_51", "33_51", "33_51");
private static final List<String> beatDownPack1 = List.of(
"31_19", "31_19", "31_19", "31_19", "31_20", "31_20", "31_20", "31_21",
"31_21", "31_21", "31_23", "31_23", "31_23", "31_24", "31_24", "31_26",
"31_26", "31_26", "31_27", "31_27", "31_27", "31_27", "31_28", "31_28",
"31_28", "31_56", "31_56", "31_56", "31_58", "31_58", "31_58", "31_59",
"31_59", "31_59", "31_60", "31_60", "31_60", "31_61", "31_61", "31_61");
private static final List<String> beatDownPack2 = List.of(
"32_37", "32_37", "32_37", "32_37", "32_44", "32_44", "32_45", "32_45",
"32_45", "32_45", "32_50", "32_50", "32_51", "32_51", "32_51", "32_51",
"32_52", "32_52", "32_52", "32_52", "32_54", "32_54", "32_54", "32_55",
"32_55", "32_56", "32_56", "32_56", "32_58", "32_58", "32_58", "32_59",
"32_60", "32_61", "32_62", "32_63", "32_64", "32_65", "32_66", "32_67");
private static final List<String> beatDownPack3 = List.of(
"33_24", "33_24", "33_24", "33_25", "33_25", "33_25", "33_26", "33_26",
"33_26", "33_27", "33_27", "33_27", "33_28", "33_28", "33_28", "33_29",
"33_29", "33_29", "33_30", "33_30", "33_30", "33_31", "33_31", "33_31",
"33_32", "33_32", "33_32", "33_34", "33_34", "33_34", "33_35", "33_35",
"33_35", "33_42", "33_42", "33_42", "33_42", "33_44", "33_44", "33_44");
private static final List<String> packA = List.of(
"31_38", "31_38", "31_38", "31_39", "31_39", "31_40", "31_40", "31_41",
"31_41", "31_41", "31_42", "31_42", "31_42", "31_43", "31_43", "31_22",
"31_22", "31_22", "31_57", "31_57", "31_45", "31_45");
private static final List<String> packB = List.of(
"32_1", "32_1", "32_1", "32_2", "32_2", "32_2", "32_4", "32_4",
"32_5", "32_5", "32_6", "32_6", "32_8", "32_8", "32_8", "32_26",
"32_26", "32_30", "32_30", "32_30", "32_49", "32_49");
private static final List<String> packC = List.of(
"32_15", "32_15", "32_15", "32_16", "32_16", "32_17", "32_17", "32_18",
"32_18", "32_19", "32_19", "32_19", "32_20", "32_20", "32_20", "32_25",
"32_25", "32_25", "32_53", "32_53", "32_47", "32_47");
private static final List<String> packD = List.of(
"31_3", "31_3", "31_3", "31_8", "31_8", "31_9", "31_9", "31_10",
"31_10", "31_11", "31_11", "31_11", "31_12", "31_12", "31_12", "31_29",
"31_29", "31_37", "31_37", "31_37", "31_46", "31_46");
private static final List<String> packE = List.of(
"31_1", "31_1", "31_2", "31_2", "31_2", "31_4", "31_4", "31_5",
"31_5", "31_5", "31_6", "31_6", "31_7", "31_7", "31_7", "31_33",
"31_33", "31_33", "31_48", "31_48", "31_47", "31_47");
private static final List<String> packF = List.of(
"32_3", "32_3", "32_3", "32_7", "32_7", "32_9", "32_9", "32_9",
"32_10", "32_10", "32_11", "32_11", "32_11", "32_12", "32_12", "32_29",
"32_29", "32_35", "32_35", "32_35", "32_48", "32_48");
private static final List<String> packG = List.of(
"32_13", "32_13", "32_13", "32_14", "32_14", "32_21", "32_21", "32_22",
"32_22", "32_23", "32_23", "32_23", "32_24", "32_24", "32_24", "32_39",
"32_39", "32_57", "32_57", "32_57", "32_46", "32_46");
private static final List<String> packH = List.of(
"31_13", "31_13", "31_13", "31_14", "31_14", "31_15", "31_15", "31_16",
"31_16", "31_17", "31_17", "31_17", "31_18", "31_18", "31_18", "31_25",
"31_25", "31_25", "31_50", "31_50", "31_44", "31_44");
private static final List<String> packI = List.of(
"33_1", "33_1", "33_3", "33_3", "33_3", "33_4", "33_4", "33_10",
"33_10", "33_10", "33_11", "33_11", "33_11", "33_12", "33_12", "33_36",
"33_36", "33_36", "33_59", "33_59", "33_56", "33_56");
private static final List<String> packJ = List.of(
"33_2", "33_2", "33_5", "33_5", "33_5", "33_6", "33_6", "33_6",
"33_7", "33_7", "33_8", "33_8", "33_9", "33_9", "33_9", "33_37",
"33_37", "33_37", "33_60", "33_60", "33_58", "33_58");
private static final List<String> packK = List.of(
"33_13", "33_13", "33_13", "33_14", "33_14", "33_14", "33_15", "33_15",
"33_16", "33_16", "33_17", "33_17", "33_17", "33_18", "33_18", "33_33",
"33_33", "33_33", "33_61", "33_61", "33_57", "33_57");
private static final List<String> packL = List.of(
"33_19", "33_19", "33_20", "33_20", "33_21", "33_21", "33_52", "33_52",
"33_52", "33_53", "33_53", "33_53", "33_54", "33_54", "33_54", "33_22",
"33_22", "33_23", "33_23", "33_23", "33_55", "33_55");
private static final List<String> shadowPacks = new ArrayList<>();
private static final List<String> supplementaryPacks = new ArrayList<>();
private static final List<String> everything = new ArrayList<>();
static {
List<String> startingDeck = List.of(
"30_1", "30_2", "30_3", "30_4", "30_5", "30_6", "30_7", "30_8",
"30_9", "30_10", "30_11", "30_12", "30_13", "30_14", "30_15", "30_16",
"30_17", "30_18", "30_19", "30_20", "30_21", "30_22", "30_23", "30_24",
"30_29", "30_30", "30_31", "30_47", "30_48", "30_32", "30_32", "30_32",
"30_33", "30_33", "30_34", "30_34", "30_35", "30_35", "30_35", "30_35",
"30_36", "30_36", "30_37", "30_37", "30_38", "30_38", "30_39", "30_39",
"30_39", "30_40", "30_40", "30_40", "30_41", "30_41", "30_42", "30_42",
"30_42", "30_42", "30_66", "30_49", "30_50", "30_51", "30_52", "30_53",
"30_54", "30_55", "30_56", "30_57");
for (String card : startingDeck) {
AVERAGE_STARTING_COUNT_MAP.merge(card, 1.0, Double::sum);
}
List.of("30_25", "30_26", "30_27", "30_28").forEach(gandalf -> AVERAGE_STARTING_COUNT_MAP.put(gandalf, 0.25));
List.of("30_43", "30_44", "30_45", "30_46").forEach(bilbo -> AVERAGE_STARTING_COUNT_MAP.put(bilbo, 0.25));
shadowPacks.addAll(swarmPack1);
shadowPacks.addAll(swarmPack2);
shadowPacks.addAll(swarmPack3);
shadowPacks.addAll(beatDownPack1);
shadowPacks.addAll(beatDownPack2);
shadowPacks.addAll(beatDownPack3);
supplementaryPacks.addAll(packA);
supplementaryPacks.addAll(packB);
supplementaryPacks.addAll(packC);
supplementaryPacks.addAll(packD);
supplementaryPacks.addAll(packE);
supplementaryPacks.addAll(packF);
supplementaryPacks.addAll(packG);
supplementaryPacks.addAll(packH);
supplementaryPacks.addAll(packI);
supplementaryPacks.addAll(packJ);
supplementaryPacks.addAll(packK);
supplementaryPacks.addAll(packL);
everything.addAll(freePeoplesPack);
everything.addAll(freePeoplesPack);
everything.addAll(freePeoplesPack);
everything.addAll(freePeoplesPack);
everything.addAll(shadowPacks);
everything.addAll(shadowPacks);
everything.addAll(supplementaryPacks);
}
public HobbitDraftCardEvaluator(LotroCardBlueprintLibrary library) {
super(library);
}
@Override
public Map<String, Double> getValuesMap(Map<String, Integer> winningMap, Map<String, Integer> losingMap, int gamesAnalyzed) {
// Merge win and lose maps (they are kept separate for potential wr info)
Map<String, Integer> mergedMap = mergeMaps(winningMap, losingMap);
// Lower count of cards from Draft Packs
Map<String, Double> startingCollectionNormalizedCardCountMap = normalizeCountByDraftPackChance(mergedMap, gamesAnalyzed);
// Normalize for uneven set size
Map<String, Double> packNormalizedCardCountMap = normalizeCountByPackType(startingCollectionNormalizedCardCountMap);
// Normalize count based on rarities
Map<String, Double> normalizedCardCountMap = normalizeCountByRarity(packNormalizedCardCountMap);
// Make sure all values are positive and in 0-1 range
Map<String, Double> shiftedMap = shift(normalizedCardCountMap);
return shiftedMap;
}
private Map<String, Double> normalizeCountByRarity(Map<String, Double> map) {
Map<String, Double> normalizedMap = new HashMap<>();
map.forEach((key, value) -> normalizedMap.put(key, value / everything.stream().filter(s -> s.equals(key)).count()));
return normalizedMap;
}
private Map<String, Double> normalizeCountByPackType(Map<String, Double> map) {
Map<String, Double> normalizedMap = new HashMap<>();
map.forEach((key, value) -> {
if (shadowPacks.contains(key)) {
normalizedMap.put(key, value * 3.0); // appear in 1/3 of drafts
} else if (freePeoplesPack.contains(key)){
normalizedMap.put(key, value); // appear in all drafts
} else if (supplementaryPacks.contains(key)) {
normalizedMap.put(key, value * 3.0); // appear in 1/3 of drafts (solo draft is using 4 packs out of 12)
}
});
return normalizedMap;
}
@Override
public Map<String, Double> getAverageStartingCardMap() {
return UnmodifiableMap.unmodifiableMap(AVERAGE_STARTING_COUNT_MAP);
}
}