验证战舰板,二维数组,如何检查板有效性的可能性?
Validating a Battleship board, 2d array, how to check possibilities for boards validity?
在给定一个包含 1 和 0 的二维数组时,在 Java 中编写代码以验证战舰板,其中 1 是船的一部分,0 是大海。
以下是需要了解的规则:
-必须有单艘战列舰(4 格尺寸)、2 艘巡洋舰(3 号尺寸)、3 艘驱逐舰(2 号尺寸)和 4 艘潜艇(1 号尺寸)。不允许任何额外的船只,以及丢失的船只。
-每艘船都必须是一条直线,除了潜艇,它们只是单格。
-船不能重叠,但可以与任何其他船接触。
我的方法只是计算 2 号 3 号 4 号船的所有可能连接,并确保它们大于所需尺寸船的数量,这并不适用于所有情况,我也是将有助于检查是否恰好有 20 个放置 1 的问题是,如果我们有 0 1 1 1 1 0 0 0 0 0 它会告诉我它是有效的,尽管它肯定不是(因为它是一排和一艘船)并且当我有以下内容时:
应该是 false 但我的 returns true->
{0,0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,1,1},
{0,0,0,0,0,0,1,1,1,0},
{0,1,0,0,0,0,0,0,0,0},
{0,1,0,0,1,1,1,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,1,1,0},
{0,0,1,0,0,0,0,1,1,0},
或
这应该是错误的,但我的 returns true->
{0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,1,1,1,0,1,1,0,0,0},
{0,1,0,0,0,0,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
这里是一个例子,代码在它应该return正确的时候工作:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,1,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
但是当我有这个例子时:
{0,0,1,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,1},
{0,0,1,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,1},
{0,0,0,0,0,0,0,0,0,1},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
应该是错误的,但我的代码 return 是正确的。那么我怎样才能找到一种方法来重组我的代码或找到一个解决方案,当我得到一个板时我真的不知道它是否有效。但是我的 Java 程序可以吗?
这是我为此尝试的代码,我的方法是制作一个列表,其中包含检查特定船只从最大到较小的所有变化(最大的是 [4 3 3 2 2 2] 不包括1's 因为它会显着增加变化量,我可以用不同的方式更有效地检查它,最小的变化是 [2 2 2 3 3 4] 4 最后,它们重复的原因是因为 2 有 x3 艘船所以 2 2 2,x2 尺寸 3 船所以 3 3 和 x1 尺寸 4 船所以只有一个)这是代码:
import java.util.*;
class Permute{
public static List<int[]> l;
Permute(){
this.l=new ArrayList<int[]>();
}
static void permute(java.util.List<Integer> arr, int k){
for(int i = k; i < arr.size(); i++){
java.util.Collections.swap(arr, i, k);
permute(arr, k+1);
java.util.Collections.swap(arr, k, i);
}
if (k == arr.size() -1){
l.add(arr.stream()
.map(i -> (i == null ? 0 : i))
.mapToInt(Integer::intValue)
.toArray());
}
}
public static List<int[]> get(){
Permute.permute(java.util.Arrays.asList(2,2,2,3,3,4), 0);Collections.reverse(l);
return l;
}
}
public class BF {
private static int[][] fields,copy;
private static int[] ship= {0,0,3,2,1};
public BF(int[][] field) {
fields=field;
// print(field);
this.copy=field;
}
public boolean validate() {
int cnt=counter(fields);
if(cnt!=20)return false;
Permute p= new Permute();//permutation constructor
List<int[]> list=p.get();//gets our specific permution
for (int i = 0; i < list.size(); i++) {//run through each option of our permution list
copy=fields;
ship=new int[]{0,0,3,2,1};//amount of ships needed
boolean flag=check(fields,list.get(i));//checks if the permution variation is true or false
int cnt1=counter(copy);//we checked boats of size 2 3 4 but not 1 which means if its valid then there should be 4 boats left
if(flag && cnt1==4)return true;
}
return false;
}
public static boolean check(int[][] field,int[] ships){
for(int i=0;i<ships.length;i++) {//go through the array and loop through the variation
int num=getBoat(fields, ships[i]);//if the boat is true on the certain boat we are checking
ship[num]--;//then -1 and at the end we want it to be [<=0,0,0,0,0]
}
int counter=0;
for(int i=2;i<ship.length;i++) {
if(ship[i]==0)counter++;//if [0,0,0]
}
if(counter==3) {return true;}//then return true
return false;
}
public static int getBoat(int[][] field,int num) {
int dire=0,r=0,c=0;String dir="row";
for (int col = 0; col < fields[0].length; col++) {
for (int row = 0; row < fields.length; row++) {//loop through each coordinate
if (copy[row][col] == 1) {//check if its part of a boat at the coor
int length=field.length;dir="row";dire=row;
for(int j=0;j<2;j++) {//go horizontal then vertical
if(j==1) {length=field[0].length;dir="col";dire=col;}//change length to vertical
if(dire+num-1<length) {//make sure we don't get outofbounds error
int cnt=0;
for(int i=0;i<num;i++) {//checks each coor according to type of boat we are checking
if(dir.equals("row")) {r=row+i;c=col;}
else if(dir.equals("col")){r=row;c=col+i;}
if(copy[r][c]==1) {//check
cnt++;//copy of battle field becomes 0
}
else copy=fields;//otherwise break this loop since it is not relevant anymore on this coor
if(cnt==num) {
for(int k=0;k<num;k++) {
if(dir.equals("row")) {r=row+k;c=col;}
else if(dir.equals("col")){r=row;c=col+k;}
copy[r][c]=0;//now we know its valid length and make it 0 on the copy
}
return cnt;
}}}} //if num is correct then return it
}
}
}return 0;
}
public static int counter(int[][] f) {// counts amount of tiles on the board with 1's
int cnt=0;
for (int col = 0; col < f[0].length; col++) {
for (int row = 0; row < f.length; row++) {
if (f[row][col] == 1) {
cnt++;
}
}
}
return cnt;
}
public static void print(int[][] field) {//prints the board
for (int row = 0; row < field.length; row++) {
for (int col = 0; col < field[0].length; col++) {
if(col<field[0].length-1 && col!=0) {
System.out.print( field[row][col]+",");
}
else if(col==field[0].length-1){
System.out.print( field[row][col]+"},");
}
else if(col==0) {
System.out.print(" {"+ field[row][col]+",");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
代码现在似乎 运行 很好,但当它假设时 return 不正确。
这里有一些代码应该 return 正确但 return 错误的示例:
{1,0,0,0,0,1,1,0,0,0},
{1,0,1,0,0,0,0,0,1,0},
{1,0,1,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,0,0,0,1,0},
{0,0,1,0,0,1,1,1,1,0},
{0,0,1,1,0,0,0,0,0,0},
{0,0,1,1,1,1,1,0,0,0},
{0,0,1,1,1,0,0,0,0,0},
{0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,1,1,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,1,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
但这些示例中的代码 return 是错误的
所以在这个例子中
{1,1,1,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
有效,因为:
[![在此处输入图片描述][1]][1]
但是我的代码采用了大小的第一个选项,这就是我的代码 returns 为 false-
时发生的情况
[![在此处输入图片描述][2]][2]
那么我需要添加什么来解决这个问题?
public class BF {
private static int[][] fields;
public BF(int[][] field) {
fields=field;
print(field);
}
public boolean validate() {
int cnt=counter(fields);
if(cnt!=20)return false;
return checkBoard(fields, new int[]{4,3,3,2,2,2},0,new int[] {3,2,1});
}
public static boolean checkBoard(int[][] board,int[] SizePlacement,int k,int[] shipAmount){
if (counter(board)==4 ) {
return true;
}
int cnt=0;//SizePlacement={4,3,3,2,2,2}, ship(changes)={3,2,1}, k(starts at 0) is placement in ships[]
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[0].length; col++) {
if(board[row][col]==1 && row+SizePlacement[k]<board.length) {//vertically
cnt=1;
for(int i=1;i<SizePlacement[k];i++) {//check vertically
if(board[row+i][col]==1) {cnt++;}
}
if(cnt>=SizePlacement[k]) {
int[][] newBoard = deepcopy(board);
newBoard= remove(newBoard , row, col, SizePlacement[k], "ver");
shipAmount[SizePlacement[k]-2]--;
if(shipAmount==new int[]{0,0,0}) {return true;}
if(k+1<SizePlacement.length && checkBoard(newBoard,SizePlacement,k+1,shipAmount)) {return true;}
shipAmount[SizePlacement[k]-2]++;
}
}
if(board[row][col]==1 && col+SizePlacement[k]<board[0].length) {//horizontally
cnt=1;
for(int i=1;i<SizePlacement[k];i++) {//check horizontally
if(board[row][col+i]==1) {cnt++;}
}
if(cnt>=SizePlacement[k]) {
int[][] newBoard = deepcopy(board);
newBoard= remove(newBoard , row, col, SizePlacement[k], "hor");
shipAmount[SizePlacement[k]-2]--;
if(shipAmount==new int[]{0,0,0}) {return true;}
if(k+1<SizePlacement.length &&checkBoard(newBoard,SizePlacement,k+1,shipAmount)) {
return true;
}
shipAmount[SizePlacement[k]-2]++;
}
}
}
}
return false;
}
public static int[][] remove(int[][] newBoard,int row,int col,int size,String orien){
int[][] removedBoard= deepcopy(newBoard);
if(orien=="ver") {
for(int i=0;i<size;i++) {
removedBoard[row+i][col]=0;
}
print(removedBoard);
return removedBoard;
}
else if(orien=="hor") {
for(int i=0;i<size;i++) {
removedBoard[row][col+i]=0;
}
print(removedBoard);
return removedBoard;
}
return removedBoard;
}
public static int[][] deepcopy(int[][] fields){
int[][] copy= new int[fields.length][fields[0].length];
for (int row = 0; row < fields.length; row++) {
for (int col = 0; col < fields[0].length; col++) {
copy[row][col]= fields[row][col];
}
}
return copy;
}
public static int counter(int[][] f) {// counts amount of tiles on the board with 1's
int cnt=0;
for (int col = 0; col < f[0].length; col++) {
for (int row = 0; row < f.length; row++) {
if (f[row][col] == 1) {
cnt++;
}
}
}
return cnt;
}
public static void print(int[][] field) {//prints the board
for (int row = 0; row < field.length; row++) {
for (int col = 0; col < field[0].length; col++) {
if(col<field[0].length-1 && col!=0) {
System.out.print( field[row][col]+",");
}
else if(col==field[0].length-1){
System.out.print( field[row][col]+"},");
}
else if(col==0) {
System.out.print(" {"+ field[row][col]+",");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
这似乎有一些错误(与解决方案不同),不确定对此的帮助将不胜感激
[1]: https://i.stack.imgur.com/bX32n.png
[2]: https://i.stack.imgur.com/tFP1N.png
如果所有船只的位置都有效,则该板有效。
这意味着您需要一个船舶数据结构。首先,一个简单的数据结构可能是 [ ship_type, [ position_1 , position_2, position_3 ] ]
一旦你有了船的数据结构,检查所有的船都在板上,没有两艘船共享一个位置,以及板上每种类型的船的正确数量是微不足道的.每种类型的正确数量的船只可以进入 Fleet
数据结构。
对于一艘船,您会很快意识到,可以用字母来渲染了解其舰队的一方的棋盘。但另一块板是“特殊的”,因为没有船舶能见度。这意味着(在真实游戏中)Battleship 由两种板组成,即命中检测板和船舶定位板。它们看起来相似只是制造的副作用。
现在 HitBoard
AI 将如何尝试追捕一艘船?好吧,他们会从随机放置或模式开始。两种方法各有优势。一旦检测到命中,它将参考场上剩余船只的数量,并开始沿水平和垂直轴探测以找到船只的完整位置。一旦它被告知“You sunk my”,它就会知道沉船必须从游戏中移除。
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
由两艘战列舰、一艘驱逐舰、一艘潜艇和一艘巡洋舰组成。如果没有额外的信息,就不可能知道大多数船只是从左到右还是从上到下。
有时甚至无法在进行游戏时知道船的位置。有趣的是,这些信息可能并不总是足以了解船只的位置。这有时会发生在船只处于紧密排列的编队中,并且您通过在已经记录命中的一排或一列(或两列)方格内进行攻击来击沉船只。在这种情况下,您的点击可能不会定义沉没项目的结束。
如果目标是在没有位置以外的任何额外信息的情况下验证棋盘,那么您需要采用不同的方法。
- 按大小排序船只。
- 在当前的棋盘位置,重复直到没有可用的船。
- Select最大的船。
- 搜索最大船只的所有可能位置,生成没有船只位置标记的新板。
- 如果无法放置飞船,则板不是有效的板配置。
- 如果船舶列表为空且板上有标记,则该板不是有效的板配置。
- 如果船舶列表为空且棋盘为空,则标记处于有效的棋盘配置中。
- 重复,针对步骤 2 中生成的板独立处理所有板。
这是一种蛮力方法;但是,在蛮力方法中有许多可能的优化。
我认为解决这个问题的最佳方法是从最大的开始迭代放置船只。算法的大致轮廓是这样的:
- Select未放置的最大船
- 考虑所有可能展示位置的列表
- 对于每个可能的放置,从数组中删除那些 1,然后重复该过程,不包括您刚刚放置的飞船
由于您没有对某些点的船舶类型进行编码,您可以从已放置的船舶上移除点,然后继续尝试放置船舶。这将随着船只数量的增加而爆炸,但我认为它对于这个小解决方案来说已经足够快了。
尝试不允许联系的解决方案:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class BattleFieldValidator {
private static final int SIZE = 10;
private static class Ship {
public int col;
public int row;
public boolean horizontal;
public int size;
@Override
public String toString() {
return "size " + size + " at (" + (row + 1) + "," + (col + 1) + ") " + (horizontal ? "horizontal" : "vertical");
}
}
public static void validateField(int[][] field) {
// 10x10 field
if(field.length != SIZE || field[0].length != SIZE) {
throw new RuntimeException("invalid field dimensions");
}
// each cell 0 or 1
for(int row = 0; row < SIZE; row++) {
for(int col = 0; col < SIZE; col++) {
int cellValue = field[row][col];
if(cellValue != 0 && cellValue != 1) {
throw new RuntimeException("invalid cell value at (" + (row + 1) + "," + (col + 1) + ")");
}
}
}
print(field);
boolean[][] discoveredShipCells = new boolean[SIZE][SIZE];
List<Ship> ships = new ArrayList<>();
for(int row = 0; row < SIZE; row++) {
for(int col = 0; col < SIZE; col++) {
if(discoveredShipCells[row][col]) {
continue;
}
if(field[row][col] == 1) {
Ship ship = discoverShip(field, discoveredShipCells, row, col);
ships.add(ship);
}
}
}
Collections.sort(ships, (a, b) -> b.size - a.size);
System.out.println("discovered ships:");
ships.forEach(s -> System.out.println(s));
if(ships.stream().filter(s -> s.size == 4).count() != 1) {
throw new RuntimeException("battleship(size 4) count != 1");
}
if(ships.stream().filter(s -> s.size == 3).count() != 2) {
throw new RuntimeException("cruiser(size 3) count != 2");
}
if(ships.stream().filter(s -> s.size == 2).count() != 3) {
throw new RuntimeException("destroyer(size 2) count != 3");
}
if(ships.stream().filter(s -> s.size == 1).count() != 4) {
throw new RuntimeException("submarine (size 1) count != 4");
}
// ok
}
private static Ship discoverShip(int[][] field, boolean[][] discoveredShipCells, int shipStartRow, int shipStartCol) {
Ship ship = new Ship();
ship.row = shipStartRow;
ship.col = shipStartCol;
int horizontalSize = 0;
for(int col = ship.col; col < SIZE && field[ship.row][col] == 1; col++) {
horizontalSize++;
discoveredShipCells[ship.row][col] = true;
}
int verticalSize = 0;
for(int row = ship.row; row < SIZE && field[row][ship.col] == 1; row++) {
verticalSize++;
discoveredShipCells[row][ship.col] = true;
}
if(horizontalSize > 1 && verticalSize > 1) {
throw new RuntimeException("ship extending both horizontally and vertically at (" + (ship.row + 1) + "," + (ship.col + 1) + ")");
}
ship.horizontal = verticalSize == 1;
ship.size = ship.horizontal ? horizontalSize : verticalSize;
// validate ship surrounded by ocean cells
if(ship.horizontal) {
// left & right
validateOceanCell(ship, field, ship.row, ship.col - 1);
validateOceanCell(ship, field, ship.row, ship.col + ship.size);
// top & bottom
for(int col = ship.col - 1; col <= ship.col + ship.size; col++) {
validateOceanCell(ship, field, ship.row - 1, col);
validateOceanCell(ship, field, ship.row - 1, col);
}
}
else {
// top & bottom
validateOceanCell(ship, field, ship.row - 1, ship.col);
validateOceanCell(ship, field, ship.row + ship.size, ship.col);
// left & right
for(int row = ship.row - 1; row <= ship.row + ship.size; row++) {
validateOceanCell(ship, field, row, ship.col - 1);
validateOceanCell(ship, field, row, ship.col + 1);
}
}
return ship;
}
private static void validateOceanCell(Ship ship, int[][] field, int row, int col) {
if(row < 0 || row >= SIZE || col < 0 || col >= SIZE) {
return /* outside field, ignore */;
}
if(field[row][col] == 1) {
throw new RuntimeException("ship " + ship + " not surrounded by whater at (" + (row + 1) + "," + (col + 1) + ")");
}
}
private static void print(int[][] field) {
System.out.println();
for(int row = 0; row < SIZE; row++) {
System.out.println(Arrays.stream(field[row]).boxed().collect(Collectors.toList()));
}
}
}
和最少的测试:
public class BattleFieldTest {
@org.junit.Test
public void test1() {
int[][] battleField = { //
{ 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, //
{ 1, 0, 1, 0, 0, 0, 0, 0, 1, 0 }, //
{ 1, 0, 1, 0, 1, 1, 1, 0, 1, 0 }, //
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
BattleFieldValidator.validateField(battleField);
}
@org.junit.Test
public void test2() {
int[][] battleField = { //
{ 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 1, 0, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, //
{ 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, //
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, };
BattleFieldValidator.validateField(battleField);
}
}
示例输出:
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0]
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
discovered ships:
size 4 at (1,1) vertical
size 3 at (3,5) horizontal
size 3 at (6,5) horizontal
size 2 at (1,6) horizontal
size 2 at (2,3) vertical
size 2 at (2,9) vertical
size 1 at (5,9) horizontal
size 1 at (7,9) horizontal
size 1 at (8,4) horizontal
size 1 at (9,8) horizontal
我认为@AlexRudenko 意味着这会更容易验证:
{0,0,0,0,0,0,0,2,2,0},
{0,0,0,0,0,0,0,3,3,3},
{0,0,0,0,0,4,4,4,4,0},
{0,2,0,0,0,0,0,0,1,0},
{0,2,0,0,3,3,3,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,2,0,0,0,0,2,1,0},
{0,0,2,0,0,0,0,2,1,0},
由于我们无法确定地区分飞船,因此我们需要尝试不同的可能性。尝试将一艘船放在一个可能的位置(保留该位置),然后递归地尝试下一艘船。 (这可能也是其他人的建议)。对于有这么几艘船的 10x10 板,计算非常快。
这是我尝试实现它的尝试。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private static final int SIZE = 10;
private static final int HORIZONTAL = 0;
private static final int VERTICAL = 1;
private static final int[] shipSizes = {4, 3, 3, 2, 2, 2};
private static final int NR_OF_ONES = 4;
private static final int[][] board1 = {
{1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0},
{1, 1, 0, 0, 1, 1, 1, 0, 1, 0},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
private static AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) {
int[][] board = board1;
validateBoard(board);
long start = System.nanoTime();
boolean valid = checkBoard(board, 0);
long time = System.nanoTime() - start;
System.out.println("Board is " + (valid ? "" : "NOT ") + "valid");
System.out.println("Checked board in " + time + " ns");
}
private static boolean checkBoard(int[][] currentBoard, int shipSizePos) {
if (shipSizePos >= shipSizes.length) {
return true;
// return countOnes(currentBoard) == NR_OF_ONES;
}
List<Ship> currentSizeShips = findPossibleShips(currentBoard, shipSizes[shipSizePos]);
boolean valid = false;
for (Ship ship : currentSizeShips) {
if(checkBoard(removeShipFromBoard(currentBoard, ship), shipSizePos + 1)) {
return true;
}
}
return valid;
}
private static int[][] removeShipFromBoard(int[][] board, Ship ship) {
int[][] newBoard = new int[SIZE][SIZE];
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if ((ship.orientation == HORIZONTAL && r == ship.row && (c >= ship.col && c < ship.col + ship.size))
|| (ship.orientation == VERTICAL && c == ship.col && (r >= ship.row && r < ship.row + ship.size))) {
newBoard[r][c] = 0;
} else {
newBoard[r][c] = board[r][c];
}
}
}
return newBoard;
}
private static List<Ship> findPossibleShips(int[][] board, int size) {
List<Ship> ships = new ArrayList<>();
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if (board[r][c] == 1) {
if (isPossibleHorizontalShip(board, size, r, c)) {
ships.add(new Ship(r, c, size, HORIZONTAL));
}
if (isPossibleVerticalShip(board, size, r, c)) {
ships.add(new Ship(r, c, size, VERTICAL));
}
}
}
}
return ships;
}
private static boolean isPossibleHorizontalShip(int[][] board, int size, int row, int col) {
if (SIZE - col < size) {
return false;
}
int c;
for (c = col; c < SIZE && board[row][c] == 1; ++c) ;
return c - col >= size;
}
private static boolean isPossibleVerticalShip(int[][] board, int size, int row, int col) {
if (SIZE - row < size) {
return false;
}
int r;
for (r = row; r < SIZE && board[r][col] == 1; ++r) ;
return r - row >= size;
}
private static int countOnes(int[][] board) {
int ones = 0;
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
ones += board[r][c];
}
}
return ones;
}
private static void validateBoard(int[][] board) {
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if (!(board[r][c] == 0 || board[r][c] == 1)) {
throw new IllegalArgumentException("Illegal character at " + r + ", " + c);
}
}
}
if (countOnes(board) != Arrays.stream(shipSizes).sum() + NR_OF_ONES) {
throw new IllegalArgumentException("Wrong number of ship pieces");
}
}
private static class Ship {
private final int row;
private final int col;
private final int size;
private final int orientation;
public Ship(int row, int col, int size, int orientation) {
this.row = row;
this.col = col;
this.size = size;
this.orientation = orientation;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ship ship = (Ship) o;
return row == ship.row && col == ship.col && size == ship.size && orientation == ship.orientation;
}
@Override
public int hashCode() {
return Objects.hash(row, col, size, orientation);
}
@Override
public String toString() {
return "Ship{" +
"row=" + row +
", col=" + col +
", size=" + size +
", orientation=" + orientation +
'}';
}
}
}
- 首先验证板以确保它只包含 0 和 1 以及正确数量的 1
- 从最大到最小浏览非 1 号船只的列表。
- 搜索当前尺码的可能展示位置,删除一个并检查下一个尺码的分支
- 当只剩下1s时,板就OK了(我们依靠验证的计数是正确的)
在给定一个包含 1 和 0 的二维数组时,在 Java 中编写代码以验证战舰板,其中 1 是船的一部分,0 是大海。 以下是需要了解的规则:
-必须有单艘战列舰(4 格尺寸)、2 艘巡洋舰(3 号尺寸)、3 艘驱逐舰(2 号尺寸)和 4 艘潜艇(1 号尺寸)。不允许任何额外的船只,以及丢失的船只。
-每艘船都必须是一条直线,除了潜艇,它们只是单格。
-船不能重叠,但可以与任何其他船接触。
我的方法只是计算 2 号 3 号 4 号船的所有可能连接,并确保它们大于所需尺寸船的数量,这并不适用于所有情况,我也是将有助于检查是否恰好有 20 个放置 1 的问题是,如果我们有 0 1 1 1 1 0 0 0 0 0 它会告诉我它是有效的,尽管它肯定不是(因为它是一排和一艘船)并且当我有以下内容时: 应该是 false 但我的 returns true->
{0,0,0,0,0,0,0,1,1,0},
{0,0,0,0,0,0,0,1,1,1},
{0,0,0,0,0,0,1,1,1,0},
{0,1,0,0,0,0,0,0,0,0},
{0,1,0,0,1,1,1,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,1,0,0,0,0,1,1,0},
{0,0,1,0,0,0,0,1,1,0},
或 这应该是错误的,但我的 returns true->
{0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,1,1,1,0,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0},
{0,1,1,1,0,1,1,0,0,0},
{0,1,0,0,0,0,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
这里是一个例子,代码在它应该return正确的时候工作:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,1,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
但是当我有这个例子时:
{0,0,1,0,0,0,0,1,1,0},
{0,1,1,0,0,0,0,0,0,0},
{1,1,1,1,1,0,0,0,0,1},
{0,0,1,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,1},
{0,0,0,0,0,0,0,0,0,1},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
应该是错误的,但我的代码 return 是正确的。那么我怎样才能找到一种方法来重组我的代码或找到一个解决方案,当我得到一个板时我真的不知道它是否有效。但是我的 Java 程序可以吗?
这是我为此尝试的代码,我的方法是制作一个列表,其中包含检查特定船只从最大到较小的所有变化(最大的是 [4 3 3 2 2 2] 不包括1's 因为它会显着增加变化量,我可以用不同的方式更有效地检查它,最小的变化是 [2 2 2 3 3 4] 4 最后,它们重复的原因是因为 2 有 x3 艘船所以 2 2 2,x2 尺寸 3 船所以 3 3 和 x1 尺寸 4 船所以只有一个)这是代码:
import java.util.*;
class Permute{
public static List<int[]> l;
Permute(){
this.l=new ArrayList<int[]>();
}
static void permute(java.util.List<Integer> arr, int k){
for(int i = k; i < arr.size(); i++){
java.util.Collections.swap(arr, i, k);
permute(arr, k+1);
java.util.Collections.swap(arr, k, i);
}
if (k == arr.size() -1){
l.add(arr.stream()
.map(i -> (i == null ? 0 : i))
.mapToInt(Integer::intValue)
.toArray());
}
}
public static List<int[]> get(){
Permute.permute(java.util.Arrays.asList(2,2,2,3,3,4), 0);Collections.reverse(l);
return l;
}
}
public class BF {
private static int[][] fields,copy;
private static int[] ship= {0,0,3,2,1};
public BF(int[][] field) {
fields=field;
// print(field);
this.copy=field;
}
public boolean validate() {
int cnt=counter(fields);
if(cnt!=20)return false;
Permute p= new Permute();//permutation constructor
List<int[]> list=p.get();//gets our specific permution
for (int i = 0; i < list.size(); i++) {//run through each option of our permution list
copy=fields;
ship=new int[]{0,0,3,2,1};//amount of ships needed
boolean flag=check(fields,list.get(i));//checks if the permution variation is true or false
int cnt1=counter(copy);//we checked boats of size 2 3 4 but not 1 which means if its valid then there should be 4 boats left
if(flag && cnt1==4)return true;
}
return false;
}
public static boolean check(int[][] field,int[] ships){
for(int i=0;i<ships.length;i++) {//go through the array and loop through the variation
int num=getBoat(fields, ships[i]);//if the boat is true on the certain boat we are checking
ship[num]--;//then -1 and at the end we want it to be [<=0,0,0,0,0]
}
int counter=0;
for(int i=2;i<ship.length;i++) {
if(ship[i]==0)counter++;//if [0,0,0]
}
if(counter==3) {return true;}//then return true
return false;
}
public static int getBoat(int[][] field,int num) {
int dire=0,r=0,c=0;String dir="row";
for (int col = 0; col < fields[0].length; col++) {
for (int row = 0; row < fields.length; row++) {//loop through each coordinate
if (copy[row][col] == 1) {//check if its part of a boat at the coor
int length=field.length;dir="row";dire=row;
for(int j=0;j<2;j++) {//go horizontal then vertical
if(j==1) {length=field[0].length;dir="col";dire=col;}//change length to vertical
if(dire+num-1<length) {//make sure we don't get outofbounds error
int cnt=0;
for(int i=0;i<num;i++) {//checks each coor according to type of boat we are checking
if(dir.equals("row")) {r=row+i;c=col;}
else if(dir.equals("col")){r=row;c=col+i;}
if(copy[r][c]==1) {//check
cnt++;//copy of battle field becomes 0
}
else copy=fields;//otherwise break this loop since it is not relevant anymore on this coor
if(cnt==num) {
for(int k=0;k<num;k++) {
if(dir.equals("row")) {r=row+k;c=col;}
else if(dir.equals("col")){r=row;c=col+k;}
copy[r][c]=0;//now we know its valid length and make it 0 on the copy
}
return cnt;
}}}} //if num is correct then return it
}
}
}return 0;
}
public static int counter(int[][] f) {// counts amount of tiles on the board with 1's
int cnt=0;
for (int col = 0; col < f[0].length; col++) {
for (int row = 0; row < f.length; row++) {
if (f[row][col] == 1) {
cnt++;
}
}
}
return cnt;
}
public static void print(int[][] field) {//prints the board
for (int row = 0; row < field.length; row++) {
for (int col = 0; col < field[0].length; col++) {
if(col<field[0].length-1 && col!=0) {
System.out.print( field[row][col]+",");
}
else if(col==field[0].length-1){
System.out.print( field[row][col]+"},");
}
else if(col==0) {
System.out.print(" {"+ field[row][col]+",");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
代码现在似乎 运行 很好,但当它假设时 return 不正确。
这里有一些代码应该 return 正确但 return 错误的示例:
{1,0,0,0,0,1,1,0,0,0},
{1,0,1,0,0,0,0,0,1,0},
{1,0,1,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,0,0,0,1,0},
{0,0,1,0,0,1,1,1,1,0},
{0,0,1,1,0,0,0,0,0,0},
{0,0,1,1,1,1,1,0,0,0},
{0,0,1,1,1,0,0,0,0,0},
{0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,1,1,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
此处为真:
{1,0,0,0,0,1,1,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,1,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
但这些示例中的代码 return 是错误的
所以在这个例子中
{1,1,1,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,1,0},
{1,1,0,0,1,1,1,0,1,0},
{1,0,0,0,0,0,0,0,0,0},
{1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
有效,因为:
[![在此处输入图片描述][1]][1]
但是我的代码采用了大小的第一个选项,这就是我的代码 returns 为 false-
时发生的情况[![在此处输入图片描述][2]][2]
那么我需要添加什么来解决这个问题?
public class BF {
private static int[][] fields;
public BF(int[][] field) {
fields=field;
print(field);
}
public boolean validate() {
int cnt=counter(fields);
if(cnt!=20)return false;
return checkBoard(fields, new int[]{4,3,3,2,2,2},0,new int[] {3,2,1});
}
public static boolean checkBoard(int[][] board,int[] SizePlacement,int k,int[] shipAmount){
if (counter(board)==4 ) {
return true;
}
int cnt=0;//SizePlacement={4,3,3,2,2,2}, ship(changes)={3,2,1}, k(starts at 0) is placement in ships[]
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[0].length; col++) {
if(board[row][col]==1 && row+SizePlacement[k]<board.length) {//vertically
cnt=1;
for(int i=1;i<SizePlacement[k];i++) {//check vertically
if(board[row+i][col]==1) {cnt++;}
}
if(cnt>=SizePlacement[k]) {
int[][] newBoard = deepcopy(board);
newBoard= remove(newBoard , row, col, SizePlacement[k], "ver");
shipAmount[SizePlacement[k]-2]--;
if(shipAmount==new int[]{0,0,0}) {return true;}
if(k+1<SizePlacement.length && checkBoard(newBoard,SizePlacement,k+1,shipAmount)) {return true;}
shipAmount[SizePlacement[k]-2]++;
}
}
if(board[row][col]==1 && col+SizePlacement[k]<board[0].length) {//horizontally
cnt=1;
for(int i=1;i<SizePlacement[k];i++) {//check horizontally
if(board[row][col+i]==1) {cnt++;}
}
if(cnt>=SizePlacement[k]) {
int[][] newBoard = deepcopy(board);
newBoard= remove(newBoard , row, col, SizePlacement[k], "hor");
shipAmount[SizePlacement[k]-2]--;
if(shipAmount==new int[]{0,0,0}) {return true;}
if(k+1<SizePlacement.length &&checkBoard(newBoard,SizePlacement,k+1,shipAmount)) {
return true;
}
shipAmount[SizePlacement[k]-2]++;
}
}
}
}
return false;
}
public static int[][] remove(int[][] newBoard,int row,int col,int size,String orien){
int[][] removedBoard= deepcopy(newBoard);
if(orien=="ver") {
for(int i=0;i<size;i++) {
removedBoard[row+i][col]=0;
}
print(removedBoard);
return removedBoard;
}
else if(orien=="hor") {
for(int i=0;i<size;i++) {
removedBoard[row][col+i]=0;
}
print(removedBoard);
return removedBoard;
}
return removedBoard;
}
public static int[][] deepcopy(int[][] fields){
int[][] copy= new int[fields.length][fields[0].length];
for (int row = 0; row < fields.length; row++) {
for (int col = 0; col < fields[0].length; col++) {
copy[row][col]= fields[row][col];
}
}
return copy;
}
public static int counter(int[][] f) {// counts amount of tiles on the board with 1's
int cnt=0;
for (int col = 0; col < f[0].length; col++) {
for (int row = 0; row < f.length; row++) {
if (f[row][col] == 1) {
cnt++;
}
}
}
return cnt;
}
public static void print(int[][] field) {//prints the board
for (int row = 0; row < field.length; row++) {
for (int col = 0; col < field[0].length; col++) {
if(col<field[0].length-1 && col!=0) {
System.out.print( field[row][col]+",");
}
else if(col==field[0].length-1){
System.out.print( field[row][col]+"},");
}
else if(col==0) {
System.out.print(" {"+ field[row][col]+",");
}
}
System.out.println("");
}
System.out.println("\n");
}
}
这似乎有一些错误(与解决方案不同),不确定对此的帮助将不胜感激 [1]: https://i.stack.imgur.com/bX32n.png [2]: https://i.stack.imgur.com/tFP1N.png
如果所有船只的位置都有效,则该板有效。
这意味着您需要一个船舶数据结构。首先,一个简单的数据结构可能是 [ ship_type, [ position_1 , position_2, position_3 ] ]
一旦你有了船的数据结构,检查所有的船都在板上,没有两艘船共享一个位置,以及板上每种类型的船的正确数量是微不足道的.每种类型的正确数量的船只可以进入 Fleet
数据结构。
对于一艘船,您会很快意识到,可以用字母来渲染了解其舰队的一方的棋盘。但另一块板是“特殊的”,因为没有船舶能见度。这意味着(在真实游戏中)Battleship 由两种板组成,即命中检测板和船舶定位板。它们看起来相似只是制造的副作用。
现在 HitBoard
AI 将如何尝试追捕一艘船?好吧,他们会从随机放置或模式开始。两种方法各有优势。一旦检测到命中,它将参考场上剩余船只的数量,并开始沿水平和垂直轴探测以找到船只的完整位置。一旦它被告知“You sunk my”,它就会知道沉船必须从游戏中移除。
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,1,1,1,1,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
由两艘战列舰、一艘驱逐舰、一艘潜艇和一艘巡洋舰组成。如果没有额外的信息,就不可能知道大多数船只是从左到右还是从上到下。
有时甚至无法在进行游戏时知道船的位置。有趣的是,这些信息可能并不总是足以了解船只的位置。这有时会发生在船只处于紧密排列的编队中,并且您通过在已经记录命中的一排或一列(或两列)方格内进行攻击来击沉船只。在这种情况下,您的点击可能不会定义沉没项目的结束。
如果目标是在没有位置以外的任何额外信息的情况下验证棋盘,那么您需要采用不同的方法。
- 按大小排序船只。
- 在当前的棋盘位置,重复直到没有可用的船。
- Select最大的船。
- 搜索最大船只的所有可能位置,生成没有船只位置标记的新板。
- 如果无法放置飞船,则板不是有效的板配置。
- 如果船舶列表为空且板上有标记,则该板不是有效的板配置。
- 如果船舶列表为空且棋盘为空,则标记处于有效的棋盘配置中。
- 重复,针对步骤 2 中生成的板独立处理所有板。
这是一种蛮力方法;但是,在蛮力方法中有许多可能的优化。
我认为解决这个问题的最佳方法是从最大的开始迭代放置船只。算法的大致轮廓是这样的:
- Select未放置的最大船
- 考虑所有可能展示位置的列表
- 对于每个可能的放置,从数组中删除那些 1,然后重复该过程,不包括您刚刚放置的飞船
由于您没有对某些点的船舶类型进行编码,您可以从已放置的船舶上移除点,然后继续尝试放置船舶。这将随着船只数量的增加而爆炸,但我认为它对于这个小解决方案来说已经足够快了。
尝试不允许联系的解决方案:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class BattleFieldValidator {
private static final int SIZE = 10;
private static class Ship {
public int col;
public int row;
public boolean horizontal;
public int size;
@Override
public String toString() {
return "size " + size + " at (" + (row + 1) + "," + (col + 1) + ") " + (horizontal ? "horizontal" : "vertical");
}
}
public static void validateField(int[][] field) {
// 10x10 field
if(field.length != SIZE || field[0].length != SIZE) {
throw new RuntimeException("invalid field dimensions");
}
// each cell 0 or 1
for(int row = 0; row < SIZE; row++) {
for(int col = 0; col < SIZE; col++) {
int cellValue = field[row][col];
if(cellValue != 0 && cellValue != 1) {
throw new RuntimeException("invalid cell value at (" + (row + 1) + "," + (col + 1) + ")");
}
}
}
print(field);
boolean[][] discoveredShipCells = new boolean[SIZE][SIZE];
List<Ship> ships = new ArrayList<>();
for(int row = 0; row < SIZE; row++) {
for(int col = 0; col < SIZE; col++) {
if(discoveredShipCells[row][col]) {
continue;
}
if(field[row][col] == 1) {
Ship ship = discoverShip(field, discoveredShipCells, row, col);
ships.add(ship);
}
}
}
Collections.sort(ships, (a, b) -> b.size - a.size);
System.out.println("discovered ships:");
ships.forEach(s -> System.out.println(s));
if(ships.stream().filter(s -> s.size == 4).count() != 1) {
throw new RuntimeException("battleship(size 4) count != 1");
}
if(ships.stream().filter(s -> s.size == 3).count() != 2) {
throw new RuntimeException("cruiser(size 3) count != 2");
}
if(ships.stream().filter(s -> s.size == 2).count() != 3) {
throw new RuntimeException("destroyer(size 2) count != 3");
}
if(ships.stream().filter(s -> s.size == 1).count() != 4) {
throw new RuntimeException("submarine (size 1) count != 4");
}
// ok
}
private static Ship discoverShip(int[][] field, boolean[][] discoveredShipCells, int shipStartRow, int shipStartCol) {
Ship ship = new Ship();
ship.row = shipStartRow;
ship.col = shipStartCol;
int horizontalSize = 0;
for(int col = ship.col; col < SIZE && field[ship.row][col] == 1; col++) {
horizontalSize++;
discoveredShipCells[ship.row][col] = true;
}
int verticalSize = 0;
for(int row = ship.row; row < SIZE && field[row][ship.col] == 1; row++) {
verticalSize++;
discoveredShipCells[row][ship.col] = true;
}
if(horizontalSize > 1 && verticalSize > 1) {
throw new RuntimeException("ship extending both horizontally and vertically at (" + (ship.row + 1) + "," + (ship.col + 1) + ")");
}
ship.horizontal = verticalSize == 1;
ship.size = ship.horizontal ? horizontalSize : verticalSize;
// validate ship surrounded by ocean cells
if(ship.horizontal) {
// left & right
validateOceanCell(ship, field, ship.row, ship.col - 1);
validateOceanCell(ship, field, ship.row, ship.col + ship.size);
// top & bottom
for(int col = ship.col - 1; col <= ship.col + ship.size; col++) {
validateOceanCell(ship, field, ship.row - 1, col);
validateOceanCell(ship, field, ship.row - 1, col);
}
}
else {
// top & bottom
validateOceanCell(ship, field, ship.row - 1, ship.col);
validateOceanCell(ship, field, ship.row + ship.size, ship.col);
// left & right
for(int row = ship.row - 1; row <= ship.row + ship.size; row++) {
validateOceanCell(ship, field, row, ship.col - 1);
validateOceanCell(ship, field, row, ship.col + 1);
}
}
return ship;
}
private static void validateOceanCell(Ship ship, int[][] field, int row, int col) {
if(row < 0 || row >= SIZE || col < 0 || col >= SIZE) {
return /* outside field, ignore */;
}
if(field[row][col] == 1) {
throw new RuntimeException("ship " + ship + " not surrounded by whater at (" + (row + 1) + "," + (col + 1) + ")");
}
}
private static void print(int[][] field) {
System.out.println();
for(int row = 0; row < SIZE; row++) {
System.out.println(Arrays.stream(field[row]).boxed().collect(Collectors.toList()));
}
}
}
和最少的测试:
public class BattleFieldTest {
@org.junit.Test
public void test1() {
int[][] battleField = { //
{ 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, //
{ 1, 0, 1, 0, 0, 0, 0, 0, 1, 0 }, //
{ 1, 0, 1, 0, 1, 1, 1, 0, 1, 0 }, //
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //
{ 0, 0, 0, 0, 1, 1, 1, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }, //
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
BattleFieldValidator.validateField(battleField);
}
@org.junit.Test
public void test2() {
int[][] battleField = { //
{ 0, 0, 0, 1, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 1, 0, 0, 1, 0, 1, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, //
{ 1, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 1, 0 }, //
{ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }, //
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, };
BattleFieldValidator.validateField(battleField);
}
}
示例输出:
[1, 0, 0, 0, 0, 1, 1, 0, 0, 0]
[1, 0, 1, 0, 0, 0, 0, 0, 1, 0]
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0]
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 0, 1, 1, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
discovered ships:
size 4 at (1,1) vertical
size 3 at (3,5) horizontal
size 3 at (6,5) horizontal
size 2 at (1,6) horizontal
size 2 at (2,3) vertical
size 2 at (2,9) vertical
size 1 at (5,9) horizontal
size 1 at (7,9) horizontal
size 1 at (8,4) horizontal
size 1 at (9,8) horizontal
我认为@AlexRudenko 意味着这会更容易验证:
{0,0,0,0,0,0,0,2,2,0},
{0,0,0,0,0,0,0,3,3,3},
{0,0,0,0,0,4,4,4,4,0},
{0,2,0,0,0,0,0,0,1,0},
{0,2,0,0,3,3,3,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,2,0,0,0,0,2,1,0},
{0,0,2,0,0,0,0,2,1,0},
由于我们无法确定地区分飞船,因此我们需要尝试不同的可能性。尝试将一艘船放在一个可能的位置(保留该位置),然后递归地尝试下一艘船。 (这可能也是其他人的建议)。对于有这么几艘船的 10x10 板,计算非常快。
这是我尝试实现它的尝试。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private static final int SIZE = 10;
private static final int HORIZONTAL = 0;
private static final int VERTICAL = 1;
private static final int[] shipSizes = {4, 3, 3, 2, 2, 2};
private static final int NR_OF_ONES = 4;
private static final int[][] board1 = {
{1, 0, 0, 0, 0, 1, 1, 0, 0, 0},
{1, 1, 0, 0, 0, 0, 0, 0, 1, 0},
{1, 1, 0, 0, 1, 1, 1, 0, 1, 0},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 1, 1, 1, 0, 0, 0},
{0, 0, 0, 1, 0, 0, 0, 0, 1, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 1, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
private static AtomicInteger counter = new AtomicInteger();
public static void main(String[] args) {
int[][] board = board1;
validateBoard(board);
long start = System.nanoTime();
boolean valid = checkBoard(board, 0);
long time = System.nanoTime() - start;
System.out.println("Board is " + (valid ? "" : "NOT ") + "valid");
System.out.println("Checked board in " + time + " ns");
}
private static boolean checkBoard(int[][] currentBoard, int shipSizePos) {
if (shipSizePos >= shipSizes.length) {
return true;
// return countOnes(currentBoard) == NR_OF_ONES;
}
List<Ship> currentSizeShips = findPossibleShips(currentBoard, shipSizes[shipSizePos]);
boolean valid = false;
for (Ship ship : currentSizeShips) {
if(checkBoard(removeShipFromBoard(currentBoard, ship), shipSizePos + 1)) {
return true;
}
}
return valid;
}
private static int[][] removeShipFromBoard(int[][] board, Ship ship) {
int[][] newBoard = new int[SIZE][SIZE];
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if ((ship.orientation == HORIZONTAL && r == ship.row && (c >= ship.col && c < ship.col + ship.size))
|| (ship.orientation == VERTICAL && c == ship.col && (r >= ship.row && r < ship.row + ship.size))) {
newBoard[r][c] = 0;
} else {
newBoard[r][c] = board[r][c];
}
}
}
return newBoard;
}
private static List<Ship> findPossibleShips(int[][] board, int size) {
List<Ship> ships = new ArrayList<>();
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if (board[r][c] == 1) {
if (isPossibleHorizontalShip(board, size, r, c)) {
ships.add(new Ship(r, c, size, HORIZONTAL));
}
if (isPossibleVerticalShip(board, size, r, c)) {
ships.add(new Ship(r, c, size, VERTICAL));
}
}
}
}
return ships;
}
private static boolean isPossibleHorizontalShip(int[][] board, int size, int row, int col) {
if (SIZE - col < size) {
return false;
}
int c;
for (c = col; c < SIZE && board[row][c] == 1; ++c) ;
return c - col >= size;
}
private static boolean isPossibleVerticalShip(int[][] board, int size, int row, int col) {
if (SIZE - row < size) {
return false;
}
int r;
for (r = row; r < SIZE && board[r][col] == 1; ++r) ;
return r - row >= size;
}
private static int countOnes(int[][] board) {
int ones = 0;
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
ones += board[r][c];
}
}
return ones;
}
private static void validateBoard(int[][] board) {
for (int r = 0; r < SIZE; ++r) {
for (int c = 0; c < SIZE; ++c) {
if (!(board[r][c] == 0 || board[r][c] == 1)) {
throw new IllegalArgumentException("Illegal character at " + r + ", " + c);
}
}
}
if (countOnes(board) != Arrays.stream(shipSizes).sum() + NR_OF_ONES) {
throw new IllegalArgumentException("Wrong number of ship pieces");
}
}
private static class Ship {
private final int row;
private final int col;
private final int size;
private final int orientation;
public Ship(int row, int col, int size, int orientation) {
this.row = row;
this.col = col;
this.size = size;
this.orientation = orientation;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ship ship = (Ship) o;
return row == ship.row && col == ship.col && size == ship.size && orientation == ship.orientation;
}
@Override
public int hashCode() {
return Objects.hash(row, col, size, orientation);
}
@Override
public String toString() {
return "Ship{" +
"row=" + row +
", col=" + col +
", size=" + size +
", orientation=" + orientation +
'}';
}
}
}
- 首先验证板以确保它只包含 0 和 1 以及正确数量的 1
- 从最大到最小浏览非 1 号船只的列表。
- 搜索当前尺码的可能展示位置,删除一个并检查下一个尺码的分支
- 当只剩下1s时,板就OK了(我们依靠验证的计数是正确的)