映射C#移动量的可能路径

本文关键字:路径 移动 映射 | 更新日期: 2023-09-27 18:20:49

我正在重新创建cluedo游戏,我想绘制玩家在掷骰子后可以移动的可能路径。

我通过绘制图片框并将其命名到映射位置来映射网格。

以下是我迄今为止可能的路径代码:

int Roll;
private void RollDice()
{
    ResetTiles();
    JimRandom Random = new JimRandom();
    //Roll DIce 1
    int dice1 = Random.Next(1, 7);
    //Roll DIce 2
    int dice2 = Random.Next(1, 7);
    Roll = dice1 + dice2;
    //Set Dice images
    pbDice1.BackgroundImage = Roller[dice1 - 1].Picture;
    pbDice2.BackgroundImage = Roller[dice2 - 1].Picture;
    btnRoll.Enabled = false;
    Test(Roll);
    //Show available moves
    Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
    Panel Current = null;
    System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
    foreach (Control c in lCurrent)
    {
        Current = c as Panel;
        CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
    }
    //Dynamic map
    List<string> possiblities = new List<string>();
    int currentRow = CurrentLoc.Y / tileWidth;
    int currentCol = CurrentLoc.X / tileHeight;
    //Find all possible start blocks
    string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
    string up = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow - 1);
    string left = String.Format("Col={0:00}-Row={1:00}", currentCol - 1, currentRow);
    string right = String.Format("Col={0:00}-Row={1:00}", currentCol + 1, currentRow);
    List<string> startBlocks = new List<string>();
    //See if down is available
    Control[] LPossible = PnlBoard.Controls.Find(down, true);
    if (LPossible.Length > 0)
    {
        startBlocks.Add(down);
    }
    //See if Up is available
    LPossible = PnlBoard.Controls.Find(up, true);
    if (LPossible.Length > 0)
    {
        startBlocks.Add(up);
    }
    //See if left is available
    LPossible = PnlBoard.Controls.Find(left, true);
    if (LPossible.Length > 0)
    {
        startBlocks.Add(left);
    }
    //See if right is available
    LPossible = PnlBoard.Controls.Find(right, true);
    if (LPossible.Length > 0)
    {
        startBlocks.Add(right);
    }
    //possiblilities 1
    foreach (string s in startBlocks)
    {
        Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
        PictureBox startBlock = lStarBlock[0] as PictureBox;
        int sRow = startBlock.Location.Y / tileWidth;
        int sCol = startBlock.Location.X / tileHeight;
        //Rows
        for (int row = sRow; row < sRow + Roll; row++)
        {
            //Columns
            for (int col = sCol; col < sCol + Roll; col++)
            {
                possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
            }
        }            
    }
    //Show possible moves
    foreach (string p in possiblities)
    {
        LPossible = PnlBoard.Controls.Find(p, true);
        if (LPossible.Length > 0)
        {
            PictureBox active = LPossible[0] as PictureBox;
            active.Image = Cluedo.Properties.Resources.TileActive;
            System.Threading.Thread.Sleep(1);
            Application.DoEvents();
        }
        //else
        //{
        //    break;
        //}
    }
}

映射C#移动量的可能路径

这里有很多不同的地方。这更像是一篇代码审查文章,但最终还是有一个解决问题的方法,也许剩下的可以帮助你改善代码的整体状态。

随机性

您正在为每个方法调用创建一个新的随机生成器实例:

JimRandom Random = new JimRandom();

如果快速连续调用该方法,这通常会导致生成相同的值。也许这就是你使用加密RNG而不是PRNG的原因?一个PRNG应该足够像这样的游戏,只要你重用它

使用正确的类型

您使用以下代码确定当前玩家位置:

//Show available moves
Control[] lCurrent = PnlBoard.Controls.Find("pnl" + CurrentPlauer, true);
Panel Current = null;
System.Drawing.Point CurrentLoc = new System.Drawing.Point(0, 0);
foreach (Control c in lCurrent)
{
    Current = c as Panel;
    CurrentLoc = new System.Drawing.Point(c.Location.X, c.Location.Y);
}

看起来CurrentPlauer是一个字符串。创建一个Player类来存储玩家的名称和当前位置会让事情变得更容易:

Point currentLocation = currentPlayer.Location;

从UI代码中分离游戏逻辑

您通过对控件进行字符串查找来检查可通过的瓦片:

