package com.spbsu.crawl.sessions;
import com.spbsu.commons.random.FastRandom;
import com.spbsu.commons.util.Pair;
import com.spbsu.crawl.bl.GameSession;
import com.spbsu.crawl.bl.Hero;
import com.spbsu.crawl.bl.Mob;
import com.spbsu.crawl.bl.crawlSystemView.MobsListener;
import com.spbsu.crawl.bl.events.HeroListener;
import com.spbsu.crawl.bl.events.MapListener;
import com.spbsu.crawl.bl.map.CrawlGameSessionMap;
import com.spbsu.crawl.bl.map.Position;
import com.spbsu.crawl.bl.map.TerrainType;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WeightedRandomWalkGameSession implements GameSession, MapListener, HeroListener, MobsListener {
private static final Logger LOG = Logger.getLogger(WeightedRandomWalkGameSession.class.getName());
private final CrawlGameSessionMap crawlMap = new CrawlGameSessionMap();
private int x;
private int y;
private double increment = 0.2;
private double prevScore = 0;
private double step = 0.1;
private int turn = 0;
private int hp;
public void alter(double score) {
if (score > prevScore) {
increment += step;
} else {
step *= -0.9;
increment += step;
}
prevScore = score;
}
@Override
public void observeMonster(final Mob mob) {
LOG.log(Level.ALL, "Observe monster " + mob.toString());
}
@Override
public void lostMonster(final Mob mob) {
LOG.log(Level.ALL, "Lost monster " + mob.toString());
}
class CellStats {
int x;
int y;
double weight;
public CellStats(int x, int y, double weight) {
this.x = x;
this.y = y;
this.weight = weight;
}
}
private TIntObjectHashMap<CellStats> stats = new TIntObjectHashMap<>();
private int idx(int x, int y) {
return x * 1000000 + y;
}
private CellStats getStats(int x, int y) {
if (!stats.containsKey(idx(x, y))) {
stats.put(idx(x, y), new CellStats(x, y, increment));
}
return stats.get(idx(x, y));
}
private void incWeight(int x, int y) {
final int key = idx(x, y);
if (stats.containsKey(key)) {
stats.get(key).weight += increment;
} else {
stats.put(key, new CellStats(x, y, 2 * increment));
}
}
private boolean canMoveTo(final int x, final int y) {
Optional<TerrainType> terrain = crawlMap.terrainOnCurrentLevel(x, y);
return !terrain.isPresent() || terrain.get() != TerrainType.WALL;
}
private Function<Mob.Action, Pair<Mob.Action, CellStats>> dirWeights = new Function<Mob.Action, Pair<Mob.Action, CellStats>>() {
@Override
public Pair<Mob.Action, CellStats> apply(Mob.Action action) {
switch (action) {
case MOVE_DOWN_LEFT:
return new Pair<>(action, getStats(x - 1, y + 1));
case MOVE_DOWN_RIGHT:
return new Pair<>(action, getStats(x + 1, y + 1));
case MOVE_DOWN:
return new Pair<>(action, getStats(x, y + 1));
case MOVE_LEFT:
return new Pair<>(action, getStats(x - 1, y));
case MOVE_RIGHT:
return new Pair<>(action, getStats(x + 1, y));
case MOVE_UP:
return new Pair<>(action, getStats(x, y - 1));
case MOVE_UP_LEFT:
return new Pair<>(action, getStats(x - 1, y - 1));
case MOVE_UP_RIGHT:
return new Pair<>(action, getStats(x + 1, y - 1));
}
return null;
}
};
private Predicate<Mob.Action> moveableDirection = new Predicate<Mob.Action>() {
@Override
public boolean test(Mob.Action action) {
switch (action) {
case MOVE_DOWN_LEFT:
return canMoveTo(x - 1, y + 1);
case MOVE_DOWN_RIGHT:
return canMoveTo(x + 1, y + 1);
case MOVE_DOWN:
return canMoveTo(x, y + 1);
case MOVE_LEFT:
return canMoveTo(x - 1, y);
case MOVE_RIGHT:
return canMoveTo(x + 1, y);
case MOVE_UP:
return canMoveTo(x, y - 1);
case MOVE_UP_LEFT:
return canMoveTo(x - 1, y - 1);
case MOVE_UP_RIGHT:
return canMoveTo(x + 1, y - 1);
default:
return false;
}
}
};
@Override
public Hero.Race race() {
return Hero.Race.Minotaur;
}
@Override
public Hero.Spec spec() {
return Hero.Spec.Fighter_Axe;
}
final Mob.Action[] moves;
{
moves = Stream.of(Mob.Action.values()).filter(action -> action.name().startsWith("MOVE"))
.collect(Collectors.toList()).toArray(new Mob.Action[]{});
}
final FastRandom rng = new FastRandom();
@Override
public Mob.Action action() {
List<Pair<Mob.Action, CellStats>> avaiableActions = Stream.of(moves).filter(moveableDirection)
.map(dirWeights)
.collect(Collectors.toList());
double totalSum = 0;
for (int i = 0; i < avaiableActions.size(); ++i) {
totalSum += Math.exp(-(avaiableActions.get(i).getSecond().weight));
}
double takenWeight = totalSum * rng.nextDouble();
int i = 0;
while (takenWeight > 0) {
takenWeight -= Math.exp(-avaiableActions.get(i).getSecond().weight);
++i;
}
--i;
avaiableActions.get(i).getSecond().weight++;
return avaiableActions.get(i).getFirst();
}
@Override
public Hero.Stat chooseStatForUpgrade() {
return Hero.Stat.Strength;
}
@Override
public void tile(final Position position, final TerrainType type) {
crawlMap.tile(position.x(), position.y(), type);
}
@Override
public void changeLevel(String id) {
crawlMap.changeLevel(id);
}
@Override
public void resetPosition() {
crawlMap.resetPosition();
}
@Override
public void heroPosition(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public void hp(int hp) {
this.hp = hp;
}
}