public class SnailMatrix {
private final int[][] data;
private SnailMatrix(int n) {
this.data = new int[n][n];
}
public static SnailMatrix ofSize(int n) {
var matrix = new SnailMatrix(n);
var initializer = new MatrixInitializer(matrix);
initialize(initializer);
return matrix;
}
private static void initialize(MatrixInitializer initializer) {
int value = 1;
while (initializer.hasNext()) {
initializer.set(value++);
initializer.next();
}
}
public int size() {
return data.length;
}
public int get(final int x, final int y) {
return data[y][x];
}
private void set(final int x, final int y, final int value) {
data[y][x] = value;
}
// ...
private static class MatrixInitializer {
private static final int[][] DIRECTIONS =
new int[][]{
{1, 0}, {0, 1}, {-1, 0}, {0, -1}
};
private final SnailMatrix matrix;
private int direction = 0;
publicprivate int x;
publicprivate int y;
public MatrixInitializer(SnailMatrix matrix) {
this.matrix = matrix;
}
public void set(int value) {
matrix.set(x, y, value);
}
public boolean hasNext() {
return isValid(x, y)
&& !isInitialized();
}
private boolean isValid(int x, int y) {
return x >= 0 && x < matrix.size()
&& y >= 0 && y < matrix.size();
}
private boolean isInitialized() {
return isInitialized(x, y);
}
private boolean isInitialized(int x, int y) {
return matrix.get(x, y) != 0;
}
private void next() {
checkDirection();
x += xDelta();
y += yDelta();
}
private void checkDirection() {
int nextX = x + xDelta();
int nextY = y + yDelta();
if (!isValid(nextX, nextY) || isInitialized(nextX, nextY)) {
changeDirection();
}
}
public int xDelta() {
return DIRECTIONS[direction][0];
}
public int yDelta() {
return DIRECTIONS[direction][1];
}
private void changeDirection() {
direction = (direction + 1) % DIRECTIONS.length;
}
}
}