Created
March 7, 2020 11:45
-
-
Save peterhellberg/3c5b30465a258f6b688a8f11955b12ba to your computer and use it in GitHub Desktop.
Sprator using gfx inspired by https://github.com/yurkth/sprator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"flag" | |
"image" | |
"image/color" | |
"time" | |
"github.com/peterhellberg/gfx" | |
) | |
// Sprator algorithm as described at | |
// https://github.com/yurkth/sprator | |
// | |
// 1. Generate 4x8 white noise. | |
// 2. Change the state according to the following rules. | |
// - Any live cell with two or three neighbors survives. | |
// - Any dead cell with one or less live neighbors becomes a live cell. | |
// - All other live cells die in the next generation. Similarly, all other dead cells stay dead. | |
// 3. Repeat steps 2 several times. | |
// 4. Flip and add a outline, complete! | |
func main() { | |
var seed int64 | |
var variant string | |
flag.Int64Var(&seed, "seed", time.Now().Unix(), "Seed value to use for the state") | |
flag.StringVar(&variant, "variant", "random", "Variant of the initial state [random, simplex]") | |
flag.Parse() | |
var state State | |
switch variant { | |
case "simplex": | |
state = SimplexState(seed) | |
default: | |
variant = "random" | |
state = RandomState(seed) | |
} | |
for i := 0; i < 2; i++ { | |
state = state.Next() | |
} | |
dst := gfx.NewImage(10, 10) | |
bc := gfx.BlockColors[int(seed)%len(gfx.BlockColors)] | |
t := gfx.ColorTransparent | |
for x := 1; x < 5; x++ { | |
for y := 1; y < 9; y++ { | |
if state.At(x-1, y-1) { | |
dst.Set(x, y, bc.Light) | |
dst.Set(5+5-x-1, y, bc.Light) | |
} else { | |
dst.Set(x, y, t) | |
dst.Set(5+5-x-1, y, t) | |
} | |
} | |
} | |
out := gfx.NewImage(10, 10) | |
for x := 0; x < 10; x++ { | |
for y := 0; y < 10; y++ { | |
if c := countNeighborhood(dst, x, y, bc.Light); c > 0 { | |
out.Set(x, y, bc.Dark) | |
} | |
} | |
} | |
gfx.DrawOver(out, out.Bounds(), dst, gfx.ZP) | |
src := gfx.NewScaledImage(out, 10) | |
fn := gfx.Sprintf("/tmp/gfx-sprator-%06d-%s.png", seed, variant) | |
gfx.Log("Saving generated sprator to %s", fn) | |
gfx.SavePNG(fn, src) | |
} | |
// ExampleState returns the starting state in the | |
// original README on https://github.com/yurkth/sprator | |
// | |
// ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ | |
// ░░▒▒▒▒▓▓▒▒ ░░▓▓▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒ | |
// ░░▓▓▒▒▒▒▒▒ ░░▒▒▒▒▓▓▓▓ ░░▓▓▒▒▒▒▓▓ | |
// ░░▒▒▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓ | |
// ░░▒▒▓▓▓▓▒▒ ➤ ░░▒▒▓▓▒▒▒▒ ➤ ░░▓▓▒▒▓▓▓▓ | |
// ░░▓▓▒▒▒▒▒▒ ➤ ░░▒▒▒▒▒▒▓▓ ➤ ░░▓▓▓▓▓▓▒▒ | |
// ░░▓▓▒▒▓▓▒▒ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓ | |
// ░░▒▒▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓ | |
// ░░▒▒▒▒▒▒▓▓ ░░▓▓▓▓▓▓▒▒ ░░▒▒▓▓▒▒▓▓ | |
// ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░ | |
// | |
func ExampleState() State { | |
return State(0x8a516a14) | |
} | |
func RandomState(seed int64) (state State) { | |
gfx.RandSeed(seed) | |
for x := 0; x < 4; x++ { | |
for y := 0; y < 8; y++ { | |
state.Set(x, y, gfx.RandIntn(2) == 0) | |
} | |
} | |
return | |
} | |
func SimplexState(seed int64) (state State) { | |
simplex := gfx.NewSimplexNoise(seed) | |
for x := 0; x < 4; x++ { | |
for y := 0; y < 8; y++ { | |
state.Set(x, y, simplex.Noise2D(float64(x), float64(y)) > 0) | |
} | |
} | |
return | |
} | |
type State uint32 | |
func (s State) At(x, y int) bool { | |
return hasBit(s, (y*4)+x) | |
} | |
func (s *State) Set(x, y int, b bool) { | |
if b { | |
*s = setBit(*s, (y*4)+x) | |
} else { | |
*s = clearBit(*s, (y*4)+x) | |
} | |
} | |
func (s State) Next() State { | |
var next State | |
for x := 0; x < 4; x++ { | |
for y := 0; y < 8; y++ { | |
n := s.Neighborhood(x, y) | |
if s.At(x, y) { | |
next.Set(x, y, n == 2 || n == 3) | |
} else { | |
next.Set(x, y, n <= 1) | |
} | |
} | |
} | |
return next | |
} | |
func (s State) Neighborhood(x, y int) int { | |
var n int | |
if y >= 1 && s.At(x, y-1) { | |
n++ | |
} | |
if y <= 6 && s.At(x, y+1) { | |
n++ | |
} | |
if x >= 1 && s.At(x-1, y) { | |
n++ | |
} | |
if x <= 2 && s.At(x+1, y) { | |
n++ | |
} | |
return n | |
} | |
func setBit(s State, pos int) State { | |
s |= (1 << pos) | |
return s | |
} | |
func clearBit(s State, pos int) State { | |
return s &^ (1 << pos) | |
} | |
func hasBit(s State, pos int) bool { | |
return (s & (1 << pos)) > 0 | |
} | |
func countNeighborhood(src image.Image, x, y int, c color.Color) int { | |
var n int | |
e := func(c1, c2 color.Color) bool { | |
c1r, c1g, c1b, c1a := c1.RGBA() | |
c2r, c2g, c2b, c2a := c2.RGBA() | |
return c1r == c2r && c1g == c2g && c1b == c2b && c1a == c2a | |
} | |
if e(src.At(x, y-1), c) { | |
n++ | |
} | |
if e(src.At(x, y+1), c) { | |
n++ | |
} | |
if e(src.At(x-1, y), c) { | |
n++ | |
} | |
if e(src.At(x+1, y), c) { | |
n++ | |
} | |
return n | |
} |
Using the seed as the state (first variant of each group of three same colored sprators)
package main
import (
"flag"
"image"
"image/color"
"time"
"github.com/peterhellberg/gfx"
)
// Sprator algorithm as described at
// https://github.com/yurkth/sprator
//
// 1. Generate 4x8 white noise.
// 2. Change the state according to the following rules.
// - Any live cell with two or three neighbors survives.
// - Any dead cell with one or less live neighbors becomes a live cell.
// - All other live cells die in the next generation. Similarly, all other dead cells stay dead.
// 3. Repeat steps 2 several times.
// 4. Flip and add a outline, complete!
func main() {
var seed int64
var variant string
flag.Int64Var(&seed, "seed", time.Now().Unix(), "Seed value to use for the state")
flag.StringVar(&variant, "variant", "random", "Variant of the initial state [random, simplex]")
flag.Parse()
var state State
switch variant {
case "binary":
state = State(uint32(seed))
case "simplex":
state = SimplexState(seed)
default:
variant = "random"
state = RandomState(seed)
}
for i := 0; i < 2; i++ {
state = state.Next()
}
dst := gfx.NewImage(10, 10)
bc := gfx.BlockColors[int(seed)%len(gfx.BlockColors)]
t := gfx.ColorTransparent
for x := 1; x < 5; x++ {
for y := 1; y < 9; y++ {
if state.At(x-1, y-1) {
dst.Set(x, y, bc.Light)
dst.Set(5+5-x-1, y, bc.Light)
} else {
dst.Set(x, y, t)
dst.Set(5+5-x-1, y, t)
}
}
}
out := gfx.NewImage(10, 10)
for x := 0; x < 10; x++ {
for y := 0; y < 10; y++ {
if c := countNeighborhood(dst, x, y, bc.Light); c > 0 {
out.Set(x, y, bc.Dark)
}
}
}
gfx.DrawOver(out, out.Bounds(), dst, gfx.ZP)
src := gfx.NewScaledImage(out, 10)
pad := gfx.NewImage(160, 160)
gfx.DrawOver(pad, pad.Bounds(), src, gfx.Pt(-30, -30))
fn := gfx.Sprintf("/tmp/gfx-sprator-%06d-%s.png", seed, variant)
gfx.Log("Saving generated sprator to %s", fn)
gfx.SavePNG(fn, pad)
}
// ExampleState returns the starting state in the
// original README on https://github.com/yurkth/sprator
//
// ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░
// ░░▒▒▒▒▓▓▒▒ ░░▓▓▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒
// ░░▓▓▒▒▒▒▒▒ ░░▒▒▒▒▓▓▓▓ ░░▓▓▒▒▒▒▓▓
// ░░▒▒▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓
// ░░▒▒▓▓▓▓▒▒ ➤ ░░▒▒▓▓▒▒▒▒ ➤ ░░▓▓▒▒▓▓▓▓
// ░░▓▓▒▒▒▒▒▒ ➤ ░░▒▒▒▒▒▒▓▓ ➤ ░░▓▓▓▓▓▓▒▒
// ░░▓▓▒▒▓▓▒▒ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓
// ░░▒▒▓▓▒▒▓▓ ░░▒▒▒▒▒▒▒▒ ░░▓▓▓▓▓▓▓▓
// ░░▒▒▒▒▒▒▓▓ ░░▓▓▓▓▓▓▒▒ ░░▒▒▓▓▒▒▓▓
// ░░░░░░░░░░ ░░░░░░░░░░ ░░░░░░░░░░
//
func ExampleState() State {
return State(0x8a516a14)
}
func RandomState(seed int64) (state State) {
gfx.RandSeed(seed)
for x := 0; x < 4; x++ {
for y := 0; y < 8; y++ {
state.Set(x, y, gfx.RandIntn(2) == 0)
}
}
return
}
func SimplexState(seed int64) (state State) {
simplex := gfx.NewSimplexNoise(seed)
for x := 0; x < 4; x++ {
for y := 0; y < 8; y++ {
state.Set(x, y, simplex.Noise2D(float64(x), float64(y)) > 0)
}
}
return
}
type State uint32
func (s State) At(x, y int) bool {
return hasBit(s, (y*4)+x)
}
func (s *State) Set(x, y int, b bool) {
if b {
*s = setBit(*s, (y*4)+x)
} else {
*s = clearBit(*s, (y*4)+x)
}
}
func (s State) Next() State {
var next State
for x := 0; x < 4; x++ {
for y := 0; y < 8; y++ {
n := s.Neighborhood(x, y)
if s.At(x, y) {
next.Set(x, y, n == 2 || n == 3)
} else {
next.Set(x, y, n <= 1)
}
}
}
return next
}
func (s State) Neighborhood(x, y int) int {
var n int
if y >= 1 && s.At(x, y-1) {
n++
}
if y <= 6 && s.At(x, y+1) {
n++
}
if x >= 1 && s.At(x-1, y) {
n++
}
if x <= 2 && s.At(x+1, y) {
n++
}
return n
}
func setBit(s State, pos int) State {
s |= (1 << pos)
return s
}
func clearBit(s State, pos int) State {
return s &^ (1 << pos)
}
func hasBit(s State, pos int) bool {
return (s & (1 << pos)) > 0
}
func countNeighborhood(src image.Image, x, y int, c color.Color) int {
var n int
e := func(c1, c2 color.Color) bool {
c1r, c1g, c1b, c1a := c1.RGBA()
c2r, c2g, c2b, c2a := c2.RGBA()
return c1r == c2r && c1g == c2g && c1b == c2b && c1a == c2a
}
if e(src.At(x, y-1), c) {
n++
}
if e(src.At(x, y+1), c) {
n++
}
if e(src.At(x-1, y), c) {
n++
}
if e(src.At(x+1, y), c) {
n++
}
return n
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
With some more padding