├── 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 |
--------------------------------------------------------------------------------