Skip to content

Commit bfc7334

Browse files
committed
First commit
1 parent c0f4acf commit bfc7334

File tree

10 files changed

+722
-0
lines changed

10 files changed

+722
-0
lines changed

README.md

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
## Generate 3D cellular automata
2+
3+
### Looking for new rules
4+
5+
Generate 16 random 5 states cellular automata after (64 iterations).
6+
7+
```
8+
$ mkdir -p explore
9+
$ bin/search -output explore -state 5 -fillseed 0.4 -iteration 64 -nb 16
10+
```
11+
12+
### Growing an interesting rule
13+
14+
#### Output each iterations (for animation)
15+
16+
```
17+
$ mkdir -p explore
18+
$ bin/grow -output explore -input output-XXXXXXX.obj -iteration 128 -worldsize 300
19+
```
20+
21+
#### Output only last iteration
22+
23+
```
24+
$ mkdir -p explore
25+
$ bin/grow -output explore -input output-XXXXXXX.obj -lastonly -iteration 128 -worldsize 300
26+
```
27+
28+
### Demos
29+
30+
![demo](https://i.gyazo.com/48f9e3d10fca5472f4971fc672896717.png)
31+
32+
![demo](https://i.gyazo.com/99056ee4d79cba57a6073287c6187c8a.png)
33+
34+
![demo](https://media.giphy.com/media/L3L6fgN9HO6mj5AokY/giphy.gif)
35+
36+
![demo](https://media.giphy.com/media/YOBFy0WPAfrueMDYvz/giphy.gif)
37+
38+
More examples :
39+
- https://www.youtube.com/watch?v=W0iA-pO_XaU
40+
- https://www.behance.net/gallery/90505477/Cellular-Automata-3D

bin/.keep

Whitespace-only changes.

cmd/grow/main.go

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"math/rand"
7+
"path/filepath"
8+
"time"
9+
10+
"github.com/tterrasson/crystal/pkg/obj"
11+
"github.com/tterrasson/crystal/pkg/rule"
12+
"github.com/tterrasson/crystal/pkg/world"
13+
)
14+
15+
func main() {
16+
fmt.Println("Starting...")
17+
iterationArg := flag.Int("iteration", 128, "Number of iteration")
18+
randomSeedArg := flag.Bool("randseed", false, "Use a random seed")
19+
inputArg := flag.String("input", "output.obj", "Input OBJ file")
20+
fillSeedArg := flag.Float64("fillseed", 0.25, "Chance to fill seed")
21+
outputLastOnlyArg := flag.Bool("lastonly", false, "Output only last iteration")
22+
worldSizeArg := flag.Int("worldsize", 100, "World size (SxSxS)")
23+
outputPathArg := flag.String("output", "explore", "Output path")
24+
25+
flag.Parse()
26+
27+
rand.Seed(time.Now().Unix())
28+
29+
reader := obj.ObjReader{*inputArg}
30+
states := reader.ExtractRuleSet(5)
31+
ruleset := rule.RuleSet{states}
32+
world := world.NewWorld(*worldSizeArg, &ruleset)
33+
34+
if *randomSeedArg {
35+
world.RandomSeed(float32(*fillSeedArg), 5)
36+
} else {
37+
seed := reader.ExtractSeed(*worldSizeArg, 200)
38+
world.SetSeed(seed)
39+
}
40+
41+
for i := 0; i < *iterationArg; i++ {
42+
if !*outputLastOnlyArg {
43+
fname := fmt.Sprintf("output-%06d.obj", i)
44+
fpath := filepath.Join(*outputPathArg, fname)
45+
fmt.Printf("Exporting to %s ...\n", fpath)
46+
world.ExportToFile(fpath)
47+
}
48+
49+
fmt.Printf("Step %d ...\n", i)
50+
world.Iterate()
51+
}
52+
53+
if *outputLastOnlyArg {
54+
fname := fmt.Sprintf("explore/output-%06d.obj", *iterationArg)
55+
fmt.Printf("Exporting to %s ...\n", fname)
56+
world.ExportToFile(fname)
57+
}
58+
}

cmd/search/main.go

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"math/rand"
7+
"path/filepath"
8+
"time"
9+
10+
"github.com/tterrasson/crystal/pkg/rule"
11+
"github.com/tterrasson/crystal/pkg/world"
12+
)
13+
14+
func main() {
15+
fmt.Println("Starting...")
16+
iterationArg := flag.Int("iteration", 64, "Number of iteration")
17+
stateArg := flag.Int("state", 3, "Number of states")
18+
fillSeedArg := flag.Float64("fillseed", 0.25, "Chance to fill seed")
19+
fillRuleArg := flag.Float64("fillrule", 0.25, "Chance to fill rule")
20+
worldSizeArg := flag.Int("worldsize", 100, "World size (SxSxS)")
21+
outputPathArg := flag.String("output", "explore", "Output path")
22+
nbArg := flag.Int("nb", 8, "Number of random CA to generate")
23+
24+
flag.Parse()
25+
26+
rand.Seed(time.Now().Unix())
27+
28+
ruleset := rule.NewRandomRuleSet(float32(*fillRuleArg), *stateArg)
29+
world := world.NewWorld(*worldSizeArg, ruleset)
30+
world.RandomSeed(float32(*fillSeedArg), *stateArg)
31+
32+
for n := 0; n < *nbArg; n++ {
33+
for i := 0; i < *iterationArg; i++ {
34+
fmt.Printf("Step %d ...\n", i)
35+
world.Iterate()
36+
}
37+
38+
fname := fmt.Sprintf("output-%f-%f.obj", rand.Float64())
39+
fpath := filepath.Join(*outputPathArg, fname)
40+
fmt.Printf("Exporting to %s ...\n", fpath)
41+
world.ExportToFile(fpath)
42+
}
43+
}

explore/colors.mtl

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
newmtl state1
2+
Ka 0.545 0.156 0.121
3+
Kd 0.545 0.156 0.121
4+
Ns 10.000
5+
illum 2
6+
newmtl state2
7+
Ka 0.835 0.439 0.188
8+
Kd 0.835 0.439 0.188
9+
Ns 10.000
10+
illum 2
11+
newmtl state3
12+
Ka 0.517 0.643 0.352
13+
Kd 0.517 0.643 0.352
14+
Ns 10.000
15+
illum 2
16+
newmtl state4
17+
Ka 0.949 0.615 0.294
18+
Kd 0.949 0.615 0.294
19+
Ns 10.000
20+
illum 2
21+
newmtl state5
22+
Ka 0.249 0.215 0.894
23+
Kd 0.249 0.215 0.894
24+
Ns 10.000
25+
illum 2

pkg/obj/objreader.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package obj
2+
3+
import (
4+
"bufio"
5+
"encoding/json"
6+
"io"
7+
"log"
8+
"os"
9+
"strconv"
10+
"strings"
11+
)
12+
13+
type ObjReader struct {
14+
Filename string
15+
}
16+
17+
func (reader *ObjReader) initSeed(size int) [][][]int {
18+
seed := make([][][]int, size)
19+
20+
for x := range seed {
21+
seed[x] = make([][]int, size)
22+
for y := range seed[x] {
23+
seed[x][y] = make([]int, size)
24+
}
25+
}
26+
27+
return seed
28+
}
29+
30+
func (reader *ObjReader) initRuleSet(maxStates int) [][][][]int {
31+
states := make([][][][]int, maxStates+1)
32+
33+
for state := 0; state <= maxStates; state++ {
34+
states[state] = make([][][]int, 7)
35+
36+
for face := 0; face < 7; face++ {
37+
states[state][face] = make([][]int, 13)
38+
39+
for edge := 0; edge < 13; edge++ {
40+
states[state][face][edge] = make([]int, 9)
41+
}
42+
}
43+
}
44+
45+
return states
46+
}
47+
48+
func (reader *ObjReader) ExtractSeed(size int, offset int) [][][]int {
49+
file, err := os.Open(reader.Filename)
50+
if err != nil {
51+
log.Panicf("failed reading file: %s", err)
52+
}
53+
defer file.Close()
54+
55+
out := reader.initSeed(size)
56+
rd := bufio.NewReader(file)
57+
58+
for {
59+
line, err := rd.ReadString('\n')
60+
61+
if strings.HasPrefix(line, "# Seed : ") {
62+
jseed := strings.Replace(line, "# Seed : ", "", 1)
63+
var seed map[string]interface{}
64+
json.Unmarshal([]byte(jseed), &seed)
65+
66+
for k, v := range seed {
67+
idx := strings.Split(k, ",")
68+
x, _ := strconv.Atoi(idx[0])
69+
y, _ := strconv.Atoi(idx[1])
70+
z, _ := strconv.Atoi(idx[2])
71+
out[x+offset][y+offset][z+offset] = int(v.(float64))
72+
}
73+
74+
return out
75+
}
76+
77+
if err == io.EOF {
78+
break
79+
}
80+
}
81+
82+
return nil
83+
}
84+
85+
func (reader ObjReader) ExtractRuleSet(maxStates int) [][][][]int {
86+
file, err := os.Open(reader.Filename)
87+
if err != nil {
88+
log.Panicf("failed reading file: %s", err)
89+
}
90+
defer file.Close()
91+
92+
out := reader.initRuleSet(maxStates)
93+
rd := bufio.NewReader(file)
94+
95+
for {
96+
line, err := rd.ReadString('\n')
97+
98+
if strings.HasPrefix(line, "# Rule : ") {
99+
jrule := strings.Replace(line, "# Rule : ", "", 1)
100+
var rule map[string]interface{}
101+
json.Unmarshal([]byte(jrule), &rule)
102+
stateSet := rule["states"].([]interface{})
103+
104+
for state := 0; state <= maxStates; state++ {
105+
faceSet := stateSet[state].([]interface{})
106+
107+
for face := 0; face < 7; face++ {
108+
edgeSet := faceSet[face].([]interface{})
109+
110+
for edge := 0; edge < 13; edge++ {
111+
cornerSet := edgeSet[edge].([]interface{})
112+
113+
for corner := 0; corner < 9; corner++ {
114+
out[state][face][edge][corner] = int(cornerSet[corner].(float64))
115+
}
116+
}
117+
}
118+
}
119+
120+
return out
121+
}
122+
123+
if err == io.EOF {
124+
break
125+
}
126+
}
127+
128+
return nil
129+
}

0 commit comments

Comments
 (0)