func (v *Vault) Rotate(n int) {
	n %= 4
	if n < 0 {
		n = 4 + n
	}
	switch n {
	case 1:
		v.rotate90()
	case 2:
		v.rotate180()
	case 3:
		v.rotate180()
		v.rotate90()
	}
}
Rotate rotates the vault content n times by 90 degrees counter-clockwise (or
clockwise for negative n values).  The result's size has dimensions
exchanged for odd n.
package rl
import (
	"fmt"
	"math/rand"
	"strings"
	"github.com/anaseto/gruid"
	"github.com/anaseto/gruid/paths"
)
type MapGen struct {
	Rand *rand.Rand
	Grid Grid
}
func (mg MapGen) WithGrid(gd Grid) MapGen {
	mg.Grid = gd
	return mg
}
func (mg MapGen) rand(n int) int {
	if n <= 0 {
		return 0
	}
	x := mg.Rand.Intn(n)
	return x
}
type RandomWalker interface {
	Neighbor(gruid.Point) gruid.Point
}
func (mg MapGen) RandomWalkCave(walker RandomWalker, c Cell, fillp float64, walks int) int {
	if fillp > 0.9 {
		fillp = 0.9
	}
	if fillp < 0.01 {
		fillp = 0.01
	}
	max := mg.Grid.Size()
	maxdigs := int(float64(max.X*max.Y) * fillp)
	digged := mg.Grid.Count(c)
	digs := digged
	wlkmax := maxdigs - digged
	if walks > 0 {
		wlkmax /= walks
	}
	for digs < maxdigs {
		p := gruid.Point{mg.rand(max.X), mg.rand(max.Y)}
		sc := mg.Grid.At(p)
		if c == sc {
			continue
		}
		mg.Grid.Set(p, c)
		digs++
		wlkdigs := 1
		outDigs := 0
		lastInRange := p
		for digs < maxdigs && wlkdigs <= wlkmax {
			q := walker.Neighbor(p)
			if !mg.Grid.Contains(p) && mg.Grid.Contains(q) && mg.Grid.AtU(q) != c {
				p = lastInRange
				continue
			}
			p = q
			if mg.Grid.Contains(p) {
				if mg.Grid.AtU(p) != c {
					mg.Grid.Set(p, c)
					digs++
					wlkdigs++
				}
				lastInRange = p
			} else {
				outDigs++
			}
			if outDigs > wlkmax || outDigs > 150 {
				outDigs = 0
				p = lastInRange
			}
		}
	}
	return digs - digged
}
type CellularAutomataRule struct {
	WCutoff1        int   
	WCutoff2        int   
	WallsOutOfRange bool  
	Reps            int   
}
func (mg MapGen) CellularAutomataCave(wall, ground Cell, winit float64, rules []CellularAutomataRule) int {
	if winit > 0.9 {
		winit = 0.9
	}
	if winit < 0.1 {
		winit = 0.1
	}
	max := mg.Grid.Size()
	mg.Grid.FillFunc(func() Cell {
		f := mg.Rand.Float64()
		if f < winit {
			return wall
		}
		return ground
	})
	bufgd := NewGrid(max.X, max.Y)
	for _, rule := range rules {
		if rule.WCutoff2 >= 25 {
			mg.applyRuleWithoutW2(wall, ground, bufgd, rule)
		} else if rule.WCutoff1 <= 0 {
			mg.applyRuleWithoutW1(wall, ground, bufgd, rule)
		} else {
			mg.applyRule(wall, ground, bufgd, rule)
		}
	}
	count := mg.Grid.Count(ground)
	return count
}
func (mg MapGen) applyRule(wall, ground Cell, bufgd Grid, rule CellularAutomataRule) {
	for i := 0; i < rule.Reps; i++ {
		bufgd.Map(func(p gruid.Point, c Cell) Cell {
			c1 := mg.countWalls(p, wall, 1, rule.WallsOutOfRange)
			c2 := mg.countWalls(p, wall, 2, rule.WallsOutOfRange)
			if c1 >= rule.WCutoff1 || c2 <= rule.WCutoff2 {
				return wall
			}
			return ground
		})
		mg.Grid.Copy(bufgd)
	}
}
func (mg MapGen) applyRuleWithoutW1(wall, ground Cell, bufgd Grid, rule CellularAutomataRule) {
	for i := 0; i < rule.Reps; i++ {
		bufgd.Map(func(p gruid.Point, c Cell) Cell {
			c2 := mg.countWalls(p, wall, 2, rule.WallsOutOfRange)
			if c2 <= rule.WCutoff2 {
				return wall
			}
			return ground
		})
		mg.Grid.Copy(bufgd)
	}
}
func (mg MapGen) applyRuleWithoutW2(wall, ground Cell, bufgd Grid, rule CellularAutomataRule) {
	for i := 0; i < rule.Reps; i++ {
		bufgd.Map(func(p gruid.Point, c Cell) Cell {
			c1 := mg.countWalls(p, wall, 1, rule.WallsOutOfRange)
			if c1 >= rule.WCutoff1 {
				return wall
			}
			return ground
		})
		mg.Grid.Copy(bufgd)
	}
}
func (mg MapGen) countWalls(p gruid.Point, w Cell, radius int, countOut bool) int {
	count := 0
	rg := gruid.Range{
		gruid.Point{p.X - radius, p.Y - radius},
		gruid.Point{p.X + radius + 1, p.Y + radius + 1},
	}
	if countOut {
		osize := rg.Size()
		rg = rg.Intersect(mg.Grid.Range())
		size := rg.Size()
		count += osize.X*osize.Y - size.X*size.Y
	} else {
		rg = rg.Intersect(mg.Grid.Range())
	}
	gd := mg.Grid.Slice(rg)
	count += gd.Count(w)
	return count
}
func (mg MapGen) KeepCC(pr *paths.PathRange, p gruid.Point, wall Cell) int {
	id := pr.CCMapAt(p)
	if id == -1 {
		mg.Grid.Fill(wall)
		return 0
	}
	count := 0
	it := mg.Grid.Iterator()
	for it.Next() {
		if pr.CCMapAt(it.P()) != id {
			it.SetCell(wall)
		} else {
			count++
		}
	}
	return count
}
type Vault struct {
	content string
	runes   string
	size    gruid.Point
}
func NewVault(s string) (*Vault, error) {
	v := &Vault{}
	err := v.Parse(s)
	return v, err
}
func (v *Vault) Content() string {
	return v.content
}
func (v *Vault) Size() gruid.Point {
	return v.size
}
func (v *Vault) SetRunes(s string) {
	v.runes = s
}
func (v *Vault) Runes() string {
	return v.runes
}
func (v *Vault) Parse(s string) error {
	x, y := 0, 0
	w := -1
	s = strings.TrimSpace(s)
	for _, r := range s {
		if r == '\n' {
			if x > w {
				if w > 0 {
					return fmt.Errorf("vault: inconsistent size:\n%s", s)
				}
				w = x
			}
			x = 0
			y++
			continue
		}
		if v.runes != "" && !strings.ContainsRune(v.runes, r) {
			return fmt.Errorf("vault contains invalid rune “%c” at %v:\n%s", r, gruid.Point{x, y}, s)
		}
		x++
	}
	if x > w {
		if w > 0 {
			return fmt.Errorf("vault: inconsistent size:\n%s", s)
		}
		w = x
	}
	if w > 0 || y > 0 {
		y++  
	}
	v.content = s
	v.size = gruid.Point{x, y}
	return nil
}
func (v *Vault) Iter(fn func(gruid.Point, rune)) {
	x, y := 0, 0
	for _, r := range v.content {
		if r == '\n' {
			x = 0
			y++
			continue
		}
		fn(gruid.Point{x, y}, r)
		x++
	}
}
func (v *Vault) Draw(gd Grid, fn func(rune) Cell) Grid {
	x, y := 0, 0
	for _, r := range v.content {
		if r == '\n' {
			x = 0
			y++
			continue
		}
		gd.Set(gruid.Point{x, y}, fn(r))
		x++
	}
	return gd.Slice(gruid.NewRange(0, 0, v.size.X, v.size.Y))
}
func (v *Vault) Reflect() {
	sb := strings.Builder{}
	sb.Grow(len(v.content))
	line := make([]rune, 0, v.Size().X)
	for _, r := range v.content {
		if r == '\n' {
			for i := len(line) - 1; i >= 0; i-- {
				sb.WriteRune(line[i])
			}
			sb.WriteRune('\n')
			line = line[:0]
			continue
		}
		line = append(line, r)
	}
	for i := len(line) - 1; i >= 0; i-- {
		sb.WriteRune(line[i])
	}
	v.content = sb.String()
}