/** * @author Luke Tyler Downey * Copyright 2011 Glow Interactive * * This software contains original work and/or modifications to * original work, which are redistributed under the following terms. * * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /** * Copyright 2011 Brian Cairns * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.glowinteractive.reforger; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; // import java.util.concurrent.Executors; // import java.util.concurrent.ExecutorService; // import java.util.concurrent.TimeUnit; public final class Reforger implements Runnable { private static final int INDEX_DEC = 0; private static final int INDEX_INC = 1; private static final int INDEX_VAL = 2; // private static final int OPTIMAL_THREADS = 4; private Float _optimalEP = 0.0f; private int[] _globalOptions; private int[][][] _reforgeMatrix; private float[] _epDeltaMax; private Model _model; private String _realm; private String _character; public Reforger(String realm, String character) { _realm = realm; _character = character; _model = new Model(); } @Override public void run() { // Get character info System.out.println("Downloading character data from Armory."); Character character = new Character("us", _realm, _character); character.parse(); StatKVMap mutable = character.mutableStats(), immutable = character.immutableStats(), cumulative = mutable.add(immutable); System.out.println("Mutable Secondary Stats (Derived From Base Itemization):"); System.out.println(mutable + "\n"); System.out.println("Immutable Secondary Stats (Derived From Bonuses, e.g. Gems, Enchants):"); System.out.println(immutable + "\n"); System.out.println("Cumulative Secondary Stats:"); System.out.println(cumulative + "\n"); // Determine current / best EP as a baseline. // float noReforgingEp = model.calculateEp(noReforgingStats.getData()); // float currentStatEp = model.calculateEp(currentStats.getData()); // bestEp = Math.max(currentStatEp, noReforgingEp) - 1; _optimalEP = _model.calculateEP(cumulative.data()); // Max EP Delta per item. HashMap<Item, Float> itemEPDeltaMax = new HashMap<Item, Float>(character.items().size()); // Per item reforge candidate lists. ArrayList<ArrayList<Candidate>> options = new ArrayList<ArrayList<Candidate>>(character.items().size()); // Global candidate list (union over options). ArrayList<Candidate> candidates = new ArrayList<Candidate>(character.items().size() * Stat.TYPE_COUNT); int maxOptions = 0; for (Item item : character.items()) { float itemDeltaMax = 0.0f; HashSet<StatKVMap> itemCandidates = item.candidates(_model.candidateMappings()); ArrayList<Candidate> itemOptions = new ArrayList<Candidate>(Stat.TYPE_COUNT); for (StatKVMap delta : itemCandidates) { Candidate option = new Candidate(item, delta); itemOptions.add(option); candidates.add(option); // Calculate the EP delta upper bound for each of this item's possible reforge values. for (Stat dec : Stat.values()) { int value = delta.value(dec); if (value < 0) { float itemDelta = _model.calculateEPDeltaMax(dec, Math.abs(value)); if (itemDelta > itemDeltaMax) { itemDeltaMax = itemDelta; } } } } if (itemOptions.size() > 0) { if (itemOptions.size() > maxOptions) { maxOptions = itemOptions.size(); } itemEPDeltaMax.put(item, itemDeltaMax); options.add(itemOptions); } } // Output candidate count / list. System.out.println("Considering " + candidates.size() + " possible reforgings."); // for (Candidate o : candidates) { // System.out.println(" " + o); // } // Compute max EP delta for sublists. _epDeltaMax = new float[options.size()]; for (int i = 0; i < options.size(); ++i) { float sublistEPDelta = 0.0f; for (int j = i + 1; j < options.size(); ++j) { Item item = options.get(j).get(0).item(); sublistEPDelta += itemEPDeltaMax.get(item); } _epDeltaMax[i] = sublistEPDelta; } // Create reforging matrix and result array. _reforgeMatrix = new int[options.size()][maxOptions][3]; _globalOptions = new int[options.size()]; for (int i = 0; i < options.size(); ++i) { ArrayList<Candidate> itemOptions = options.get(i); for (int j = 0; j < maxOptions; ++j) { if (j < itemOptions.size()) { Candidate o = itemOptions.get(j); _reforgeMatrix[i][j][INDEX_DEC] = o.indexDecreased(); _reforgeMatrix[i][j][INDEX_INC] = o.indexIncreased(); _reforgeMatrix[i][j][INDEX_VAL] = o.deltaValue(); } else { _reforgeMatrix[i][j][INDEX_DEC] = -1; _reforgeMatrix[i][j][INDEX_INC] = -1; _reforgeMatrix[i][j][INDEX_VAL] = 0; } } } long startTime, endTime; System.out.print("Calculating . . . "); startTime = System.currentTimeMillis(); computeOptimalCandidate(cumulative.data(), 0); endTime = System.currentTimeMillis(); long time = endTime - startTime; System.out.println("done."); System.out.println("Calculation time: " + time + " ms."); // Decode result array. ArrayList<Candidate> resultCandidates = new ArrayList<Candidate>(_globalOptions.length); for (int i = 0; i < _globalOptions.length; ++i) { if (_globalOptions[i] != -1) { resultCandidates.add(options.get(i).get(_globalOptions[i])); } } ArrayList<Item> unusedItems = new ArrayList<Item>(character.items()); System.out.println(); System.out.println("Optimal reforging:"); for (Candidate o : resultCandidates) { System.out.println(o); unusedItems.remove(o.item()); } if (!unusedItems.isEmpty()) { System.out.println("Not reforged:"); for (Item item : unusedItems) { System.out.println(" " + item); } } // printStats("Stats with no reforgings (EP = %.1f):", noReforgingStats); // printStats("Stats with current reforgings (EP = %.1f):", currentStats); // Calculate stats after recommended reforgings. StatKVMap resultStats = cumulative; for (Candidate o : resultCandidates) { resultStats = resultStats.add(o.delta()); } System.out.println(); System.out.println("Reforged Stats:"); System.out.println(resultStats); System.out.println(); // double currentEP = _model.calculateEP(cumulative.data()); // double recommendedEp = _model.calculateEP(recommendStats.data()); // double improvement = Math.round((recommendedEp - currentEP) * 10) / 10.0; // printStats("Stats with recommended reforgings (EP = %.1f):", recommendStats); // System.out.println("Improvement over current reforgings: " + improvement); } private boolean computeOptimalCandidate(int[] stats, int depth) { int[][] currentOptions = _reforgeMatrix[depth]; boolean improved = false; // Base case if (depth + 1 == _epDeltaMax.length) { for (int i = 0; i < currentOptions.length; ++i) { int indexDec = currentOptions[i][INDEX_DEC]; if (indexDec == -1) { break; } int indexInc = currentOptions[i][INDEX_INC]; int deltaValue = currentOptions[i][INDEX_VAL]; stats[indexDec] -= deltaValue; stats[indexInc] += deltaValue; float resultEP = _model.calculateEP(stats); if (resultEP > _optimalEP) { _optimalEP = resultEP; _globalOptions[depth] = i; improved = true; } stats[indexDec] += deltaValue; stats[indexInc] -= deltaValue; } return improved; } double currentEPDeltaMax = _epDeltaMax[depth]; for (int i = 0; i < currentOptions.length; ++i) { int indexDec = currentOptions[i][INDEX_DEC]; if (indexDec == -1) { break; } int indexInc = currentOptions[i][INDEX_INC]; int deltaValue = currentOptions[i][INDEX_VAL]; stats[indexDec] -= deltaValue; stats[indexInc] += deltaValue; float resultEP = _model.calculateEP(stats); if (resultEP + currentEPDeltaMax > _optimalEP) { if (computeOptimalCandidate(stats, depth + 1)) { _globalOptions[depth] = i; improved = true; } } stats[indexDec] += deltaValue; stats[indexInc] -= deltaValue; } float currentEP = _model.calculateEP(stats); if (currentEP + currentEPDeltaMax > _optimalEP) { if (computeOptimalCandidate(stats, depth + 1)) { _globalOptions[depth] = -1; improved = true; } } return improved; } }