cellular_automata 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/bin/cell +44 -1
- data/cellular_automata.gemspec +4 -1
- data/lib/cellular_automata/board.rb +38 -38
- data/lib/cellular_automata/cell.rb +12 -69
- data/lib/cellular_automata/rule.rb +27 -0
- data/lib/cellular_automata/version.rb +1 -1
- data/lib/cellular_automata.rb +4 -1
- metadata +45 -7
- data/bin/console +0 -14
- data/bin/setup +0 -7
- data/lib/cellular_automata/animator.rb +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebba5fb93b878df9d9d9e87e16301f9df0c32cae
|
4
|
+
data.tar.gz: 505da2bb9ad0ed69bc29c8acc86f67ad7a8df745
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2846a0d673eb8cb36ec8d30225bd7cdc5615621c05bb91f6645024e5206f06e7618f5a802966b86d70d03fb36d1c97a5b48fc261dd7bca1816cd8a4a3d41a19f
|
7
|
+
data.tar.gz: 5628a93b80ec95eca7d7ebf03b4d9f6360852da1d55e8c147c30edf8b30c8768a8d5bca3b475131ba656cb47cc7b5cf892e5e453fa1928e53d36d0d134f0b509
|
data/README.md
CHANGED
data/bin/cell
CHANGED
@@ -1,5 +1,48 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'cellular_automata'
|
4
|
+
require 'optparse'
|
4
5
|
|
5
|
-
|
6
|
+
def opts_from_cli
|
7
|
+
options = {}
|
8
|
+
opt_parser = OptionParser.new do |opts|
|
9
|
+
opts.program_name = File.basename(__FILE__)
|
10
|
+
opts.banner = "#{opts.program_name} [options] RULE"
|
11
|
+
opts.on('-w WIDTH', '--width WIDTH', 'Set width') { |w| options[:width] = w.to_i }
|
12
|
+
opts.on('-h HEIGHT', '--height HEIGHT', 'Set height') { |h| options[:height] = h.to_i }
|
13
|
+
opts.on('-r REFRESH', '--refresh REFRESH', 'Set refresh rate in seconds') { |r| options[:refresh] = r.to_f }
|
14
|
+
opts.on('-v', '--version', 'Print version information') do
|
15
|
+
puts "#{File.basename(__FILE__)} #{CellularAutomata::VERSION}"
|
16
|
+
exit true
|
17
|
+
end
|
18
|
+
opts.on('--help', 'Display this screen') do
|
19
|
+
puts opts
|
20
|
+
exit true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
begin
|
24
|
+
opt_parser.parse!
|
25
|
+
rescue OptionParser::InvalidOption => e
|
26
|
+
puts e.message
|
27
|
+
exit false
|
28
|
+
end
|
29
|
+
options
|
30
|
+
end
|
31
|
+
|
32
|
+
opts = opts_from_cli
|
33
|
+
opts[:width] ||= 120
|
34
|
+
opts[:height] ||= 30
|
35
|
+
opts[:refresh] ||= 0.1
|
36
|
+
rule = ARGV[0] || 'B3S2'
|
37
|
+
board = CellularAutomata::Board.new(width: opts[:width], height: opts[:height], rule: rule)
|
38
|
+
|
39
|
+
trap 'SIGINT' do
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
while true do
|
44
|
+
puts "\e[H\e[2J"
|
45
|
+
board.tick!
|
46
|
+
puts board.to_s
|
47
|
+
sleep opts[:refresh]
|
48
|
+
end
|
data/cellular_automata.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["ffleming@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{A simulation of cellular automata}
|
13
|
-
spec.description = %q{A
|
13
|
+
spec.description = %q{A set of 0-player games, of which Conway's Game of Life is a member.}
|
14
14
|
spec.homepage = "https://github.com/ffleming/cellular_automata"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
@@ -22,4 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1", ">= 1.8"
|
24
24
|
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
26
|
+
spec.add_development_dependency 'byebug', '~> 4'
|
27
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.1'
|
25
28
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
class CellularAutomata::Board
|
2
|
-
attr_reader :width, :height
|
3
|
-
def initialize(width: 80, height: 20)
|
4
|
-
@height
|
5
|
-
@width
|
6
|
-
@
|
7
|
-
CellularAutomata::
|
2
|
+
attr_reader :width, :height, :rule
|
3
|
+
def initialize(rule: 'B3S2', width: 80, height: 20)
|
4
|
+
@height = height
|
5
|
+
@width = width
|
6
|
+
@state = build_array
|
7
|
+
@rule = CellularAutomata::Rule.new(rule)
|
8
8
|
seed!
|
9
9
|
end
|
10
10
|
|
11
11
|
def to_s
|
12
12
|
line = '+' << ('-' * width) << "+\n"
|
13
13
|
ret = '' << line
|
14
|
-
@
|
14
|
+
@state.each do |row|
|
15
15
|
ret << "|"
|
16
16
|
row.each do |cell|
|
17
17
|
ret << cell.to_s
|
@@ -21,56 +21,56 @@ class CellularAutomata::Board
|
|
21
21
|
ret << line
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def tick!
|
25
|
+
next_state = Marshal.load(Marshal.dump @state)
|
25
26
|
each_cell do |cell|
|
26
|
-
|
27
|
-
when 0..1
|
28
|
-
cell.die!
|
29
|
-
when 3
|
30
|
-
cell.live!
|
31
|
-
when 4..8
|
32
|
-
cell.die!
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def animate(steps=1000, refresh=0.1)
|
38
|
-
(1..steps).each do |i|
|
39
|
-
puts "\e[H\e[2J"
|
40
|
-
cycle!
|
41
|
-
puts self.to_s
|
42
|
-
sleep refresh
|
27
|
+
next_state[cell.y][cell.x].send rule.process(neighbor_population_of cell) #= next_cell #cell.send(rule.process(adj_pop))
|
43
28
|
end
|
29
|
+
@state = next_state
|
44
30
|
end
|
45
31
|
|
46
32
|
private
|
47
33
|
|
48
34
|
def each_cell
|
49
|
-
(0..height-1).each do |
|
50
|
-
(0..width-1).each do |
|
51
|
-
yield @
|
35
|
+
(0..height-1).each do |y|
|
36
|
+
(0..width-1).each do |x|
|
37
|
+
yield @state[y][x]
|
52
38
|
end
|
53
39
|
end
|
54
|
-
|
55
40
|
end
|
56
41
|
|
57
42
|
def seed!
|
58
|
-
|
59
|
-
(0..width-1).each do |col|
|
60
|
-
@array[row][col].live! if rand < 0.2
|
61
|
-
end
|
62
|
-
end
|
43
|
+
each_cell { |c| c.live! if rand < 0.1 }
|
63
44
|
end
|
64
45
|
|
65
46
|
def build_array
|
66
47
|
arr = []
|
67
|
-
(0..height-1).each do |
|
68
|
-
arr[
|
69
|
-
(0..width-1).each do |
|
70
|
-
arr[
|
48
|
+
(0..height-1).each do |y|
|
49
|
+
arr[y] = []
|
50
|
+
(0..width-1).each do |x|
|
51
|
+
arr[y][x] = CellularAutomata::Cell.new(row: y, column: x, alive: false)
|
71
52
|
end
|
72
53
|
end
|
73
54
|
return arr
|
74
55
|
end
|
56
|
+
|
57
|
+
def neighbor_population_of(cell)
|
58
|
+
neighbors_of(cell).select(&:alive?).length
|
59
|
+
end
|
60
|
+
|
61
|
+
def cell_at(y, x)
|
62
|
+
return nil if x < 0 || y < 0
|
63
|
+
return nil if y > @state.length-1
|
64
|
+
return nil if x > @state[0].length-1
|
65
|
+
return @state[y][x]
|
66
|
+
end
|
67
|
+
|
68
|
+
def neighbors_of(cell)
|
69
|
+
y = cell.y ; x = cell.x
|
70
|
+
[ cell_at(y-1, x-1), cell_at(y-1, x ), cell_at(y-1, x+1),
|
71
|
+
cell_at(y, x+1), cell_at(y , x-1),
|
72
|
+
cell_at(y+1, x-1), cell_at(y+1, x ), cell_at(y+1, x+1) ].compact
|
73
|
+
end
|
74
|
+
|
75
75
|
end
|
76
76
|
|
@@ -1,31 +1,12 @@
|
|
1
1
|
class CellularAutomata::Cell
|
2
|
-
|
3
|
-
attr_accessor :array
|
4
|
-
end
|
5
|
-
|
6
|
-
attr_accessor :row, :column
|
7
|
-
attr_reader :alive
|
2
|
+
attr_reader :alive, :row, :column
|
8
3
|
alias x column
|
9
|
-
alias x= column=
|
10
4
|
alias y row
|
11
|
-
alias y= row=
|
12
5
|
alias alive? alive
|
13
|
-
def initialize(row: , column:
|
6
|
+
def initialize(alive: false, row: , column:)
|
7
|
+
@alive = alive
|
14
8
|
@row = row
|
15
9
|
@column = column
|
16
|
-
@alive = false
|
17
|
-
end
|
18
|
-
|
19
|
-
def die!
|
20
|
-
@alive = false
|
21
|
-
end
|
22
|
-
|
23
|
-
def live!
|
24
|
-
@alive = true
|
25
|
-
end
|
26
|
-
|
27
|
-
def dead?
|
28
|
-
!alive?
|
29
10
|
end
|
30
11
|
|
31
12
|
def to_s
|
@@ -33,60 +14,22 @@ class CellularAutomata::Cell
|
|
33
14
|
'*'
|
34
15
|
end
|
35
16
|
|
36
|
-
def
|
37
|
-
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def neighbors
|
43
|
-
[
|
44
|
-
n_neighbor,
|
45
|
-
ne_neighbor,
|
46
|
-
e_neighbor,
|
47
|
-
se_neighbor,
|
48
|
-
s_neighbor,
|
49
|
-
sw_neighbor,
|
50
|
-
w_neighbor,
|
51
|
-
nw_neighbor
|
52
|
-
].compact
|
53
|
-
end
|
54
|
-
|
55
|
-
def n_neighbor
|
56
|
-
neighbor(y-1, x)
|
57
|
-
end
|
58
|
-
|
59
|
-
def ne_neighbor
|
60
|
-
neighbor(y-1, x+1)
|
61
|
-
end
|
62
|
-
|
63
|
-
def e_neighbor
|
64
|
-
neighbor(y, x+1)
|
65
|
-
end
|
66
|
-
|
67
|
-
def se_neighbor
|
68
|
-
neighbor(y+1, x+1)
|
69
|
-
end
|
70
|
-
|
71
|
-
def s_neighbor
|
72
|
-
neighbor(y+1, x)
|
17
|
+
def live!
|
18
|
+
@alive = true
|
73
19
|
end
|
74
20
|
|
75
|
-
def
|
76
|
-
|
21
|
+
def die!
|
22
|
+
@alive = false
|
77
23
|
end
|
78
24
|
|
79
|
-
def
|
80
|
-
neighbor(y, x+1)
|
25
|
+
def survive!
|
81
26
|
end
|
82
27
|
|
83
|
-
def
|
84
|
-
|
28
|
+
def dead?
|
29
|
+
!alive?
|
85
30
|
end
|
86
31
|
|
87
|
-
def
|
88
|
-
|
89
|
-
return nil if r < 0 || c < 0
|
90
|
-
return CellularAutomata::Cell::array[r][c] rescue nil
|
32
|
+
def copy
|
33
|
+
self.class.new(alive: alive?, row: row, column: column)
|
91
34
|
end
|
92
35
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class CellularAutomata::Rule
|
2
|
+
def initialize(rule_string)
|
3
|
+
@rule_hash = process_rule! rule_string
|
4
|
+
end
|
5
|
+
|
6
|
+
def process(input)
|
7
|
+
return @rule_hash[input] || raise(ArgumentError.new("I don't know what to do with #{input.class} #{input}"))
|
8
|
+
end
|
9
|
+
|
10
|
+
def process2(input)
|
11
|
+
@rule_hash[input] != :die!
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def process_rule!(rule_string)
|
17
|
+
rules = rule_string.scan(/[BS]\d+/)
|
18
|
+
raise ArgumentError.new('Invalid rule string') if rules.length != 2
|
19
|
+
birth_string = rules.select {|s| s.start_with?('B')}.first
|
20
|
+
survive_string = rules.select {|s| s.start_with?('S')}.first
|
21
|
+
(birth, survive) = [birth_string, survive_string].map { |s| s[1..-1].split('').map(&:to_i) }
|
22
|
+
death = ((0..8).to_a - birth) - survive
|
23
|
+
{live!: birth, survive!: survive, die!: death}.each_with_object({}) do |(k, v), ret|
|
24
|
+
v.each {|int| ret[int] = k}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/cellular_automata.rb
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
require 'pry'
|
2
|
+
require 'byebug'
|
3
|
+
require 'pry-byebug'
|
1
4
|
require "cellular_automata/version"
|
2
5
|
require "cellular_automata/cell"
|
6
|
+
require "cellular_automata/rule"
|
3
7
|
require "cellular_automata/board"
|
4
|
-
require "cellular_automata/animator"
|
5
8
|
|
6
9
|
module CellularAutomata
|
7
10
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cellular_automata
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Forrest Fleming
|
@@ -44,13 +44,53 @@ dependencies:
|
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
46
|
version: '10.0'
|
47
|
-
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: pry
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.10'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0.10'
|
61
|
+
- !ruby/object:Gem::Dependency
|
62
|
+
name: byebug
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4'
|
68
|
+
type: :development
|
69
|
+
prerelease: false
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '4'
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: pry-byebug
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '3.1'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '3.1'
|
89
|
+
description: A set of 0-player games, of which Conway's Game of Life is a member.
|
48
90
|
email:
|
49
91
|
- ffleming@gmail.com
|
50
92
|
executables:
|
51
93
|
- cell
|
52
|
-
- console
|
53
|
-
- setup
|
54
94
|
extensions: []
|
55
95
|
extra_rdoc_files: []
|
56
96
|
files:
|
@@ -61,13 +101,11 @@ files:
|
|
61
101
|
- README.md
|
62
102
|
- Rakefile
|
63
103
|
- bin/cell
|
64
|
-
- bin/console
|
65
|
-
- bin/setup
|
66
104
|
- cellular_automata.gemspec
|
67
105
|
- lib/cellular_automata.rb
|
68
|
-
- lib/cellular_automata/animator.rb
|
69
106
|
- lib/cellular_automata/board.rb
|
70
107
|
- lib/cellular_automata/cell.rb
|
108
|
+
- lib/cellular_automata/rule.rb
|
71
109
|
- lib/cellular_automata/version.rb
|
72
110
|
homepage: https://github.com/ffleming/cellular_automata
|
73
111
|
licenses:
|
data/bin/console
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "cellular_automata"
|
5
|
-
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
8
|
-
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
-
# require "pry"
|
11
|
-
# Pry.start
|
12
|
-
|
13
|
-
require "irb"
|
14
|
-
IRB.start
|
data/bin/setup
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
module CellularAutomata::Animator
|
2
|
-
class << self
|
3
|
-
attr_accessor :board
|
4
|
-
def board
|
5
|
-
@board ||= CellularAutomata::Board.new(width: 120, height: 30)
|
6
|
-
end
|
7
|
-
def animate(steps=1000, refresh=0.1)
|
8
|
-
(1..steps).each do
|
9
|
-
puts "\e[H\e[2J"
|
10
|
-
board.cycle!
|
11
|
-
puts board.to_s
|
12
|
-
sleep refresh
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|