├── CodingameFramework ├── .gitignore ├── src │ ├── test │ │ ├── resources │ │ │ ├── map1.txt │ │ │ └── map2.txt │ │ └── java │ │ │ └── fr │ │ │ ├── framework │ │ │ ├── MapUtilsTest.java │ │ │ ├── point │ │ │ │ └── DirectionUtilsTest.java │ │ │ ├── TestUtils.java │ │ │ └── list │ │ │ │ └── ByteListUtilsTest.java │ │ │ └── code │ │ │ └── utils │ │ │ ├── search │ │ │ ├── way │ │ │ │ └── WayUtilsTest.java │ │ │ └── heap │ │ │ │ └── HeapTest.java │ │ │ ├── floodfill │ │ │ └── PlaceUtilsTest.java │ │ │ └── NextMoveUtilsTest.java │ └── main │ │ └── java │ │ └── fr │ │ ├── code │ │ ├── variable │ │ │ ├── Constant.java │ │ │ └── Parameter.java │ │ ├── utils │ │ │ ├── search │ │ │ │ ├── way │ │ │ │ │ ├── NoMoreWayException.java │ │ │ │ │ ├── WayCache.java │ │ │ │ │ └── WayUtils.java │ │ │ │ ├── heap │ │ │ │ │ ├── HeapCache.java │ │ │ │ │ └── Heap.java │ │ │ │ └── WayBeamSearcher.java │ │ │ ├── Utils.java │ │ │ ├── FirstRoundUtils.java │ │ │ ├── NextMoveUtils.java │ │ │ └── floodfill │ │ │ │ └── PlaceUtils.java │ │ ├── Player.java │ │ └── object │ │ │ └── Game.java │ │ └── framework │ │ ├── function │ │ └── BytePredicate.java │ │ ├── timeout │ │ ├── TimeoutException.java │ │ ├── TimeoutUtils.java │ │ └── GarbageCollectorUtils.java │ │ ├── random │ │ ├── RandomUtils.java │ │ └── XORShiftRandom.java │ │ ├── StringUtils.java │ │ ├── AssertUtils.java │ │ ├── point │ │ ├── Point.java │ │ ├── Direction.java │ │ ├── PointUtils.java │ │ └── DirectionUtils.java │ │ ├── timer │ │ └── Timer.java │ │ ├── ByteUtils.java │ │ ├── FrameworkConstant.java │ │ ├── logger │ │ └── Logger.java │ │ ├── list │ │ └── ByteListUtils.java │ │ ├── MapUtils.java │ │ └── merger │ │ └── ClassMerger.java └── pom.xml └── README.md /CodingameFramework/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.classpath 3 | /.settings/ 4 | /.project 5 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/resources/map1.txt: -------------------------------------------------------------------------------- 1 | .#..... 2 | .#.###. 3 | ...#... 4 | .#.#.## 5 | ...#.#. 6 | ####.#. 7 | ...#.#. 8 | ...#.#. 9 | ...#... -------------------------------------------------------------------------------- /CodingameFramework/src/test/resources/map2.txt: -------------------------------------------------------------------------------- 1 | .....#.... 2 | .....#.... 3 | ...####... 4 | .##...#... 5 | #..##.#... 6 | .....#.... 7 | ....#..... 8 | ...#...... 9 | ...#...... -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/variable/Constant.java: -------------------------------------------------------------------------------- 1 | package fr.code.variable; 2 | 3 | public class Constant { 4 | 5 | public static int PLAYER_ID; 6 | public static int OPPONENT_ID; 7 | } 8 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/function/BytePredicate.java: -------------------------------------------------------------------------------- 1 | package fr.framework.function; 2 | 3 | @FunctionalInterface 4 | public interface BytePredicate { 5 | 6 | boolean test(byte p); 7 | } 8 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/timeout/TimeoutException.java: -------------------------------------------------------------------------------- 1 | package fr.framework.timeout; 2 | 3 | public class TimeoutException extends Exception { 4 | 5 | public static final TimeoutException EXCEPTION = new TimeoutException(); 6 | } 7 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/way/NoMoreWayException.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.way; 2 | 3 | public class NoMoreWayException extends RuntimeException { 4 | 5 | public static final NoMoreWayException EXCEPTION = new NoMoreWayException(); 6 | } 7 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/Utils.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils; 2 | 3 | import fr.code.object.Game; 4 | import fr.framework.logger.Logger; 5 | 6 | public class Utils { 7 | 8 | private static final Logger logger = Logger.getLogger(Utils.class); 9 | 10 | protected static Game game = Game.getInstance(); 11 | 12 | protected static void printAction() {} 13 | } 14 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/random/RandomUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.random; 2 | 3 | import java.util.Random; 4 | 5 | public class RandomUtils { 6 | 7 | // can switch to new Random() also 8 | private static Random random = new XORShiftRandom(); 9 | 10 | /** return random int i : 0 <= i < nb */ 11 | public static int chooseRandom(int nb) { 12 | return random.nextInt(nb); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/StringUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | public class StringUtils { 4 | 5 | public static String toString(Object... p) { 6 | StringBuilder str = new StringBuilder(); 7 | int i = 0; 8 | for (Object o : p) { 9 | str.append(o == null ? "null" : o.toString()); 10 | if (++i < p.length) { 11 | str.append(' '); 12 | } 13 | } 14 | return str.toString(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/Player.java: -------------------------------------------------------------------------------- 1 | package fr.code; 2 | 3 | import java.util.Scanner; 4 | import fr.code.utils.NextMoveUtils; 5 | import fr.code.utils.Utils; 6 | 7 | public class Player extends Utils { 8 | 9 | public static void main(String args[]) { 10 | Scanner in = new Scanner(System.in); 11 | game.init(in); 12 | while (true) { 13 | game.update(in); 14 | NextMoveUtils.proceed(); 15 | printAction(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/AssertUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | import fr.framework.logger.Logger; 4 | 5 | public class AssertUtils { 6 | 7 | private static final Logger logger = Logger.getLogger(AssertUtils.class); 8 | 9 | public static void test(boolean b, Object... o) { 10 | if (!b) { 11 | String str = StringUtils.toString(o); 12 | logger.error(str); 13 | throw new IllegalStateException(str); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/point/Point.java: -------------------------------------------------------------------------------- 1 | package fr.framework.point; 2 | 3 | public class Point { 4 | 5 | private int x; 6 | private int y; 7 | 8 | public Point(int x, int y) { 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | public int getX() { 14 | return x; 15 | } 16 | 17 | public int getY() { 18 | return y; 19 | } 20 | 21 | @Override 22 | public String toString() { 23 | return "(" + x + ", " + y + ")"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/timer/Timer.java: -------------------------------------------------------------------------------- 1 | package fr.framework.timer; 2 | 3 | public class Timer { 4 | 5 | private static Timer instance = new Timer(); 6 | 7 | private long timeStart; 8 | 9 | private Timer() {} 10 | 11 | public static Timer getInstance() { 12 | return instance; 13 | } 14 | 15 | public void init() { 16 | timeStart = System.currentTimeMillis(); 17 | } 18 | 19 | public long getTime() { 20 | return System.currentTimeMillis() - timeStart; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/timeout/TimeoutUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.timeout; 2 | 3 | import fr.framework.timer.Timer; 4 | 5 | public class TimeoutUtils { 6 | 7 | private static Timer timer = Timer.getInstance(); 8 | 9 | public static void stopTimeException(int timeout) throws TimeoutException { 10 | if (isStopTime(timeout)) { 11 | throw TimeoutException.EXCEPTION; 12 | } 13 | } 14 | 15 | public static boolean isStopTime(int timeout) { 16 | return timer.getTime() >= timeout; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/ByteUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | public class ByteUtils { 4 | 5 | public static byte[] copy(byte[] array) { 6 | return copy(array, array.length); 7 | } 8 | 9 | public static byte[] copy(byte[] array, int length) { 10 | byte[] ret = new byte[length]; 11 | System.arraycopy(array, 0, ret, 0, length); 12 | return ret; 13 | } 14 | 15 | public static void copy(byte[] source, byte[] destination) { 16 | System.arraycopy(source, 0, destination, 0, source.length); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/heap/HeapCache.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.heap; 2 | 3 | public class HeapCache { 4 | 5 | private static final Heap[] CACHE = new Heap[2]; 6 | 7 | static { 8 | for (int i = 0; i < CACHE.length; i++) { 9 | Heap tmp = new Heap(i); 10 | CACHE[i] = tmp; 11 | } 12 | } 13 | 14 | public static Heap get(int num) { 15 | Heap ret = CACHE[num]; 16 | return ret; 17 | } 18 | 19 | public static void setLimit(int limit) { 20 | CACHE[0].setLimit(limit); 21 | CACHE[1].setLimit(limit); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/FrameworkConstant.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | public class FrameworkConstant { 4 | 5 | // height and width might be defined in the game's inputs 6 | public static int HEIGHT = 8; 7 | public static int WIDTH = 8; 8 | public static int CASE_NB = HEIGHT * WIDTH; 9 | 10 | public static final int OUT = -1; 11 | 12 | public static final int FREE = 0; 13 | public static final int WALL = 1; 14 | // could be more than two values (FREE, WALL, PLAYER_BASE, TAVERN...) that why i used byte array 15 | // for map and not bitboards 16 | 17 | } 18 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/FirstRoundUtils.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils; 2 | 3 | import fr.framework.MapUtils; 4 | import fr.framework.logger.Logger; 5 | import fr.framework.timeout.GarbageCollectorUtils; 6 | 7 | public class FirstRoundUtils extends Utils { 8 | 9 | private static final Logger logger = Logger.getLogger(FirstRoundUtils.class); 10 | 11 | public static void proceed(byte[] map) { 12 | if (game.isFirstRound()) { 13 | // init all caches 'around positions', 'distances'... 14 | MapUtils.initCache(map); 15 | GarbageCollectorUtils.avoidTimeOut(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/variable/Parameter.java: -------------------------------------------------------------------------------- 1 | package fr.code.variable; 2 | 3 | public class Parameter { 4 | 5 | public static int TIMEOUT_FIRST_ROUND = 950; 6 | public static final int TIMEOUT = 90; 7 | public static final int AVOID_TIMEOUT_NB = 2_000_000; 8 | 9 | public static final boolean REMOVE_LOG = false; 10 | public static final boolean REMOVE_ASSERT = false; 11 | 12 | public static final int BEAM_NB = 500; 13 | public static final int WAY_CACHE_NB = 70_000; 14 | public static int BEAM_MAX_DEPTH = 20; 15 | 16 | public static int WAY_SAVE_LENGTH = 3; 17 | 18 | public static final int TIMEOUT_START_INDEX = 10; 19 | } 20 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/random/XORShiftRandom.java: -------------------------------------------------------------------------------- 1 | package fr.framework.random; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * @see Javamex XORShift 7 | * random number generators 8 | * @see Apache 10 | * XORShiftRandom.java 11 | */ 12 | public class XORShiftRandom extends Random { 13 | 14 | private long seed = System.nanoTime(); 15 | 16 | public XORShiftRandom() {} 17 | 18 | @Override 19 | protected int next(int bits) { 20 | // N.B. Not thread-safe! 21 | long x = this.seed; 22 | x ^= (x << 21); 23 | x ^= (x >>> 35); 24 | x ^= (x << 4); 25 | this.seed = x; 26 | x &= ((1L << bits) - 1); 27 | return (int) x; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/object/Game.java: -------------------------------------------------------------------------------- 1 | package fr.code.object; 2 | 3 | import java.util.Scanner; 4 | import fr.framework.logger.Logger; 5 | import fr.framework.timer.Timer; 6 | 7 | public class Game { 8 | 9 | private static final Logger logger = Logger.getLogger(Game.class); 10 | 11 | private static final Game instance = new Game(); 12 | 13 | private int round = 0; 14 | 15 | private Game() {} 16 | 17 | public static Game getInstance() { 18 | return instance; 19 | } 20 | 21 | public void init(Scanner in) {} 22 | 23 | public void update(Scanner in) { 24 | round++; 25 | // read first game input 26 | Timer.getInstance().init(); 27 | logger.error("round", round); 28 | // read next game inputs 29 | 30 | } 31 | 32 | public boolean isFirstRound() { 33 | return round == 1; 34 | } 35 | 36 | public int getRound() { 37 | return round; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/framework/MapUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import java.io.IOException; 5 | import org.junit.Test; 6 | import fr.framework.logger.Logger; 7 | import fr.framework.point.PointUtils; 8 | 9 | public class MapUtilsTest { 10 | 11 | private static final Logger logger = Logger.getLogger(MapUtilsTest.class); 12 | 13 | @Test 14 | public void test() throws IOException { 15 | 16 | byte[] map = TestUtils.initMap("map1"); 17 | 18 | MapUtils.initCache(map); 19 | 20 | int position = PointUtils.getPosition(2, 0); 21 | byte[] aroundPositions = MapUtils.getAroundPositions(position); 22 | logger.error( 23 | PointUtils.toPoint(position), "around positions", PointUtils.toPoint(aroundPositions)); 24 | assertEquals(2, MapUtils.getNbAroundPositions(position)); 25 | 26 | map[PointUtils.getPosition(1, 7)] = 2; 27 | logger.error(MapUtils.toString(map)); 28 | 29 | assertEquals(4, MapUtils.getNbAroundPositions(PointUtils.getPosition(1, 7))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/way/WayCache.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.way; 2 | 3 | import fr.code.variable.Parameter; 4 | import fr.framework.logger.Logger; 5 | 6 | public class WayCache { 7 | 8 | private static final Logger logger = Logger.getLogger(WayCache.class); 9 | 10 | private static final byte[][] CACHE = new byte[Parameter.WAY_CACHE_NB][]; 11 | private static int maxIndex; 12 | private static int index; 13 | 14 | public static void reset() { 15 | maxIndex = Math.max(maxIndex, index); 16 | index = 0; 17 | } 18 | 19 | public static byte[] getNext() throws NoMoreWayException { 20 | try { 21 | byte[] ret = CACHE[index]; 22 | if (ret == null) { 23 | ret = WayUtils.constructWay(); 24 | CACHE[index] = ret; 25 | } 26 | index++; 27 | return ret; 28 | } catch (IndexOutOfBoundsException e) { 29 | throw NoMoreWayException.EXCEPTION; 30 | } 31 | } 32 | 33 | /** to see the max number of used ways, to adjust the cache size */ 34 | public static void printIndex() { 35 | logger.error("WayCacheIndex=", index, "maxIndex=", maxIndex); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/framework/point/DirectionUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.framework.point; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertTrue; 5 | import org.junit.Test; 6 | import fr.framework.FrameworkConstant; 7 | import fr.framework.logger.Logger; 8 | 9 | public class DirectionUtilsTest { 10 | 11 | private static final Logger logger = Logger.getLogger(DirectionUtilsTest.class); 12 | 13 | @Test 14 | public void test() { 15 | for (byte p = 0; p < FrameworkConstant.CASE_NB; p++) { 16 | for (Direction dir : Direction.values()) { 17 | int nextPosition = DirectionUtils.construct(p, dir); 18 | if (nextPosition != FrameworkConstant.OUT) { 19 | assertEquals(dir, DirectionUtils.getDirectionTo(p, nextPosition)); 20 | } else { 21 | int x = PointUtils.getX(p); 22 | int y = PointUtils.getY(p); 23 | logger.error(x, y, dir); 24 | assertTrue( 25 | x == 0 26 | || x == FrameworkConstant.WIDTH - 1 27 | || y == 0 28 | || y == FrameworkConstant.HEIGHT - 1); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/code/utils/search/way/WayUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.way; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import java.util.Random; 7 | import org.junit.Test; 8 | import fr.framework.FrameworkConstant; 9 | import fr.framework.logger.Logger; 10 | 11 | public class WayUtilsTest { 12 | 13 | private static final Logger logger = Logger.getLogger(WayUtilsTest.class); 14 | 15 | @Test 16 | public void test1() { 17 | Random random = new Random(); 18 | byte[] way = WayUtils.constructWay(); 19 | for (int i = 0; i < 1_000_000; i++) { 20 | double value = Double.MAX_VALUE * random.nextDouble(); 21 | WayUtils.setScore(way, value); 22 | assertEquals(value, WayUtils.getScore(way), 0); 23 | } 24 | } 25 | 26 | @Test 27 | public void test2() { 28 | byte[] way = WayUtils.constructWay(); 29 | for (int i = 0; i < FrameworkConstant.CASE_NB; i++) { 30 | assertFalse(WayUtils.isDonePosition(way, i)); 31 | WayUtils.setDonePosition(way, i); 32 | assertTrue(WayUtils.isDonePosition(way, i)); 33 | logger.error(WayUtils.toMapString(way)); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/code/utils/floodfill/PlaceUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.floodfill; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import java.io.IOException; 5 | import org.junit.Test; 6 | import fr.framework.FrameworkConstant; 7 | import fr.framework.MapUtils; 8 | import fr.framework.TestUtils; 9 | import fr.framework.list.ByteListUtils; 10 | import fr.framework.logger.Logger; 11 | import fr.framework.point.PointUtils; 12 | 13 | public class PlaceUtilsTest { 14 | 15 | private static final Logger logger = Logger.getLogger(PlaceUtilsTest.class); 16 | 17 | @Test 18 | public void test() throws IOException { 19 | byte[] map = TestUtils.initMap("map2"); 20 | MapUtils.initCache(map); 21 | byte placeNb = 2; 22 | int[] results = {0, 0, 14, 38, 4, 17}; 23 | 24 | logger.error("START PLACE TESTS"); 25 | for (byte p = 0; p < FrameworkConstant.CASE_NB; p++) { 26 | byte[] place = PlaceUtils.getPlace(p, map); 27 | if (!ByteListUtils.isEmpty(place)) { 28 | int size = ByteListUtils.size(place); 29 | for (int i = 0; i < size; i++) { 30 | byte p2 = ByteListUtils.get(place, i); 31 | map[p2] = placeNb; 32 | } 33 | assertEquals(results[placeNb], size); 34 | logger.error(placeNb, PointUtils.toPoint(p), MapUtils.toString(map)); 35 | placeNb++; 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/timeout/GarbageCollectorUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.timeout; 2 | 3 | import fr.code.variable.Parameter; 4 | import fr.framework.logger.Logger; 5 | import fr.framework.timer.Timer; 6 | 7 | public class GarbageCollectorUtils { 8 | 9 | private static final Logger logger = Logger.getLogger(GarbageCollectorUtils.class); 10 | 11 | private static long start; 12 | 13 | public static void avoidTimeOut() { 14 | StringBuilder[] tmp = new StringBuilder[Parameter.AVOID_TIMEOUT_NB]; 15 | for (int i = 0; i < Parameter.AVOID_TIMEOUT_NB; i++) { 16 | tmp[i] = new StringBuilder(); 17 | } 18 | tmp = null; 19 | garbageCollect(); 20 | } 21 | 22 | public static void garbageCollect() { 23 | // no logger because in some multis like UTTT i removed the Logger class to try improving the 24 | // performances 25 | System.err.println( 26 | (Timer.getInstance().getTime()) 27 | + " GARBAGE COLLECTOR START " 28 | + Runtime.getRuntime().freeMemory()); 29 | System.gc(); 30 | System.err.println( 31 | (Timer.getInstance().getTime()) 32 | + " GARBAGE COLLECTOR END " 33 | + Runtime.getRuntime().freeMemory()); 34 | } 35 | 36 | public static void initFreeMemory() { 37 | start = Runtime.getRuntime().freeMemory(); 38 | } 39 | 40 | /** to check if garbage collectable objects are created during each round */ 41 | public static void printUsedMemory() { 42 | logger.error("GARBAGE COLECTOR", Runtime.getRuntime().freeMemory() - start); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/point/Direction.java: -------------------------------------------------------------------------------- 1 | package fr.framework.point; 2 | 3 | public enum Direction { 4 | UP(0, -1, 0), 5 | DOWN(0, 1, 1), 6 | LEFT(-1, 0, 2), 7 | RIGHT(1, 0, 3); 8 | private int num; 9 | private int x; 10 | private int y; 11 | 12 | private static final Direction[] numToDirection = new Direction[4]; 13 | 14 | // To avoid calling Direction.values() which return a new array at each call 15 | public static final Direction[] values = new Direction[4]; 16 | 17 | static { 18 | Direction[] values = Direction.values(); 19 | for (int i = 0; i < values.length; i++) { 20 | Direction value = values[i]; 21 | numToDirection[value.getNum()] = value; 22 | Direction.values[i] = value; 23 | } 24 | } 25 | 26 | private Direction(int x, int y, int num) { 27 | this.x = x; 28 | this.y = y; 29 | this.num = num; 30 | } 31 | 32 | public int getNum() { 33 | return num; 34 | } 35 | 36 | public int getX() { 37 | return x; 38 | } 39 | 40 | public int getY() { 41 | return y; 42 | } 43 | 44 | public Direction getOpposite() { 45 | switch (this) { 46 | case LEFT: 47 | return Direction.RIGHT; 48 | case RIGHT: 49 | return Direction.LEFT; 50 | case UP: 51 | return Direction.DOWN; 52 | default: 53 | return UP; 54 | } 55 | } 56 | 57 | public static Direction fromNum(int num) { 58 | return numToDirection[num]; 59 | } 60 | 61 | public boolean is(int num) { 62 | return num == this.num; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/NextMoveUtils.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils; 2 | 3 | import fr.code.utils.search.WayBeamSearcher; 4 | import fr.code.utils.search.way.WayCache; 5 | import fr.code.utils.search.way.WayUtils; 6 | import fr.framework.MapUtils; 7 | import fr.framework.logger.Logger; 8 | import fr.framework.point.PointUtils; 9 | import fr.framework.timeout.GarbageCollectorUtils; 10 | 11 | public class NextMoveUtils extends Utils { 12 | 13 | private static final Logger logger = Logger.getLogger(NextMoveUtils.class); 14 | 15 | public static void proceed() { 16 | // calls to different steps 17 | } 18 | 19 | /** for demo purpose usually the code would be in the proceed() method */ 20 | public static byte[] proceed(int startPosition, int endPosition, byte[] map) { 21 | GarbageCollectorUtils.initFreeMemory(); 22 | FirstRoundUtils.proceed(map); 23 | 24 | // for testing purposes the search is not a beam search but a BFS (the heap is never full and 25 | // all ways have the same score) 26 | byte[] way = WayBeamSearcher.getInstance().findWay(startPosition, endPosition, map); 27 | if (way == null) { 28 | logger.error( 29 | "NO WAY FROM", PointUtils.toPoint(startPosition), "TO", PointUtils.toPoint(endPosition)); 30 | } else { 31 | logger.error(WayUtils.toString(way)); 32 | byte[] tmpMap = MapUtils.copy(map); 33 | for (int i = 0; i < WayUtils.getLength(way); i++) { 34 | tmpMap[WayUtils.getPosition(way, i)] = 2; 35 | } 36 | logger.error(MapUtils.toString(tmpMap)); 37 | } 38 | WayCache.printIndex(); 39 | GarbageCollectorUtils.printUsedMemory(); 40 | return way; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CodingameFramework/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | fr.codingame.framewok 6 | CodingameFramework 7 | 0.0.1-SNAPSHOT 8 | 9 | 1.8 10 | 1.8 11 | 12 | 13 | 14 | junit 15 | junit 16 | 4.12 17 | test 18 | 19 | 20 | org.powermock 21 | powermock-api-support 22 | 1.7.1 23 | test 24 | 25 | 26 | org.powermock 27 | powermock-api-mockito2 28 | 1.7.1 29 | test 30 | 31 | 32 | org.powermock 33 | powermock-module-junit4 34 | 1.7.1 35 | test 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-compiler-plugin 43 | 3.2 44 | 45 | 1.8 46 | 1.8 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/framework/TestUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.List; 8 | import org.powermock.reflect.Whitebox; 9 | import fr.code.object.Game; 10 | import fr.framework.logger.Logger; 11 | import fr.framework.timer.Timer; 12 | 13 | public class TestUtils { 14 | 15 | private static final Logger logger = Logger.getLogger(TestUtils.class); 16 | 17 | private static byte[] map; 18 | 19 | public static byte[] initMap(String fileName) throws IOException { 20 | List lines = readMapFromFile(fileName); 21 | Whitebox.setInternalState(Game.getInstance(), "round", 1); 22 | Timer.getInstance().init(); 23 | logger.error("round", Game.getInstance().getRound()); 24 | 25 | FrameworkConstant.WIDTH = lines.get(0).length(); 26 | FrameworkConstant.HEIGHT = lines.size(); 27 | FrameworkConstant.CASE_NB = FrameworkConstant.WIDTH * FrameworkConstant.HEIGHT; 28 | logger.error( 29 | "width", 30 | FrameworkConstant.WIDTH, 31 | "height", 32 | FrameworkConstant.HEIGHT, 33 | "case nb", 34 | FrameworkConstant.CASE_NB); 35 | AssertUtils.test(FrameworkConstant.CASE_NB <= Byte.MAX_VALUE); 36 | map = MapUtils.extract(lines); 37 | logger.error(MapUtils.toString(map)); 38 | return map; 39 | } 40 | 41 | private static List readMapFromFile(String fileName) throws IOException { 42 | Path root = Paths.get("src\\test\\resources"); 43 | Path path = root.resolve(fileName + ".txt"); 44 | logger.error(path.toAbsolutePath()); 45 | return Files.readAllLines(path); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/logger/Logger.java: -------------------------------------------------------------------------------- 1 | package fr.framework.logger; 2 | 3 | import fr.framework.timer.Timer; 4 | 5 | public class Logger { 6 | public enum Level { 7 | DEBUG, 8 | ERROR, 9 | NONE; 10 | } 11 | 12 | // set to NONE to deactivate all loggers 13 | private static final Level LEVEL = Level.ERROR; 14 | private Level levelSpecific; 15 | 16 | private Class clazz; 17 | 18 | private Logger(Class clazz) { 19 | this.clazz = clazz; 20 | } 21 | 22 | public static Logger getLogger(Class clazz) { 23 | return new Logger(clazz); 24 | } 25 | 26 | public static Logger getLogger(Class clazz, Level levelSpecific) { 27 | Logger ret = new Logger(clazz); 28 | ret.levelSpecific = levelSpecific; 29 | return ret; 30 | } 31 | 32 | public void error(Object... s) { 33 | print(s); 34 | } 35 | 36 | public void debug(Object... s) { 37 | if (getLevel() == Level.DEBUG) { 38 | print(s); 39 | } 40 | } 41 | 42 | private static final StringBuilder sb = new StringBuilder(5000); 43 | 44 | public void print(Object... s) { 45 | if (getLevel() == Level.NONE) { 46 | return; 47 | } 48 | sb.delete(0, sb.length()); 49 | sb.append(Timer.getInstance().getTime()); 50 | sb.append(' '); 51 | sb.append(clazz.getSimpleName()); 52 | sb.append(" : "); 53 | int i = 0; 54 | for (Object o : s) { 55 | sb.append(o == null ? "null" : o.toString()); 56 | if (++i < s.length) { 57 | sb.append(' '); 58 | } 59 | } 60 | System.err.println(sb); 61 | } 62 | 63 | private Level getLevel() { 64 | if (levelSpecific == null || LEVEL == Level.NONE) { 65 | return LEVEL; 66 | } 67 | return levelSpecific; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/code/utils/search/heap/HeapTest.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.heap; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import org.junit.Test; 8 | import fr.framework.logger.Logger; 9 | 10 | public class HeapTest { 11 | 12 | private static final Logger logger = Logger.getLogger(HeapTest.class); 13 | 14 | private Heap heap = new Heap(0); 15 | 16 | // this list contains all the values added to the heap 17 | // it's then sorted to assure that the bigger elements have been kept in the heap 18 | private List list = new ArrayList<>(); 19 | 20 | @Test 21 | public void test() { 22 | 23 | heap.setLimit(9); 24 | 25 | insert(15); 26 | insert(9); 27 | insert(10); 28 | insert(8); 29 | insert(6); 30 | insert(3); 31 | insert(4); 32 | insert(2); 33 | insert(20); 34 | insert(30); 35 | insert(-1); 36 | insert(25); 37 | insert(42); 38 | insert(-1); 39 | insert(-80); 40 | insert(200); 41 | insert(-10); 42 | } 43 | 44 | private void insert(double value) { 45 | heap.insert(null, value); 46 | list.add(value); 47 | Collections.sort(list); 48 | int minIndex = Math.max(0, list.size() - heap.getLimit()); 49 | logger.error("minIndex", minIndex); 50 | 51 | double min = list.get(minIndex); 52 | logger.error("value", value, "min", min, heap); 53 | assertEquals(min, heap.getValue(0), 0); 54 | 55 | List tmpList = new ArrayList<>(); 56 | for (int i = 0; i < heap.size(); i++) { 57 | tmpList.add(heap.getValue(i)); 58 | } 59 | Collections.sort(tmpList); 60 | 61 | for (int i = minIndex; i < list.size(); i++) { 62 | assertEquals(list.get(i), tmpList.get(i - minIndex), 0); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/code/utils/NextMoveUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertNull; 5 | import java.io.IOException; 6 | import org.junit.BeforeClass; 7 | import org.junit.Test; 8 | import fr.code.variable.Parameter; 9 | import fr.framework.TestUtils; 10 | import fr.framework.logger.Logger; 11 | import fr.framework.point.PointUtils; 12 | 13 | public class NextMoveUtilsTest { 14 | 15 | private static final Logger logger = Logger.getLogger(NextMoveUtilsTest.class); 16 | 17 | private static byte[] map; 18 | 19 | @BeforeClass 20 | public static void setup() throws IOException { 21 | map = TestUtils.initMap("map1"); 22 | Parameter.TIMEOUT_FIRST_ROUND = 1000_000; 23 | Parameter.BEAM_MAX_DEPTH = 100; 24 | } 25 | 26 | @Test 27 | public void test1() { 28 | 29 | int startPosition = PointUtils.getPosition(0, 0); 30 | int endPosition = PointUtils.getPosition(2, 0); 31 | 32 | byte[] way = NextMoveUtils.proceed(startPosition, endPosition, map); 33 | assertNotNull(way); 34 | } 35 | 36 | @Test 37 | public void test2() { 38 | int startPosition = PointUtils.getPosition(0, 0); 39 | int endPosition = PointUtils.getPosition(0, 7); 40 | 41 | byte[] way = NextMoveUtils.proceed(startPosition, endPosition, map); 42 | assertNull(way); 43 | } 44 | 45 | @Test 46 | public void test3() { 47 | int startPosition = PointUtils.getPosition(0, 0); 48 | int endPosition = PointUtils.getPosition(6, 4); 49 | 50 | byte[] way = NextMoveUtils.proceed(startPosition, endPosition, map); 51 | assertNotNull(way); 52 | } 53 | 54 | @Test 55 | public void test4() { 56 | // start in wall 57 | int startPosition = PointUtils.getPosition(1, 0); 58 | int endPosition = PointUtils.getPosition(0, 7); 59 | 60 | byte[] way = NextMoveUtils.proceed(startPosition, endPosition, map); 61 | assertNull(way); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CodingameFramework/src/test/java/fr/framework/list/ByteListUtilsTest.java: -------------------------------------------------------------------------------- 1 | package fr.framework.list; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertFalse; 5 | import static org.junit.Assert.assertTrue; 6 | import org.junit.Test; 7 | import fr.framework.logger.Logger; 8 | 9 | public class ByteListUtilsTest { 10 | 11 | private static final Logger logger = Logger.getLogger(ByteListUtilsTest.class); 12 | 13 | @Test 14 | public void test() { 15 | byte[] list = new byte[30]; 16 | ByteListUtils.add(list, (byte) 1); 17 | ByteListUtils.add(list, (byte) -1); 18 | ByteListUtils.add(list, (byte) 0); 19 | ByteListUtils.add(list, (byte) -2); 20 | ByteListUtils.add(list, (byte) 3); 21 | ByteListUtils.add(list, (byte) -2); 22 | 23 | int size = 6; 24 | assertSize(size, list); 25 | 26 | assertEquals(-2, ByteListUtils.getLast(list)); 27 | 28 | assertTrue(ByteListUtils.contains(list, (byte) -1)); 29 | assertFalse(ByteListUtils.contains(list, (byte) -5)); 30 | 31 | assertEquals(3, ByteListUtils.count(list, b -> b < 0)); 32 | 33 | ByteListUtils.removeIf(list, b -> b < 0); 34 | size -= 3; 35 | assertSize(size, list); 36 | 37 | ByteListUtils.removeByIndex(list, 3); 38 | assertSize(size, list); 39 | 40 | ByteListUtils.removeByIndex(list, 5); 41 | assertSize(size, list); 42 | 43 | ByteListUtils.removeByIndex(list, 1); 44 | size--; 45 | assertSize(size, list); 46 | 47 | ByteListUtils.removeByValue(list, (byte) 5); 48 | assertSize(size, list); 49 | 50 | ByteListUtils.removeByValue(list, (byte) 3); 51 | size--; 52 | assertSize(size, list); 53 | 54 | ByteListUtils.add(list, (byte) -2); 55 | size++; 56 | ByteListUtils.addAll(list, list); 57 | size *= 2; 58 | assertSize(size, list); 59 | 60 | ByteListUtils.addAll(list, list); 61 | size *= 2; 62 | assertSize(size, list); 63 | } 64 | 65 | private void assertSize(int size, byte[] list) { 66 | assertEquals(size, ByteListUtils.size(list)); 67 | logger.error(ByteListUtils.toString(list)); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/floodfill/PlaceUtils.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.floodfill; 2 | 3 | import fr.framework.FrameworkConstant; 4 | import fr.framework.MapUtils; 5 | import fr.framework.list.ByteListUtils; 6 | 7 | public class PlaceUtils { 8 | 9 | private static final int[] DONE_MAP = new int[FrameworkConstant.CASE_NB]; 10 | private static final byte[] RET_LIST = new byte[FrameworkConstant.CASE_NB + 1]; 11 | private static final byte[][] TMP_LISTS = new byte[2][]; 12 | private static int DONE_COUNT = 0; 13 | 14 | static { 15 | for (int i = 0; i < TMP_LISTS.length; i++) { 16 | TMP_LISTS[i] = new byte[FrameworkConstant.CASE_NB + 1]; 17 | } 18 | } 19 | 20 | public static int getPlaceSize(byte start, byte[] map) { 21 | return ByteListUtils.size(getPlace(start, map, Integer.MAX_VALUE)); 22 | } 23 | 24 | public static byte[] getPlace(byte start, byte[] map) { 25 | return getPlace(start, map, Integer.MAX_VALUE); 26 | } 27 | 28 | public static byte[] getPlace(byte start, byte[] map, int limit) { 29 | ByteListUtils.clear(RET_LIST); 30 | if (!MapUtils.isFree(map, start)) { 31 | return RET_LIST; 32 | } 33 | 34 | incrementDoneCount(); 35 | DONE_MAP[start] = DONE_COUNT; 36 | ByteListUtils.add(RET_LIST, start); 37 | 38 | byte[] current = TMP_LISTS[0]; 39 | ByteListUtils.clear(current); 40 | ByteListUtils.add(current, start); 41 | 42 | int l = 0; 43 | while (!ByteListUtils.isEmpty(current) && l < limit) { 44 | l++; 45 | byte[] next = TMP_LISTS[l % 2]; 46 | ByteListUtils.clear(next); 47 | int size = ByteListUtils.size(current); 48 | for (int j = 0; j < size; j++) { 49 | byte p = ByteListUtils.get(current, j); 50 | byte[] nexts = MapUtils.getAroundPositions(p); 51 | for (int i = 0; i < nexts.length; i++) { 52 | byte p2 = nexts[i]; 53 | if (DONE_MAP[p2] != DONE_COUNT) { 54 | ByteListUtils.add(next, p2); 55 | DONE_MAP[p2] = DONE_COUNT; 56 | ByteListUtils.add(RET_LIST, p2); 57 | } 58 | } 59 | } 60 | current = next; 61 | } 62 | return RET_LIST; 63 | } 64 | 65 | private static void incrementDoneCount() { 66 | DONE_COUNT++; 67 | if (DONE_COUNT == 0) { 68 | for (int i = 0; i < DONE_MAP.length; i++) { 69 | DONE_MAP[i] = 0; 70 | } 71 | DONE_COUNT++; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/point/PointUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.point; 2 | 3 | import java.util.Collection; 4 | import java.util.List; 5 | import java.util.Scanner; 6 | import java.util.stream.Collectors; 7 | import java.util.stream.IntStream; 8 | import fr.framework.FrameworkConstant; 9 | 10 | public class PointUtils { 11 | 12 | private static Point[] pointCache; 13 | 14 | private static final Point OUT = new Point(-1, -1); 15 | 16 | public static Point toPoint(int p) { 17 | if (p == FrameworkConstant.OUT) { 18 | return OUT; 19 | } 20 | if (pointCache == null) { 21 | pointCache = new Point[FrameworkConstant.CASE_NB]; 22 | } 23 | Point ret = pointCache[p]; 24 | if (ret == null) { 25 | ret = new Point(getX(p), getY(p)); 26 | pointCache[p] = ret; 27 | } 28 | return ret; 29 | } 30 | 31 | /** for logging purpose */ 32 | public static List toPoint(byte[] array) { 33 | return IntStream.range(0, array.length) 34 | .mapToObj(i -> array[i]) 35 | .map(PointUtils::toPoint) 36 | .collect(Collectors.toList()); 37 | } 38 | 39 | /** for logging purpose */ 40 | public static List toPoint(Collection collection) { 41 | return collection.stream().map(PointUtils::toPoint).collect(Collectors.toList()); 42 | } 43 | 44 | public static int getPosition(Scanner in) { 45 | int x = in.nextInt(); 46 | int y = in.nextInt(); 47 | return getPosition(x, y); 48 | } 49 | 50 | public static int getPosition(int x, int y) { 51 | return x + FrameworkConstant.WIDTH * y; 52 | } 53 | 54 | public static int distance(int p1, int p2) { 55 | return getDeltaX(p1, p2) + getDeltaY(p1, p2); 56 | } 57 | 58 | private static int getDeltaX(int p1, int p2) { 59 | return Math.abs(getX(p1) - getX(p2)); 60 | } 61 | 62 | private static int getDeltaY(int p1, int p2) { 63 | return Math.abs(getY(p1) - getY(p2)); 64 | } 65 | 66 | public static boolean isNextTo(int p1, int p2) { 67 | return distance(p1, p2) <= 1; 68 | } 69 | 70 | public static int getX(int p) { 71 | return p % FrameworkConstant.WIDTH; 72 | } 73 | 74 | public static int getY(int p) { 75 | return p / FrameworkConstant.WIDTH; 76 | } 77 | 78 | public static String toXYString(int p) { 79 | return getX(p) + " " + getY(p); 80 | } 81 | 82 | public static boolean isIn(int p) { 83 | return p >= 0 && p < FrameworkConstant.CASE_NB; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/point/DirectionUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.point; 2 | 3 | import fr.framework.FrameworkConstant; 4 | import fr.framework.logger.Logger; 5 | 6 | public class DirectionUtils { 7 | 8 | private static final Logger logger = Logger.getLogger(DirectionUtils.class); 9 | 10 | public static String toString(Direction dir) { 11 | if (dir == null) { 12 | return null; 13 | } 14 | switch (dir) { 15 | case UP: 16 | return "N"; 17 | case DOWN: 18 | return "S"; 19 | case RIGHT: 20 | return "E"; 21 | default: 22 | return "W"; 23 | } 24 | } 25 | 26 | public static Direction fromString(String dir) { 27 | switch (dir) { 28 | case "N": 29 | return Direction.UP; 30 | case "S": 31 | return Direction.DOWN; 32 | case "E": 33 | return Direction.RIGHT; 34 | case "W": 35 | return Direction.LEFT; 36 | default: 37 | throw new IllegalStateException("invalid direction"); 38 | } 39 | } 40 | 41 | public static Direction getDirectionTo(int p1, int p2) { 42 | int x = PointUtils.getX(p2) - PointUtils.getX(p1); 43 | if (x != 0) { 44 | x /= Math.abs(x); 45 | } 46 | int y = PointUtils.getY(p2) - PointUtils.getY(p1); 47 | if (y != 0) { 48 | y /= Math.abs(y); 49 | } 50 | for (Direction dir : Direction.values) { 51 | if (dir.getX() == x && dir.getY() == y) { 52 | return dir; 53 | } 54 | } 55 | throw new IllegalStateException( 56 | "no direction " + PointUtils.toPoint(p1) + " to " + PointUtils.toPoint(p2)); 57 | } 58 | 59 | public static int construct(int p, Direction dir) { 60 | return construct(p, dir.getNum()); 61 | } 62 | 63 | public static int construct(int p, int dir) { 64 | if (Direction.DOWN.is(dir)) { 65 | if (PointUtils.getY(p) == FrameworkConstant.HEIGHT - 1) { 66 | p = FrameworkConstant.OUT; 67 | } else { 68 | p += FrameworkConstant.WIDTH; 69 | } 70 | } else if (Direction.LEFT.is(dir)) { 71 | if (PointUtils.getX(p) == 0) { 72 | p = FrameworkConstant.OUT; 73 | } else { 74 | p--; 75 | } 76 | } else if (Direction.RIGHT.is(dir)) { 77 | if (PointUtils.getX(p) == FrameworkConstant.WIDTH - 1) { 78 | p = FrameworkConstant.OUT; 79 | } else { 80 | p++; 81 | } 82 | } else if (Direction.UP.is(dir)) { 83 | if (PointUtils.getY(p) == 0) { 84 | p = FrameworkConstant.OUT; 85 | } else { 86 | p -= FrameworkConstant.WIDTH; 87 | } 88 | } else { 89 | throw new IllegalStateException("no direction " + dir); 90 | } 91 | return p; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodingameFramework 2 | 3 | This project contains some __Java__ utilities/tips for __[Codingame](https://www.codingame.com/multiplayer)__. 4 | 5 | When i started on Codingame, i tried to reach legend in a lot of multis. It seems like an opportunity to practice Java 8. My bots were mostly heuristic with no use of a timer. 6 | But then, after pushing back as much as possible, i had to do [UTTT](https://www.codingame.com/multiplayer/bot-programming/tic-tac-toe). It changed my vision of Codingame and now i'm redoing all my previous bots. 7 | 8 | Here are some Java tips that i've learned so far : 9 | 10 | * Use a [Merger](CodingameFramework/src/main/java/fr/framework/merger/ClassMerger.java) that allows you to __code in multiple files__. I coded the [CodinGame Sponsored Challenge](https://www.codingame.com/multiplayer/optimization/codingame-sponsored-contest) in one file. I had to stop when it wasn't readable anymore (still got to redo it). 11 | 12 | * Use a __personal [Logger](CodingameFramework/src/main/java/fr/framework/logger/Logger.java)__ printing time elapsed and class name. 13 | 14 | * Go back to the source. __Forget about Java 8__ (Stream, Optional...). I even removed foreach loops in UTTT. 15 | 16 | * I use mostly __primitives and arrays__ rather than objects. In this project to store a position, i used a byte rather than an object with x and y fields (see [PointUtils](CodingameFramework/src/main/java/fr/framework/point/PointUtils.java)). 17 | 18 | * I almost __don’t use List, Set, Map__... anymore. I have small [utilities](CodingameFramework/src/main/java/fr/framework/list/ByteListUtils.java) to use arrays instead of collections (basically the first element of the array is the number of elements in the "list"). It avoids boxing/unboxing, the creation of "useless" objects (Integer, Byte...), to clear a "list" you only set the first element to 0... Look at [PlaceUtils](CodingameFramework/src/main/java/fr/code/utils/floodfill/PlaceUtils.java) to see a use case. 19 | 20 | * I use __[assertions](CodingameFramework/src/main/java/fr/framework/AssertUtils.java)__ rather than Unit Tests to make sure that there's no bugs/regressions. 21 | For example in [Spring Challenge 2021](https://www.codingame.com/multiplayer/bot-programming/spring-challenge-2021) : i kept the state of the game at the beginning of my last turn, then i "guessed" the opponent actions, used my simulation to play my action and his, and compare (with assertions) the result and the game inputs. 22 | 23 | * Create __caches__ ([around positions](CodingameFramework/src/main/java/fr/framework/MapUtils.java), distances...) 24 | 25 | * One of the main problems using Java in CodingGame (besides its slowness) is garbage collector timeouts. One way to reduce them is to implement the trick in [GarbageCollectorUtils](CodingameFramework/src/main/java/fr/framework/timeout/GarbageCollectorUtils.java) (see also [JVM memory issues](https://www.codingame.com/forum/t/java-jvm-memory-issues/1494)). But mostly you must __avoid creating garbage collectable objects__. For an example, you can look at the [Beam Search](CodingameFramework/src/main/java/fr/code/utils/search/WayBeamSearcher.java) implementation. 26 | 27 | I'm also starting to use bitboards (but no specific tips for Java). 28 | 29 | There's an [associated post](https://www.codingame.com/forum/t/java-codingame-tips/191870) in the Codingame forum. 30 | Hope it will be helpful. 31 | 32 | 33 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/heap/Heap.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.heap; 2 | 3 | import java.util.Arrays; 4 | import fr.code.variable.Parameter; 5 | 6 | /** 7 | * The element with the lowest score is always at the index 0. It's removed when an 'object' with a 8 | * highest score is added https://en.wikipedia.org/wiki/Heap_(data_structure) 9 | */ 10 | public class Heap { 11 | 12 | private static final int MAX_NB = Parameter.BEAM_NB; 13 | 14 | private int limit = MAX_NB; 15 | private byte[][] items = new byte[MAX_NB][]; 16 | private double[] values = new double[MAX_NB]; 17 | private int nb; 18 | private final int index; 19 | 20 | public Heap(int index) { 21 | this.index = index; 22 | } 23 | 24 | private void siftUp() { 25 | int k = nb - 1; 26 | while (k > 0) { 27 | int p = (k - 1) / 2; 28 | byte[] item = items[k]; 29 | byte[] parent = items[p]; 30 | double valueItem = values[k]; 31 | double valueParent = values[p]; 32 | 33 | if (valueItem < valueParent) { 34 | // swap 35 | items[k] = parent; 36 | items[p] = item; 37 | values[k] = valueParent; 38 | values[p] = valueItem; 39 | // move up one level 40 | k = p; 41 | } else { 42 | break; 43 | } 44 | } 45 | } 46 | 47 | public void insert(byte[] item, double value) { 48 | boolean add = true; 49 | if (nb == limit) { 50 | if (value > values[0]) { 51 | // insert 52 | delete(); 53 | } else { 54 | add = false; 55 | } 56 | } 57 | if (add) { 58 | items[nb] = item; 59 | values[nb] = value; 60 | nb++; 61 | siftUp(); 62 | } 63 | } 64 | 65 | private void siftDown() { 66 | int k = 0; 67 | int l = 2 * k + 1; 68 | while (l < nb) { 69 | int max = l; 70 | int r = l + 1; 71 | if (r < nb) { // there is a right child 72 | if (values[r] < values[l]) { 73 | max = r; 74 | } 75 | } 76 | if (values[k] > values[max]) { 77 | // switch 78 | byte[] temp = items[k]; 79 | double tempValue = values[k]; 80 | items[k] = items[max]; 81 | values[k] = values[max]; 82 | items[max] = temp; 83 | values[max] = tempValue; 84 | k = max; 85 | l = 2 * k + 1; 86 | } else { 87 | break; 88 | } 89 | } 90 | } 91 | 92 | public byte[] delete() { 93 | byte[] hold = items[0]; 94 | items[0] = items[nb - 1]; 95 | values[0] = values[nb - 1]; 96 | nb--; 97 | siftDown(); 98 | return hold; 99 | } 100 | 101 | public int size() { 102 | return nb; 103 | } 104 | 105 | public boolean isEmpty() { 106 | return nb == 0; 107 | } 108 | 109 | public void clear() { 110 | nb = 0; 111 | } 112 | 113 | public byte[] get(int j) { 114 | return items[j]; 115 | } 116 | 117 | public double getValue(int j) { 118 | return values[j]; 119 | } 120 | 121 | public int getLimit() { 122 | return limit; 123 | } 124 | 125 | public void setLimit(int limit) { 126 | this.limit = Math.min(limit, MAX_NB); 127 | } 128 | 129 | public int getIndex() { 130 | return index; 131 | } 132 | 133 | @Override 134 | public String toString() { 135 | return "Heap [nb = " + nb + ", " + Arrays.toString(Arrays.copyOf(values, nb)) + "]"; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/list/ByteListUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework.list; 2 | 3 | import java.util.Arrays; 4 | import fr.framework.AssertUtils; 5 | import fr.framework.function.BytePredicate; 6 | 7 | /** copy/paste for IntListUtils, ShortListUtils... */ 8 | public class ByteListUtils { 9 | 10 | public static void add(byte[] array, byte p) { 11 | array[size(array) + 1] = p; 12 | array[0]++; 13 | } 14 | 15 | public static void removeByValue(byte[] array, byte p) { 16 | boolean remove = false; 17 | for (int i = 0; i < size(array); i++) { 18 | byte value = get(array, i); 19 | if (remove) { 20 | set(array, (byte) (i - 1), value); 21 | } else if (value == p) { 22 | remove = true; 23 | } 24 | } 25 | if (remove) { 26 | array[0]--; 27 | } 28 | } 29 | 30 | public static void removeByIndex(byte[] array, int index) { 31 | boolean remove = size(array) > index; 32 | for (int i = index + 1; i < size(array); i++) { 33 | byte value = get(array, i); 34 | set(array, (byte) (i - 1), value); 35 | } 36 | if (remove) { 37 | array[0]--; 38 | } 39 | } 40 | 41 | public static void clear(byte[] array) { 42 | array[0] = 0; 43 | } 44 | 45 | public static int size(byte[] array) { 46 | return array[0]; 47 | } 48 | 49 | public static boolean isEmpty(byte[] array) { 50 | return size(array) == 0; 51 | } 52 | 53 | public static byte get(byte[] array, int i) { 54 | return array[i + 1]; 55 | } 56 | 57 | public static void set(byte[] array, int i, byte newValue) { 58 | array[i + 1] = newValue; 59 | } 60 | 61 | public static boolean contains(byte[] array, byte i) { 62 | for (int j = 0; j < size(array); j++) { 63 | if (get(array, j) == i) { 64 | return true; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | public static byte[] cut(byte[] array) { 71 | byte[] tmp = new byte[size(array)]; 72 | System.arraycopy(array, 1, tmp, 0, tmp.length); 73 | return tmp; 74 | } 75 | 76 | public static void copy(byte[] source, byte[] destination) { 77 | System.arraycopy(source, 0, destination, 0, size(source) + 1); 78 | } 79 | 80 | public static void addAll(byte[] source, byte[] destination) { 81 | System.arraycopy(source, 1, destination, size(destination) + 1, size(source)); 82 | destination[0] += source[0]; 83 | } 84 | 85 | public static byte getLast(byte[] array) { 86 | int size = size(array); 87 | AssertUtils.test(size > 0, "EMPTY BYTE LIST"); 88 | return get(array, size - 1); 89 | } 90 | 91 | public static void removeIf(byte[] array, BytePredicate pred) { 92 | int size = size(array); 93 | int index = 1; 94 | int newSize = size; 95 | for (int j = 0; j < size; j++) { 96 | byte p = get(array, j); 97 | if (pred.test(p)) { 98 | newSize--; 99 | } else { 100 | array[index++] = p; 101 | } 102 | } 103 | array[0] = (byte) newSize; 104 | } 105 | 106 | public static int count(byte[] array, BytePredicate pred) { 107 | int size = size(array); 108 | int count = 0; 109 | for (int j = 0; j < size; j++) { 110 | byte p = get(array, j); 111 | if (pred.test(p)) { 112 | count++; 113 | } 114 | } 115 | return count; 116 | } 117 | 118 | public static String toString(byte[] array) { 119 | return Arrays.toString(cut(array)); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/MapUtils.java: -------------------------------------------------------------------------------- 1 | package fr.framework; 2 | 3 | import java.util.List; 4 | import fr.framework.logger.Logger; 5 | import fr.framework.point.Direction; 6 | import fr.framework.point.DirectionUtils; 7 | import fr.framework.point.PointUtils; 8 | 9 | public class MapUtils { 10 | 11 | private static final Logger logger = Logger.getLogger(MapUtils.class); 12 | 13 | private static byte[][] aroundPositionCache; 14 | 15 | public static boolean isFree(byte[] map, int p) { 16 | return map[p] == FrameworkConstant.FREE; 17 | } 18 | 19 | public static byte[] copy(byte[] map) { 20 | return ByteUtils.copy(map); 21 | } 22 | 23 | public static void copy(byte[] source, byte[] destination) { 24 | ByteUtils.copy(source, destination); 25 | } 26 | 27 | public static byte[] createNewMap() { 28 | return new byte[FrameworkConstant.CASE_NB]; 29 | } 30 | 31 | public static int getNbAroundPositions(int p) { 32 | return getAroundPositions(p).length; 33 | } 34 | 35 | public static byte[] getAroundPositions(int p) { 36 | return aroundPositionCache[p]; 37 | } 38 | 39 | public static void initCache(byte[] map) { 40 | logger.error("INIT CACHE START"); 41 | aroundPositionCache = new byte[FrameworkConstant.CASE_NB][]; 42 | for (int p = 0; p < FrameworkConstant.CASE_NB; p++) { 43 | if (isFree(map, p)) { 44 | calculateAroundPositions(p, map); 45 | } 46 | } 47 | logger.error("INIT CACHE END"); 48 | } 49 | 50 | private static void calculateAroundPositions(int p, byte[] map) { 51 | byte[] ret = new byte[4]; 52 | Direction[] dirs = Direction.values; 53 | int index = 0; 54 | for (int i = 0; i < dirs.length; i++) { 55 | Direction direction = dirs[i]; 56 | int p2 = DirectionUtils.construct(p, direction); 57 | if (PointUtils.isIn(p2) && isFree(map, p2)) { 58 | ret[index++] = (byte) p2; 59 | } 60 | } 61 | aroundPositionCache[p] = ByteUtils.copy(ret, index); 62 | } 63 | 64 | public static String toString(byte[] map) { 65 | StringBuilder str = new StringBuilder(); 66 | for (int p = 0; p < FrameworkConstant.CASE_NB; p++) { 67 | if (p % FrameworkConstant.WIDTH == 0) { 68 | str.append('\n'); 69 | } 70 | int v = map[p]; 71 | char c; 72 | if (v == FrameworkConstant.WALL) { 73 | c = '#'; 74 | } else if (v == FrameworkConstant.FREE) { 75 | c = '.'; 76 | } else { 77 | if (v >= 10) v = 9; 78 | c = Character.forDigit(v, 10); 79 | } 80 | str.append(c); 81 | } 82 | return str.toString(); 83 | } 84 | 85 | public static byte[] extract(List lines) { 86 | byte[] map = createNewMap(); 87 | int lineNum = 0; 88 | for (String line : lines) { 89 | for (int x = 0; x < line.length(); x++) { 90 | char c = line.charAt(x); 91 | fromInput(c, lineNum, map); 92 | lineNum++; 93 | } 94 | } 95 | return map; 96 | } 97 | 98 | public static byte[] extract(String line, int lineNum, byte[] map) { 99 | int i = lineNum * FrameworkConstant.WIDTH; 100 | for (int x = 0; x < FrameworkConstant.WIDTH; x++) { 101 | fromInput(line.charAt(x), i + x, map); 102 | } 103 | return map; 104 | } 105 | 106 | public static void fromInput(char c, int p, byte[] map) { 107 | int v; 108 | if (c == '#') { 109 | v = FrameworkConstant.WALL; 110 | } else if (c == '.') { 111 | v = FrameworkConstant.FREE; 112 | } else { 113 | throw new IllegalStateException("WRONG CHAR *" + c + "* " + p); 114 | } 115 | map[p] = (byte) v; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/WayBeamSearcher.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search; 2 | 3 | import fr.code.utils.Utils; 4 | import fr.code.utils.search.heap.Heap; 5 | import fr.code.utils.search.heap.HeapCache; 6 | import fr.code.utils.search.way.NoMoreWayException; 7 | import fr.code.utils.search.way.WayCache; 8 | import fr.code.utils.search.way.WayUtils; 9 | import fr.code.variable.Parameter; 10 | import fr.framework.MapUtils; 11 | import fr.framework.logger.Logger; 12 | import fr.framework.timeout.TimeoutException; 13 | import fr.framework.timeout.TimeoutUtils; 14 | 15 | public class WayBeamSearcher extends Utils { 16 | 17 | private static final Logger logger = Logger.getLogger(WayBeamSearcher.class); 18 | 19 | private static WayBeamSearcher instance = new WayBeamSearcher(); 20 | private int indexStartTimeout; 21 | private int timeout; 22 | 23 | private WayBeamSearcher() {} 24 | 25 | public static WayBeamSearcher getInstance() { 26 | return instance; 27 | } 28 | 29 | /** 30 | * For a real beam search there wouldn't be an end position. It's only for testing purposes. As it 31 | * stands it's more a breadth-first search. In the tests the heap will never be full. 32 | */ 33 | public byte[] findWay(int startPosition, int endPosition, byte[] map) { 34 | 35 | WayCache.reset(); 36 | initTimeOut(); 37 | Heap currentWays = HeapCache.get(0); 38 | currentWays.clear(); 39 | 40 | byte[] startWay = WayUtils.calculateStartWay(map, startPosition); 41 | if (startWay == null) { 42 | // no way (in a wall...) 43 | return null; 44 | } 45 | currentWays.insert(startWay, WayUtils.getScore(startWay)); 46 | int count = 0; 47 | int simNb = 0; 48 | try { 49 | while (true) { 50 | count++; 51 | if (count >= Parameter.BEAM_MAX_DEPTH) { 52 | logger.error("TOO LONG", count); 53 | break; 54 | } 55 | logger.error("count =", count, ", heap size =", currentWays.size()); 56 | Heap nextWays = HeapCache.get(1 - currentWays.getIndex()); 57 | nextWays.clear(); 58 | boolean testTimeout = count >= indexStartTimeout; 59 | int length = currentWays.size(); 60 | for (int j = 0; j < length; j++) { 61 | if (testTimeout) { 62 | // to avoid looking up current time on the first loop iterations when it's sure that 63 | // there will 64 | // be no timeout 65 | TimeoutUtils.stopTimeException(timeout); 66 | } 67 | byte[] way = currentWays.get(j); 68 | 69 | byte[] nextPositions = MapUtils.getAroundPositions(WayUtils.getLastPosition(way)); 70 | 71 | for (int i = 0; i < nextPositions.length; i++) { 72 | simNb++; 73 | int nextPosition = nextPositions[i]; 74 | byte[] nextWay = WayUtils.calculateNextWay(way, nextPosition); 75 | if (nextWay != null) { 76 | // only for the BFS JUnit test 77 | if (WayUtils.getLastPosition(nextWay) == endPosition) { 78 | return nextWay; 79 | } 80 | nextWays.insert(nextWay, WayUtils.getScore(nextWay)); 81 | } 82 | } 83 | } 84 | // uncomment to handle properly the end game of a real beam search 85 | // if (nextWays.isEmpty()) { 86 | // logger.error("OVER"); 87 | // break; 88 | // } 89 | currentWays = nextWays; 90 | } 91 | } catch (NoMoreWayException e) { 92 | logger.error("NO MORE WAY IN CACHE"); 93 | } catch (TimeoutException e) { 94 | logger.error("TIME OUT", timeout); 95 | } 96 | logger.error("END count = ", count, ", ways =", currentWays.size(), ", simNb =", simNb); 97 | return chooseBestWay(currentWays); 98 | } 99 | 100 | private void initTimeOut() { 101 | timeout = game.isFirstRound() ? Parameter.TIMEOUT_FIRST_ROUND : Parameter.TIMEOUT; 102 | indexStartTimeout = Parameter.TIMEOUT_START_INDEX; 103 | } 104 | 105 | private byte[] chooseBestWay(Heap ways) { 106 | int length = ways.size(); 107 | double maxScore = Double.NEGATIVE_INFINITY; 108 | byte[] bestWay = null; 109 | for (int j = 0; j < length; j++) { 110 | double score = ways.getValue(j); 111 | if (score > maxScore) { 112 | bestWay = ways.get(j); 113 | maxScore = score; 114 | } 115 | } 116 | return bestWay; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/framework/merger/ClassMerger.java: -------------------------------------------------------------------------------- 1 | package fr.framework.merger; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.Paths; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | import java.util.stream.Stream; 12 | import fr.code.variable.Parameter; 13 | 14 | public class ClassMerger { 15 | 16 | private static Path root = Paths.get("src\\main\\java"); 17 | 18 | // to exclude whole package 19 | private static List excludedPackages = Arrays.asList("fr.framework.merger"); 20 | 21 | // to exclude single package (here it could be empty the ClassMerger package is already excluded) 22 | private static List excludedFiles = 23 | Arrays.asList(ClassMerger.class).stream().map(c -> c.getName()).collect(Collectors.toList()); 24 | 25 | public static void main(String[] args) throws IOException { 26 | Stream files = 27 | Files.walk(root) 28 | .filter(p -> p.getFileName().toString().endsWith(".java")) 29 | .filter( 30 | p -> 31 | excludedPackages 32 | .stream() 33 | .noneMatch(s -> p.toString().replace(File.separator, ".").contains(s))) 34 | .filter( 35 | p -> 36 | excludedFiles 37 | .stream() 38 | .noneMatch(s -> p.toString().replace(File.separator, ".").contains(s))); 39 | System.out.println("import java.util.*;"); 40 | System.out.println("import java.text.*;"); 41 | System.out.println("import java.util.function.*;"); 42 | System.out.println("import java.util.stream.*;"); 43 | System.out.println("import java.util.concurrent.*;"); 44 | System.out.println("import java.util.Map.*;"); 45 | 46 | files.forEach(ClassMerger::printFileWithoutImports); 47 | } 48 | 49 | private static void printFileWithoutImports(Path p) { 50 | StringBuilder sb = new StringBuilder(); 51 | try { 52 | Files.lines(p) 53 | .map(s -> s.trim()) 54 | .filter(s -> !s.startsWith("import ")) 55 | .filter(s -> !s.startsWith("package ")) 56 | .map(s -> s.replaceAll("public class ", "class ")) 57 | .map(s -> s.replaceAll("private", "")) 58 | .map(s -> s.replaceAll("public enum ", "enum ")) 59 | .map(s -> s.replaceAll("public interface ", "interface ")) 60 | .map(s -> s.replaceAll("public abstract ", "abstract ")) 61 | .map( // to remove useless spaces 62 | s -> 63 | s.replaceAll(" == ", "==") 64 | .replaceAll(" \\{", "{") 65 | .replaceAll(", ", ",") 66 | .replaceAll(" = ", "=") 67 | .replaceAll("} ", "}") 68 | .replaceAll(" = ", "=") 69 | .replaceAll(" > ", ">") 70 | .replaceAll(" >= ", ">=") 71 | .replaceAll(" < ", "<") 72 | .replaceAll(" <= ", "<=") 73 | .replaceAll(" \\+= ", "+=") 74 | .replaceAll(" \\*= ", "*=") 75 | .replaceAll(" -= ", "-=") 76 | .replaceAll(" \\)", ")") 77 | .replaceAll(" ^ ", "^") 78 | .replaceAll(" && ", "&&") 79 | .replaceAll(" \\|\\| ", "||") 80 | .replaceAll(" & ", "&") 81 | .replaceAll(" \\| ", "|") 82 | .replaceAll(" == ", "==") 83 | .replaceAll(" != ", "!=") 84 | .replaceAll(" \\? ", "?") 85 | .replaceAll(", ", ",") 86 | .replaceAll(" : ", ":") 87 | .replaceAll(": ", ":") 88 | .replaceAll(" \\+ ", "+") 89 | .replaceAll(" - ", "-") 90 | .replaceAll(" \\* ", "*") 91 | .replaceAll(" %= ", "%=") 92 | .replaceAll(" % ", "%") 93 | .replaceAll(" / ", "/") 94 | .replaceAll(" \\(", "(") 95 | .replaceAll("; ", ";") 96 | .replaceAll(" -> ", "->") 97 | .replaceAll(" => ", "=>") 98 | .replaceAll(" =>", "=>") 99 | .replaceAll(" ` `", "` `") 100 | .replaceAll(" ``", "``") 101 | .replaceAll("\\) ", ")") 102 | .replaceAll("//.*", "")) 103 | .map(s -> s.trim()) 104 | .filter(s -> !s.startsWith("//")) // remove comments 105 | .filter(s -> !s.isEmpty()) 106 | .forEach(s -> sb.append(s.equals("@Override") ? s + " " : (s + "\n"))); 107 | String str = sb.toString(); 108 | if (Parameter.REMOVE_ASSERT) { 109 | str = str.replaceAll("AssertUtils\\.test\\([^\\;]+\\;", ""); 110 | } 111 | if (Parameter.REMOVE_LOG) { 112 | str = str.replaceAll("logger\\.error\\([^\\;]+\\;", ""); 113 | } 114 | System.out.println(str); 115 | } catch (IOException e) { 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /CodingameFramework/src/main/java/fr/code/utils/search/way/WayUtils.java: -------------------------------------------------------------------------------- 1 | package fr.code.utils.search.way; 2 | 3 | import fr.code.utils.Utils; 4 | import fr.code.variable.Parameter; 5 | import fr.framework.AssertUtils; 6 | import fr.framework.FrameworkConstant; 7 | import fr.framework.MapUtils; 8 | import fr.framework.point.PointUtils; 9 | 10 | public class WayUtils extends Utils { 11 | 12 | private static int index = 0; 13 | private static final int SCORE_INDEX = index; 14 | 15 | static { 16 | index += 8; 17 | } 18 | 19 | private static final int MAP_INDEX = index; 20 | private static final int BIT_NB = 8; 21 | 22 | static { 23 | index += Math.ceil(FrameworkConstant.CASE_NB / (double) BIT_NB); 24 | } 25 | 26 | private static final int WAY_LENGTH_INDEX = index++; 27 | private static final int WAY_POSITIONS_START_INDEX = index; 28 | 29 | static { 30 | index += Parameter.WAY_SAVE_LENGTH; 31 | } 32 | 33 | private static final int LENGTH = index; 34 | // usually there are more fields. 35 | // We could allow to go through the same position multiple times (for a real beam search)... 36 | 37 | public static byte[] constructWay() { 38 | return new byte[LENGTH]; 39 | } 40 | 41 | public static void reset(byte[] way) { 42 | resetLength(way); 43 | setScore(way, 0); 44 | } 45 | 46 | public static byte[] calculateStartWay(byte[] map, int startPosition) { 47 | if (MapUtils.isFree(map, startPosition)) { 48 | byte[] ret = WayCache.getNext(); 49 | reset(ret); 50 | addNextPosition(ret, startPosition); 51 | setScore(ret, calculateScore(ret)); 52 | return ret; 53 | } 54 | return null; 55 | } 56 | 57 | public static byte[] calculateNextWay(byte[] way, int nextPosition) { 58 | if (isDonePosition(way, nextPosition)) { 59 | // if you don't allow going back 60 | return null; 61 | } 62 | byte[] ret = copy(way); 63 | addNextPosition(ret, nextPosition); 64 | setScore(ret, calculateScore(ret)); 65 | return ret; 66 | } 67 | 68 | public static byte[] copy(byte[] way) { 69 | byte[] ret = WayCache.getNext(); 70 | System.arraycopy(way, 0, ret, 0, WAY_LENGTH_INDEX + 1 + getLastPositionIndex(way)); 71 | return ret; 72 | } 73 | 74 | private static void addNextPosition(byte[] way, int newPosition) { 75 | setPosition(way, newPosition, getLength(way)); 76 | incrementLength(way); 77 | } 78 | 79 | public static void setPosition(byte[] way, int position, int round) { 80 | int index = getPositionIndex(way, round); 81 | setField(way, WAY_POSITIONS_START_INDEX + index, position); 82 | setDonePosition(way, position); 83 | } 84 | 85 | public static int getLength(byte[] way) { 86 | return getField(way, WAY_LENGTH_INDEX); 87 | } 88 | 89 | public static int getPosition(byte[] way, int round) { 90 | if (round < 0) { 91 | return FrameworkConstant.OUT; 92 | } 93 | int index = getPositionIndex(way, round); 94 | return getField(way, WAY_POSITIONS_START_INDEX + index); 95 | } 96 | 97 | private static int getLastPositionIndex(byte[] way) { 98 | return getPositionIndex(way, getLength(way) - 1); 99 | } 100 | 101 | private static int getPositionIndex(byte[] way, int round) { 102 | if (round >= Parameter.WAY_SAVE_LENGTH) { 103 | return Parameter.WAY_SAVE_LENGTH - 1; 104 | } 105 | return round; 106 | } 107 | 108 | public static int getLastPosition(byte[] way) { 109 | return getPosition(way, getLastPositionIndex(way)); 110 | } 111 | 112 | private static void incrementLength(byte[] way) { 113 | way[WAY_LENGTH_INDEX]++; 114 | } 115 | 116 | private static void resetLength(byte[] way) { 117 | setField(way, WAY_LENGTH_INDEX, 0); 118 | } 119 | 120 | public static void setDonePosition(byte[] way, int position) { 121 | way[MAP_INDEX + position / BIT_NB] |= 1 << (position % BIT_NB); 122 | } 123 | 124 | public static boolean isDonePosition(byte[] way, int position) { 125 | return (way[MAP_INDEX + position / BIT_NB] & (1 << (position % BIT_NB))) != 0; 126 | } 127 | 128 | public static void setScore(byte[] way, double dblValue) { 129 | long l = Double.doubleToLongBits(dblValue); 130 | for (int i = 7; i >= 0; i--) { 131 | setField(way, SCORE_INDEX + i, (byte) l & 0xFF); 132 | l >>= 8; 133 | } 134 | double tmp = getScore(way); 135 | AssertUtils.test(dblValue == tmp, tmp, dblValue); 136 | } 137 | 138 | public static double getScore(byte[] way) { 139 | long result = 0; 140 | for (int i = 0; i < 8; i++) { 141 | result <<= 8; 142 | result |= (getField(way, SCORE_INDEX + i) & 0xFF); 143 | } 144 | return Double.longBitsToDouble(result); 145 | } 146 | 147 | private static int getField(byte[] way, int index) { 148 | return way[index]; 149 | } 150 | 151 | private static void setField(byte[] way, int index, int value) { 152 | way[index] = (byte) value; 153 | } 154 | 155 | public static double calculateScore(byte[] way) { 156 | // heuristic...(usually in another class) 157 | return 0; 158 | } 159 | 160 | public static boolean contains(byte[] way, int p) { 161 | for (int i = 0; i < getLength(way); i++) { 162 | if (p == getPosition(way, i)) { 163 | return true; 164 | } 165 | } 166 | return false; 167 | } 168 | 169 | public static String toString(byte[] way) { 170 | if (way == null) { 171 | return null; 172 | } 173 | StringBuilder sb = new StringBuilder(); 174 | sb.append('['); 175 | sb.append(getLength(way)); 176 | sb.append(',').append(' '); 177 | for (int i = 0; i <= getLastPositionIndex(way); i++) { 178 | if (i > 0) { 179 | sb.append(',').append(' '); 180 | } 181 | sb.append(PointUtils.toPoint(getPosition(way, i))); 182 | } 183 | sb.append(']'); 184 | sb.append(", score = "); 185 | sb.append(getScore(way)); 186 | return sb.toString(); 187 | } 188 | 189 | public static String toMapString(byte[] way) { 190 | if (way == null) { 191 | return null; 192 | } 193 | byte[] map = MapUtils.createNewMap(); 194 | for (int p = 0; p < map.length; p++) { 195 | map[p] = (byte) (isDonePosition(way, p) ? FrameworkConstant.WALL : FrameworkConstant.FREE); 196 | } 197 | return MapUtils.toString(map); 198 | } 199 | } 200 | --------------------------------------------------------------------------------