001package com.fs.starfarer.api.impl.campaign.terrain; 002 003import java.util.Arrays; 004import java.util.Random; 005import java.util.zip.DataFormatException; 006import java.util.zip.Deflater; 007import java.util.zip.Inflater; 008 009import com.fs.starfarer.api.util.IntervalUtil; 010 011public class HyperspaceAutomaton { 012 013 protected transient int [][] cells; 014 protected transient int [][] next; 015 016 protected String savedCells, savedNext; 017 018 protected IntervalUtil interval; 019 protected boolean doneWithIteration = false; 020 protected int currentColumn = 0; 021 protected int width; 022 protected int height; 023 public HyperspaceAutomaton(int width, int height, float minInterval, float maxInterval) { 024 this.width = width; 025 this.height = height; 026 cells = new int [width][height]; 027 next = new int [width][height]; 028 029 interval = new IntervalUtil(minInterval, maxInterval); 030 031 setLive(0.1f); 032 interval.forceIntervalElapsed(); 033 } 034 035 Object readResolve() { 036 if (savedCells != null) { 037 try { 038 cells = decodeTiles(savedCells, width, height); 039 } catch (DataFormatException e) { 040 throw new RuntimeException("Error decoding hyperspace automaton tiles", e); 041 } 042 } else { 043 // shouldn't be here, if we are then savedTiles == null and something went badly wrong 044 cells = new int [width][height]; 045 } 046 if (savedNext != null) { 047 try { 048 next = decodeTiles(savedNext, width, height); 049 } catch (DataFormatException e) { 050 throw new RuntimeException("Error decoding hyperspace automaton tiles", e); 051 } 052 } else { 053 // shouldn't be here, if we are then savedTiles == null and something went badly wrong 054 next = new int [width][height]; 055 } 056 057 return this; 058 } 059 060 Object writeReplace() { 061 savedCells = encodeTiles(cells); 062 savedNext = encodeTiles(next); 063 return this; 064 } 065 066 067 public IntervalUtil getInterval() { 068 return interval; 069 } 070 071 public void advance(float days) { 072 updateCells(days); 073 074 interval.advance(days); 075 if (interval.intervalElapsed()) { 076 //System.out.println("Max reached:" + currentColumn); 077 doneWithIteration = false; 078 setLive(0.01f); 079 //setLive(0.05f); 080 cells = next; 081 next = new int [cells.length][cells[0].length]; 082 for (int i = 0; i < next.length; i++) { 083 for (int j = 0; j < next[0].length; j++) { 084 next[i][j] = cells[i][j]; 085 } 086 } 087 currentColumn = 0; 088 } 089 } 090 091 092 public int[][] getCells() { 093 return cells; 094 } 095 096 protected void updateCells(float days) { 097 if (currentColumn >= cells.length) return; 098 099 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