Starsector API
Loading...
Searching...
No Matches
HyperspaceAutomaton.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.campaign.terrain;
2
3import java.util.Arrays;
4import java.util.Random;
5import java.util.zip.DataFormatException;
6import java.util.zip.Deflater;
7import java.util.zip.Inflater;
8
9import com.fs.starfarer.api.util.IntervalUtil;
10
11public class HyperspaceAutomaton {
12
13 protected transient int [][] cells;
14 protected transient int [][] next;
15
16 protected String savedCells, savedNext;
17
18 protected IntervalUtil interval;
19 protected boolean doneWithIteration = false;
20 protected int currentColumn = 0;
21 protected int width;
22 protected int height;
23 public HyperspaceAutomaton(int width, int height, float minInterval, float maxInterval) {
24 this.width = width;
25 this.height = height;
26 cells = new int [width][height];
27 next = new int [width][height];
28
29 interval = new IntervalUtil(minInterval, maxInterval);
30
31 setLive(0.1f);
32 interval.forceIntervalElapsed();
33 }
34
35 Object readResolve() {
36 if (savedCells != null) {
37 try {
39 } catch (DataFormatException e) {
40 throw new RuntimeException("Error decoding hyperspace automaton tiles", e);
41 }
42 } else {
43 // shouldn't be here, if we are then savedTiles == null and something went badly wrong
44 cells = new int [width][height];
45 }
46 if (savedNext != null) {
47 try {
49 } catch (DataFormatException e) {
50 throw new RuntimeException("Error decoding hyperspace automaton tiles", e);
51 }
52 } else {
53 // shouldn't be here, if we are then savedTiles == null and something went badly wrong
54 next = new int [width][height];
55 }
56
57 return this;
58 }
59
60 Object writeReplace() {
63 return this;
64 }
65
66
67 public IntervalUtil getInterval() {
68 return interval;
69 }
70
71 public void advance(float days) {
72 updateCells(days);
73
74 interval.advance(days);
75 if (interval.intervalElapsed()) {
76 //System.out.println("Max reached:" + currentColumn);
77 doneWithIteration = false;
78 setLive(0.01f);
79 //setLive(0.05f);
80 cells = next;
81 next = new int [cells.length][cells[0].length];
82 for (int i = 0; i < next.length; i++) {
83 for (int j = 0; j < next[0].length; j++) {
84 next[i][j] = cells[i][j];
85 }
86 }
87 currentColumn = 0;
88 }
89 }
90
91
92 public int[][] getCells() {
93 return cells;
94 }
95
96 protected void updateCells(float days) {
97 if (currentColumn >= cells.length) return;
98
99 int chunk = (int) ((days * 1.5f) / interval.getMinInterval() * cells.length);
100 if (chunk < 1) chunk = 1;
101 int maxColumn = currentColumn + chunk;
102
103 // 0: dead
104 // 1: live
105 // 2: dying
106 for (int i = currentColumn; i < cells.length && i < maxColumn; i++) {
107 for (int j = 0; j < cells[0].length; j++) {
108 int val = (int) cells[i][j];
109 if (val == 0) {
110 int count = getLiveCountAround(i, j);
111 if (count == 2) {
112 next[i][j] = 1;
113 }
114// else if ((float) Math.random() < getRandomLifeChance()) {
115// next[i][j] = 1;
116// }
117 } else if (val == 1) {
118 next[i][j] = 2;
119 } else if (val == 2) {
120 next[i][j] = 0;
121 }
122
123// int val = (int) cells[i][j];
124// int count = getLiveCountAround(i, j);
125// if (count < 2) {
126// next[i][j] = 0;
127// } else if (val != 0 && (count == 2 || count == 3)) {
128// // nothing happens
129// } else if (count > 3) {
130// next[i][j] = 0;
131// } else if (val == 0 && count == 3) {
132// next[i][j] = 1;
133// }
134//
135// if ((float) Math.random() < getRandomLifeChance()) {
136// next[i][j] = 1;
137// }
138 }
139 }
140
141 currentColumn = maxColumn;
142 }
143
144 public void setLive(float fraction) {
145 float count = (float) (next.length * next[0].length) * fraction;
146 if (count <= 0) {
147 count = (float) Math.random() < count ? 1 : 0;
148 }
149
150 Random r = new Random();
151 for (int i = 0; i < count; i++) {
152 int x = r.nextInt(next.length);
153 int y = r.nextInt(next[0].length);
154 next[x][y] = 1;
155 }
156 }
157
158// protected float getRandomLifeChance() {
159// return 0.001f;
160// }
161
162 protected int getLiveCountAround(int x, int y) {
163 int count = 0;
164 for (int i = Math.max(0, x - 1); i <= Math.min(x + 1, cells.length - 1); i++) {
165 for (int j = Math.max(0, y - 1); j <= Math.min(y + 1, cells[0].length - 1); j++) {
166 if (i == x && j == y) continue;
167 if (cells[i][j] == 1) {
168 count++;
169 }
170 }
171 }
172 return count;
173 }
174
175
176 public static String encodeTiles(int [][] tiles) {
177 int w = tiles.length;
178 int h = tiles[0].length;
179 int total = w * h;
180
181 int [] masks = new int [] {
182 128 + 64,
183 32 + 16,
184 8 + 4,
185 2 + 1,
186 };
187
188 int bitPair = 0;
189 int curr = 0;
190 //List<Byte> bytes = new ArrayList<Byte>();
191 byte [] input = new byte [(int) Math.ceil(total / 4)];
192 for (int i = 0; i < total; i++) {
193 int x = i % w;
194 int y = i / w;
195 int val = tiles[x][y];
196 int mask = masks[bitPair];
197
198 if (val >= 0) {
199 //curr = (curr | mask);
200 curr = curr | ((val << (8 - (bitPair + 1) * 2)) & mask);
201 }
202 bitPair++;
203 bitPair %= 4;
204
205 if (bitPair == 0) {
206 input[i/4] = ((byte) curr);
207 curr = 0;
208 }
209 }
210 if (bitPair != 0) {
211 input[(total - 1) / 8] = ((byte) curr);
212 curr = 0;
213 }
214
215 /*
216 List<Byte> bytes = new ArrayList<Byte>();
217 String seq = "";
218 for (int i = 0; i < total; i++) {
219 int x = i % w;
220 int y = i / w;
221 int val = tiles[x][y];
222 String curr = "00";
223 if (val == 1) curr = "01";
224 if (val == 2) curr = "10";
225 if (val == 3) curr = "11";
226 seq += curr;
227 if (seq.length() == 8) {
228 int b = Integer.parseInt(seq, 2);
229 bytes.add((byte) b);
230 seq = "";
231 }
232 }
233 if (seq.length() > 0) {
234 while (seq.length() < 8) {
235 seq = seq + "0";
236 }
237 int b = Integer.parseInt(seq, 2);
238 bytes.add((byte) b);
239 }
240
241 byte [] input = new byte [bytes.size()];
242 for (int i = 0; i < bytes.size(); i++) {
243 input[i] = bytes.get(i);
244 }
245 */
246
247 Deflater compresser = new Deflater();
248 compresser.setInput(input);
249 compresser.finish();
250
251 StringBuilder result = new StringBuilder();
252 byte [] temp = new byte[100];
253
254 while (!compresser.finished()) {
255 int read = compresser.deflate(temp);
256 result.append(BaseTiledTerrain.toHexString(Arrays.copyOf(temp, read)));
257 }
258
259 compresser.end();
260
261 return result.toString();
262 }
263
264 public static int [][] decodeTiles(String string, int w, int h) throws DataFormatException {
265 byte [] input = BaseTiledTerrain.toByteArray(string);
266
267 Inflater decompresser = new Inflater();
268 decompresser.setInput(input);
269
270 int [] masks = new int [] {
271 128 + 64,
272 32 + 16,
273 8 + 4,
274 2 + 1,
275 };
276
277 int [][] tiles = new int [w][h];
278 int total = w * h;
279 int curr = 0;
280
281 byte [] temp = new byte[100];
282 OUTER: while (!decompresser.finished()) {
283 int read = decompresser.inflate(temp);
284 for (int i = 0; i < read; i++) {
285 byte b = temp[i];
286 for (int j = 3; j >= 0; j--) {
287 int x = curr % w;
288 int y = curr / w;
289 curr++;
290
291 if (curr > total) break OUTER;
292
293 tiles[x][y] = (b >> j * 2) & 3;
294 }
295 /*
296 for (int j = 7; j >= 0; j-=2) {
297 int x = curr % w;
298 int y = curr / w;
299 curr++;
300
301 if (curr > total) break OUTER;
302
303 if ((b & (0x01 << j)) == 0 && (b & (0x01 << j)) == 0) {
304 tiles[x][y] = 0;
305 } else if ((b & (0x01 << j)) == 0 && (b & (0x01 << j)) > 0) {
306 tiles[x][y] = 1;
307 } else if ((b & (0x01 << j)) > 0 && (b & (0x01 << j)) == 0) {
308 tiles[x][y] = 2;
309 } else if ((b & (0x01 << j)) > 0 && (b & (0x01 << j)) > 0) {
310 tiles[x][y] = 3;
311 }
312 }
313 */
314 }
315 }
316
317 decompresser.end();
318
319 return tiles;
320 }
321
322
323 public static void main(String[] args) throws DataFormatException {
324
325 int [][] tiles = new int[][] {
326 {0, 1, 1, 3, 1},
327 {3, 1, 2, 3, 2},
328 {2, 2, 1, 3, 3},
329 {1, 1, 2, 3, 2},
330 };
331
332// int w = 128;
333// int h = 128;
334// int [][] tiles = new int [w][h];
335//
336// for (int i = 0; i < w; i++) {
337// for (int j = 0; j < h; j++) {
338// if ((float) Math.random() > 0.8f) {
339// tiles[i][j] = 0;
340// } else {
341// tiles[i][j] = -1;
342// }
343// }
344// }
345
346
347 System.out.println("Original:");
348 for (int i = 0; i < tiles.length; i++) {
349 for (int j = 0; j < tiles[0].length; j++) {
350 System.out.print(String.format("% 2d,", tiles[i][j]));
351 }
352 System.out.println();
353 }
354
355 String result = encodeTiles(tiles);
356 System.out.println(result);
357 //System.out.println(result.length() + ", would be " + (w * h / 4) + " without compression");
358 int [][] tilesBack = decodeTiles(result, tiles.length, tiles[0].length);
359
360 System.out.println("Decoded:");
361 for (int i = 0; i < tilesBack.length; i++) {
362 for (int j = 0; j < tilesBack[0].length; j++) {
363 System.out.print(String.format("% 2d,", tilesBack[i][j]));
364 }
365 System.out.println();
366 }
367
368 boolean equals = true;
369 for (int i = 0; i < tiles.length; i++) {
370 for (int j = 0; j < tiles[0].length; j++) {
371 if (tiles[i][j] != tilesBack[i][j]) {
372 equals = false;
373 }
374 }
375 }
376
377 System.out.println("Equal: " + equals);
378
379// for (int x = 0; x < tilesBack.length; x++) {
380// for (int y = 0; y < tilesBack.length; y++) {
381// System.out.print(tilesBack[x][y] + " ");
382// }
383// System.out.println();
384// }
385 }
386}
387
388
389
390
391
392
HyperspaceAutomaton(int width, int height, float minInterval, float maxInterval)