<Query Kind="Program">
<Reference><RuntimeDirectory>\System.Drawing.dll</Reference>
<Namespace>System.Drawing</Namespace>
</Query>
void Main()
{
var roomDescs = new RoomsProvider();
roomDescs.Load(@"C:\Users\Admin\Documents\isaac\levelgen\resources\rooms");
var bot1Total = new int[14];
var bot2Total = new int[14];
foreach (var fp in new DirectoryInfo(@"C:\Users\Admin\Documents\isaac\levelgen\dump2").GetFiles())
{
using (var file = File.OpenRead(fp.FullName))
{
Console.WriteLine(file.Name);
var reader = new LayoutReader(file, roomDescs);
StageBlock sb;
while ((sb = reader.Read()) != null)
{
for (var i = 0; i < sb.Stages.Length; i++)
{
if (sb.Stages[i].StageId == 9 || sb.Stages[i].StageId == 12 || (sb.Stages[i].StageId == 10 && sb.Stages[i].StageType == 1) || (sb.Stages[i].StageId == 11 && sb.Stages[i].StageType == 1))
continue;
var seedStr = SeedToString(sb.Seed);
//Console.WriteLine("{0} (Stage {1})", SeedToString(sb.Seed), sb.Stages[i].StageId);
//Console.WriteLine("--------");
Func<Room, string> roomToChar = (room) =>
{
if (room == null)
return "O";
if (room.StageIndex == 0 && room.RoomType == RoomType.ROOM_DEFAULT && room.RoomId == 2)
return "S";
if (room.RoomType == RoomType.ROOM_BOSS)
return "B";
return "X";
};
for (var y = 0; y < 13; y++)
{
var line = string.Join("", Enumerable.Range(0, 13).Select(k => roomToChar(sb.Stages[i].RoomGrid[y][k])));
//Console.WriteLine(line);
}
var bot = new Bot1();
bot.Start(sb.Stages[i]);
do
{
} while (bot.Think());
var bot2 = new Bot2();
bot2.Start(sb.Stages[i]);
do
{
} while (bot2.Think());
//Console.WriteLine(bot.RoomsVisited);
//Console.WriteLine(bot2.RoomsVisited);
bot1Total[sb.Stages[i].StageId] += bot.RoomsVisited;
bot2Total[sb.Stages[i].StageId] += bot2.RoomsVisited;
}
}
}
}
bot1Total.Dump();
bot2Total.Dump();
}
public class RoomState
{
public bool Visited = false;
public bool Revealed = false;
public Room Room;
}
public class BotState
{
public Stage Stage;
public Point Pos;
public RoomState[][] GridState;
public Room CurrentRoom { get { return GridState[Pos.Y][Pos.X].Room; }}
public BotState(Stage stage)
{
Stage = stage;
GridState = new RoomState[13][];
for (var j = 0; j < GridState.Length; j++)
GridState[j] = new RoomState[13];
var gridStateArr = new RoomState[Stage.Rooms.Length];
for (var y = 0; y < 13; y++)
{
for (var x = 0; x < 13; x++)
{
var room = stage.RoomGrid[y][x];
if (room == null)
{
GridState[y][x] = new RoomState();
continue;
}
var state = gridStateArr[room.RoomOffset] ?? new RoomState { Room = room};
gridStateArr[room.RoomOffset] = state;
GridState[y][x] = state;
}
}
}
}
public abstract class Bot
{
public BotState State;
public Room[][] Grid { get { return State.Stage.RoomGrid; } }
public RoomState[][] GridState { get { return State.GridState; } }
public Point GetStart()
{
for (var y = 0; y < 13; y++)
{
for (var x = 0; x < 13; x++)
{
var room = Grid[y][x];
if (room == null)
continue;
if (room.StageIndex == 0 && room.RoomType == RoomType.ROOM_DEFAULT && room.RoomId == 2 //Standard start room
|| ((room.StageIndex == 16 || room.StageIndex == 17) && room.RoomType == RoomType.ROOM_DEFAULT && room.RoomId == 0)) //Chest/DarkRoom start rooms
{
return new Point(x, y);
}
}
}
throw new Exception("Start room not found D:");
}
public Point GetEnd()
{
for (var y = 0; y < 13; y++)
{
for (var x = 0; x < 13; x++)
{
var room = Grid[y][x];
if (room == null)
continue;
if (room.RoomType == RoomType.ROOM_BOSS)
{
return new Point(x, y);
}
}
}
throw new Exception("Boss room not found D:");
}
public virtual void Start(Stage stage)
{
State = new BotState(stage);
State.Pos = GetStart();
}
public abstract bool Think();
public virtual void End()
{
}
public static bool InBounds(Point p)
{
return p.X >= 0 && p.Y >= 0 && p.X < 13 && p.Y < 13;
}
//The points are using the top/left most grid x/y.
#region shape doors
public static Dictionary<RoomShape, Point[]> ShapeDoors = new Dictionary<UserQuery.RoomShape, System.Drawing.Point[]>
{
// U R D L, U R D L
{RoomShape.ROOMSHAPE_1x1, new [] { new Point(0, -1), new Point(1, 0), new Point(0, 1), new Point(-1, 0), default(Point), default(Point), default(Point), default(Point) } },
{RoomShape.ROOMSHAPE_1x2, new [] { new Point(0, -1), new Point(1, 0), new Point(0, 2), new Point(-1, 0), default(Point), new Point(1, 1), default(Point), new Point(-1, 1) } },
{RoomShape.ROOMSHAPE_2x1, new [] { new Point(0, -1), new Point(2, 0), new Point(0, 1), new Point(-1, 0), new Point(1, -1), default(Point), new Point(1, 1), default(Point) } },
{RoomShape.ROOMSHAPE_2x2, new [] { new Point(0, -1), new Point(2, 0), new Point(0, 2), new Point(-1, 0), new Point(1, -1), new Point(2, 1), new Point(1, 2), new Point(-1, 1)} },
{RoomShape.ROOMSHAPE_IH, new [] { default(Point), new Point(1, 0), default(Point), new Point(-1, 0) , default(Point), default(Point), default(Point), default(Point) } },
{RoomShape.ROOMSHAPE_IIH, new [] { default(Point), new Point(2, 0), default(Point), new Point(-1, 0) , default(Point), default(Point), default(Point), default(Point) } },
{RoomShape.ROOMSHAPE_IV, new [] { new Point(0, -1), default(Point), new Point(0, 1), default(Point), default(Point), default(Point), default(Point), default(Point) } },
{RoomShape.ROOMSHAPE_IIV, new [] { new Point(0, -1), default(Point), new Point(0, 2), default(Point), default(Point), default(Point), default(Point), default(Point) } },
{RoomShape.ROOMSHAPE_LTL, new [] { new Point(-1, 0), new Point(1, 0), new Point(-1, 2), new Point(-1, 0), new Point(0, -1), new Point(1, 1), new Point(0, 2), new Point(-2, 1) } },
{RoomShape.ROOMSHAPE_LTR, new [] { new Point(0, -1), new Point(1, 0), new Point(0, 2), new Point(-1, 0), new Point(1, 0), new Point(2, 1), new Point(1, 2), new Point(-1, 1) } },
{RoomShape.ROOMSHAPE_LBL, new [] { new Point(0, -1), new Point(2, 0), new Point(0, 1), new Point(-1, 0), new Point(1, -1), new Point(2, 1), new Point(1, 2), new Point(0,-1) } },
{RoomShape.ROOMSHAPE_LBR, new [] { new Point(0, -1), new Point(2, 0), new Point(0, 2), new Point(-1, 0), new Point(1, -1), new Point(1, 1), new Point(1, 1), new Point(-1, 1) } },
};
#endregion
}
public class Bot1 : Bot
{
Stack<Point> Visited = new Stack<System.Drawing.Point>();
public int RoomsVisited;
static RoomState[] exitRooms = new RoomState[8];
#region weights
static Dictionary<RoomShape, float> ShapeWeights = new Dictionary<UserQuery.RoomShape, float>
{
{RoomShape.ROOMSHAPE_1x1, 1},
{RoomShape.ROOMSHAPE_1x2, 1.5f},
{RoomShape.ROOMSHAPE_2x1, 1.5f},
{RoomShape.ROOMSHAPE_2x2, 2},
{RoomShape.ROOMSHAPE_IH, 1f},
{RoomShape.ROOMSHAPE_IIH, 1.5f},
{RoomShape.ROOMSHAPE_IIV, 1.5f},
{RoomShape.ROOMSHAPE_IV, 1f},
{RoomShape.ROOMSHAPE_LBL, 1.75f},
{RoomShape.ROOMSHAPE_LBR, 1.75f},
{RoomShape.ROOMSHAPE_LTL, 1.75f},
{RoomShape.ROOMSHAPE_LTR, 1.75f},
};
#endregion
void RevealRooms()
{
//Revealing secret rooms is fine for now since we don't actually enter them.
GridState[State.Pos.Y][State.Pos.X].Revealed = true;
var exits = ShapeDoors[State.CurrentRoom.Shape];
for (var i = 0; i < exits.Length; i++)
{
if (exits[i] == default(Point))
continue;
var dstP = State.Pos.Add(exits[i]);
if (!InBounds(dstP))
continue;
var dstRoom = State.GridState[dstP.Y][dstP.X];
dstRoom.Revealed = true;
}
}
bool IsPossible(Point pos, HashSet<Point> visited)
{
var room = State.GridState[pos.Y][pos.X];
if (!room.Revealed)
return true;
if (room.Revealed && room.Room == null)
return false;
visited.Add(pos);
var exits = ShapeDoors[room.Room.Shape];
for (var i = 0; i < exits.Length; i++)
{
if (exits[i] == default(Point))
continue;
var dstP = State.Pos.Add(exits[i]);
if (!InBounds(dstP))
continue;
if (visited.Contains(dstP))
continue;
var dstRoom = State.GridState[dstP.Y][dstP.X];
if (dstRoom.Revealed && dstRoom.Room != null && !IsPossible(dstP, visited))
return false;
}
return true;
}
Point ChooseNextRoom()
{
var exits = ShapeDoors[State.CurrentRoom.Shape];
for (var i = 0; i < exits.Length; i++)
{
exitRooms[i] = null;
if (exits[i] == default(Point))
continue;
var dstP = State.Pos.Add(exits[i]);
if (!InBounds(dstP))
continue;
var dstRoom = State.GridState[dstP.Y][dstP.X];
if (dstRoom.Visited || dstRoom.Room == null)
continue;
if (dstRoom.Room.RoomType == RoomType.ROOM_BOSS)
return dstP;
if (dstRoom.Room.RoomType != RoomType.ROOM_DEFAULT)
continue;
if (!IsPossible(dstP, new HashSet<Point>()))
{
Func<RoomState, string> roomToChar = (room) =>
{
if (room == null || room.Room == null)
return "O";
return room.Visited ? "1" : "2";
};
for (var y = 0; y < 13; y++)
{
var line = string.Join("", Enumerable.Range(0, 13).Select(k => roomToChar(this.GridState[y][k])));
Console.WriteLine(line);
}
IsPossible(dstP, new HashSet<Point>());
continue;
}
exitRooms[i] = dstRoom;
}
Array.Sort(exitRooms, (l, r) =>
{
if (l == null && r != null)
return 1;
if (r == null && l != null)
return -1;
if (l == null && r == null)
return 0;
return ShapeWeights[l.Room.Shape].CompareTo(ShapeWeights[r.Room.Shape]);
});
return exitRooms[0] != null && exitRooms[0].Room != null ? new Point(exitRooms[0].Room.RoomX, exitRooms[0].Room.RoomY) : new Point(-1,-1);
}
public override bool Think()
{
GridState[State.Pos.Y][State.Pos.X].Visited = true;
RevealRooms();
var nextRoomPos = ChooseNextRoom();
if (nextRoomPos.X == -1 && nextRoomPos.Y == -1)
{
if (Visited.Count < 1)
{
Func<RoomState, string> roomToChar = (room) =>
{
if (room == null || room.Room == null)
return "O";
return room.Visited ? "1" : "2";
};
for (var y = 0; y < 13; y++)
{
var line = string.Join("", Enumerable.Range(0, 13).Select(k => roomToChar(this.GridState[y][k])));
Console.WriteLine(line);
}
throw new Exception("Ran out of choices before finding the boss room");
}
State.Pos = Visited.Pop();
return true;
}
RoomsVisited++;
var nextRoom = Grid[nextRoomPos.Y][nextRoomPos.X];
var adjNextRoomPos = new Point(nextRoom.RoomX, nextRoom.RoomY);
Visited.Push(State.Pos);
State.Pos = adjNextRoomPos;
if (State.CurrentRoom.RoomType == RoomType.ROOM_BOSS)
return false;
return true;
}
}
public class Bot2 : Bot
{
Stack<Point> Visited = new Stack<System.Drawing.Point>();
public int RoomsVisited;
static RoomState[] exitRooms = new RoomState[8];
#region weights
static Dictionary<RoomShape, float> ShapeWeights = new Dictionary<UserQuery.RoomShape, float>
{
{RoomShape.ROOMSHAPE_1x1, 1},
{RoomShape.ROOMSHAPE_1x2, 1.5f},
{RoomShape.ROOMSHAPE_2x1, 1.5f},
{RoomShape.ROOMSHAPE_2x2, 2},
{RoomShape.ROOMSHAPE_IH, 1f},
{RoomShape.ROOMSHAPE_IIH, 1.5f},
{RoomShape.ROOMSHAPE_IIV, 1.5f},
{RoomShape.ROOMSHAPE_IV, 1f},
{RoomShape.ROOMSHAPE_LBL, 1.75f},
{RoomShape.ROOMSHAPE_LBR, 1.75f},
{RoomShape.ROOMSHAPE_LTL, 1.75f},
{RoomShape.ROOMSHAPE_LTR, 1.75f},
};
#endregion
Point ChooseNextRoom()
{
var exits = ShapeDoors[State.CurrentRoom.Shape];
for (var i = 0; i < exits.Length; i++)
{
exitRooms[i] = null;
if (exits[i] == default(Point))
continue;
var dstP = State.Pos.Add(exits[i]);
if (!InBounds(dstP))
continue;
var dstRoom = State.GridState[dstP.Y][dstP.X];
if (dstRoom.Visited || dstRoom.Room == null)
continue;
if (dstRoom.Room.RoomType != RoomType.ROOM_DEFAULT && dstRoom.Room.RoomType != RoomType.ROOM_BOSS)
continue;
exitRooms[i] = dstRoom;
}
Array.Sort(exitRooms, (l, r) =>
{
if (l == null && r != null)
return 1;
if (r == null && l != null)
return -1;
if (l == null && r == null)
return 0;
if (l.Room == null && r.Room != null)
return 1;
if (r.Room == null && l.Room != null)
return -1;
return -ShapeWeights[l.Room.Shape].CompareTo(ShapeWeights[r.Room.Shape]);
});
return exitRooms[0] != null && exitRooms[0].Room != null ? new Point(exitRooms[0].Room.RoomX, exitRooms[0].Room.RoomY) : new Point(-1, -1);
}
public override bool Think()
{
GridState[State.Pos.Y][State.Pos.X].Visited = true;
var nextRoomPos = ChooseNextRoom();
if (nextRoomPos.X == -1 && nextRoomPos.Y == -1)
{
if (Visited.Count < 1)
throw new Exception("Ran out of choices before finding the boss room");
State.Pos = Visited.Pop();
return true;
}
RoomsVisited++;
var nextRoom = Grid[nextRoomPos.Y][nextRoomPos.X];
var adjNextRoomPos = new Point(nextRoom.RoomX, nextRoom.RoomY);
Visited.Push(State.Pos);
State.Pos = adjNextRoomPos;
if (State.CurrentRoom.RoomType == RoomType.ROOM_BOSS)
return false;
return true;
}
}
public static class PointExt
{
public static Point Add(this Point l, Point r)
{
return new Point(l.X+r.X, l.Y + r.Y);
}
}
public class RoomDescriptor
{
public RoomType RoomType;
public int RoomId; //variant
public int RoomSubType;
public RoomShape RoomShape;
}
public class RoomsProvider
{
public Dictionary<int, Dictionary<Tuple<RoomType, int>, RoomDescriptor>> StageRooms;
public void Load(string path)
{
var xmlFiles = new DirectoryInfo(path).GetFiles("*.xml");
StageRooms = new Dictionary<int, Dictionary<Tuple<RoomType, int>, RoomDescriptor>>();
for (var i = 0; i < xmlFiles.Length; i++)
{
var stageIdx = int.Parse(Regex.Match(xmlFiles[i].Name, "^(\\d\\d)").Groups[1].Value);
var doc = XDocument.Load(xmlFiles[i].FullName);
var xmlRooms = doc.XPathSelectElements("//rooms/room").ToList();
var rooms = new Dictionary<Tuple<RoomType, int>, RoomDescriptor>();
for (var j = 0; j < xmlRooms.Count; j++)
{
var id = xmlRooms[j].Get<int>("variant");
var type = (RoomType)xmlRooms[j].Get<int>("type");
rooms[Tuple.Create(type, id)] = new RoomDescriptor
{
RoomType = type,
RoomId = id,
RoomSubType = xmlRooms[j].Get<int>("subtype"),
RoomShape = (RoomShape)xmlRooms[j].Get<int>("shape")
};
}
StageRooms[stageIdx] = rooms;
}
StageRooms[3] = StageRooms[1];
StageRooms[6] = StageRooms[4];
StageRooms[9] = StageRooms[7];
StageRooms[12] = StageRooms[10];
}
}
public class LayoutReader
{
Stream stream;
RoomsProvider RoomsProv;
public LayoutReader(Stream s, RoomsProvider roomsProv)
{
stream = s;
RoomsProv = roomsProv;
}
public StageBlock Read()
{
if (stream.Position >= stream.Length)
return null;
var stages = new Stage[14];
var seed = stream.ReadUInt32();
for (var i = 0; i < 14; i++)
{
var stageId = stream.ReadInt8();
var stageType = stream.ReadInt8();
var roomCount = stream.ReadInt8();
var gridCount = stream.ReadInt8();
var grid = new Room[13][];
for (var j = 0; j < grid.Length; j++)
grid[j] = new Room[13];
var seedStr = SeedToString(seed);
var rooms = new Room[roomCount];
for (var j = 0; j < gridCount; j++)
{
var sRoom = Room.ReadRoom(stream, RoomsProv);
var room = rooms[sRoom.RoomOffset] ?? sRoom; //Have all of the grid parts of a room point to the same reference.
grid[sRoom.RoomY][sRoom.RoomX] = room;
rooms[room.RoomOffset] = room;
}
var iAmEror = Room.ReadNonGridRoom(stream, RoomsProv);
var crawlSpace = Room.ReadNonGridRoom(stream, RoomsProv);
var blackMarket = Room.ReadNonGridRoom(stream, RoomsProv);
Room bossRush = null;
if (stageId == 6)
bossRush = Room.ReadNonGridRoom(stream, RoomsProv);
stages[i] = new Stage(stageId, stageType, grid, rooms);
}
return new StageBlock { Seed = seed, Stages = stages };
}
}
public class Room
{
public int StageIndex;
public int RoomOffset;
public int RoomX;
public int RoomY;
public RoomType RoomType;
public int RoomSubType;
public int RoomId;
public RoomShape Shape;
public static Room ReadRoom(Stream s, RoomsProvider prov)
{
var r = new Room
{
RoomOffset = s.ReadInt8(),
RoomX = s.ReadInt8(),
RoomY = s.ReadInt8(),
StageIndex = s.ReadInt8(),
RoomType = (RoomType)s.ReadInt8(),
RoomSubType = s.ReadInt8(),
RoomId = s.ReadInt16(),
};
r.Shape = prov.StageRooms[r.StageIndex][Tuple.Create(r.RoomType, r.RoomId)].RoomShape;
return r;
}
public static Room ReadNonGridRoom(Stream s, RoomsProvider prov)
{
var r = new Room
{
RoomOffset = -1,
RoomX = -1,
RoomY = -1,
StageIndex = s.ReadInt8(),
RoomType = (RoomType)s.ReadInt8(),
RoomSubType = s.ReadInt8(),
RoomId = s.ReadInt16(),
};
r.Shape = prov.StageRooms[r.StageIndex][Tuple.Create(r.RoomType, r.RoomId)].RoomShape;
return r;
}
}
public enum FloorType
{
Special = 0,
Basement = 1,
Cellar = 2,
BurningBasement = 3,
Caves = 4,
Catacombs = 5,
FloodedCaves = 6,
Depths = 7,
Necropolis = 8,
DankDepths = 9,
Womb = 10,
Utero = 11,
ScarredWomb = 12,
BlueWomb = 13,
Sheol = 14,
Cathedral = 15,
DarkRoom = 16,
Chest = 17,
}
public enum RoomType
{
ROOM_NULL = 0,
ROOM_DEFAULT = 1,
ROOM_SHOP = 2,
ROOM_ERROR = 3,
ROOM_TREASURE = 4,
ROOM_BOSS = 5,
ROOM_MINIBOSS = 6,
ROOM_SECRET = 7,
ROOM_SUPERSECRET = 8,
ROOM_ARCADE = 9,
ROOM_CURSE = 10,
ROOM_CHALLENGE = 11,
ROOM_LIBRARY = 12,
ROOM_SACRIFICE = 13,
ROOM_DEVIL = 14,
ROOM_ANGEL = 15,
ROOM_DUNGEON = 16,
ROOM_BOSSRUSH = 17,
ROOM_ISAACS = 18,
ROOM_BARREN = 19,
ROOM_CHEST = 20,
ROOM_DICE = 21,
ROOM_BLACK_MARKET = 22,
ROOM_GREED_EXIT = 23,
NUM_ROOMTYPES = 24
}
public enum RoomShape
{
ROOMSHAPE_NULL = 0,
ROOMSHAPE_1x1 = 1,
ROOMSHAPE_IH = 2,
ROOMSHAPE_IV = 3,
ROOMSHAPE_1x2 = 4,
ROOMSHAPE_IIV = 5,
ROOMSHAPE_2x1 = 6,
ROOMSHAPE_IIH = 7,
ROOMSHAPE_2x2 = 8,
ROOMSHAPE_LTL = 9,
ROOMSHAPE_LTR = 10,
ROOMSHAPE_LBL = 11,
ROOMSHAPE_LBR = 12,
NUM_ROOMSHAPES = 13
}
public class Stage
{
public int StageId;
public int StageType;
public Room[][] RoomGrid;
public Room[] Rooms;
public Stage(int stageId, int stageType, Room[][] grid, Room[] rooms)
{
StageId = stageId;
StageType = stageType;
RoomGrid = grid;
Rooms = rooms;
}
}
public class StageBlock
{
public uint Seed;
public Stage[] Stages;
}
enum Direction : int
{
None = 0,
North = 1,
East = 2,
South = 4,
West = 8,
NorthEast = North | East,
NorthWest = North | West,
SouthEast = South | East,
SouthWest = South | West,
}
// Define other methods and classes here
static string SeedToString(uint num)
{
const string chars = "ABCDEFGHJKLMNPQRSTWXYZ01234V6789";
byte x = 0;
var tnum = num;
while (tnum != 0)
{
x += ((byte)tnum);
x += (byte)(x + (x >> 7));
tnum >>= 5;
}
num ^= 0x0FEF7FFD;
tnum = (num) << 8 | x;
var ret = new char[8];
for (int i = 0; i < 6; i++)
{
ret[i] = chars[(int)(num >> (27 - (i * 5)) & 0x1F)];
}
ret[6] = chars[(int)(tnum >> 5 & 0x1F)];
ret[7] = chars[(int)(tnum & 0x1F)];
return new string(ret);
}
class Rng
{
public uint seed;
public int shift1;
public int shift2;
public int shift3;
public uint next()
{
var num = seed;
num ^= num >> shift1;
num ^= num << shift2;
num ^= num >> shift3;
seed = num;
return num;
}
};
Comments