string down = String.Format("Col={0:00}-Row={1:00}", currentCol, currentRow + 1);
// ...
Control[] LPossible = PnlBoard.Controls.Find(down, true);
if (LPossible.Length > 0)
{
    startBlocks.Add(down);
}

通常,2D阵列用于像这样的瓦片映射,可能封装在TilemapMap类中。这使得使用分幅更加自然,因为您可以直接在分幅坐标中工作,而不必在UI和分幅坐标之间进行转换。它还将代码更干净地分解为游戏逻辑和UI部分(没有UI,你的帖子中的代码是不可能测试的):

// TileMap class:
public bool IsPassable(int x, int y)
{
    if (x < 0 || x >= Width || y < 0 || y >= Height)
        return false;
    return tiles[x][y] != Tile.Wall;  // enum Tile { Wall, Ballroom, DiningRoom, Hall, ... }
}
// When called from your Board code:
if (map.IsPassable(currentLocation.X, currentLocation.Y + 1))
    startBlocks.Add(new Point(currentLocation.X, currentLocation.Y + 1));

减少重复

至于检查所有直接相邻的瓦片,不需要重复相同的代码4次:

// Let's make a utility function:
public static IEnumerable<Point> GetNeighboringPositions(Point position)
{
    yield return new Point(position.X - 1, position.Y);
    yield return new Point(position.X, position.Y - 1);
    yield return new Point(position.X + 1, position.Y);
    yield return new Point(position.X, position.Y + 1);
}
// In the Board code:
foreach (Point neighboringPosition in GetNeighboringPositions(currentPosition))
{
    if (map.IsPassable(neighboringPosition.X, neighboringPosition.Y))
        startBlocks.Add(neighboringPosition);
}

确定有效移动

最后,我们得到了决定当前玩家可以移动到哪些区块的代码:

//possiblilities 1
foreach (string s in startBlocks)
{
    Control[] lStarBlock = PnlBoard.Controls.Find(s, true);
    PictureBox startBlock = lStarBlock[0] as PictureBox;
    int sRow = startBlock.Location.Y / tileWidth;
    int sCol = startBlock.Location.X / tileHeight;
    //Rows
    for (int row = sRow; row < sRow + Roll; row++)
    {
        //Columns
        for (int col = sCol; col < sCol + Roll; col++)
        {
            possiblities.Add(String.Format("Col={0:00}-Row={1:00}", col, row));
        }
    }            
}

这样做的目的是检查一个矩形区域,使用起始位置作为其左上角。它对多达4个相邻位置执行此操作,因此矩形将部分重叠。这是行不通的。如果地图上没有任何障碍物,像这样的东西,再加上曼哈顿的距离检查,可能会奏效(如果你不忘向左和向上看的话)。或者更好的是,一些奇特的循环检查菱形区域。

然而,你有很多墙需要处理,所以你需要一种不同的方法。玩家的当前位置在距离0处。它的直接邻居在距离1处。他们的邻居在距离2处——除了那些距离较低的瓷砖(已经覆盖的瓷砖)。距离2处的瓦片的任何邻居要么在距离3处,要么已经被覆盖。当然,墙砖必须跳过。

因此,你需要跟踪哪些瓷砖已经被覆盖,以及你还需要检查哪些相邻的瓷砖,直到你用完移动点。让我们把它总结成一种可重复使用的方法:

public List<Point> GetReachableTiles(Point currentPosition, int maxDistance)
{
    List<Point> coveredTiles = new List<Point> { currentPosition };
    List<Point> boundaryTiles = new List<Point> { currentPosition };
    for (int distance = 0; distance < maxDistance; distance++)
    {
        List<Point> nextBoundaryTiles = new List<Point>();
        foreach (Point position in boundaryTiles)
        {
            foreach (Point pos in GetNeighboringPositions(position))
            {
                // You may also want to check against other player positions, if players can block each other:
                if (!coveredTiles.Contains(pos) && !boundaryTiles.Contains(pos) && map.IsPassable(pos.X, pos.Y))
                {
                    // We found a passable tile:
                    coveredTiles.Add(pos);
                    // And we want to check its neighbors in the next 'distance' iteration, too:
                    nextBoundaryTiles.Add(pos);
                }
            }
        }
        // The next 'distance' iteration should check the neighbors of the current boundary tiles:
        boundaryTiles = nextBoundaryTiles;
    }
    return coveredTiles;
}