在Battleships随机放置算法中避免死胡同

时间:2013-05-02 11:54:10

标签: algorithm

我需要一些建议来建立一种算法,根据船舶不能重叠或接触的规则(甚至是对角线)将多艘船放置在船上。在拿起随机位置后,我能以什么方式确保其余船只仍有足够的空间?

例如,我想在6x6板(2D阵列)上安装5艘船。船的尺寸为:5,4,3,1,1。有几种方法可以安排它们,如下所示:

 -------------
| 1 1 1 1 1 . |
| . . . . . . |
| 2 2 2 2 . 4 |
| . . . . . . |
| 3 3 3 . . 5 |
| . . . . . . |
 -------------

随机算法如下所示:

1. Get next ship
2. Get random cell and orientation
3. Try to fit ship (find any conflicts)
    3a. If ship cannot fit, try different 
        cell and orientation, untill all cells 
        have been tried (function fails)
    3b. If ship fits, get another ship (goto 1) 

但如果我使用它,我很可能会这样结束(编辑:改为反映在步骤0中按大小排序):

 -------------
| . 3 3 3 . 4 |   5
| . . . . . . |
| . 2 2 2 2 . |
| . . . . . . |
| 1 1 1 1 1 . |
| . . . . . . |
 ------------- 

意味着1个单元大小的船没有位置。我怎么能避免这样的问题?如何在3b中实现函数verifyRestShipsWillFit()

3 个答案:

答案 0 :(得分:2)

使用某种启发式对船舶进行排序,例如最大的第一。然后开始使用空白板和完整的船只列表来递归放置。它本质上是一个树搜索:

请记住,如果您有不可变类,则使用递归总是更容易。将船放置在电路板上会创建一个电路板,而无需更换电路板。

Board GetRandomBoard()
{
   List<Ship> ships = { .. }   // List of all ships.
   Board = Board.Empty();
   Board result = PlaceShips(ships, board);
   return result; // You could have a retry here as well if it fails.
}

private Board PlaceShips(IEnumerable<Ship> shipsRemaining, Board board)
{    
   Ship shipToPlace = shipsRemaining.FirstOrDefault();

   // If all ships were placed, we are done.
   if (shipToPlace == null)
      return board;

   int attempts = 0;       
   while (attempts++ < MaxAttempts)
   {
       // Get a position for the new ship that is OK with the current board.
       ShipPosition pos = GetRandomShipPosition(board, shipToPlace); 

       // If it isn't possible to find such a position, this branch is bad.
       if (pos == null)
          return null;

       // Create a new board, including the new ship.
       Board newBoard = new board.WithShip(shipToplace, pos);

       // Recurse by placing remaining ships on the new board.
       board nextBoard = PlaceShips(shipsRemaining.Skip(1).ToList(), newBoard);
       if (nextBoard != null)
          return nextBoard;
   }
   return null;
}

答案 1 :(得分:1)

对于像这样的小问题,最容易做到

1. Get next ship
2. Get random cell and orientation
3. Try to fit ship (find any conflicts)
    3a. If ship doesn't fit there, **delete all ships and start over**
    3b. If ship fits, get another ship (goto 1) 

如果您对终止感到偏执,请设置(高)迭代限制并回退到确定性配置。

答案 2 :(得分:0)

首先放置最大的船只。我的alghoritm与你的完全一样,但是在第1步之前,我会添加另一个步骤来按大小排序。

编辑:可能的是,使用该地图大小,alghotirm仍然会失败。在3a和3b之间添加另一个步骤:

3a.1 - if the function fails, restart from scratch.

第二次编辑:有另一种选择:不是插入船只,而是插入他们的hitbox。一个hitbox和一艘船及其周围的坐标一样大。允许碰撞箱重叠,只要它们的核心(船将在哪里)不重叠,并且还允许碰撞箱泄漏到地图外(但同样,核心不能泄漏)。

无论采用哪种方法,我都会尝试使解决方案与地图大小无关。