Skip to content

Commit

Permalink
feat: Add a first GUI implementation (#1)
Browse files Browse the repository at this point in the history
fix: Allow user to pass 0 as a target of a characteristic to force the algorithm to find equipments with no negative value
fix: Fix a score computation bug with elementary mastery that was giving a higher score for builds than expected

Co-authored-by: charles-eddy.parpet <[email protected]>
  • Loading branch information
CharlyRien and CharlotteAuHaras authored Nov 13, 2023
1 parent 6331c4f commit 9edc221
Show file tree
Hide file tree
Showing 9,969 changed files with 1,927 additions and 111,261 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
30 changes: 30 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Build

on: [push, workflow_dispatch, workflow_call]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout project sources
uses: actions/checkout@v4

- name: Setup Gradle
uses: gradle/gradle-build-action@v2

- name: Verify everything is working fine
run: ./gradlew test

- name: Create Jars
run: ./gradlew jar

- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: build-result
path: |
.
gui/build/libs/*.jar
!gui/build/
!gui/src/
!.git
if-no-files-found: error
24 changes: 24 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Deploy
on: [workflow_dispatch]
jobs:
build:
# Use the Build workflow described above
uses: ./.github/workflows/build.yml
deploy:
needs: [build]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Download artifact
uses: actions/download-artifact@v3
with:
# This is the name of the "Upload artifact" action above.
name: build-result
- name: Run Conveyor
uses: hydraulic-software/conveyor/actions/[email protected]
with:
extra_flags: '-f ./gui/conveyor.conf'
command: make copied-site
signing_key: ${{ secrets.SIGNING_KEY }}
agree_to_license: 1
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<br />
<div align="center">
<a href="https://github.com/CharlyRien/wakfu-autobuilder">
<img src="images/logo.jpg" alt="Logo" width="512" height="512">
<img src="gui/src/main/resources/logo.png" alt="Logo" width="512" height="512">
</a>

<h3 align="center">Wakfu autobuilder</h3>
Expand Down
5 changes: 2 additions & 3 deletions autobuilder/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,14 @@ dependencies {
implementation("com.github.kittinunf.fuel:fuel:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-coroutines:2.3.1")
implementation("com.github.kittinunf.fuel:fuel-kotlinx-serialization:2.3.1")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3")
implementation("de.vandermeer:asciitable:0.3.2")
implementation("io.github.oshai:kotlin-logging-jvm:5.0.0")
implementation("org.slf4j:slf4j-simple:2.0.7")
}

kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
jvmToolchain(21)
}

tasks.test {
Expand Down
78 changes: 57 additions & 21 deletions autobuilder/src/main/kotlin/me/chosante/autobuilder/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ import io.github.oshai.kotlinlogging.KotlinLogging
import kotlin.system.exitProcess
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.runBlocking
import me.chosante.autobuilder.domain.Character
import me.chosante.autobuilder.domain.CharacterClass
Expand Down Expand Up @@ -61,18 +68,22 @@ import me.chosante.common.Characteristic.WILLPOWER
import me.chosante.common.Characteristic.WISDOM
import me.chosante.common.Equipment
import me.chosante.common.Rarity
import me.tongfei.progressbar.ConsoleProgressBarConsumer
import me.tongfei.progressbar.ProgressBar
import me.tongfei.progressbar.ProgressBarBuilder
import me.tongfei.progressbar.ProgressBarStyle

private val logger = KotlinLogging.logger {}
const val VERSION = "1.81.1.13"
internal const val VERSION = "1.81.1.15"

fun main(args: Array<String>) = WakfuAutobuild().main(args)

val additionalHelpOnStats =
private val additionalHelpOnStats =
"""note that, it is also possible to pass an optional weight with the following format
|-> x:y | x being the number wanted, y the weight of the statistic you want to put (default is 1)
""".trimMargin()

class WakfuAutobuild :
private class WakfuAutobuild :
CliktCommand(
help = """This program helps Wakfu players easily find the best combination of equipment for their character
|at a given level by taking into account their desired stats and providing build suggestions
Expand Down Expand Up @@ -146,7 +157,7 @@ HUPPERMAGE"""
" the names have to be French for now, can be used like that: --forced-items 'Gelano','Amulette du Bouftou',..."
).split(",").default(listOf())

private val excludeItems: List<String> by option(
private val excludedItems: List<String> by option(
"--items-a-exclure",
"--excluded-items",
help = "Used to tell the algorithm to exclude specific items to not be in the final build," +
Expand Down Expand Up @@ -401,6 +412,7 @@ HUPPERMAGE"""
help = "Use this flag to indicate that you want to stop searching when you have 100% build match already found."
).flag(default = false, defaultForHelp = "disabled")

@OptIn(ExperimentalCoroutinesApi::class)
override fun run() {
val character = Character(characterClass ?: CharacterClass.UNKNOWN, levelWanted)
val targetStats = TargetStats(
Expand Down Expand Up @@ -450,21 +462,34 @@ HUPPERMAGE"""
}

runBlocking {
val bestCombination = WakfuBestBuildFinderAlgorithm
.run(
WakfuBestBuildParams(
character = character,
targetStats = targetStats,
searchDuration = searchDuration,
stopWhenBuildMatch = stopWhenBuildMatch,
maxRarity = maxRarity,
forcedItems = forceItems,
excludeItems = excludeItems
val bestCombination = progressBar().use { progressBar ->
WakfuBestBuildFinderAlgorithm
.run(
WakfuBestBuildParams(
character = character,
targetStats = targetStats,
searchDuration = searchDuration,
stopWhenBuildMatch = stopWhenBuildMatch,
maxRarity = maxRarity,
forcedItems = forceItems,
excludedItems = excludedItems
)
)
)
.also {
println(
"""Research result
.buffer(CONFLATED)
.onEach {
progressBar.setExtraMessage("${it.individualMatchPercentage}% match found so far")
progressBar.stepTo(it.progressPercentage.toLong())
delay(1000L)
}
.onCompletion {
progressBar.stepTo(progressBar.max)
}
.toList()
.last()
.individual
.also {
println(
"""Research result
|Given vs. Expected characteristics
|
|Equipment
Expand All @@ -486,9 +511,10 @@ HUPPERMAGE"""
|
|Major
|${it.characterSkills.major.asASCIITable()}
""".trimMargin()
)
}
""".trimMargin()
)
}
}

if (createZenithBuild) {
try {
Expand Down Expand Up @@ -520,6 +546,16 @@ HUPPERMAGE"""
}
}

private fun progressBar(): ProgressBar {
return ProgressBarBuilder()
.hideEta()
.setTaskName("Best build research")
.setInitialMax(100)
.setStyle(ProgressBarStyle.UNICODE_BLOCK)
.setConsumer(ConsoleProgressBarConsumer(System.err))
.build()
}

private fun Assignable<*>.asASCIITable() = with(AsciiTable()) {
val characterSkills = this@asASCIITable
with(context) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import me.chosante.common.Characteristic
class TargetStats(targetStats: List<TargetStat>) :
HashSet<TargetStat>(targetStats) {
private val targetStatToWeight = targetStats.associateWeights(100)
val totalExpectedScore = sumOf { it.target * targetStatToWeight.getValue(it) * it.userDefinedWeight }
val totalExpectedScore = sumOf { it.target * targetStatToWeight.getValue(it) }
val expectedScoreByCharacteristic: Map<TargetStat, Double> =
associateWith { it.target * targetStatToWeight.getValue(it) * it.userDefinedWeight }
associateWith { it.target * targetStatToWeight.getValue(it) }

fun weight(targetStat: TargetStat): Double =
targetStatToWeight.getValue(targetStat)
Expand Down Expand Up @@ -47,32 +47,15 @@ class TargetStats(targetStats: List<TargetStat>) :
Characteristic.RESISTANCE_ELEMENTARY_FIRE
)
}.associate { it.characteristic to it.target }

// fun removeCharacteristicValues(characteristicValues: Map<Characteristic, Int>): TargetStats {
// val characteristicsToRemove = characteristicValues.keys.filterNot { it == Characteristic.HP }
// return TargetStats(this.mapNotNull {
// if (it.characteristic in characteristicsToRemove) {
// val newTarget = it.target - characteristicValues.getValue(it.characteristic).coerceAtLeast(0)
// if (newTarget == 0) {
// return@mapNotNull null
// }
// TargetStat(
// characteristic = it.characteristic,
// target = newTarget,
// userDefinedWeight = it.userDefinedWeight
// )
// } else {
// it
// }
// })
// }
}

fun List<TargetStat>.associateWeights(desiredValue: Int): Map<TargetStat, Double> {
fun List<TargetStat>.associateWeights(normalizeValue: Int): Map<TargetStat, Double> {
return associateWith {
(
desiredValue.toBigDecimal().setScale(2, RoundingMode.HALF_UP) / it.target.toBigDecimal()
.setScale(2, RoundingMode.HALF_UP)
).toDouble() * it.userDefinedWeight
if (it.target == 0) {
return@associateWith 0.0
}

val normalizedWeight = normalizeValue.toBigDecimal().setScale(2, RoundingMode.HALF_UP) / it.target.toBigDecimal().setScale(2, RoundingMode.HALF_UP)
normalizedWeight.toDouble() * it.userDefinedWeight
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ data class Agility(
val lock: Lock = Lock(0),
val dodge: Dodge = Dodge(0),
val initiative: Initiative = Initiative(0),
val dodgeAndLock: SkillCharacteristic.PairedCharacteristic = SkillCharacteristic.PairedCharacteristic(
name = "Lock and Dodge",
maxPointsAssignable = Int.MAX_VALUE,
first = DodgeAndLock.Dodge(0),
second = DodgeAndLock.Lock(0)
),
val dodgeAndLock: SkillCharacteristic.PairedCharacteristic = DodgeAndLock(0),
val willpower: Willpower = Willpower(0),
) : Assignable<Agility> {
init {
Expand Down Expand Up @@ -89,7 +84,12 @@ sealed class AgilityCharacteristic(
name = "Initiative"
)

class DodgeAndLock {
class DodgeAndLock(pointsAssigned: Int) : PairedCharacteristic(
"Dodge and lock",
first = Dodge(pointsAssigned),
second = Lock(pointsAssigned),
maxPointsAssignable = Int.MAX_VALUE
) {
class Dodge(pointsAssigned: Int) :
AgilityCharacteristic(
pointsAssigned = pointsAssigned,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ sealed class SkillCharacteristic(
pointsAssigned = pointsToAssign
}

class PairedCharacteristic(
open class PairedCharacteristic(
name: String,
val first: SkillCharacteristic,
val second: SkillCharacteristic,
Expand Down Expand Up @@ -234,12 +234,21 @@ fun <T : Assignable<T>> T.assignRandomPoints(pointsToAssign: Int, targetCharacte
}

private fun <T : Assignable<T>> T.filterOnlyWantedTargetCharacteristics(targetCharacteristics: List<Characteristic>): List<SkillCharacteristic> {
return getCharacteristics().filter {
if (it is SkillCharacteristic.PairedCharacteristic) {
it.first.characteristic in targetCharacteristics ||
it.second.characteristic in targetCharacteristics
} else {
it.characteristic in targetCharacteristics
return getCharacteristics().filter { skillCharacteristic ->
when {
skillCharacteristic is SkillCharacteristic.PairedCharacteristic ->
skillCharacteristic.first.characteristic in targetCharacteristics ||
skillCharacteristic.second.characteristic in targetCharacteristics

skillCharacteristic.characteristic == Characteristic.RESISTANCE_ELEMENTARY -> targetCharacteristics.any {
it in resistanceElementaryCharacteristics
}

skillCharacteristic.characteristic == Characteristic.MASTERY_ELEMENTARY -> targetCharacteristics.any {
it in masteryElementaryCharacteristics
}

else -> skillCharacteristic.characteristic in targetCharacteristics
}
}
}
Expand All @@ -260,3 +269,19 @@ private fun <T : Assignable<T>> T.assignPointsToCharacteristic(
0
}
}

private val masteryElementaryCharacteristics = listOf(
Characteristic.MASTERY_ELEMENTARY_EARTH,
Characteristic.MASTERY_ELEMENTARY_WATER,
Characteristic.MASTERY_ELEMENTARY_WIND,
Characteristic.MASTERY_ELEMENTARY_FIRE,
Characteristic.MASTERY_ELEMENTARY
)

private val resistanceElementaryCharacteristics = listOf(
Characteristic.RESISTANCE_ELEMENTARY_EARTH,
Characteristic.RESISTANCE_ELEMENTARY_WATER,
Characteristic.RESISTANCE_ELEMENTARY_WIND,
Characteristic.RESISTANCE_ELEMENTARY_FIRE,
Characteristic.RESISTANCE_ELEMENTARY
)
Loading

0 comments on commit 9edc221

Please sign in to comment.