doxxx / ffxiv-craft-opt-web

Web frontend for FFXIV Crafting Optimizer service.
zlib License
224 stars 198 forks source link

Possible 8-ball Algorithm #86

Closed Thortok closed 9 years ago

Thortok commented 9 years ago

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
doxxx commented 9 years ago

This is not something that we wish to pursue.

Thortok commented 9 years ago

Ah well, it was a fun mental exercise. I miss coding. =(