| 1 |
import { random } from "jga-algorithms-random"; |
| 2 |
import { DifficultyEnum, Encounter, Hero, ModularSet, ResultEnum, Scenario } from "../@models"; |
| 3 |
import { createSelectElement, createTitle } from "../helper"; |
| 4 |
import { EncountersService, HeroesService } from "../services"; |
| 5 |
|
| 6 |
export const loadProgressionEncounters = ( |
| 7 |
body: HTMLBodyElement, |
| 8 |
encounters: Encounter[], |
| 9 |
heroes: Hero[], |
| 10 |
scenarios: Scenario[], |
| 11 |
modules: ModularSet[], |
| 12 |
): void => { |
| 13 |
// order scenarios by play count |
| 14 |
const gamesScenarios = encounters |
| 15 |
.map((encounter) => encounter.scenario.name) |
| 16 |
// .map((scenario) => scenario) |
| 17 |
.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map()); |
| 18 |
const unorderedScenarios = scenarios.reduce((acc, e) => { |
| 19 |
acc.set(e, (acc.get(e) || 0) + (gamesScenarios.get(e.name) || 0)); |
| 20 |
|
| 21 |
return acc; |
| 22 |
}, new Map()); |
| 23 |
|
| 24 |
const orderedScenarios = new Map([...unorderedScenarios.entries()].sort((a, b) => a[1] - b[1])); |
| 25 |
|
| 26 |
// eslint-disable-next-line compat/compat |
| 27 |
// console.table(orderedScenarios); |
| 28 |
|
| 29 |
const scenariosWinEncounters = encounters |
| 30 |
.filter((encounter) => encounter.result === ResultEnum.WON) |
| 31 |
.reduce((acc, e) => { |
| 32 |
if (!acc.has(e.scenario.name)) { |
| 33 |
acc.set(e.scenario.name, []); |
| 34 |
} |
| 35 |
|
| 36 |
const newHeroes = acc.get(e.scenario.name); |
| 37 |
[e] |
| 38 |
.flatMap((encounter) => encounter.heroes) |
| 39 |
.filter((hero) => null != hero) |
| 40 |
.map((hero) => hero.name) |
| 41 |
.filter((hero, i, array) => array.indexOf(hero) === i) |
| 42 |
.forEach((hero) => newHeroes.push(hero)); |
| 43 |
|
| 44 |
acc.set(e.scenario.name, newHeroes.sort()); |
| 45 |
|
| 46 |
return acc; |
| 47 |
}, new Map()); |
| 48 |
|
| 49 |
const progressionsEncounters: Map<Scenario, Partial<Encounter>[]> = new Map(); |
| 50 |
Array.from(orderedScenarios.keys()).forEach((scenario) => { |
| 51 |
heroes.forEach((hero) => { |
| 52 |
if ( |
| 53 |
!scenariosWinEncounters.has(scenario.name) || |
| 54 |
!scenariosWinEncounters.get(scenario.name).includes(hero.name) |
| 55 |
) { |
| 56 |
if (!progressionsEncounters.has(scenario)) { |
| 57 |
progressionsEncounters.set(scenario, []); |
| 58 |
} |
| 59 |
|
| 60 |
const tryHeroes = progressionsEncounters.get(scenario) as Partial<Encounter>[]; |
| 61 |
tryHeroes.push({ |
| 62 |
scenario: scenario, |
| 63 |
modularSets: scenario.modularSets, |
| 64 |
difficulty: DifficultyEnum.STANDARD, |
| 65 |
heroes: [hero], |
| 66 |
}); |
| 67 |
|
| 68 |
progressionsEncounters.set(scenario, tryHeroes); |
| 69 |
} |
| 70 |
}); |
| 71 |
}); |
| 72 |
|
| 73 |
// console.table(scenariosLostEncounters); |
| 74 |
// eslint-disable-next-line compat/compat |
| 75 |
console.table(scenariosWinEncounters); |
| 76 |
// eslint-disable-next-line compat/compat |
| 77 |
console.table(progressionsEncounters); |
| 78 |
|
| 79 |
createTitle("Suggested encounters", body); |
| 80 |
|
| 81 |
const table = document.createElement("table"); |
| 82 |
const firstRow = document.createElement("tr"); |
| 83 |
const scenario = document.createElement("th"); |
| 84 |
scenario.innerText = "Scenario"; |
| 85 |
const modularsets = document.createElement("th"); |
| 86 |
modularsets.innerText = "Modular Sets"; |
| 87 |
// const difficulty = document.createElement("th"); |
| 88 |
// difficulty.innerText = "Difficulty"; |
| 89 |
const hero1 = document.createElement("th"); |
| 90 |
hero1.innerText = "Hero 1"; |
| 91 |
const hero2 = document.createElement("th"); |
| 92 |
hero2.innerText = "Hero 2"; |
| 93 |
// const hero3 = document.createElement("th"); |
| 94 |
// hero3.innerText = "Hero 3"; |
| 95 |
// const hero4 = document.createElement("th"); |
| 96 |
// hero4.innerText = "Hero 4"; |
| 97 |
const result = document.createElement("th"); |
| 98 |
result.innerText = "Result"; |
| 99 |
const add = document.createElement("th"); |
| 100 |
|
| 101 |
firstRow.appendChild(scenario); |
| 102 |
firstRow.appendChild(modularsets); |
| 103 |
// firstRow.appendChild(difficulty); |
| 104 |
firstRow.appendChild(hero1); |
| 105 |
firstRow.appendChild(hero2); |
| 106 |
// firstRow.appendChild(hero3); |
| 107 |
// firstRow.appendChild(hero4); |
| 108 |
firstRow.appendChild(result); |
| 109 |
firstRow.appendChild(add); |
| 110 |
|
| 111 |
table.appendChild(firstRow); |
| 112 |
|
| 113 |
// load encounters |
| 114 |
progressionsEncounters.forEach((scenario) => { |
| 115 |
// pick a random one |
| 116 |
const encounters = Array.from(scenario); |
| 117 |
const encounter = encounters[random(0, encounters.length - 1)]; |
| 118 |
|
| 119 |
// scenario.forEach((encounter) => { |
| 120 |
const row = document.createElement("tr"); |
| 121 |
|
| 122 |
const scenarioElement = document.createElement("td"); |
| 123 |
scenarioElement.innerText = encounter.scenario?.name as string; |
| 124 |
|
| 125 |
const modularsets = document.createElement("td"); |
| 126 |
modularsets.innerText = |
| 127 |
null != encounter.modularSets ? encounter.modularSets.map((modularSet) => modularSet.name).join(", ") : ""; |
| 128 |
|
| 129 |
// const difficulty = document.createElement("td"); |
| 130 |
// difficulty.innerText = encounter.difficulty as string; |
| 131 |
|
| 132 |
const allreadyWonHeroes = scenariosWinEncounters.get(encounter.scenario?.name) || []; |
| 133 |
const excludedHeroes = heroes.filter((hero) => !allreadyWonHeroes.includes(hero.name)); |
| 134 |
|
| 135 |
const hero1 = document.createElement("td"); |
| 136 |
const hero1Element = createSelectElement( |
| 137 |
encounter.heroes[0], |
| 138 |
excludedHeroes.map((hero) => hero.name), |
| 139 |
); |
| 140 |
hero1.appendChild(hero1Element); |
| 141 |
|
| 142 |
const hero2 = document.createElement("td"); |
| 143 |
const hero2Element = createSelectElement( |
| 144 |
null, |
| 145 |
heroes.map((hero) => hero.name), |
| 146 |
); |
| 147 |
hero2.appendChild(hero2Element); |
| 148 |
|
| 149 |
// const hero3 = document.createElement("td"); |
| 150 |
// const hero3Element = createSelectElement(null, Object.values(HeroEnum)); |
| 151 |
// hero3.appendChild(hero3Element); |
| 152 |
|
| 153 |
// const hero4 = document.createElement("td"); |
| 154 |
// const hero4Element = createSelectElement(null, Object.values(HeroEnum)); |
| 155 |
// hero4.appendChild(hero4Element); |
| Warning |
Row 156, Column 15: "'modularSetsRating' is assigned a value but never used."
@typescript-eslint/no-unused-vars
|
| 156 |
const modularSetsRating = encounter.modularSets |
| 157 |
?.map((modularSet) => modularSet.rating) |
| 158 |
.reduce((acc, rating) => (acc += rating), 0); |
| 159 |
|
| 160 |
const result = document.createElement("td"); |
| 161 |
const resultElement = createSelectElement(null, Object.values(ResultEnum)); |
| 162 |
result.appendChild(resultElement); |
| 163 |
|
| 164 |
const add = document.createElement("td"); |
| 165 |
const button = document.createElement("button"); |
| 166 |
button.type = "button"; |
| 167 |
button.innerText = "Add !"; |
| 168 |
button.onclick = async () => { |
| 169 |
const heroesService = new HeroesService(); |
| 170 |
encounter.id = null; |
| 171 |
|
| 172 |
if ("" !== hero2Element.value) { |
| 173 |
encounter.heroes.push(await heroesService.find(hero2Element.value)); |
| 174 |
} |
| 175 |
|
| 176 |
encounter.result = Object.values(ResultEnum).find((x) => x === resultElement.value) || undefined; |
| 177 |
console.log(encounter); |
| 178 |
|
| 179 |
const encountersService = new EncountersService(); |
| 180 |
encountersService.save(encounter).then(() => window.location.reload()); |
| 181 |
}; |
| 182 |
add.appendChild(button); |
| 183 |
|
| 184 |
row.appendChild(scenarioElement); |
| 185 |
row.appendChild(modularsets); |
| 186 |
// row.appendChild(difficulty); |
| 187 |
row.appendChild(hero1); |
| 188 |
row.appendChild(hero2); |
| 189 |
// row.appendChild(hero3); |
| 190 |
// row.appendChild(hero4); |
| 191 |
row.appendChild(result); |
| 192 |
row.appendChild(add); |
| 193 |
|
| 194 |
table.appendChild(row); |
| 195 |
// }); |
| 196 |
}); |
| 197 |
|
| 198 |
// Add random encounters |
| 199 |
table.appendChild(addRandomEncounter(encounters, scenarios, modules, heroes)); |
| 200 |
|
| 201 |
body.appendChild(table); |
| 202 |
}; |
| 203 |
|
| 204 |
const addRandomEncounter = ( |
| 205 |
encounters: Encounter[], |
| 206 |
scenarios: Scenario[], |
| 207 |
modularsets: ModularSet[], |
| 208 |
heroes: Hero[], |
| 209 |
): HTMLTableRowElement => { |
| 210 |
const row = document.createElement("tr"); |
| 211 |
|
| 212 |
const [weightedScenarios, weightedModularSets, weightedHeroes] = getWeightedData( |
| 213 |
encounters, |
| 214 |
scenarios, |
| 215 |
modularsets, |
| 216 |
heroes, |
| 217 |
); |
| 218 |
|
| 219 |
const randomScenario = weightedScenarios[random(0, weightedScenarios.length - 1)]; |
| 220 |
const randomModularSet = weightedModularSets[random(0, weightedModularSets.length - 1)]; |
| 221 |
const randomHero = weightedHeroes[random(0, weightedHeroes.length - 1)]; |
| 222 |
|
| 223 |
const scenario = document.createElement("td"); |
| 224 |
const secnarioElement = createSelectElement( |
| 225 |
randomScenario, |
| 226 |
scenarios.map((scenario) => scenario.name), |
| 227 |
); |
| 228 |
scenario.appendChild(secnarioElement); |
| 229 |
|
| 230 |
const modularSet = document.createElement("td"); |
| 231 |
const modularSetElement = createSelectElement( |
| 232 |
randomModularSet, |
| 233 |
modularsets.map((modularSet) => modularSet.name), |
| 234 |
); |
| 235 |
modularSet.appendChild(modularSetElement); |
| 236 |
|
| 237 |
const hero1 = document.createElement("td"); |
| 238 |
const hero1Element = createSelectElement( |
| 239 |
randomHero, |
| 240 |
heroes.map((hero) => hero.name), |
| 241 |
); |
| 242 |
hero1.appendChild(hero1Element); |
| 243 |
|
| 244 |
const hero2 = document.createElement("td"); |
| 245 |
const hero2Element = createSelectElement( |
| 246 |
null, |
| 247 |
heroes.map((hero) => hero.name), |
| 248 |
); |
| 249 |
hero2.appendChild(hero2Element); |
| 250 |
|
| 251 |
const result = document.createElement("td"); |
| 252 |
const resultElement = createSelectElement(null, Object.values(ResultEnum)); |
| 253 |
result.appendChild(resultElement); |
| 254 |
|
| 255 |
const add = document.createElement("td"); |
| 256 |
const button = document.createElement("button"); |
| 257 |
button.type = "button"; |
| 258 |
button.innerText = "Add !"; |
| 259 |
button.onclick = async () => { |
| 260 |
const heroesService = new HeroesService(); |
| 261 |
|
| 262 |
const heroes = [randomHero]; |
| 263 |
|
| 264 |
if ("" !== hero2Element.value) { |
| 265 |
heroes.push(await heroesService.find(hero2Element.value)); |
| 266 |
} |
| 267 |
|
| 268 |
const result = Object.values(ResultEnum).find((x) => x === resultElement.value) || undefined; |
| 269 |
|
| 270 |
const encounter: Encounter = new Encounter({ |
| 271 |
id: null, |
| 272 |
difficulty: DifficultyEnum.STANDARD, |
| 273 |
scenario: randomScenario, |
| 274 |
modularSets: [randomModularSet], |
| 275 |
heroes, |
| 276 |
result, |
| 277 |
}); |
| 278 |
|
| 279 |
console.log(encounter); |
| 280 |
|
| 281 |
const encountersService = new EncountersService(); |
| 282 |
encountersService.save(encounter).then(() => window.location.reload()); |
| 283 |
}; |
| 284 |
add.appendChild(button); |
| 285 |
|
| 286 |
row.appendChild(scenario); |
| 287 |
row.appendChild(modularSet); |
| 288 |
row.appendChild(hero1); |
| 289 |
row.appendChild(hero2); |
| 290 |
row.appendChild(result); |
| 291 |
row.appendChild(add); |
| 292 |
|
| 293 |
return row; |
| 294 |
}; |
| 295 |
|
| 296 |
const getWeightedData = ( |
| 297 |
encounters: Encounter[], |
| 298 |
scenarios: Scenario[], |
| 299 |
modularsets: ModularSet[], |
| 300 |
heroes: Hero[], |
| 301 |
): [Scenario[], ModularSet[], Hero[]] => { |
| 302 |
const base = encounters.length * 5; |
| 303 |
const weightedScenarios: Scenario[] = []; |
| 304 |
const weightedModularSets: ModularSet[] = []; |
| 305 |
const weightedHeroes: Hero[] = []; |
| 306 |
|
| 307 |
const gamesHeroes = encounters |
| 308 |
.flatMap((encounter) => encounter.heroes) |
| 309 |
.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map()); |
| 310 |
|
| 311 |
const gamesModularSets = encounters |
| 312 |
.flatMap((encounter) => encounter.modularSets) |
| 313 |
.map((module) => module) |
| 314 |
.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map()); |
| 315 |
|
| 316 |
const gamesScenarios = encounters |
| 317 |
.map((encounter) => encounter.scenario) |
| 318 |
.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map()); |
| 319 |
|
| 320 |
modularsets.forEach((module) => { |
| 321 |
const pastOccurrences = gamesModularSets.has(module.name) ? gamesModularSets.get(module.name) : 0; |
| 322 |
|
| 323 |
for (let i = 0; i < base - pastOccurrences; i++) { |
| 324 |
weightedModularSets.push(module); |
| 325 |
} |
| 326 |
}); |
| 327 |
|
| 328 |
scenarios.forEach((scenario) => { |
| 329 |
const pastOccurrences = gamesScenarios.has(scenario.name) ? gamesScenarios.get(scenario.name) : 0; |
| 330 |
|
| 331 |
for (let i = 0; i < base - pastOccurrences; i++) { |
| 332 |
weightedScenarios.push(scenario); |
| 333 |
} |
| 334 |
}); |
| 335 |
|
| 336 |
heroes.forEach((hero) => { |
| 337 |
const pastOccurrences = gamesHeroes.has(hero.name) ? gamesHeroes.get(hero.name) : 0; |
| 338 |
|
| 339 |
for (let i = 0; i < base - pastOccurrences; i++) { |
| 340 |
weightedHeroes.push(hero); |
| 341 |
} |
| 342 |
}); |
| 343 |
|
| 344 |
return [weightedScenarios, weightedModularSets, weightedHeroes]; |
| 345 |
}; |
| 346 |
|