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
}
Parse updates the vault's textual content. Each line in the string should
have the same length (leading and trailing spaces are removed
automatically). Only characters defined by SetRunes are allowed in the
textual content.
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
}