-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathexample_test.go
149 lines (137 loc) · 5.67 KB
/
example_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package disjoint_test
import (
"fmt"
"github.com/spakin/disjoint/v2"
"math/rand"
)
// Draw a maze. This is my favorite use of disjoint-set forests. The
// algorithm works by repeatedly knocking down walls between two sets
// of rooms that are not part of the same union and merging those
// rooms into a single union. A union represents connected
// components—rooms in the maze that are mutually reachable. A single
// union implies that every room is reachable from every other room.
//
// This is a fairly long example, but note that only half of it relates to
// generating the maze. The other half renders the maze using Unicode
// box-drawing characters.
func Example_maze() {
const width = 50 // Width of maze in rooms (must be > 1)
const height = 10 // Height of maze in rooms (must be > 1)
// A room is identified by its walls and by the other rooms it can reach.
type Room struct {
N bool // North side of room is a wall
S bool // South side of room is a wall
E bool // East side of room is a wall
W bool // West side of room is a wall
Reaches *disjoint.Element // Element in a set of reachable rooms
}
// Initialize the maze data structure.
maze := make([][]Room, height)
for y := range maze {
maze[y] = make([]Room, width)
for x := range maze[y] {
// Start with all walls present and no other rooms reachable.
maze[y][x].N = true
maze[y][x].S = true
maze[y][x].E = true
maze[y][x].W = true
maze[y][x].Reaches = disjoint.NewElement()
}
}
// Repeatedly remove walls until a single connected component remains.
rand.Seed(5552368)
for cc := width * height; cc > 1; {
// Because of symmetry, we need only connect to the right or
// down rather than in all four directions.
x0 := rand.Intn(width)
y0 := rand.Intn(height)
x1 := x0
y1 := y0
dir := rand.Intn(2)
if dir == 0 && x0 < width-1 {
x1++ // Go right.
} else if dir == 1 && y0 < height-1 {
y1++ // Go down.
} else {
continue // Can't go in the desired direction
}
if maze[y0][x0].Reaches.Find() == maze[y1][x1].Reaches.Find() {
continue // Already connected
}
// Tear down the wall.
if dir == 0 {
// Right/left
maze[y0][x0].E = false
maze[y1][x1].W = false
} else {
// Down/up
maze[y0][x0].S = false
maze[y1][x1].N = false
}
maze[y0][x0].Reaches.Union(maze[y1][x1].Reaches)
cc--
}
// Punch holes for an entry (UL) and exit (LR).
maze[0][0].W = false
maze[height-1][width-1].E = false
// Convert the grid of rooms to a grid of walls. Walls are staggered
// spatially by half a room vertically and horizontally.
type Walls struct {
N bool // Northbound wall from cell center
S bool // Southbound wall from cell center
E bool // Eastbound wall from cell center
W bool // Westbound wall from cell center
}
walls := make([][]Walls, height+1)
for y := range walls {
walls[y] = make([]Walls, width+1)
}
for y := 0; y < height+1; y++ {
for x := 0; x < width+1; x++ {
if y < height {
if x < width {
walls[y][x].E = maze[y][x].N
walls[y][x].S = maze[y][x].W
}
if x > 0 {
walls[y][x].W = maze[y][x-1].N
walls[y][x].S = maze[y][x-1].E
}
}
if y > 0 {
if x > 0 {
walls[y][x].W = maze[y-1][x-1].S
walls[y][x].N = maze[y-1][x-1].E
}
if x < width {
walls[y][x].E = maze[y-1][x].S
walls[y][x].N = maze[y-1][x].W
}
}
}
}
// Define a map from wall types to Unicode box-drawing characters.
wallsToGlyph := []rune{' ', '╴', '╶', '─', '╷', '┐', '┌', '┬', '╵', '┘', '└', '┴', '│', '┤', '├', '┼'}
// Define a map from Booleans to integers.
boolToInt := map[bool]int{false: 0, true: 1}
// Output the glyph corresponding to each cell of walls.
for _, row := range walls {
for _, cell := range row {
val := boolToInt[cell.N]<<3 | boolToInt[cell.S]<<2 | boolToInt[cell.E]<<1 | boolToInt[cell.W]
fmt.Printf("%c", wallsToGlyph[val])
}
fmt.Println("")
}
// Output:
// ╶───┬─────┬┬──┬────┬────┬─┬──┬┬─┬───┬─┬─┬───┬───┬─┐
// ╷╷╶┐├┐┌┐╷╶┤│╶─┤╷╶┬─┘╶┐╷╷│╶┴─┐╵╵╶┴─╴╷│╷└╴├┬─╴│╷╶┬┴╴│
// ├┴╴├┤╵│└┘┌┘╵╶┬┼┘╶┴┬╴╷│├┤├─╴┌┴┐╶┐╶──┤└┤╶┬┘├╴┌┴┤╷└╴┌┤
// ├╴╷│└┬┼┐┌┤╷╶┬┘│╷╷╷╵╶┼┴┘╵├╴╶┼╴╵╷├─╴┌┘╶┤╷│┌┘╷│┌┤│╶┐╵│
// │┌┘╵╶┤╵╵│││┌┴╴│││└┐╷├╴╷╶┴╴┌┼─╴│└┐╶┼─┐││╵├╴│╵╵└┘╶┼┬┤
// ├┼┬╴╷└─┐╵│├┘╷╶┴┴┤╷││├─┴╴┌┐│╵╶┬┴┐├─┤┌┘└┘╶┤┌┘╶┐┌─╴╵╵│
// │╵│╶┴┐╷╵╷╵╵┌┴┐╶┐╵└┼┼┤┌┐╷│││╶─┼╴└┘┌┘├┬┐┌┐└┘╶┐├┘╶┬┐╶┤
// ├╴├┐┌┴┤╷├╴╶┴┐╵╶┤╷┌┘╵├┘╵├┤│╵╷┌┼┐┌┐├┐╵│└┤╵╷┌╴└┴┐╶┤│╷│
// │╷╵╵│╶┘├┼╴┌─┴──┼┼┤╷╷├╴╶┘││╶┤╵│╵╵││╵╶┘╷│╷├┴┐╶┬┼╴│└┴┤
// ├┴╴┌┴╴╷│╵╷╵╷╷┌╴│╵╵│└┘╶┐╶┘│╶┴─┘╷╷├┴╴╷╶┤╵└┤╶┴╴╵└┐╵╶┐╵
// └──┴──┴┴─┴─┴┴┴─┴──┴───┴──┴────┴┴┴──┴─┴──┴─────┴──┴╴
}