public Maze(ArrayPoint2D size, ArrayPoint2D start, ArrayPoint2D end) { Cells = new List <List <int> >(size.r); for (int i = 0; i < size.r; i++) { Cells.Add(new List <int>(size.c)); for (int j = 0; j < size.c; j++) { Cells[i].Add(0); } } HolWalls = new List <List <bool> >(size.r - 1); for (int i = 0; i < size.r - 1; i++) { HolWalls.Add(new List <bool>(size.c)); for (int j = 0; j < size.c; j++) { HolWalls[i].Add(true); } } VerWalls = new List <List <bool> >(size.r); for (int i = 0; i < size.r; i++) { VerWalls.Add(new List <bool>(size.c - 1)); for (int j = 0; j < size.c - 1; j++) { VerWalls[i].Add(true); } } }
public PathNode(ArrayPoint2D point, PathNode history) { this.point = point; path = new List <ArrayPoint2D>(history.path) { this.point }; }
public PathNode(ArrayPoint2D point) { this.point = point; path = new List <ArrayPoint2D> { this.point }; }
// Recursive Backtracker 방식의 미로 생성 // maze: 완전 미로를 생성할 변수 // action: 벽을 제거할 때마다 실행되기를 원하는 함수 public static void RecursiveBacktracker(Maze maze, Action action) { #region INITIALIZE ArrayPoint2D size = maze.GetSize(); List <List <bool> > hasVisited = new List <List <bool> >(); for (int i = 0; i < size.r; i++) { hasVisited.Add(new List <bool>(size.c)); for (int j = 0; j < size.c; j++) { hasVisited[i].Add(false); } } #endregion using (FileStream fs = new FileStream("test.log", FileMode.Create)) { StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); #region ALGORITHM // CODE STARTS HERE // ArrayPoint2D current = maze.StartPoint; hasVisited[current.r][current.c] = true; sw.WriteLine("Recursive Backtracker 생성입니다. 현재 위치는 " + current + " 입니다."); // LOG Stack <ArrayPoint2D> PointStack = new Stack <ArrayPoint2D>(); while (!HasVisitedAll(hasVisited)) { List <ArrayPoint2D> avail = new List <ArrayPoint2D>(); AddNeighbor(avail, current, size); avail.RemoveAll(_ => hasVisited[_.r][_.c]); if (avail.Count() != 0) { ArrayPoint2D target = avail[random.Next(avail.Count())]; PointStack.Push(current); sw.WriteLine("스택에 " + current + " 가 푸시되었습니다."); // LOG maze.RemoveWallBetween(current, target, action); sw.WriteLine(current + " 와 " + target + " 사이 벽이 제거되었습니다."); // LOG action(); // GRAPHIC hasVisited[target.r][target.c] = true; current = target; sw.WriteLine("현재 위치는 이제 " + current + " 입니다."); // LOG } else { current = PointStack.Pop(); sw.WriteLine("현재 위치를 스택에서 팝된 데이터로 설정합니다. " + current + " 가 팝되었습니다."); // LOG } } #endregion sw.Flush(); } }
public bool HasWallBetween(ArrayPoint2D pos0, ArrayPoint2D pos1) { if (pos0.r == pos1.r) { return(VerWalls[pos0.r][pos0.c > pos1.c ? pos1.c : pos0.c]); } else if (pos0.c == pos1.c) { return(HolWalls[pos0.r > pos1.r ? pos1.r : pos0.r][pos0.c]); } else { return(false); } }
public bool RemoveWallBetween(ArrayPoint2D pos0, ArrayPoint2D pos1, Action action) { if (pos0.r == pos1.r) { VerWalls[pos0.r][pos0.c > pos1.c ? pos1.c : pos0.c] = false; } else if (pos0.c == pos1.c) { HolWalls[pos0.r > pos1.r ? pos1.r : pos0.r][pos0.c] = false; } else { return(false); } action(); Thread.Sleep(MainWindow.delayTime); return(true); }
public static bool Open(ref Maze maze) { StreamReader sr; try { sr = new StreamReader("maze.mzl"); } catch (FileNotFoundException) { MessageBoxResult result = MessageBox.Show("maze.mzl 파일이 존재하지 않습니다.", "Wait..."); return(false); } string line; line = sr.ReadLine().Trim(); string[] sizeString = line.Split(' '); ArrayPoint2D size = new ArrayPoint2D(int.Parse(sizeString[0]), int.Parse(sizeString[1])); maze = new Maze(size, new ArrayPoint2D(0, 0), new ArrayPoint2D(0, 0)); for (int i = 0; i < size.r - 1; i++) { int[] read = Array.ConvertAll(sr.ReadLine().Trim().Split(' '), int.Parse); for (int j = 0; j < size.c; j++) { maze.HolWalls[i][j] = read[j] == 1 ? true : false; } } for (int i = 0; i < size.r; i++) { int[] read = Array.ConvertAll(sr.ReadLine().Trim().Split(' '), int.Parse); for (int j = 0; j < size.c - 1; j++) { maze.VerWalls[i][j] = read[j] == 1 ? true : false; } } return(true); }
// position을 기준으로 이웃한 셀 위치를 List에 저장합니다. // List<> list: 저장할 List // ArrayPoint2D position: 기준 위치 // ArrayPoint2D mazeSize: 미로 크기 static void AddNeighbor(List <ArrayPoint2D> list, ArrayPoint2D position, ArrayPoint2D mazeSize) { if (position.r != 0) { list.Add(new ArrayPoint2D(position.r - 1, position.c)); } if (position.c != 0) { list.Add(new ArrayPoint2D(position.r, position.c - 1)); } if (position.r != mazeSize.r - 1) { list.Add(new ArrayPoint2D(position.r + 1, position.c)); } if (position.c != mazeSize.c - 1) { list.Add(new ArrayPoint2D(position.r, position.c + 1)); } }
public WallWithDirection(bool isVerWall, ArrayPoint2D wall) { IsVerWall = isVerWall; Wall = wall; }
// HuntAndKill 방식의 미로 생성 // maze: 완전 미로를 생성할 변수 // action: 벽을 제거할 때마다 실행되기를 원하는 함수 public static void HuntAndKill(Maze maze, Action action) { #region INITIALIZE ArrayPoint2D size = maze.GetSize(); List <List <bool> > hasVisited = new List <List <bool> >(); for (int i = 0; i < size.r; i++) { hasVisited.Add(new List <bool>(size.c)); for (int j = 0; j < size.c; j++) { hasVisited[i].Add(false); } } #endregion using (FileStream fs = new FileStream("test.log", FileMode.Create)) { StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); #region ALGORITHM // CODE STARTS HERE // ArrayPoint2D current = new ArrayPoint2D(random.Next(size.r), random.Next(size.c)); hasVisited[current.r][current.c] = true; sw.WriteLine("Hunt-And-Kill 생성입니다. 현재 위치는 " + current + " 입니다."); // LOG while (!HasVisitedAll(hasVisited)) { List <ArrayPoint2D> avail = new List <ArrayPoint2D>(); AddNeighbor(avail, current, size); avail.RemoveAll(_ => hasVisited[_.r][_.c]); while (avail.Count() != 0) { ArrayPoint2D target = avail[random.Next(avail.Count())]; maze.RemoveWallBetween(current, target, action); sw.WriteLine("미로를 생성 중입니다. " + current + " 와 " + target + " 사이 벽이 제거되었습니다."); hasVisited[target.r][target.c] = true; current = target; avail.Clear(); AddNeighbor(avail, current, size); avail.RemoveAll(_ => hasVisited[_.r][_.c]); } bool restart = false; // 이중 반복문 탈출을 위해 ArrayPoint2D loop; for (int i = 0; i < size.r; i++) { for (int j = 0; j < size.c; j++) { loop = new ArrayPoint2D(i, j); if (!hasVisited[loop.r][loop.c]) { List <ArrayPoint2D> related = new List <ArrayPoint2D>(); AddNeighbor(related, loop, size); related.RemoveAll(_ => !hasVisited[_.r][_.c]); if (related.Count == 0) { continue; } ArrayPoint2D target = related[random.Next(related.Count())]; maze.RemoveWallBetween(loop, target, action); sw.WriteLine("새로운 지역을 탐색합니다. " + loop + ", " + target + " 사이 벽이 제거되었습니다."); hasVisited[loop.r][loop.c] = true; current = loop; restart = true; sw.WriteLine("새로운 현재 위치는 " + current + " 입니다."); // LOG break; } } if (restart) { break; } } } // CODE ENDS HERE // #endregion sw.Flush(); } }
// Kruskal 방식의 미로 생성 // maze: 완전 미로를 생성할 변수 // action: 벽을 제거할 때마다 실행되기를 원하는 함수 public static void Kruskal(Maze maze, Action action) { #region INITIALIZE ArrayPoint2D size = maze.GetSize(); DisjointSet <ArrayPoint2D> disjointSet = new DisjointSet <ArrayPoint2D>(); List <WallWithDirection> WallList = new List <WallWithDirection>(); List <List <int> > CellIndex = new List <List <int> >(); int removedCount = 0; int removedCountTarget = (size.r * size.c) - 1; for (int i = 0; i < size.r - 1; i++) { for (int j = 0; j < size.c; j++) { WallList.Add(new WallWithDirection(false, new ArrayPoint2D(i, j))); } } for (int i = 0; i < size.r; i++) { for (int j = 0; j < size.c - 1; j++) { WallList.Add(new WallWithDirection(true, new ArrayPoint2D(i, j))); } } for (int i = 0; i < size.r; i++) { CellIndex.Add(new List <int>()); for (int j = 0; j < size.c; j++) { CellIndex[i].Add(-1); } } #endregion using (FileStream fs = new FileStream("test.log", FileMode.Create)) { StreamWriter sw = new StreamWriter(fs, Encoding.UTF8); #region ALGORITHM // CODE STARTS HERE // //WallList.Count != 0 || while (removedCount != removedCountTarget) { sw.WriteLine("Recursive Backtracker 생성입니다."); // LOG int rand = random.Next(WallList.Count); WallWithDirection target = WallList[rand]; ArrayPoint2D ULPos = target.Wall; ArrayPoint2D DRPos = target.Wall + (target.IsVerWall ? new ArrayPoint2D(0, 1) : new ArrayPoint2D(1, 0)); sw.WriteLine("현재 대상 벽은 " + ULPos + " 와 " + DRPos + " 사이의 벽입니다."); // LOG ArrayPoint2D[] targetCells = { ULPos, DRPos }; foreach (var item in targetCells) { if (CellIndex[item.r][item.c] == -1) { CellIndex[item.r][item.c] = disjointSet.GetTreeCount(); disjointSet.MakeSet(item); sw.WriteLine("Disjoint Set에 " + item + "을 추가합니다."); // LOG } } if (disjointSet.Find(CellIndex[ULPos.r][ULPos.c]) != disjointSet.Find(CellIndex[DRPos.r][DRPos.c])) { disjointSet.Union(CellIndex[ULPos.r][ULPos.c], CellIndex[DRPos.r][DRPos.c]); maze.RemoveWallBetween(ULPos, DRPos, action); sw.WriteLine(ULPos + " 와 " + DRPos + " 사이 벽이 제거되었습니다."); // LOG removedCount++; } WallList.RemoveAt(rand); } // CODE ENDS HERE // #endregion sw.Flush(); } }
public bool IsValidPosition(ArrayPoint2D size) { return(r > -1 && c > -1 && r < size.r && c < size.c); }
public static void BFS(Maze maze, Action action) { #region INITIALIZE ArrayPoint2D size = maze.GetSize(); Queue <PathNode> que = new Queue <PathNode>(); List <List <bool> > hasVisited = new List <List <bool> >(); ArrayPoint2D[] dirs = { new ArrayPoint2D(-1, 0), new ArrayPoint2D(1, 0), new ArrayPoint2D(0, -1), new ArrayPoint2D(0, 1) }; PathNode answer = new PathNode(); for (int i = 0; i < size.r; i++) { hasVisited.Add(new List <bool>(size.c)); for (int j = 0; j < size.c; j++) { hasVisited[i].Add(false); } } #endregion que.Enqueue(new PathNode(maze.StartPoint)); hasVisited[maze.StartPoint.r][maze.StartPoint.c] = true; while (que.Count != 0) { PathNode target = que.Dequeue(); maze.Cells[target.point.r][target.point.c] = 1; if (target.point == maze.EndPoint) { answer = target; break; } foreach (var item in dirs) { ArrayPoint2D enqueuePos = target.point + item; if (!enqueuePos.IsValidPosition(size)) { continue; } if (maze.HasWallBetween(target.point, enqueuePos)) { continue; } if (hasVisited[enqueuePos.r][enqueuePos.c]) { continue; } que.Enqueue(new PathNode(enqueuePos, target)); hasVisited[enqueuePos.r][enqueuePos.c] = true; } action(); Thread.Sleep(MainWindow.delayTime); } foreach (var item in answer.path) { maze.Cells[item.r][item.c] = 2; action(); Thread.Sleep(MainWindow.delayTime); } }
private void MenuGenerateButton(object sender, RoutedEventArgs e) { int inputRow = 0, inputCol = 0; #region EXCEPTION try { delayTime = int.Parse(DelayTimeTextBox.Text); if (delayTime < 0) { throw new InvaildDelayTimeException("지연 시간은 0 이상이여야 합니다."); } } catch (FormatException) { MessageBoxResult result = MessageBox.Show("지연 시간 입력란에는 숫자만 입력할 수 있습니다.", "Wait..."); return; } catch (InvaildDelayTimeException) { MessageBoxResult result = MessageBox.Show("지연 시간은 0 이상이여야 합니다.", "Wait..."); return; } try { inputRow = int.Parse(RowInputTextBox.Text); inputCol = int.Parse(ColInputTextBox.Text); if (inputRow < 1 || inputCol < 1) { throw new InvaildMazeSizeException("미로의 행과 열의 크기는 2 이상이여야 합니다."); } } catch (FormatException) { MessageBoxResult result = MessageBox.Show("미로의 크기 입력란에는 숫자만 입력할 수 있습니다.", "Wait..."); return; } catch (InvaildMazeSizeException) { MessageBoxResult result = MessageBox.Show("미로의 행과 열의 크기는 2 이상이여야 합니다.", "Wait..."); return; } #endregion ArrayPoint2D size = new ArrayPoint2D(inputRow, inputCol); mainMaze = new Maze(size, new ArrayPoint2D(0, 0), new ArrayPoint2D(9, 10)); Prepare(); switch (GenerateAlgComboBox.SelectedIndex) { case 0: MazeGenerator.RecursiveBacktracker(mainMaze, ShowMaze); mainMaze.isMaze = true; break; case 1: MazeGenerator.Kruskal(mainMaze, ShowMaze); mainMaze.isMaze = true; break; case 2: MazeGenerator.HuntAndKill(mainMaze, ShowMaze); mainMaze.isMaze = true; break; default: mainMaze.isMaze = false; break; } ShowMaze(); }