(This post is the continuation of Snail matrix in Java. Also, this post has an continuation.)
As previously, a snail matrix is any square matrix \$M_n = \mathbb{N}^{n \times n}\$ with cell elements from the set \$\{ 1, 2, \ldots, n^2 \}\$ such that they resemble the snail pattern. For example, for \$n = 4\$, \$M_n\$ looks like that:
1 2 3 4
12 13 14 5
11 16 15 6
10 9 8 7
This time, I have incorporated an awesome answer by Alexander Ivanchenko, and I ended up with this:
package com.github.coderodde.fun.snailmatrix;
/**
* This class provides a static method {@link SnailMatrix#ofSize(int) } for
* generating snail matrices. A snail matrix is any square matrix with all the
* cell values {@code 1, 2, ..., n x n} in a snail pattern. For example, for
* {@code n = 3}, the matrix will look like this:
* <code>
* 1 2 3
* 8 9 4
* 7 6 5
* </code>
*/
public class SnailMatrix {
/**
* The actual matrix data.
*/
private final int[][] data;
/**
* Constructs this snail matrix.
*
* @param n the width and height of this matrix.
*/
private SnailMatrix(final int n) {
this.data = new int[n][n];
}
/**
* Creates a snail matrix of dimensions {@code n x n}.
*
* @param n the width and height of the resulting matrix.
*
* @return the snail matrix.
*/
public static SnailMatrix ofSize(final int n) {
var matrix = new SnailMatrix(n);
var initializer = new MatrixInitializer(matrix);
initialize(initializer);
return matrix;
}
/**
* Initializes the matrix.
*
* @param initializer the intializer object.
*/
private static void initialize(final MatrixInitializer initializer) {
int value = 1;
while (initializer.hasNext()) {
initializer.set(value++);
initializer.next();
}
}
/**
* Returns the width/height of this matrix.
*/
public int size() {
return data.length;
}
/**
* Returns the value from column {@code x} and row {@code y}.
*
* @param x the {@code x}-coordinate of the target cell.
* @param y the {@code y}-coordinate of the target cell.
*
* @return the value of the target cell.
*/
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;
}
/**
* This class implements the snail matrix initializer.
*/
private static final 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;
private int x;
private int y;
private MatrixInitializer(final SnailMatrix matrix) {
this.matrix = matrix;
}
private void set(int value) {
matrix.set(x, y, value);
}
private boolean hasNext() {
return isValid(x, y) && !isInitialized();
}
private boolean isValid(final int x, final int y) {
return 0 <= x && x < matrix.size()
&& 0 <= y && y < matrix.size();
}
private boolean isInitialized() {
return isInitialized(x, y);
}
private boolean isInitialized(final int x, final int y) {
return matrix.get(x, y) != 0;
}
private void next() {
checkDirection();
x += xDelta();
y += yDelta();
}
private void checkDirection() {
final int nextX = x + xDelta();
final int nextY = y + yDelta();
if (!isValid(nextX, nextY) || isInitialized(nextX, nextY)) {
changeDirection();
}
}
private int xDelta() {
return DIRECTIONS[direction][0];
}
private int yDelta() {
return DIRECTIONS[direction][1];
}
private void changeDirection() {
direction = (direction + 1) % DIRECTIONS.length;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
final int capacity = size() * size();
final int cellLength = Integer.toString(capacity).length();
final String cellFormat = String.format("%%%dd ", cellLength);
for (int y = 0; y < size(); y++) {
for (int x = 0; x < size(); x++) {
sb.append(String.format(cellFormat, get(x, y)));
}
sb.append("\n");
}
sb.deleteCharAt(sb.length() - 1); // Delete last newline character.
return sb.toString();
}
public static void main(String[] args) {
final int n = 4;
System.out.println(SnailMatrix.ofSize(n));
}
}
Critique request
As always, is there any improvement to be made?