public void Generate() { InitializeMap(); ClearMap(); Stack <PairRowCol> st = new Stack <PairRowCol>(); System.Random rng = new System.Random(); var currentPos = new PairRowCol { Row = rng.Next(Height), Col = rng.Next(Width) }; st.Push(currentPos); MarkVisited(currentPos); while (st.Count > 0) { var uns = GetUnvisitedNeighbors(currentPos).ToArray(); if (uns.Length > 0) { var nextPos = uns[rng.Next(uns.Length)]; Connect(currentPos, nextPos); st.Push(nextPos); MarkVisited(nextPos); currentPos = nextPos; } else { currentPos = st.Pop(); } } CleanWalls(); }
private void Connect(PairRowCol p1, PairRowCol p2) { if (p1.Row == p2.Row) { if (p1.Col < p2.Col) { _map[p1.Row, p1.Col].WallState ^= InvertX ? MazeCell.WallStates.Left : MazeCell.WallStates.Right; _map[p2.Row, p2.Col].WallState ^= InvertX ? MazeCell.WallStates.Right : MazeCell.WallStates.Left; } else if (p1.Col > p2.Col) { _map[p1.Row, p1.Col].WallState ^= InvertX ? MazeCell.WallStates.Right : MazeCell.WallStates.Left; _map[p2.Row, p2.Col].WallState ^= InvertX ? MazeCell.WallStates.Left : MazeCell.WallStates.Right; } } if (p1.Col == p2.Col) { if (p1.Row < p2.Row) { _map[p1.Row, p1.Col].WallState ^= InvertY ? MazeCell.WallStates.Top : MazeCell.WallStates.Bottom; _map[p2.Row, p2.Col].WallState ^= InvertY ? MazeCell.WallStates.Bottom : MazeCell.WallStates.Top; } else if (p1.Row > p2.Row) { _map[p1.Row, p1.Col].WallState ^= InvertY ? MazeCell.WallStates.Bottom : MazeCell.WallStates.Top; _map[p2.Row, p2.Col].WallState ^= InvertY ? MazeCell.WallStates.Top : MazeCell.WallStates.Bottom; } } }
PairRowCol GetRandomNonBlockedCell(int nRandom = 1) { PairRowCol pos = PairRowCol.Create(0, 0); int iterCnt = 0; do { pos.Row = Random.Range(0, MazeRows); pos.Col = Random.Range(0, MazeColumns); } while (iterCnt++ < nRandom && _mg.GetCell(pos).WallState.HasFlag(MazeGenerator.MazeCell.WallStates.All)); return(pos); }
private IEnumerable <PairRowCol> GetNeighbors(PairRowCol p) { var l = new PairRowCol { Row = p.Row, Col = p.Col - 1 }; // left var r = new PairRowCol { Row = p.Row, Col = p.Col + 1 }; // right var a = new PairRowCol { Row = p.Row - 1, Col = p.Col }; // above var b = new PairRowCol { Row = p.Row + 1, Col = p.Col }; // below return(new[] { l, r, a, b }.Where(ValidPos)); }
private void CleanWalls() { for (int row = 1; row < Height; ++row) { for (int col = 1; col < Width; ++col) { var current = GetCell(PairRowCol.Create(row, col)); var left = GetCell(PairRowCol.Create(row, col - 1)); var above = GetCell(PairRowCol.Create(row - 1, col)); if (left.WallState.HasFlag(MazeCell.WallStates.Right) && current.WallState.HasFlag(MazeCell.WallStates.Left)) { left.WallState ^= MazeCell.WallStates.Right; } if (above.WallState.HasFlag(MazeCell.WallStates.Bottom) && current.WallState.HasFlag(MazeCell.WallStates.Top)) { above.WallState ^= MazeCell.WallStates.Bottom; } } } }
private bool ValidPos(PairRowCol p) { return(ValidPos(p.Row, p.Col)); }
private void MarkVisited(PairRowCol p) { MarkVisited(p.Row, p.Col); }
public MazeCell GetCell(PairRowCol p) => GetCell(p.Row, p.Col);
private IEnumerable <PairRowCol> GetUnvisitedNeighbors(PairRowCol p) { return(GetNeighbors(p).Where(_p => !_map[_p.Row, _p.Col].Visited)); }
// Start is called before the first frame update void Start() { Cursor.lockState = CursorLockMode.Confined; _mg.Height = MazeRows; _mg.Width = MazeColumns; _mg.InvertY = true; _mg.Generate(); var halfHeight = MazeRows / 2f; var halfWidth = MazeColumns / 2f; for (int row = 0; row < MazeRows; ++row) { for (int col = 0; col < MazeColumns; ++col) { Vector3 cellPos = new Vector3(col - halfWidth + 0.5f, 0, row - halfHeight + 0.5f); var cell = _mg.GetCell(row, col); if (cell.WallState.HasFlag(MazeGenerator.MazeCell.WallStates.Top)) { GameObject g = Instantiate(_horizontalWallPrefab, _wallParent is null ? this.transform : _wallParent.transform); g.name = $"Wall (N) ({row}, {col})"; g.transform.position = cellPos + new Vector3(0, g.transform.localScale.y / 2, +0.5f); } if (cell.WallState.HasFlag(MazeGenerator.MazeCell.WallStates.Bottom)) { GameObject g = Instantiate(_horizontalWallPrefab, _wallParent is null ? this.transform : _wallParent.transform); g.name = $"Wall (S) ({row}, {col})"; g.transform.position = cellPos + new Vector3(0, g.transform.localScale.y / 2, -0.5f); } if (cell.WallState.HasFlag(MazeGenerator.MazeCell.WallStates.Left)) { GameObject g = Instantiate(_verticalWallPrefab, _wallParent is null ? this.transform : _wallParent.transform); g.name = $"Wall (W) ({row}, {col})"; g.transform.position = cellPos + new Vector3(-0.5f, g.transform.localScale.y / 2, 0); } if (cell.WallState.HasFlag(MazeGenerator.MazeCell.WallStates.Right)) { GameObject g = Instantiate(_verticalWallPrefab, _wallParent is null ? this.transform : _wallParent.transform); g.name = $"Wall (E) ({row}, {col})"; g.transform.position = cellPos + new Vector3(+0.5f, g.transform.localScale.y / 2, 0); } } } { var ballPos = GetRandomNonBlockedCell(); var ballLocalPos = CellToLocal(ballPos); SpawnBall(CellToLocal(ballPos) + new Vector3(0, InitialBallHeight, 0)); PairRowCol winPos = PairRowCol.Create(0, 0); var dist = (CellToLocal(winPos) - ballLocalPos).sqrMagnitude; for (int row = 0; row < MazeRows; ++row) { for (int col = 0; col < MazeColumns; ++col) { PairRowCol pos = PairRowCol.Create(row, col); if (_mg.GetCell(pos).WallState.HasFlag(MazeGenerator.MazeCell.WallStates.All)) { continue; } var d = (CellToLocal(pos) - ballLocalPos).sqrMagnitude; if (d > dist) { dist = d; winPos = pos; } } } if (_debug) { SpawnWinningSpot(CellToLocal(PairRowCol.Create(ballPos.Row, ballPos.Col - 1)) + new Vector3(0, _winningSpotPrefab.transform.localScale.y / 2, 0)); } else { SpawnWinningSpot(CellToLocal(winPos) + new Vector3(0, _winningSpotPrefab.transform.localScale.y / 2, 0)); } } }
Vector3 CellToLocal(PairRowCol p) { return(new Vector3(p.Col - MazeColumns / 2f + 0.5f, 0, p.Row - MazeRows / 2f + 0.5f)); }