Итак, я пытаюсь выяснить, сколько смежных плиток есть у каждого игрока, и подсчитать общий счет. Подсчет очков осуществляется путем подсчета количества смежных плиток (соседних означает, что они имеют общий край, а не по диагонали) на территории, умножения этого числа на себя и сложения значений всех территорий вместе.
Пока у меня есть приведенный ниже код, который разбивает все пробелы на игрока и может печатать, занято ли пространство игроком, но я застрял в вычислении, сколько плиток смежно друг с другом.
public void calculate(PowerToken[][] allTokens){
boolean[][] occupiedRed = new boolean[9][9];
boolean[][] occupiedWhite = new boolean[9][9];
boolean[][] countedRed = new boolean[9][9];
boolean[][] countedWhite = new boolean[9][9];
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
if (allTokens[i][j].isOnBoard()) {
if (allTokens[i][j].getPlayerColor() == Color.RED){
occupiedRed[i][j] = true;
} else {
occupiedWhite[i][j] = true;
}
}
}
}
// Test printing
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
System.out.print(occupiedWhite[i][j] + " ");
}
System.out.println();
}
System.out.println();
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
System.out.print(occupiedRed[i][j] + " ");
}
System.out.println();
}
}
Класс токенов силы
public class PowerToken {
private int positionX;
private int positionY;
private boolean onBoard;
private Color playerColor;
public PowerToken(int positionX, int positionY) {
setPositionX(positionX);
setPositionY(positionY);
}
public PowerToken(int positionX, int positionY, boolean onBoard, Color playerColor) {
setPositionX(positionX);
setPositionY(positionY);
setOnBoard(onBoard);
setPlayerColor(playerColor);
}
public int getPositionX() {
return positionX;
}
public void setPositionX(int positionX) {
this.positionX = positionX;
}
public int getPositionY() {
return positionY;
}
public void setPositionY(int positionY) {
this.positionY = positionY;
}
public boolean isOnBoard() {
return onBoard;
}
public void setOnBoard(boolean onBoard) {
this.onBoard = onBoard;
}
public Color getPlayerColor() {
return playerColor;
}
public void setPlayerColor(Color playerColor) {
this.playerColor = playerColor;
}
@Override
public String toString() {
return "PowerToken{" +
"positionX = " + positionX +
", positionY = " + positionY +
", onBoard = " + onBoard +
", playerColor = " + playerColor +
'}';
}
}
Так, например, на этом изображении есть 2 набора из 3 смежных плиток в каждой и 1 набор из 4 смежных плиток.
Обновление 2 Это код, который у меня есть сейчас, однако в результате я получаю пустую карту
public void calculate(PowerToken[][] allTokens){
boolean[][] occupiedRed = new boolean[9][9];
boolean[][] occupiedWhite = new boolean[9][9];
boolean[][] countedRed = new boolean[9][9];
boolean[][] countedWhite = new boolean[9][9];
int scoreRed;
int scoreWhite;
for (int i = 0; i < 9; i++){
for (int j = 0; j < 9; j++){
if (allTokens[i][j].isOnBoard()) {
if (allTokens[i][j].getPlayerColor() == Color.RED){
occupiedRed[i][j] = true;
} else {
occupiedWhite[i][j] = true;
}
}
}
}
List<List<Point>> clusters = new ArrayList<>();
int n = occupiedRed.length;
int[][] label = new int[n][n];
int last = 1;
List<int[]> touching = new ArrayList<>();
//first pass
for(int i = 1; i<n; i++){
for(int j = 1; j<n; j++){
if (occupiedRed[i][j]){
//4 way connectivity.
int north = 0;
int west = 0;
int next = 0;
if ( i-1 > 0 && occupiedRed[i-1][j]){
west = label[i-1][j];
}
if ( j-1 < 0 && occupiedRed[i][j-1]){
north = label[i][j-1];
}
if (west != 0){
if (north != 0){
if (north == west){
next = north;
} else if (north < west){
next = north;
touching.add( new int[]{north, west} );
} else{
next = west;
touching.add( new int[]{west, north} );
}
} else{
next = west;
}
} else if ( north != 0 ){
next = north;
} else{
next = last++;
}
} else{
continue;
}
}
}
Map<Integer, Integer> map = touching.stream()
.collect(Collectors.toMap(
arr -> arr[0], // key mapping function
arr -> arr[1] // value mapping function
));
System.out.println(map);
}
Да, я изменю свой пост, чтобы добавить его. я добавил это
Да, я также добавлю схему к вопросу
Да, квадрат 2x2 - это 4 смежных квадрата/плитки.
Как мне это сделать? Я не встречал ни того, ни другого раньше
Вот теория, стоящая за этим. Я могу написать это, хотя это займет немного времени. en.wikipedia.org/wiki/…
Я еще не работал с алгоритмами раньше, поэтому я совершенно не уверен, с чего начать, даже с псевдокодом.
Я обновил свой ответ, он завершен, и я считаю, что он работает.
По сути, вы собираетесь использовать алгоритм связанных компонентов. На входе у вас будет таблица истинности 9x9 для одного из цветов.
static Map<Integer, List<int[]>> cluster( boolean[][] occupied )
Это двухпроходный алгоритм компонента соединения. Первый проход проходим и делаем предварительные метки, так же создаем «касательный» список, который отслеживает соседние друг с другом регионы.
int n = occupied.length;
int[][] label = new int[n][n];
int last = 1;
List<int[]> touching = new ArrayList<>();
//first pass
for(int i = 0; i<n; i++){
for(int j = 0; j<n; j++){
if (occupied[i][j]){
//4 way connectivity.
int north = 0;
int west = 0;
int next = 0;
if ( i > 0 && occupied[i-1][j]){
west = label[i-1][j];
}
if ( j > 0 && occupied[i][j-1]){
north = label[i][j-1];
}
if (west != 0){
if (north != 0){
//two labelled regions.
if (north == west){
//they're the same, it's ok.
next = north;
} else if (north < west){
//label it with the minimum one
next = north;
touching.add( new int[]{west, north} );
} else{
next = west;
touching.add( new int[]{north, west} );
}
} else{
next = west;
}
} else if ( north != 0 ){
next = north;
} else{
next = last++;
}
label[i][j] = next; //assign the label.
} else{
//not occupied.
continue;
}
}
}
Это первый проход, в конце первого прохода области помечаются, но есть соприкасающиеся области, которые нужно объединить. Это можно сделать со списком touching, но его нужно немного доработать.
На этом первом шаге все соприкасающиеся метки объединяются в связанные кластеры.
List<TreeSet<Integer>> clusters = new ArrayList<>();
for(int[] pair: touching){
TreeSet<Integer> pair0 = null;
TreeSet<Integer> pair1 = null;
for(TreeSet<Integer> cluster: clusters){
if (cluster.contains(pair[0])){
pair0 = cluster;
}
if (cluster.contains(pair[1])){
pair1 = cluster;
}
}
if ( pair0 == null && pair1 == null){
//new cluster
TreeSet<Integer> newCluster = new TreeSet<>();
newCluster.add(pair[0]);
newCluster.add(pair[1]);
clusters.add(newCluster);
} else if ( pair0 == null ){
pair1.add(pair[0]);
} else if ( pair1 == null ){
pair0.add(pair[1]);
} else{
if (pair0 == pair1) continue;
//both labels are already in clusters but different clusters.
pair0.addAll(pair1);
clusters.remove(pair1);
}
}
Это проходит и создает сопоставление, так что все значения в кластере будут сопоставлены с наименьшим значением.
Map<Integer, Integer> fin = new HashMap<>();
for( TreeSet<Integer> cluster: clusters){
Integer low = cluster.first();
for(Integer i: cluster){
fin.put(i, low);
}
}
Второй проход — это просто вопрос связывания меток с их ключами.
Map<Integer, List<int[]>> result = new HashMap<>();
//second pass
for(int i = 0; i<n; i++){
for(int j = 0; j<n; j++){
if ( occupied[i][j] ){
Integer f = fin.computeIfAbsent( label[i][j], k -> k );
result.computeIfAbsent( f, k -> new ArrayList<>() ).add( new int[]{i, j} );
label[i][j] = f;
}
}
}
Результат будет содержать метку и набор int[], которые представляют все точки. Чтобы использовать это.
Map<Integer, List<int[]>> whiteClusters = cluster(occupiedWhite);
Map<Integer, List<int[]>> redClusters = cluster(occupiedRed);
Для полноты вот компилируемая рабочая версия.
import java.util.*;
public class ConnComp{
static void printArray(int[][] arr){
for(int[] row: arr){
for(int i: row){
System.out.printf("%3d",i);
}
System.out.println();
}
}
static void printArray(boolean[][] arr){
for(boolean[] row: arr){
for(boolean i: row){
System.out.print(i ? " t" : " f");
}
System.out.println();
}
}
static Map<Integer, List<int[]>> cluster( boolean[][] occupied ){
int n = occupied.length;
int[][] label = new int[n][n];
int last = 1;
List<int[]> touching = new ArrayList<>();
//first pass
for(int i = 0; i<n; i++){
for(int j = 0; j<n; j++){
if (occupied[i][j]){
//4 way connectivity.
int north = 0;
int west = 0;
int next = 0;
if ( i > 0 && occupied[i-1][j]){
west = label[i-1][j];
}
if ( j > 0 && occupied[i][j-1]){
north = label[i][j-1];
}
if (west != 0){
if (north != 0){
//two labelled regions.
if (north == west){
//they're the same, it's ok.
next = north;
} else if (north < west){
//label it with the minimum one
next = north;
touching.add( new int[]{west, north} );
} else{
next = west;
touching.add( new int[]{north, west} );
}
} else{
next = west;
}
} else if ( north != 0 ){
next = north;
} else{
next = last++;
}
label[i][j] = next; //assign the label.
} else{
//not occupied.
continue;
}
}
}
//map reduce
Comparator<int[]> c = Comparator.comparingInt( arr -> arr[0] );
Comparator<int[]> c2 = Comparator.comparingInt( arr -> arr[1] );
c = c.thenComparing(c2);
touching.sort( c );
List<TreeSet<Integer>> clusters = new ArrayList<>();
for(int[] pair: touching){
TreeSet<Integer> pair0 = null;
TreeSet<Integer> pair1 = null;
for(TreeSet<Integer> cluster: clusters){
if (cluster.contains(pair[0])){
pair0 = cluster;
}
if (cluster.contains(pair[1])){
pair1 = cluster;
}
}
if ( pair0 == null && pair1 == null){
//new cluster
TreeSet<Integer> newCluster = new TreeSet<>();
newCluster.add(pair[0]);
newCluster.add(pair[1]);
clusters.add(newCluster);
} else if ( pair0 == null ){
pair1.add(pair[0]);
} else if ( pair1 == null ){
pair0.add(pair[1]);
} else{
if (pair0 == pair1) continue;
//both labels are already in clusters but different clusters.
pair0.addAll(pair1);
clusters.remove(pair1);
}
}
Map<Integer, Integer> fin = new HashMap<>();
for( TreeSet<Integer> cluster: clusters){
Integer low = cluster.first();
for(Integer i: cluster){
fin.put(i, low);
}
}
System.out.println("occupied");
printArray(occupied);
System.out.println("labelled");
printArray(label);
Map<Integer, List<int[]>> result = new HashMap<>();
//second pass
for(int i = 0; i<n; i++){
for(int j = 0; j<n; j++){
if ( occupied[i][j] ){
Integer f = fin.computeIfAbsent( label[i][j], k -> k );
result.computeIfAbsent( f, k -> new ArrayList<>() ).add( new int[]{i, j} );
label[i][j] = f;
}
}
}
System.out.println("after labelling");
printArray(label);
return result;
}
static Random r = new Random(42);
static boolean[][] getBoard(){
int n = 9;
boolean[][] board = new boolean[n][n];
for(int i = 0; i<50; i++){
int x = r.nextInt(n);
int y = r.nextInt(n);
board[x][y] = true;
}
return board;
}
public static void main(String[] args){
cluster(getBoard());
cluster(getBoard());
}
}
Я не уверен, что это правильный способ закончить это? for(int i = 1; i<n; i++) { for (int j = 1; j < n; j++) { if (occupiedRed[i][j]) { label[i][j] = touching.get(i)[j]; } } } Похоже, что нет, так как я не получаю хороших результатов, когда распечатываю этикетку.
Как бы вы порекомендовали мне правильно преобразовать его?
Map<Integer, Integer> map = touching.stream() .collect(Collectors.toMap( arr -> arr[0], // key mapping function arr -> arr[1] // value mapping function ));
Кажется, это не дает мне хороших результатов, это дает мне пустую карту
Я обновил исходный пост кодом, который у меня есть прямо сейчас, который, похоже, ничего не генерирует.
Можете ли вы предоставить класс PowerToken?