This repository has been archived by the owner on Feb 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Output.cs
285 lines (275 loc) · 12.3 KB
/
Output.cs
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
using System.IO;
using System.Text.Json;
using System.Collections.Generic;
namespace LevelGenerator
{
class Output
{
/// The JSON extension.
private static readonly string JSON = ".json";
/// The operational system directory separator char.
private static readonly char SEPARATOR = Path.DirectorySeparatorChar;
/// The filename separator char.
private static readonly char FILENAME_SEPARATOR = '-';
/// The general target for the search for files.
private static readonly string SEARCH_FOR = "*";
/// String to initialize empty strings or convert values of other types
/// during concatenation.
private static readonly string EMPTY_STR = "";
/// The default JSON options.
private static readonly JsonSerializerOptions JSON_OPTIONS =
new JsonSerializerOptions(){ WriteIndented = true };
/// The results folder name.
/// This folder stores the collected data to evaluate the approach.
private static readonly string RESULTS_FOLDER_NAME = @"results";
/// The filename of the evolutionary process data.
/// This file is created only when the enemies are saved separately.
private static readonly string DATA_FILENAME = @"data";
/// Write the collected data from the evolutionary process.
public static void WriteData(
Population _solution,
Data _data
) {
// Create folder to store the results
Directory.CreateDirectory(RESULTS_FOLDER_NAME);
// Create the base name according to the entered parameters
string basename = RESULTS_FOLDER_NAME + SEPARATOR;
basename += GetFolderName(_data);
Directory.CreateDirectory(basename);
// Calculate the number of directories in the folder
int count = Directory.GetDirectories(
basename, SEARCH_FOR, SearchOption.TopDirectoryOnly
).Length;
// Create a folder for the resulting set of dungeon levels
basename = basename + SEPARATOR + count;
Directory.CreateDirectory(basename);
// Save the evolutionary process data
string datafn = basename + SEPARATOR + DATA_FILENAME + JSON;
string json = JsonSerializer.Serialize(_data, JSON_OPTIONS);
File.WriteAllText(datafn, json);
// Save each individual in the create folder
for (int e = 0; e < _solution.dimension.exp; e++)
{
for (int l = 0; l < _solution.dimension.len; l++)
{
Individual individual = _solution.map[e, l];
if (individual != null)
{
SaveLevel(individual, basename, (e, l));
}
}
}
}
/// Return the folder name for the entered parameters.
private static string GetFolderName(
Data _data
) {
Parameters prs = _data.parameters;
string foldername = EMPTY_STR;
foldername += EMPTY_STR + prs.time + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.population + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.intermediate + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.mutation + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.competitors + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.weight + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.inclusive + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.rooms + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.keys + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.locks + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.enemies + FILENAME_SEPARATOR;
foldername += EMPTY_STR + prs.linearCoefficient;
return foldername;
}
private static void SaveLevel(
Individual _individual,
string _basename,
(int x, int y) _coordinate
) {
// Get the dungeon component
Dungeon dungeon = _individual.dungeon;
// Initialize the grid bounds
int minX = RoomGrid.LEVEL_GRID_OFFSET;
int minY = RoomGrid.LEVEL_GRID_OFFSET;
int maxX = -RoomGrid.LEVEL_GRID_OFFSET;
int maxY = -RoomGrid.LEVEL_GRID_OFFSET;
// The list of keys and locks in the level
List<int> keys = new List<int>();
List<int> locks = new List<int>();
// Calculate the grid bounds and get the level keys and locked doors
foreach (Room room in dungeon.rooms)
{
// Update grid bounds
minX = minX > room.x ? room.x : minX;
minY = minY > room.y ? room.y : minY;
maxX = room.x > maxX ? room.x : maxX;
maxY = room.y > maxY ? room.y : maxY;
// Add the keys and locked doors in the level
if (room.type == RoomType.Key) {
keys.Add(room.key);
}
if (room.type == RoomType.Locked)
{
locks.Add(room.key);
}
}
// Initialize the auxiliary map
int sizeX = maxX - minX + 1;
int sizeY = maxY - minY + 1;
int[,] map = new int[2 * sizeX, 2 * sizeY];
for (int i = 0; i < 2 * sizeX; i++)
{
for (int j = 0; j < 2 * sizeY; j++)
{
map[i, j] = (int) Common.RoomCode.E;
}
}
// Set the corridors, keys and locked rooms
RoomGrid grid = dungeon.grid;
for (int i = minX; i < maxX + 1; i++)
{
for (int j = minY; j < maxY + 1; j++)
{
// Get the even positions
int iep = (i - minX) * 2;
int jep = (j - minY) * 2;
// Get the respective room
Room current = grid[i, j];
if (current != null)
{
if (current.type == RoomType.Normal)
{
map[iep, jep] = (int) Common.RoomCode.N;
}
else if (current.type == RoomType.Key)
{
int _key = keys.IndexOf(current.key);
map[iep, jep] = _key + 1;
}
else if (current.type == RoomType.Locked)
{
int _lock = locks.IndexOf(current.key);
map[iep, jep] = _lock == locks.Count - 1 ?
(int) Common.RoomCode.B :
(int) Common.RoomCode.N;
}
// Get current room parent
Room parent = current.parent;
if (parent != null)
{
// Get the corridor between both rooms
int x = parent.x - current.x + iep;
int y = parent.y - current.y + jep;
// If the current room is locked
if (current.type == RoomType.Locked)
{
// Then, the corridor is locked
int _key = keys.IndexOf(current.key);
map[x, y] = -(_key + 1);
}
else
{
// Otherwise it is an usual corridor
map[x, y] = (int) Common.RoomCode.C;
}
}
}
}
}
// Prepare the level to be written
IndividualFile ifile = new IndividualFile();
// Save the dungeon attributes
ifile.dimensions = new Dimensions(
2 * (maxX - minX + 1),
2 * (maxY - minY + 1)
);
ifile.numberOfRooms = _individual.dungeon.rooms.Count;
ifile.numberOfKeys = _individual.dungeon.keyIds.Count;
ifile.numberOfLocks = _individual.dungeon.lockIds.Count;
ifile.linearCoefficient = _individual.linearCoefficient;
ifile.neededLocks = _individual.neededLocks;
ifile.neededRooms = _individual.neededRooms;
ifile.exploration = _individual.exploration;
ifile.leniency = _individual.leniency;
// Save the quality attributes of the dungeon
ifile.generation = _individual.generation;
ifile.fitness = _individual.fitness;
ifile.fGoal = _individual.fGoal;
ifile.fRooms = _individual.fRooms;
ifile.fKeys = _individual.fKeys;
ifile.fLocks = _individual.fLocks;
ifile.fLinearCoefficient = _individual.fLinearCoefficient;
ifile.fEnemySparsity = _individual.fEnemySparsity;
ifile.fSTD = _individual.fSTD;
ifile.fNeededRooms = _individual.fNeededRooms;
ifile.fNeededLocks = _individual.fNeededLocks;
// Set the list of rooms
for (int i = 0; i < sizeX * 2; i++)
{
for (int j = 0; j < sizeY * 2; j++)
{
IndividualFile.Room room = null;
int x = i / 2 + minX;
int y = j / 2 + minY;
if (map[i, j] != (int) Common.RoomCode.E)
{
// Create a new empty room
room = new IndividualFile.Room
{
coordinates = new Coordinates(i, j)
};
if (i + minX * 2 == 0 && j + minY * 2 == 0)
{
// Set up the starting room
room.type = "s";
room.enemies = grid[x, y].enemies;
}
else if (map[i, j] == (int) Common.RoomCode.B)
{
// Set up the boss/goal room
room.type = "B";
room.enemies = grid[x, y].enemies;
}
else if (map[i, j] == (int) Common.RoomCode.C)
{
// Set up corridor
room.type = "c";
}
else if (map[i, j] < 0)
{
// Set up the a locked corridor
room.type = "c";
room.locks = new List<int> { map[i, j] };
}
else if (map[i, j] > 0)
{
// Set up a room with a key
room.keys = new List<int> { map[i, j] };
room.enemies = grid[x, y].enemies;
}
else
{
// Set up a normal room
if (grid[x, y] != null)
{
room.enemies = grid[x, y].enemies;
}
}
}
// If the room exists, then add it to the list of rooms
if (room != null)
{
ifile.rooms.Add(room);
}
}
}
// Build the filename
string filename = _basename + SEPARATOR +
"level" + FILENAME_SEPARATOR +
_coordinate.x + FILENAME_SEPARATOR +
_coordinate.y + JSON;
// Serialize and write the level file
string json = JsonSerializer.Serialize(ifile, JSON_OPTIONS);
File.WriteAllText(filename, json);
}
}
}