package net.bytten.metazelda;

import java.util.ArrayList;
import java.util.List;

public class LennasDungeonGenerator extends DungeonGenerator {

    // Red key, Green key, Blue key, Equipment (Spring)
    private int numKeys = 4;
    // Aim for 20 rooms +/- 25%
    private int targetRoomCount = 20;
    
    public LennasDungeonGenerator(long seed) {
        super(seed);
    }

    @Override
    protected Integer chooseAdjacentSpace(Room room) {
        return super.chooseAdjacentSpace(room);
    }

    @Override
    protected boolean chooseCreateNewItem() {
        return getRandom().nextFloat() <
            1.0f - (float)dungeon.itemCount() / (float)getNumKeys();
    }
    
    @Override
    protected boolean chooseCreatePaddingRoom(int depth) {
        return depth < 3 && getRandom().nextFloat() <
            1.0f - (float)dungeon.roomCount() / (float)getTargetRoomCount();
    }

    @Override
    protected Room chooseExistingRoom() {
        List<Room> rooms = dungeon.computeBoundaryRooms();
        List<Room> filtered = new ArrayList<Room>(rooms.size());
        for (Room room: rooms) {
            if (room.coords.y < 0)
                filtered.add(room);
        }
        if (filtered.size() == 0)
            filtered.add(dungeon.get(0,0));
        return filtered.get(getRandom().nextInt(filtered.size()));
    }

    @Override
    protected boolean chooseLinkNeighborOneWay(Room room, Room neighbor) {
        return !room.isGoal() && !neighbor.isGoal() &&
                super.chooseLinkNeighborOneWay(room, neighbor);
    }

    @Override
    protected Symbol choosePlacedItem() {
        return super.choosePlacedItem();
    }

    @Override
    protected boolean chooseReuseItem() {
        return super.chooseReuseItem();
    }
    
    public boolean desirable() {
        return dungeon != null &&
            dungeon.itemCount() == getNumKeys() &&
            dungeon.roomCount() >= getTargetRoomCount() * 0.75 &&
            dungeon.roomCount() <= getTargetRoomCount() * 1.25;
    }

    @Override
    public Dungeon generate() {
        while (!desirable()) {
            if (dungeon != null) {
                System.out.println("Discarding undesirable design and trying again");
            }
            super.generate();
        }
        return dungeon;
    }

    public int getNumKeys() {
        return numKeys;
    }

    public void setNumKeys(int numKeys) {
        this.numKeys = numKeys;
    }

    public int getTargetRoomCount() {
        return targetRoomCount;
    }

    public void setTargetRoomCount(int targetRoomCount) {
        this.targetRoomCount = targetRoomCount;
    }

    @Override
    protected boolean newRoomAllowedInSpace(Coords xy) {
        return super.newRoomAllowedInSpace(xy) && xy.y <= 0;
    }

}

@@ -378,192 +378,1495 @@
   %7D%0A
-%0A    @Override%0A    protected Integer chooseAdjacentSpace(Room room) %7B%0A        return super.chooseAdjacentSpace(room);%0A    %7D%0A%0A    @Override%0A    protected boolean chooseCreateNewItem() %7B
+    %0A    private float computeSpaceChoiceProbFactor(Room room, Coords xy) %7B%0A        int x = Math.abs(xy.x),%0A            y = Math.abs(xy.y);%0A        if (x %3C Math.abs(room.coords.x)) x = 0;%0A        if (y %3C Math.abs(room.coords.y)) y = 0;%0A        int dist = Math.max(x, y);%0A        return 1.0f / (2*dist + 1.0f);%0A    %7D%0A%0A    @Override%0A    protected Integer chooseAdjacentSpace(Room room) %7B%0A        // Choose a random direction, but bias it so that we stay near 0,0%0A        float dirs%5B%5D = new float%5BDirection.NUM_DIRS%5D;%0A        float sum = 0.0f;%0A        boolean atLeastOne = false;%0A        for (int d = 0; d %3C dirs.length; ++d) %7B%0A            Coords xy = room.coords.nextInDirection(d);%0A            if (newRoomAllowedInSpace(xy)) %7B%0A                float val = computeSpaceChoiceProbFactor(room, xy);%0A                dirs%5Bd%5D = val;%0A                sum += val;%0A                atLeastOne = true;%0A            %7D else %7B%0A                dirs%5Bd%5D = 0.0f;%0A            %7D%0A        %7D%0A        if (!atLeastOne) return null;%0A        for (int d = 0; d %3C dirs.length; ++d) %7B%0A            dirs%5Bd%5D /= sum;%0A        %7D%0A        float p = getRandom().nextFloat();%0A        float x = 0.0f;%0A        int d = 0;%0A        while (d %3C dirs.length) %7B%0A            x += dirs%5Bd%5D;%0A            if (x %3E p) %7B%0A                return d;%0A            %7D%0A            ++d;%0A        %7D%0A        return null;%0A    %7D%0A%0A    @Override%0A    protected boolean chooseCreateNewItem() %7B%0A        // choose probabilities to aim towards a specific number of keys
 %0A   
@@ -2052,24 +2052,98 @@
 nt depth) %7B%0A
+        // choose probabilities to aim towards a specific number of rooms%0A
         retu

tweak space selection probabilities to make dungeons generally more balanced.