TLDR: Optaplanner में मल्टीथ्रेडिंग को सक्षम करना एक-लाइनर माना जाता है, लेकिन यह एक अपवाद फेंकता है
मैं एक वीडियोगेम में कॉन्फ़िगर करने योग्य लोडआउट का उपयोग करके क्षति गणना को अनुकूलित करने का प्रयास कर रहा हूं। संदर्भ के लिए, एक खिलाड़ी प्रत्येक आइटम को "reforge" के साथ कॉन्फ़िगर कर सकता है, जो ताकत या आलोचना क्षति के आंकड़े जोड़ता है। अंतिम क्षति गणना को ताकत और क्रिट क्षति के संयोजन के रूप में अधिकतम किया जाना चाहिए। इस कारण से, मैं वस्तुओं के लिए रीफोर्ज आवंटित करने के लिए ऑप्टाप्लानर का उपयोग कर रहा हूं।
हालांकि, एक्सएमएल सॉल्वर कॉन्फ़िगरेशन में <moveThreadCount>AUTO</moveThreadCount>
के माध्यम से मल्टीथ्रेडिंग को सक्षम करना एक अपवाद फेंकता है (जो सिंगल-थ्रेडेड निष्पादन में नहीं होता है):
Caused by: java.lang.IllegalStateException: The externalObject (ReforgeProblemFact(id=897f4bab-80e0-4eb9-a1d7-974f7cddfd9e, name=Fierce, rarity=COMMON, strength=4, critDamage=0)) with planningId ((class net.javaman.optaplanner_reproducible.ReforgeProblemFact,897f4bab-80e0-4eb9-a1d7-974f7cddfd9e)) has no known workingObject (null).
Maybe the workingObject was never added because the planning solution doesn't have a @ProblemFactCollectionProperty annotation on a member with instances of the externalObject's class (class net.javaman.optaplanner_reproducible.ReforgeProblemFact).
यह SO प्रश्न समान है, लेकिन इसका उत्तर इस उदाहरण में अपवाद को ठीक नहीं करता है।
मैंने नीचे दिए गए कोड में पैकेज और आयात हटा दिए हैं। पूर्ण GitHub रिपॉजिटरी लिंक
परियोजना संरचना:
src/main/
kotlin/
net/javaman/optaplanner_reproducible/
Rarity.kt
ReforgeProblemFact.kt
ItemPlanningEntity.kt
ReforgePlanningSolution.kt
MaximizeDamageConstraintProvider.kt
Main.kt
resources/
reforgeSolverConfig.xml
दुर्लभता.केटी:
enum class Rarity {
COMMON,
RARE,
LEGENDARY
}
ReforgeProblemFact.kt:
data class ReforgeProblemFact(
@PlanningId
val id: UUID,
val name: String,
val rarity: Rarity,
val strength: Int,
val critDamage: Int
)
ItemPlanningEntity.kt:
@PlanningEntity
data class ItemPlanningEntity @JvmOverloads constructor(
@PlanningId
val id: UUID? = null,
val rarity: Rarity? = null,
@PlanningVariable(valueRangeProviderRefs = ["reforgeRange"])
var reforge: ReforgeProblemFact? = null,
@ValueRangeProvider(id = "reforgeRange")
@ProblemFactCollectionProperty
val availableReforges: List<ReforgeProblemFact>? = null
)
ReforgePlanningSolution.kt:
@PlanningSolution
class ReforgePlanningSolution @JvmOverloads constructor(
@PlanningEntityCollectionProperty
val availableItems: List<ItemPlanningEntity>? = null,
@PlanningScore
val score: HardSoftScore? = null,
)
MaximizeDamageConstraintProvider.kt:
class MaximizeDamageConstraintProvider : ConstraintProvider {
override fun defineConstraints(factory: ConstraintFactory): Array<Constraint> = arrayOf(maximizeDamage(factory))
// This approach does not take full advantage of incremental solving,
// but it is necessary to compute strength and critDamage together in the same equation
private fun maximizeDamage(factory: ConstraintFactory) = factory.from(ItemPlanningEntity::class.java)
.map(ItemPlanningEntity::reforge) // Get each item's reforge
.groupBy({ 0 }, toList { reforge: ReforgeProblemFact? -> reforge }) // Compile into one List<ReforgeProblemFact>
.reward("damage", HardSoftScore.ONE_SOFT) { _, reforges: List<ReforgeProblemFact?> ->
val strengthSum = reforges.stream().collect(Collectors.summingInt { reforge -> reforge?.strength ?: 0 })
val critDamageSum = reforges.stream().collect(Collectors.summingInt { reforge -> reforge?.critDamage ?: 0 })
(100 + strengthSum) * (100 + critDamageSum)
}
}
मेन.केटी:
class Main {
companion object {
private val allReforges = listOf(
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.COMMON, 0, 3),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.COMMON, 4, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.COMMON, 2, 1),
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.RARE, 1, 3),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.RARE, 5, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.RARE, 3, 2),
ReforgeProblemFact(UUID.randomUUID(), "Clean", Rarity.LEGENDARY, 1, 4),
ReforgeProblemFact(UUID.randomUUID(), "Fierce", Rarity.LEGENDARY, 6, 0),
ReforgeProblemFact(UUID.randomUUID(), "Shiny", Rarity.LEGENDARY, 4, 2),
)
private val solverManager: SolverManager<ReforgePlanningSolution, UUID> = SolverManager.create(
SolverConfig.createFromXmlResource("reforgeSolverConfig.xml")
)
@JvmStatic
fun main(args: Array<String>) {
val availableItems = generateAvailableItems(
mapOf(
Rarity.COMMON to 4,
Rarity.RARE to 3,
Rarity.LEGENDARY to 1
)
)
val solverJob = solverManager.solve(UUID.randomUUID(), ReforgePlanningSolution(availableItems))
val solution = solverJob.finalBestSolution
solution.availableItems!!
.map { it.reforge!! }
.forEach { println(it.rarity.name + " " + it.name) }
}
private fun generateAvailableItems(itemCounts: Map<Rarity, Int>): MutableList<ItemPlanningEntity> {
val availableItems = mutableListOf<ItemPlanningEntity>()
for (itemCount in itemCounts) {
for (count in 0 until itemCount.value) {
val rarity = itemCount.key
availableItems.add(
ItemPlanningEntity(
UUID.randomUUID(),
rarity,
null,
allReforges.filter { it.rarity == rarity }
)
)
}
}
return availableItems
}
}
}
3 जवाब
मैंने समान SO प्रश्न पर दोबारा गौर किया . उत्तर के कुछ अलग संस्करणों की कोशिश करने के बाद, यह आखिरकार काम कर गया। यहाँ अन्य पोस्ट की तुलना में अधिक विस्तृत विवरण दिया गया है:
प्रत्येक PlanningEntity
का ProblemFactCollectionProperty
PlanningSolution
में मुख्य ProblemFactCollectionProperty
का हिस्सा होना चाहिए। इसका मतलब यह है कि इकाई और समाधान दोनों को अपने समस्या तथ्यों को परिभाषित करना चाहिए। यहां मेरे लिए यह तय किया गया है:
ItemPlanningEntity.kt को वही रखें।
ReforgePlanningSolution.kt में एक वैश्विक ProblemFactCollectionProperty
शामिल करें:
@PlanningSolution
class ReforgePlanningSolution @JvmOverloads constructor(
@PlanningEntityCollectionProperty
val availableItems: List<ItemPlanningEntity>? = null,
@ProblemFactCollectionProperty
val allReforges: List<ReforgeProblemFact>? = null,
@PlanningScore
val score: HardSoftScore? = null
)
Main.kt में समाधान को तत्काल करते समय वैश्विक संग्रह को परिभाषित करें:
val solverJob = solverManager.solve(UUID.randomUUID(), ReforgePlanningSolution(availableItems, allReforges))
बाहरी ऑब्जेक्ट (ReforgeProblemFact(id=897f...)) प्लानिंगId के साथ ((कक्षा ReforgeProblemFact,897f...)) कोई ज्ञात कार्यशील वस्तु (शून्य) नहीं है।
उस planningId ((class ReforgeProblemFact
का कोई मतलब नहीं है, क्योंकि आपके मॉडल में प्लानिंगआईड क्लास यूयूआईडी है। PlanningIdLookUpStrategy
लाइन 71 के कोड को देखते हुए, त्रुटि संदेश सही है। उस लाइन पर ब्रेकपॉइंट लगाएं और देखें कि किस तरह की क्लास प्लानिंग आईडी वैरिएबल है। यह एक यूयूआईडी होना चाहिए।
extractPlanningId
, "class net.javaman.optaplanner_reproducible.ReforgeProblemFact" -> "04bb4456-4fc5-4a1b-bdb7-7bc570da5142"
का ImmutablePair
लौटाता है। मैं कुछ और खुदाई करूँगा।
idToWorkingObjectMap
में केवल ItemPlanningEntity
की प्रविष्टियां हैं; कोई ReforgeProblemFact
प्रविष्टियां नहीं हैं। मैंने कई दिन पहले इस बारे में एक टिकट खोला था, लेकिन कोई प्रतिक्रिया नहीं मिली, इसलिए मैंने SO पर पोस्ट किया: PLANNER-2556
OptaPlanner को अपग्रेड करें, उदाहरण के लिए हाल ही में जारी 8.12.0.Final में, मददगार पाने के लिए इस तरह त्रुटि संदेश:
Caused by: java.lang.IllegalStateException: The externalObject (2018-10-01T10:15-12:15) with planningId ((class org.optaplanner.examples.conferencescheduling.domain.Timeslot, 0)) has no known workingObject (null).
Maybe the workingObject was never added because the planning solution doesn't have a @ProblemFactCollectionProperty annotation on a member with instances of the externalObject's class (class org.optaplanner.examples.conferencescheduling.domain.Timeslot).
at org.optaplanner.core.impl.domain.lookup.PlanningIdLookUpStrategy.lookUpWorkingObject(PlanningIdLookUpStrategy.java:76)
इसमें एक त्रुटि संदेश है जिसमें "शायद" रेखा है जो सीधे दूसरे उत्तर में दिखाए गए समाधान को इंगित करती है।
संबंधित सवाल
नए सवाल
java
जावा एक उच्च स्तरीय प्रोग्रामिंग भाषा है। इस टैग का उपयोग तब करें जब आपको भाषा का उपयोग करने या समझने में समस्या हो। इस टैग का उपयोग शायद ही कभी किया जाता है और इसका उपयोग अक्सर [वसंत], [वसंत-बूट], [जकार्ता-ई], [Android], [javafx], [हडूप], [श्रेणी] और [मावेन] के साथ किया जाता है।
FULL_ASSERT
चालू करते हैं तो क्या होगा? हो सकता है कि इनपुट मोड उन मानों का उपयोग करता हो जो मान श्रेणी में नहीं हैं।FULL_ASSERT
त्रुटि को नहीं बदलता