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