-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathInterface.cs
More file actions
315 lines (286 loc) · 14.2 KB
/
Interface.cs
File metadata and controls
315 lines (286 loc) · 14.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
using System;
using System.Collections.Generic;
using System.IO;
using Game.LevelGenerator.LevelSOs;
using Game.LevelManager;
using Game.NarrativeGenerator.Quests;
using UnityEditor;
using UnityEngine;
using Util;
namespace Game.LevelGenerator
{
public static class Interface
{
/**
* Prints the dungeon in the console, saves into a file, and can even save in a csv that is not used anymore
* We now save it directly into a Unity's Resource Directory
*/
public static void PrintNumericalGridWithConnections(
Individual _individual,
Fitness _fitness, QuestLine _questLine)
{
Dungeon dun = _individual.dungeon;
//List of keys and locked rooms in the level
List<int> lockedRooms = new List<int>();
List<int> keys = new List<int>();
string foldername = "Assets/Resources/Experiment/Dungeons";
var filename = GetFilename(_individual, _fitness);
DungeonFileSo dungeonFileSO = ScriptableObject.CreateInstance<DungeonFileSo>();
//saves where the dungeon grid begins and ends in each direction
foreach (Room room in dun.Rooms)
{
if (room.Type1 == RoomType.Key)
{
keys.Add(room.Key);
}
else if (room.Type1 == RoomType.Locked)
{
lockedRooms.Add(room.Key);
}
}
dun.SetBoundariesFromRoomList();
//The size is normalized to be always positive (easier to handle a matrix)
dun.SetDimensionsFromBoundaries();
//Creates a matrix to hold each room and corridor (there may be a corridor between each room, that must be saved
//hence 2*size
int[,] map = new int[2 * dun.DungeonDimensions.Width, 2 * dun.DungeonDimensions.Height];
//The top of the dungeon's file in unity must contain its dimensions
dungeonFileSO.dimensions = new Dimensions(2 * dun.DungeonDimensions.Width, 2 * dun.DungeonDimensions.Height);
dungeonFileSO.fitness = _individual.fitness;
//We initialize the map with the equivalent of an empty cell
for (int i = 0; i < 2 * dun.DungeonDimensions.Width; ++i)
{
for (int j = 0; j < 2 * dun.DungeonDimensions.Height; ++j)
{
map[i, j] = Common.RoomType.NOTHING;
}
}
InitializeMapFromDungeon(dun, map, keys, lockedRooms);
InitializeDungeonSoFromMap(dungeonFileSO, dun, map);
//The assetdatabase stuff only works in the Unity's Editor
//As is, we can't save a level file in a released build of the game
#if UNITY_EDITOR
int count = 0;
string path;
Directory.CreateDirectory(foldername);
//Saves the file with the name of its input for the EA and adds a number at the end if a file with the same name exists
//This prevents the file is overwritten
path = AssetDatabase.AssetPathToGUID($"{foldername}/{filename}.txt");
while (path != "")
{
count++;
path = AssetDatabase.AssetPathToGUID($"{foldername}/{filename}-{count}.txt");
}
if (count > 0)
filename += "-" + count;
filename = foldername + "/" + filename;
int sameFilenameCounter = 0;
if (File.Exists(filename + ".asset"))
{
do
{
sameFilenameCounter++;
} while (File.Exists(filename + "-" + sameFilenameCounter + ".asset"));
filename += "-" + sameFilenameCounter;
}
AssetDatabase.CreateAsset(dungeonFileSO, filename + ".asset");
_questLine.DungeonFileSos.Add(dungeonFileSO);
#endif
}
private static void InitializeDungeonSoFromMap(DungeonFileSo dungeonFileSO, Dungeon dun, int[,] map)
{
dungeonFileSO.rooms = new List<SORoom>();
//Now we print it/save to a file/whatever
for (var i = 0; i < dun.DungeonDimensions.Width * 2; ++i)
{
for (var j = 0; j < dun.DungeonDimensions.Height * 2; ++j)
{
SORoom roomDataInFile;
// Calculate the room position in the grid
int x = i / 2 + dun.DungeonBoundaries.MinBoundaries.X;
int y = j / 2 + dun.DungeonBoundaries.MinBoundaries.Y;
//If cell is empty, do nothing (or print empty space in console)
if (map[i, j] == Common.RoomType.NOTHING)
{
roomDataInFile = null;
}
//If there is something (room or corridor) print/save
else
{
var roomType = map[i, j];
var roomGrid = dun.DungeonGrid[x, y];
var coordinates = new Coordinates(i + dun.DungeonBoundaries.MinBoundaries.X * 2, j + dun.DungeonBoundaries.MinBoundaries.Y * 2);
//For Unity's dungeon file we need to save the x and y position of the room
roomDataInFile = new SORoom(i, j);
ConvertEaDungeonToSoDungeon(coordinates, roomDataInFile, roomGrid, roomType);
}
if (roomDataInFile != null)
{
dungeonFileSO.rooms.Add(roomDataInFile);
}
}
}
}
private static void ConvertEaDungeonToSoDungeon(Coordinates coordinates, SORoom roomDataInFile, Room roomGrid,
int roomType)
{
//If room is in (0,0) it is the starting one, we mark it with an "s" and save the "s"
if (coordinates.X == 0 && coordinates.Y == 0)
{
roomDataInFile.type = Constants.RoomTypeString.START;
roomDataInFile.TotalEnemies = roomGrid.Enemies;
}
//If it is a corridor, writes "c" in the file
else if (roomType == Common.RoomType.CORRIDOR)
{
roomDataInFile.type = Constants.RoomTypeString.CORRIDOR;
}
//If is the boss room, writes "B". Currently is where the Triforce is located
else if (roomType == Common.RoomType.BOSS)
{
roomDataInFile.type = Constants.RoomTypeString.BOSS;
roomDataInFile.TotalEnemies = roomGrid.Enemies;
}
//If negative, is a locked corridor, save it as the negative number of the key that opens it
else if (roomType < 0)
{
roomDataInFile.type = Constants.RoomTypeString.LOCK;
roomDataInFile.locks = new List<int>
{
roomType
};
}
//If it was a room with treasure, save it as a "T"
else if (roomType == Common.RoomType.TREASURE)
{
roomDataInFile.type = Constants.RoomTypeString.TREASURE;
roomDataInFile.treasures = 1;
roomDataInFile.npcs = 1;
roomDataInFile.TotalEnemies = roomGrid.Enemies;
}
//If the room has a positive value, it holds a key.
//Save the key index so we know what key it is
else if (roomType > 0)
{
roomDataInFile.TotalEnemies = roomGrid.Enemies;
roomDataInFile.type = Constants.RoomTypeString.KEY;
roomDataInFile.keys = new List<int>
{
roomType
};
}
//If the cell was none of the above, it must be an empty room
else
{
roomDataInFile.type = Constants.RoomTypeString.NORMAL;
roomDataInFile.TotalEnemies = roomGrid.Enemies;
}
}
private static void InitializeMapFromDungeon(Dungeon dun, int[,] map, List<int> keys, List<int> lockedRooms)
{
//Now we visit each room and save the info on the corresponding cell of the matrix
for (int i = dun.DungeonBoundaries.MinBoundaries.X; i < dun.DungeonBoundaries.MaxBoundaries.X + 1; ++i)
{
for (int j = dun.DungeonBoundaries.MinBoundaries.Y; j < dun.DungeonBoundaries.MaxBoundaries.Y + 1; ++j)
{
//Converts the coordinate of the original grid (can be negative) to the positive ones used in the matrix
int iPositive = i - dun.DungeonBoundaries.MinBoundaries.X;
int jPositive = j - dun.DungeonBoundaries.MinBoundaries.Y;
//Gets the actual room
Room actualRoom = dun.DungeonGrid[i, j];
//If there is something in this position in the grid:
SetRoomTypeInMap(map, keys, lockedRooms, actualRoom, iPositive, jPositive);
}
}
}
private static void SetRoomTypeInMap(int[,] map, List<int> keys, List<int> lockedRooms, Room actualRoom, int iPositive, int jPositive)
{
if (actualRoom != null)
{
switch (actualRoom.Type1)
{
//If it is a normal room, check if is a leaf node. We are currently placing treasures there
//If not a leaf, just save as an empty room for now
//TODO: change to handle the new format of having the room's Key ID followed by amount of treasure and them enemy difficulty
//Will have to change to an array or something, with 0 treasures and 0 difficulty meaning no treasure and no enemy inside
case RoomType.Normal:
SetNormalRoomData(map, actualRoom, iPositive, jPositive);
break;
//If the room has a key, saves the corresponding key index in the matrix
//TODO: Must also change to allow the generation of treasures and enemies
case RoomType.Key:
map[iPositive * 2, jPositive * 2] = keys.IndexOf(actualRoom.Key) + 1;
break;
//If the room is locked from its parent, check if it is a boss room by checking if the key to open is the last one created
//It guarantees at least that is the deepest key in the tree, but not the longest route
//TODO: Must also change to allow the generation of treasures and enemies
case RoomType.Locked when lockedRooms.IndexOf(actualRoom.Key) == lockedRooms.Count - 1:
map[iPositive * 2, jPositive * 2] = Common.RoomType.BOSS;
break;
case RoomType.Locked:
map[iPositive * 2, jPositive * 2] = Common.RoomType.TREASURE;
break;
//If it is not a room, something is wrong
default:
Console.WriteLine("Something went wrong printing the tree!\n");
Console.WriteLine("This Room type does not exist!\n\n");
break;
}
//As (for now) every room must be connected to its parent or children
//We need only to check its parent to create the corridors
Room parent = actualRoom.Parent;
if (parent == null) return;
int x = parent.X - actualRoom.X + 2 * iPositive;
int y = parent.Y - actualRoom.Y + 2 * jPositive;
//If corridor is lockes, save the index of the key that opens it
//But as a negative value. A negative corridor is locked!
//If not, save it only as a normal corridor
if (actualRoom.Type1 == RoomType.Locked)
{
map[x, y] = -(keys.IndexOf(actualRoom.Key) + 1);
}
else
{
map[x, y] = Common.RoomType.CORRIDOR;
}
}
}
private static void SetNormalRoomData(int[,] map, Room actualRoom, int iPositive, int jPositive)
{
if (actualRoom.IsLeafNode())
{
map[iPositive * 2, jPositive * 2] = Common.RoomType.TREASURE;
}
else
{
map[iPositive * 2, jPositive * 2] = Common.RoomType.EMPTY;
}
}
private static string GetFilename(Individual _individual, Fitness _fitness)
{
// Get the coordinate values corresponding to the Elite
float ce = _individual.exploration;
float le = _individual.leniency;
int e = SearchSpace.GetCoefficientOfExplorationIndex(ce);
int l = SearchSpace.GetLeniencyIndex(le);
(float, float)[] listCE = SearchSpace.CoefficientOfExplorationRanges();
(float, float)[] listLE = SearchSpace.LeniencyRanges();
string strCE = ("" + listCE[e])
.Replace(" ", "").Replace("(", "")
.Replace(")", "").Replace(",", "~");
string strLE = ("" + listLE[l])
.Replace(" ", "").Replace("(", "")
.Replace(")", "").Replace(",", "~");
// Set the dungeon filename
string filename = "";
filename = "R" + _fitness.DesiredRooms +
"-K" + _fitness.DesiredKeys +
"-L" + _fitness.DesiredLocks +
"-E" + _fitness.DesiredEnemies +
"-L" + _fitness.DesiredLinearity +
"-CE" + strCE +
"-LE" + strLE;
return filename;
}
}
}