Ugh, github is confusing for a newbie. I'm trying to figure out how to put this in a txt file in a fork, or send it as a private message or something, but ultimately this seems to be the only way I can find to communicate. -.- Meh. Anyway.
I've been thinking about how to go about making a more 'systematic' algorithm that loops through every possibility and then runs probabilities on each, rather than randomly generating sequences and hoping that each generation may eventually come up with a different moveset that possibly/hopefully results in something better than the ones before. Why be random when you can be systematic?
So I've spent the last couple hours coming up with this rough algorithm idea. Since my crafters are only level 15 I've only considered the abilities available to me, but it's basically a proof of concept kind of thing where it would easily adapt to include each of the abilities in the game. Warning, this is going to be long. =P And also rough. This is just like a first draft. I already know there's plenty to touch up, long before it even reaches the point of getting coded.
The main concept is that it runs on a recursive for loop. The loop would run through each of the abilities available, and then each combination of abilities, until it finds ones that qualify as a complete sequence, records the data to an array, and then moves on to try the rest of the abilities. Systematically going through every possible combination of abilities, until the sequence either results in running out of progress (a success) or running out of durability (potential failure).
Here's an example array the loop would be running off of:
Example array:
1 - Basic Synth
2 - Basic touch
3 - Master's Mend
4 - Steady Hand
5 - Inner Quiet
6 - Observe
7 - Rumination
8 - Ingenuity
9 - Rapid Synthesis
10 - Manipulation
11 - Waste Not
12 - Careful Synthesis
13 - Tricks of the Trade
14 - Hasty Touch
And here's the loop which is the core of the whole algorithm:
for i = 1 to abilityArray.length, i++
if performedAction = true
append ability.name to currentActionSequence array
if ( currentDurability <= 0 ) OR ( currentMinProgress >= difficulty ) OR ( ( pruneResults = true ) AND ( currentMaxProgress >= difficulty ) )
append currentActionSequence, currentMaxSuccess, currentMaxQuality, currentMaxQualitySuccess, currentMinProgress to the array results[counter]
counter += 1
performedAction = false
return
end
end
performedAction = false
Run function based on abilityArray.i
if performedAction = true
recurse the for loop (for i = 1 to abilityArray.length, i++), pass it the name of abilityArray.i and all current stats/stacks
end
end
I hope that comes through as legible. Right now it doesn't have a check for whether the sequence even succeeds or not, but once the results are pruned and sorted, any unsuccessful results will be at the very bottom of the list anyway. I'm thinking that only the top X results are displayed (where user decides X for themselves.) 5 would probably be a good default, maybe.
Ultimately this results in a results array that contains an array sequence with the list of abilities used, as well as the relevant stats returned by that sequence.
Sorting and pruning the array would occur, and would be easy to algorithm for. Essentially you want the ones with the highest success listed first, then if tie the highest quality output, then if tie the highest remaining durability, then if tie the one with fewer steps, then if tie the highest remaining CP.
Next is an algorithm to prune out duplicates. Essentially if the abilities used are the exact same, then prune out in a similar method to the above. If abilities are the same but one has higher success, prune the lesser. If all that is the same but one has higher quality output, prune the lesser. If all that is the same but one has higher remaining durability, prune the lesser. If all that is the same but one has fewer steps, prune the one with more. If all that is the same and one has higher remaining cp, prune the lesser. If all of that is the same and it's just the order of abilities that is the only difference at all, then prune the 2nd as a duplicate.
An additional prune results option would have the sequences stop if/when progress reaches max, assuming 100% success rate on synthesis. This shortens the macros to prevent extra synth steps past the point of it already being completed. Turning this option off adds more synth steps in the case of failure (if applicable) so the user does not have to manually finish in the event of failure.
By using this method, it would be easy to adapt to the 8-ball method simply by allowing the algorithm to run with 'starting' stats of a craft already in progress. At that point you'd just have to create some UI that would allow the user to input the in-progress stats as they go along, and the solver would automatically regenerate the solution list after each step. Right now the algorithm is set for max quality because of chance of failure, but in an 8-ball setting, detecting that the max quality is already achieved could easily be incorporated into quality-increasing steps getting skipped.
And finally, here's a list of the abilities available at level 15 (including cross class) and how they would 'plug in' to the main for loop for calculation purposes. Anywhere I use X or Y I'm basically depending on the reverse engineered formulas you already have in place for determining progress and quality and all that good stuff. These would basically be functions that get called when the for loop needs to calculate the results of a potential next step.
Right now conditions are ignored, and probability is calculated as minimum and maximum. Essentially the minimum is what you want to push as high as possible and potentially sort by, and the maximum would feed into your montecarlo simulations to try to attempt to find the most realistic chance of actual outcome. In the 8-ball setting, the montecarlo may or may not 'guess' at when conditions would apply to future steps or how successful future actions might be. The current step of any given 8-ball sequence's step would have a definite probability based on current stacks/conditions/stats, but future ones might need guessing in order to 'try for the best outcome.' That's a bit beyond my current skill, though, so I'll stick to systematic and min/max probabilities. =D
Anyway, here's a function for each of the level 15 and below abilities, including cross class:
Basic synth:
progress = X
successRate = .9
durabilityCost = 10
if steadyhandstacks > 0
successRate = successRate + .2
if successRate > 1
successRate = 1
end
steadyhandstacks -= 1
end
if ingenuitystacks > 0
improve X
ingenuitystacks -= 1
if wastenotstacks > 0
durabilityCost = 5
wastenotstacks -= 1
currentDurability = currentDurability - durabilityCost
if manipulationstacks > 0 and currentDurability > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
if currentMaxSuccess = 0
currentMaxSuccess = successRate
else
currentMaxSuccess *= successRate
if successRate = 1
currentMinProgress += X
currentMaxProgress += X
performedAction = true
Basic Touch
if currentMaxProgress >= difficulty
return
end
cpCost = 18
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
quality = X
successRate = .7
durabilityCost = 10
if steadyhandstacks > 0
successRate = successRate + .2
if successRate > 1
successRate = 1
end
steadyhandstacks -= 1
end
if ingenuitystacks > 0
improve X (?) // Not sure if level improves quality/control
ingenuitystacks -= 1
if wastenotstacks > 0
durabilityCost = 5
wastenotstacks -= 1
currentDurability = currentDurability - durabilityCost
if manipulationstacks > 0 and currentDurability > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
if currentMaxQualitySuccess = 0
currentMaxQualitySuccess = successRate
else
currentMaxQualitySuccess *= successRate
if successRate = 1
if guaranteedIQStacks > 0
currentMinQuality += X adjusted to guaranteedIQStacks
else
currentMinQuality += X
if innerQuiet = true
guaranteedIQStacks += 1
iQMaxStacks += 1
end
else if innerQuiet = true
adjust X based on iQMaxStacks
iQMaxStacks += 1
currentMaxQuality += X
else
currentMaxQuality += X
performedAction = true
Master's Mend
cpCost = 92
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
end
currentDurability += 30
if currentDurability > durability
currentDurability = durability
end
performedAction = true
Steady Hand
cpCost = 22
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
steadyhandstacks = 5
performedAction = true
Inner Quiet
if currentMaxProgress >= difficulty
return
end
cpCost = 18
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
innerQuiet = true
performedAction = true
Observe
if currentMaxProgress >= difficulty
return
end
cpCost = 14
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
performedAction = true
Rumination
if innerQuiet = false
return
end
if class ~= Carpenter
if maxCrossClassUsed = true
return
end
if RuinationUsed = false
crossClassUsed += 1
RuinationUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
if guaranteedIQStacks > 0
currentCP += X adjusted to guaranteedIQStacks
else
currentCP += X based on iQMaxStacks using Y
guaranteedIQStacks = 0
iQMaxStacks = 0
performedAction = true
Ingenuity
if class ~= Blacksmith
if maxCrossClassUsed = true
return
end
if IngenuityUsed = false
crossClassUsed += 1
IngenuityUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
cpCost = 24
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
ingenuitystacks = 5
performedAction = true
Rapid synth:
if class ~= Armorer
if maxCrossClassUsed = true
return
end
if RapidSynthUsed = false
crossClassUsed += 1
RapidSynthUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
progress = 2.5X
successRate = .5
durabilityCost = 10
if steadyhandstacks > 0
successRate = successRate + .2
if successRate > 1
successRate = 1
end
steadyhandstacks -= 1
end
if ingenuitystacks > 0
improve X // Maybe?
ingenuitystacks -= 1
if wastenotstacks > 0
durabilityCost = 5
wastenotstacks -= 1
currentDurability = currentDurability - durabilityCost
if manipulationstacks > 0 and currentDurability > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
if currentMaxSuccess = 0
currentMaxSuccess = successRate
else
currentMaxSuccess *= successRate
if successRate = 1
currentMinProgress += X
currentMaxProgress += X
performedAction = true
Manipulation
if class ~= Goldsmith
if maxCrossClassUsed = true
return
end
if ManipulationUsed = false
crossClassUsed += 1
ManipulationUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
cpCost = 88
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if wastenotstacks > 0
wastenotstacks -= 1
if manipulationstacks > 0
currentDurability += 10
if currentDurability > durability
currentDurability = durability
end
manipulationstacks = 3
performedAction = true
Waste Not
if class ~= Leatherworker
if maxCrossClassUsed = true
return
end
if WasteNotUsed = false
crossClassUsed += 1
WasteNotUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
cpCost = 56
if currentCP >= cpCost
currentCP -= cpCost
else
return
end
if steadyhandstacks > 0
steadyhandstacks -= 1
if ingenuitystacks > 0
ingenuitystacks -= 1
if manipulationstacks > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
wastenotstacks = 5
performedAction = true
CarefulSynth
if class ~= Weaver
if maxCrossClassUsed = true
return
end
if CarefulSynthUsed = false
crossClassUsed += 1
CarefulSynthUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
progress = .9X
successRate = 1
durabilityCost = 10
if steadyhandstacks > 0
successRate = successRate + .2
if successRate > 1
successRate = 1
end
steadyhandstacks -= 1
end
if ingenuitystacks > 0
improve X // Maybe?
ingenuitystacks -= 1
if wastenotstacks > 0
durabilityCost = 5
wastenotstacks -= 1
currentDurability = currentDurability - durabilityCost
if manipulationstacks > 0 and currentDurability > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
if currentMaxSuccess = 0
currentMaxSuccess = successRate
else
currentMaxSuccess *= successRate
if successRate = 1
currentMinProgress += X
currentMaxProgress += X
performedAction = true
Tricks of the Trade
return // No point in it now until conditions are considered
Hasty Touch
if currentMaxProgress >= difficulty
return
end
if class ~= Culinarian
if maxCrossClassUsed = true
return
end
if HastyTouchUsed = false
crossClassUsed += 1
HastyTouchUsed = true
if crossClassUsed = 3
maxCrossClassUsed = true
end
end
end
quality = X
successRate = .5
durabilityCost = 10
if steadyhandstacks > 0
successRate = successRate + .2
if successRate > 1
successRate = 1
end
steadyhandstacks -= 1
end
if ingenuitystacks > 0
improve X (?) // Not sure if level improves quality/control
ingenuitystacks -= 1
if wastenotstacks > 0
durabilityCost = 5
wastenotstacks -= 1
currentDurability = currentDurability - durabilityCost
if manipulationstacks > 0 and currentDurability > 0
currentDurability += 10
manipulationstacks -= 1
if currentDurability > durability
currentDurability = durability
end
end
if currentMaxQualitySuccess = 0
currentMaxQualitySuccess = successRate
else
currentMaxQualitySuccess *= successRate
if successRate = 1
if guaranteedIQStacks > 0
currentMinQuality += X adjusted to guaranteedIQStacks
else
currentMinQuality += X
if innerQuiet = true
guaranteedIQStacks += 1
iQMaxStacks += 1
end
else if innerQuiet = true
adjust X based on iQMaxStacks
iQMaxStacks += 1
currentMaxQuality += X
else
currentMaxQuality += X
performedAction = true
Ugh, github is confusing for a newbie. I'm trying to figure out how to put this in a txt file in a fork, or send it as a private message or something, but ultimately this seems to be the only way I can find to communicate. -.- Meh. Anyway.
I've been thinking about how to go about making a more 'systematic' algorithm that loops through every possibility and then runs probabilities on each, rather than randomly generating sequences and hoping that each generation may eventually come up with a different moveset that possibly/hopefully results in something better than the ones before. Why be random when you can be systematic?
So I've spent the last couple hours coming up with this rough algorithm idea. Since my crafters are only level 15 I've only considered the abilities available to me, but it's basically a proof of concept kind of thing where it would easily adapt to include each of the abilities in the game. Warning, this is going to be long. =P And also rough. This is just like a first draft. I already know there's plenty to touch up, long before it even reaches the point of getting coded.
The main concept is that it runs on a recursive for loop. The loop would run through each of the abilities available, and then each combination of abilities, until it finds ones that qualify as a complete sequence, records the data to an array, and then moves on to try the rest of the abilities. Systematically going through every possible combination of abilities, until the sequence either results in running out of progress (a success) or running out of durability (potential failure).
Here's an example array the loop would be running off of: Example array: 1 - Basic Synth 2 - Basic touch 3 - Master's Mend 4 - Steady Hand 5 - Inner Quiet 6 - Observe 7 - Rumination 8 - Ingenuity 9 - Rapid Synthesis 10 - Manipulation 11 - Waste Not 12 - Careful Synthesis 13 - Tricks of the Trade 14 - Hasty Touch
And here's the loop which is the core of the whole algorithm:
I hope that comes through as legible. Right now it doesn't have a check for whether the sequence even succeeds or not, but once the results are pruned and sorted, any unsuccessful results will be at the very bottom of the list anyway. I'm thinking that only the top X results are displayed (where user decides X for themselves.) 5 would probably be a good default, maybe.
Ultimately this results in a results array that contains an array sequence with the list of abilities used, as well as the relevant stats returned by that sequence.
Sorting and pruning the array would occur, and would be easy to algorithm for. Essentially you want the ones with the highest success listed first, then if tie the highest quality output, then if tie the highest remaining durability, then if tie the one with fewer steps, then if tie the highest remaining CP.
Next is an algorithm to prune out duplicates. Essentially if the abilities used are the exact same, then prune out in a similar method to the above. If abilities are the same but one has higher success, prune the lesser. If all that is the same but one has higher quality output, prune the lesser. If all that is the same but one has higher remaining durability, prune the lesser. If all that is the same but one has fewer steps, prune the one with more. If all that is the same and one has higher remaining cp, prune the lesser. If all of that is the same and it's just the order of abilities that is the only difference at all, then prune the 2nd as a duplicate.
An additional prune results option would have the sequences stop if/when progress reaches max, assuming 100% success rate on synthesis. This shortens the macros to prevent extra synth steps past the point of it already being completed. Turning this option off adds more synth steps in the case of failure (if applicable) so the user does not have to manually finish in the event of failure.
By using this method, it would be easy to adapt to the 8-ball method simply by allowing the algorithm to run with 'starting' stats of a craft already in progress. At that point you'd just have to create some UI that would allow the user to input the in-progress stats as they go along, and the solver would automatically regenerate the solution list after each step. Right now the algorithm is set for max quality because of chance of failure, but in an 8-ball setting, detecting that the max quality is already achieved could easily be incorporated into quality-increasing steps getting skipped.
And finally, here's a list of the abilities available at level 15 (including cross class) and how they would 'plug in' to the main for loop for calculation purposes. Anywhere I use X or Y I'm basically depending on the reverse engineered formulas you already have in place for determining progress and quality and all that good stuff. These would basically be functions that get called when the for loop needs to calculate the results of a potential next step.
Right now conditions are ignored, and probability is calculated as minimum and maximum. Essentially the minimum is what you want to push as high as possible and potentially sort by, and the maximum would feed into your montecarlo simulations to try to attempt to find the most realistic chance of actual outcome. In the 8-ball setting, the montecarlo may or may not 'guess' at when conditions would apply to future steps or how successful future actions might be. The current step of any given 8-ball sequence's step would have a definite probability based on current stacks/conditions/stats, but future ones might need guessing in order to 'try for the best outcome.' That's a bit beyond my current skill, though, so I'll stick to systematic and min/max probabilities. =D
Anyway, here's a function for each of the level 15 and below abilities, including cross class: