Skip to content

Instantly share code, notes, and snippets.

Created April 26, 2013 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/5470357 to your computer and use it in GitHub Desktop.
Save anonymous/5470357 to your computer and use it in GitHub Desktop.
package catus;
import catus.Profile.Slot;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
public class Reforger111 {
public int resultCount = 50; // number of results to generate
public int coreCount = 0; // number of cores to use (0 = automatic)
public int hitExpMin = 2550; // hit/exp objective (quasi-lower-bound)
public int hitExpRange = 5; // hit/exp range (quasi-upper-bound = min + range)
public boolean critGreater = false; // force crit to be greater (for bear form)
public int masteryGap = 0; // force mastery - max(haste, crit) > masteryGap
public AtomicBoolean abort; // set to true if we should stop
public ProgBar progBar = ProgBar.NONE; // progress bar api
static public final StatT[] STATS = {StatT.HIT, StatT.EXP, StatT.MASTERY, StatT.HASTE, StatT.CRIT};
static private final Comparator<int[]> CMP_HIT_EXP = new Comparator<int[]>() {
@Override
public int compare(int[] a, int[] b) {
int temp = a[0] - b[0];
return temp == 0 ? a[1] - b[1] : temp;
}
};
static private class S {
Slot slot;
int[][] perms, perms_he;
ReforgePair[] reforges;
int statCount;
}
static private class R {
final int score;
final byte[] cfg;
final int[] stat;
R(int score, byte[] cfg, int[] stat) {
this.score = score;
this.cfg = cfg;
this.stat = stat;
}
}
static private final Comparator<R> CMP_SCORE = new Comparator<R>() {
@Override
public int compare(R a, R b) {
return a.score - b.score;
}
};
public void reforge(Profile prof, Set<SlotT> slotSet) {
final long t = System.currentTimeMillis();
progBar.indeterminate();
// determine the following:
// what slots are active
// unreforgable base stats
// universe of reforgings for each slot
// universe of reforgings of hit/exp for each slot
final int statNum = STATS.length;
final int[] statBase = new int[statNum];
final int slotNum;
final S[] slots = new S[prof.SLOTS.length];
{
int num = 0;
for (Slot x: prof.SLOTS) {
if (x.item == null) {
continue;
}
if (slotSet == null || slotSet.contains(x.type)) {
int[] slotStat = new int[statNum];
int count = 0;
for (int i = 0; i < statNum; i++) {
StatT t = STATS[i];
statBase[i] += x.sumStat_unreforgeable(t);
int sum = slotStat[i] = x.sumStat_itemOnly(t);
if (sum > 0) {
count++;
}
}
if (count > 0 && count < statNum) {
S temp = new S();
temp.slot = x;
temp.statCount = count;
int len = 1 + count * (statNum - count);
temp.reforges = new ReforgePair[len];
temp.perms = new int[len][];
temp.perms[0] = slotStat;
int idx = 1;
for (int j = 0; j < statNum; j++) {
int stat = slotStat[j];
if (stat > 0) {
for (int k = 0; k < statNum; k++) {
if (slotStat[k] == 0) {
int[] copy = Arrays.copyOf(slotStat, statNum);
int chg = (int)(stat * 0.4);
copy[j] = stat - chg;
copy[k] = chg;
temp.reforges[idx] = ReforgePair.make(STATS[j], STATS[k]);
temp.perms[idx++] = copy;
}
}
}
}
TreeSet<int[]> heSet = new TreeSet(CMP_HIT_EXP);
for (int[] v: temp.perms) {
heSet.add(v);
}
temp.perms_he = heSet.toArray(new int[heSet.size()][]);
slots[num++] = temp;
}
} else {
for (int i = 0; i < statNum; i++) {
statBase[i] += x.sumStat(STATS[i]);
}
}
}
if (num == 0) {
throw new RuntimeException("There is nothing to reforge");
}
slotNum = num;
}
// dump reforge universe
/*
for (int i = 0; i < gearNum; i++) {
S s = gear[i];
System.out.println(s.slot.item.name);
for (int[] v: s.perms) {
System.out.println(Arrays.toString(v));
}
System.out.println();
}
*/
// compute min/max reforging values
int[] statMin = new int[statNum];
int[] statMax = new int[statNum];
for (int j = 0; j < statNum; j++) {
int sumMin = statBase[0];
int sumMax = sumMin;
for (int i = 0; i < slotNum; i++) {
int[][] perms = slots[i].perms;
int min = perms[0][j];
int max = min;
for (int k = 1; k < perms.length; k++) {
int val = perms[k][j];
if (val < min) {
min = val;
} else if (val > max) {
max = val;
}
}
sumMin += min;
sumMax += max;
}
statMin[j] = sumMin;
statMax[j] = sumMax;
}
//System.out.println("Min: " + Arrays.toString(statMin));
//System.out.println("Max: " + Arrays.toString(statMax));
// find valid hit/exp min/max based on objective
final int gap = Math.max(0, hitExpRange);
final int hitMin = Math.max(statMin[0], hitExpMin);
final int expMin = Math.max(statMin[1], hitExpMin);
final int hitMax = Math.min(statMax[0], hitMin + gap);
final int expMax = Math.min(statMax[1], expMin + gap);
// localize base stats
final int hit0 = statBase[0];
final int exp0 = statBase[1];
final int mr0 = statBase[2];
final int hr0 = statBase[3];
final int cr0 = statBase[4];
final boolean ignore = !critGreater;
final int[] eoq = new int[0]; // we can't stick null in deque
final ArrayDeque<int[]> possible = new ArrayDeque();
final PriorityQueue<R> results = new PriorityQueue(resultCount * 2, CMP_SCORE);
// consumer:
// given a valid hit/exp reforging
// find all possible 1:1:1s with matching hit/exp prefix
final int threadCount = Math.max(1, coreCount == 0 ? Runtime.getRuntime().availableProcessors() - 1 : coreCount);
for (int i = 0; i < threadCount; i++) {
new Thread() {
@Override
public void run() {
final int mg = masteryGap;
int[] cfg = new int[slotNum];
int[] bound = new int[slotNum];
int[][] reindex = new int[slotNum][10];
int[][][] space = new int[slotNum][10][];
try {
while (true) {
int[] cfg_he;
synchronized (possible) {
while (possible.isEmpty()) {
possible.wait();
if (abort != null && abort.get()) {
return;
}
}
cfg_he = possible.removeFirst();
if (cfg_he == eoq) {
break;
}
}
long tot = 1;
int hit = hit0;
int exp = exp0;
for (int i = 0; i < slotNum; i++) {
int[] re = reindex[i];
int[][] sp = space[i];
int[] match = slots[i].perms_he[cfg_he[i]];
int h = match[0];
int e = match[1];
int c = 0;
int[][] perms = slots[i].perms;
for (int j = 0; j < perms.length; j++) {
int[] v = perms[j];
if (v[0] == h && v[1] == e) {
re[c] = j;
sp[c++] = v;
}
}
tot *= bound[i] = c;
hit += h;
exp += e;
}
bound[slotNum - 1]++;
for (int i = 0; i < slotNum; i++) {
cfg[i] = 0;
}
for (long p = 0; p < tot; p++) {
int mr = mr0;
int hr = hr0;
int cr = cr0;
for (int i = 0; i < slotNum; i++) {
int[] stat = space[i][cfg[i]];
mr += stat[2];
hr += stat[3];
cr += stat[4];
}
_: if (mr > mg + hr && mr > mg + cr && (ignore || cr > hr)) {
synchronized (results) {
int score = (cr + hr) * 2 + mr;
if (results.size() >= resultCount) {
int worst = results.peek().score;
if (score < worst) {
break _;
} else if (score > worst) {
while (results.peek().score == worst) {
results.remove();
}
}
}
byte[] sol = new byte[slotNum];
for (int i = 0; i < slotNum; i++) {
sol[i] = (byte)reindex[i][cfg[i]];
}
results.add(new R(score, sol, new int[]{hit, exp, mr, hr, cr}));
}
}
for (int i = 0; ++cfg[i] == bound[i]; i++) {
cfg[i] = 0;
}
}
}
} catch (InterruptedException err) {
// die
}
synchronized (results) {
results.notify();
}
}
}.start();
}
// compute size of hit/exp universe
long tot = 1;
int[] bound = new int[slotNum];
int[][][] space_he = new int[slotNum][][];
for (int i = 0; i < slotNum; i++) {
int[][] perms = space_he[i] = slots[i].perms_he;
tot *= bound[i] = perms.length;
}
//System.out.println("Hit/Exp Perms: " + tot);
progBar.determinate();
// producer:
// for each unique hit/exp permutation
// add to queue if it meets objective
final long freqMask = 0x1FFF;
bound[slotNum - 1]++; // prevent bounds check
int posCount = 0;
int[] cfg = new int[slotNum];
for (long p = 0; p < tot; p++) {
int hit = hit0;
int exp = exp0;
for (int i = 0; i < slotNum; i++) {
int[] stat = space_he[i][cfg[i]];
hit += stat[0];
exp += stat[1];
}
if (hit >= hitMin && hit <= hitMax && exp >= expMin && exp <= expMax) {
posCount++;
synchronized (possible) {
possible.add(Arrays.copyOf(cfg, slotNum));
possible.notify();
}
}
for (int i = 0; ++cfg[i] == bound[i]; i++) {
cfg[i] = 0;
}
if ((p & freqMask) == freqMask) {
progBar.fraction(p / (double)tot);
}
}
progBar.indeterminate();
System.out.println("Found " + posCount + " possible: " + Fmt.msDur_since(t));
// signal consumers to shutdown
synchronized (possible) {
for (int i = 0; i < threadCount; i++) {
possible.add(eoq);
}
possible.notifyAll();
}
// wait for results
try {
while (true) {
synchronized (results) {
results.wait(1000);
}
synchronized (possible) {
if (possible.isEmpty()) {
break;
}
if (abort != null && abort.get()) {
possible.notifyAll();
throw new RuntimeException("Aborted");
}
}
}
} catch (InterruptedException err) {
// what the fuck
}
System.out.println("Found solutions: " + results.size());
// rank and trim results (note: heap is not sorted)
R[] res = results.toArray(new R[results.size()]);
Arrays.sort(res, Collections.reverseOrder(results.comparator()));
if (res.length > resultCount) {
res = Arrays.copyOf(res, resultCount);
}
// dump results
for (R x: res) {
System.out.println(x.score + " = " + Arrays.toString(x.stat));
}
// print best
R best = res[0];
for (int i = 0; i < slotNum; i++) {
S s = slots[i];
s.slot.setReforge(s.reforges[best.cfg[i]]);
}
System.out.print("Hit: " + prof.gearStat(StatT.HIT));
System.out.print(" / Exp: " + prof.gearStat(StatT.EXP));
System.out.print(" / Mastery: " + prof.gearStat(StatT.MASTERY));
System.out.print(" / Haste: " + prof.gearStat(StatT.HASTE));
System.out.print(" / Crit: " + prof.gearStat(StatT.CRIT));
System.out.println(prof.toReforgerade());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment