将随机数放在网格中

时间:2013-08-20 20:52:31

标签: c++ algorithm

我需要在网格中放置数字,使其不会相互碰撞。此数字位置应该是随机的,可以是水平或垂直的。这些数字基本上表明了船舶的位置。所以船只的点数应该在一起,需要是随机的,不应该碰撞。

我试过了:

int main()
{
    srand(time(NULL));
    int Grid[64];
    int battleShips;
    bool battleShipFilled;

    for(int i = 0; i < 64; i++)
        Grid[i]=0;

    for(int i = 1; i <= 5; i++)
    {
        battleShips = 1;
        while(battleShips != 5)
        {
            int horizontal = rand()%2;
            if(horizontal == 0)
            {
                battleShipFilled = false;
                while(!battleShipFilled)
                {
                    int row = rand()%8;
                    int column = rand()%8;

                    while(Grid[(row)*8+(column)] == 1)
                    {
                        row = rand()%8;
                        column = rand()%8;
                    }

                    int j = 0;
                    if(i == 1) j= (i+1);
                    else j= i;

                    for(int k = -j/2; k <= j/2; k++)
                    {
                        int numberOfCorrectLocation = 0;
                        while(numberOfCorrectLocation != j)
                        {
                            if(row+k> 0 && row+k<8)
                            {
                                if(Grid[(row+k)*8+(column)] == 1) break;
                                numberOfCorrectLocation++;
                            }
                        }
                        if(numberOfCorrectLocation !=i) break;
                    }

                    for(int k = -j/2; k <= j/2; k++)
                        Grid[(row+k)*8+(column)] = 1;
                    battleShipFilled = true;    
                }
                battleShips++;
            }
            else
            {
                battleShipFilled = false;
                while(!battleShipFilled)
                {
                    int row = rand()%8;
                    int column = rand()%8;

                    while(Grid[(row)*8+(column)] == 1)
                    {
                        row = rand()%8;
                        column = rand()%8;
                    }

                    int j = 0;
                    if(i == 1) j= (i+1);
                    else j= i;

                    for(int k = -j/2; k <= j/2; k++)
                    {
                        int numberOfCorrectLocation = 0;
                        while(numberOfCorrectLocation != i)
                        {
                            if(row+k> 0 && row+k<8)
                            {
                                if(Grid[(row)*8+(column+k)] == 1) break;
                                numberOfCorrectLocation++;
                            }
                        }
                        if(numberOfCorrectLocation !=i) break;
                    }

                    for(int k = -j/2; k <= j/2; k++)
                        Grid[(row)*8+(column+k)] = 1;
                    battleShipFilled = true;    
                }
                battleShips++;
            }
        }
    }
}

但我编写的代码无法在8x8网格中随机生成数字。

需要一些指导如何解决这个问题。如果有更好的方法,请告诉我......

应该如何看待:

enter image description here

我的代码在做什么: 基本上,我将5艘不同大小的船放在一个网格上。对于每一个,我检查是否要将其水平或垂直随机放置。之后,我检查周围是否填满。如果没有,我把它们放在那里。或者我重复这个过程。

重点:我需要使用while,for循环..

4 个答案:

答案 0 :(得分:1)

你更好地使用递归来解决这个问题。这将为您的算法提供放松的可能性。我的意思是你可以部署每艘船并将下一部分放置在船的随机末端,然后检查新放置的船舶部件是否有相邻的瓷砖为空,然后进入下一个。如果碰巧它接触另一艘船它将由于递归性质它将移除放置的瓷砖并尝试另一端。如果船舶的位置无效,则应将船舶放置在不同的地方并重新开始。

我在一个单词搜索游戏中使用过这个解决方案,在这个游戏中,必须使用单词来填充。工作得很完美。

这是我的单词搜索游戏中的代码:

bool generate ( std::string word, BuzzLevel  &level, CCPoint position, std::vector<CCPoint> &placed, CCSize lSize )
{
    std::string cPiece;

    if ( word.size() == 0 ) return true;
    if ( !level.inBounds ( position ) ) return false;
    cPiece += level.getPiece(position)->getLetter();
    int l = cPiece.size();
    if ( (cPiece != " ") && (word[0] != cPiece[0]) ) return false;
    if ( pointInVec (position, placed) ) return false;
    if ( position.x >= lSize.width || position.y >= lSize.height || position.x < 0 || position.y < 0 ) return false;

    placed.push_back(position);

    bool used[6];
    for ( int t = 0; t < 6; t++ ) used[t] = false;

    int adj;
    while ( (adj = HexCoord::getRandomAdjacentUnique(used)) != -1 )
    {
    CCPoint nextPosition = HexCoord::getAdjacentGridPositionInDirection((eDirection) adj, position);

    if ( generate ( word.substr(1, word.size()), level, nextPosition, placed, lSize ) ) return true;

}

    placed.pop_back();
    return false;    
}

CCPoint getRandPoint ( CCSize size )
{
    return CCPoint ( rand() % (int)size.width, rand() % (int)size.height);
}


void generateWholeLevel ( BuzzLevel &level,
                                   blockInfo* info,
                                   const CCSize &levelSize, 
                                   vector<CCLabelBMFont*> wordList
                                   )
{
    for ( vector<CCLabelBMFont*>::iterator iter = wordList.begin();
         iter != wordList.end(); iter++ )
    {
        std::string cWord = (*iter)->getString();
       // CCLog("Curront word %s", cWord.c_str() );
        vector<CCPoint> wordPositions;

        int iterations = 0;
        while ( true )
        {
            iterations++;
            //CCLog("iteration %i", iterations );
            CCPoint cPoint = getRandPoint(levelSize);
            if ( generate (cWord, level, cPoint, wordPositions, levelSize ) )
            {
                //Place pieces here
                for ( int t = 0; t < cWord.size(); t++ )
                {
                    level.getPiece(wordPositions[t])->addLetter(cWord[t]);
                }
                break;
            }

            if ( iterations > 1500 )
            {
                level.clear();
                generateWholeLevel(level, info, levelSize, wordList);
                return;
            }
        }
    }
}

我可以补充说,游戏中使用的形状是蜂窝状。信可以向任何方向吹,所以上面的代码比我想要的更复杂,但是会提供一个起点。

当我回到家时,我会提供更适合的东西,因为我现在没有足够的时间。

答案 1 :(得分:1)

我可以在代码中看到潜在的无限循环

int j = 0;
if(i == 1) j= (i+1);
else j= i;

for(int k = -j/2; k <= j/2; k++)
{
    int numberOfCorrectLocation = 0;
    while(numberOfCorrectLocation != i)
    {
        if(row+k> 0 && row+k<8)
        {
            if(Grid[(row)*8+(column+k)] == 1) break;
            numberOfCorrectLocation++;
        }
    }
    if(numberOfCorrectLocation !=i) break;
}

这里,没有什么可以阻止行为0,因为它早先是assignd rand%8,并且k可以被赋予负值(因为j可以是正数)。一旦发生这种情况,任何事情都不会以while循环结束。

另外,我建议以更面向对象的方式重新解决这个问题(或者至少将main()中的代码分解为多个更短的函数)。就个人而言,我发现代码有点难以理解。

答案 2 :(得分:1)

一个非常快速且可能有错误的例子,说明如何通过使用一些OOP来真正清理解决方案并使其更加灵活:

enum Orientation {
    Horizontal,
    Vertical
};

struct Ship {
    Ship(unsigned l = 1, bool o = Horizontal) : length(l), orientation(o) {}
    unsigned char length;
    bool orientation;
};

class Grid {
public:
    Grid(const unsigned w = 8, const unsigned h = 8) : _w(w), _h(h) {
        grid.resize(w * h);
        foreach (Ship * sp, grid) {
            sp = nullptr;
        }
    }

    bool addShip(Ship * s, unsigned x, unsigned y) {
        if ((x <= _w) && (y <= _h)) { // if in valid range
            if (s->orientation == Horizontal) {
                if ((x + s->length) <= _w) { // if not too big
                    int p = 0; //check if occupied
                    for (int c1 = 0; c1 < s->length; ++c1) if (grid[y * _w + x + p++]) return false;
                    p = 0; // occupy if not
                    for (int c1 = 0; c1 < s->length; ++c1)  grid[y * _w + x + p++] = s;
                    return true;
                } else return false;
            } else {
                if ((y + s->length) <= _h) {
                    int p = 0; // check
                    for (int c1 = 0; c1 < s->length; ++c1) {
                        if (grid[y * _w + x + p]) return false;
                        p += _w;
                    }
                    p = 0; // occupy
                    for (int c1 = 0; c1 < s->length; ++c1) {
                        grid[y * _w + x + p] = s;
                        p += _w;
                    }
                    return true;
                } else return false;
            }
        } else return false;
    }

    void drawGrid() {
        for (int y = 0; y < _h; ++y) {
            for (int x = 0; x < _w; ++x) {
                if (grid.at(y * w + x)) cout << "|S";
                else cout << "|_";
            }
            cout << "|" << endl;
        }
        cout << endl;
    }

    void hitXY(unsigned x, unsigned y) {
        if ((x <= _w) && (y <= _h)) {
            if (grid[y * _w + x]) cout << "You sunk my battleship" << endl;
            else cout << "Nothing..." << endl;
        }
    }

private:
    QVector<Ship *> grid;
    unsigned _w, _h;
};

基本思想是创建一个任意大小的网格,并使其能够在任意坐标处“加载”任意长度的船只。你需要检查尺寸是否太大,如果瓷砖还没有被占用,那就是它,另一个是方向 - 如果水平然后增量为+1,如果垂直增量为+宽度。

这使得使用这些方法可以灵活地使用随机数据快速填充网格:

int main() {
    Grid g(20, 20);
    g.drawGrid();
    unsigned shipCount = 20;

    while (shipCount) {
        Ship * s = new Ship(qrand() % 8 + 2, qrand() %2);
        if (g.addShip(s, qrand() % 20, qrand() % 20)) --shipCount;
        else delete s;
    }
    cout << endl;
    g.drawGrid();

    for (int i = 0; i < 20; ++i) g.hitXY(qrand() % 20, qrand() % 20);
}

当然,你可以进一步扩展它,使击中的船只下沉并从网格中消失,使船只可以移动并翻转它们的方向。您甚至可以使用对角线方向。通过改进基于OOP的解决方案,可以获得很大的灵活性和潜力。

显然,你会在生产代码中加入一些限制,因为目前你可以创建0x0的网格和长度为0的船只。无论如何,它只是一个简单的例子。我正在使用Qt,因此使用Qt容器,但它与std容器一样。

答案 3 :(得分:-1)

我尝试用Java重写你的程序,它可以根据需要运行。随意询问任何未明确编码的内容。我没有重新检查它,所以它可能有自己的错误。它可以进一步优化和清理,但是因为它已经过了午夜,我宁愿不这样做:)

public static void main(String[] args) {

    Random generator = new Random();

    int Grid[][] = new int[8][8];

    for (int battleShips = 0; battleShips < 5; battleShips++) {
        boolean isHorizontal = generator.nextInt(2) == 0 ? true : false;

        boolean battleShipFilled = false;

        while (!battleShipFilled) {             
            // Select a random row and column for trial             
            int row = generator.nextInt(8);
            int column = generator.nextInt(8);

            while (Grid[row][column] == 1) {
                row = generator.nextInt(8);
                column = generator.nextInt(8);
            }

            int lengthOfBattleship = 0;
            if (battleShips == 0) // Smallest ship should be of length 2
                lengthOfBattleship = (battleShips + 2);
            else    // Other 4 ships has the length of 2, 3, 4 & 5
                lengthOfBattleship = battleShips + 1;

            int numberOfCorrectLocation = 0;

            for (int k = 0; k < lengthOfBattleship; k++) {
                if (isHorizontal && row + k > 0 && row + k < 8) {
                    if (Grid[row + k][column] == 1)
                        break;
                } else if (!isHorizontal && column + k > 0 && column + k < 8) {
                    if (Grid[row][column + k] == 1)
                        break;
                } else {
                    break;
                }

                numberOfCorrectLocation++;
            }

            if (numberOfCorrectLocation == lengthOfBattleship) {
                for (int k = 0; k < lengthOfBattleship; k++) {
                    if (isHorizontal)
                        Grid[row + k][column] = 1;
                    else
                        Grid[row][column + k] = 1;
                }
                battleShipFilled = true;
            }

        }
    }
}

一些要点。

  • 正如@Kindread在另一个答案中所说,代码有一个无限循环条件,必须消除。

  • 此算法将使用过多资源来查找解决方案,应对其进行优化。

  • 应避免使用代码重复,因为这会导致更多维护成本(对于这种特定情况可能不是问题),以及可能的错误。

希望这个答案有所帮助......