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
        }
    }
}
1
Gabriel Pizarro 28 अक्टूबर 2021, 02:31
बढ़िया, यह कौन सा वीडियो गेम है?
 – 
Geoffrey De Smet
28 अक्टूबर 2021, 16:16
यदि आप FULL_ASSERT चालू करते हैं तो क्या होगा? हो सकता है कि इनपुट मोड उन मानों का उपयोग करता हो जो मान श्रेणी में नहीं हैं।
 – 
Geoffrey De Smet
28 अक्टूबर 2021, 16:26
यह टूल हाइपिक्सल स्काईब्लॉक, एक Minecraft सर्वर के लिए है। अधिक जानकारी के लिए, SkyBlockSimplified देखें, जिसे मैं फिर से लिखने में मदद कर रहा हूं। FULL_ASSERT त्रुटि को नहीं बदलता
 – 
Gabriel Pizarro
28 अक्टूबर 2021, 18:21

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))
1
Gabriel Pizarro 28 अक्टूबर 2021, 18:49
त्रुटि संदेश को इसे "शायद" पंक्ति में इंगित करना चाहिए। हम इसे ठीक कर देंगे।
 – 
Geoffrey De Smet
29 अक्टूबर 2021, 13:10
यह जनवरी 2019 में पहले से ही तय था, कृपया सही त्रुटि संदेश प्राप्त करने के लिए OptaPlanner को अपग्रेड करें।
 – 
Geoffrey De Smet
29 अक्टूबर 2021, 13:27

बाहरी ऑब्जेक्ट (ReforgeProblemFact(id=897f...)) प्लानिंगId के साथ ((कक्षा ReforgeProblemFact,897f...)) कोई ज्ञात कार्यशील वस्तु (शून्य) नहीं है।

उस planningId ((class ReforgeProblemFact का कोई मतलब नहीं है, क्योंकि आपके मॉडल में प्लानिंगआईड क्लास यूयूआईडी है। PlanningIdLookUpStrategy लाइन 71 के कोड को देखते हुए, त्रुटि संदेश सही है। उस लाइन पर ब्रेकपॉइंट लगाएं और देखें कि किस तरह की क्लास प्लानिंग आईडी वैरिएबल है। यह एक यूयूआईडी होना चाहिए।

0
Geoffrey De Smet 28 अक्टूबर 2021, 16:24
extractPlanningId, "class net.javaman.optaplanner_reproducible.ReforgeProblemFact" -> "04bb4456-4fc5-4a1b-bdb7-7bc570da5142" का ImmutablePair लौटाता है। मैं कुछ और खुदाई करूँगा।
 – 
Gabriel Pizarro
28 अक्टूबर 2021, 18:27
ऐसा लगता है कि idToWorkingObjectMap में केवल ItemPlanningEntity की प्रविष्टियां हैं; कोई ReforgeProblemFact प्रविष्टियां नहीं हैं। मैंने कई दिन पहले इस बारे में एक टिकट खोला था, लेकिन कोई प्रतिक्रिया नहीं मिली, इसलिए मैंने SO पर पोस्ट किया: PLANNER-2556
 – 
Gabriel Pizarro
28 अक्टूबर 2021, 18:37
मैंने इसी तरह के SO प्रश्न पर दोबारा गौर किया और अंत में काम का जवाब मिला। आपकी सहायताके लिए धन्यवाद! मैं इस त्रुटि का उत्तर देने वाले दस्तावेज़ीकरण में एक पैराग्राफ जोड़ने की सलाह दूंगा
 – 
Gabriel Pizarro
28 अक्टूबर 2021, 18:49

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)

इसमें एक त्रुटि संदेश है जिसमें "शायद" रेखा है जो सीधे दूसरे उत्तर में दिखाए गए समाधान को इंगित करती है।

0
Geoffrey De Smet 29 अक्टूबर 2021, 13:29