abstracta 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 +7 -0
- data/.coco.yml +7 -0
- data/.document +3 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/.yardopts +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +83 -0
- data/Rakefile +35 -0
- data/abstracta.gemspec +60 -0
- data/bin/abstracta +18 -0
- data/features/.gitkeep +0 -0
- data/features/abstracta.feature +1 -0
- data/features/step_definitions/.gitkeep +0 -0
- data/features/step_definitions/abstracta_steps.rb +1 -0
- data/gemspec.yml +18 -0
- data/lib/abstracta.rb +18 -0
- data/lib/abstracta/developer.rb +52 -0
- data/lib/abstracta/extend/range.rb +5 -0
- data/lib/abstracta/genome.rb +31 -0
- data/lib/abstracta/occupant.rb +20 -0
- data/lib/abstracta/territory.rb +63 -0
- data/lib/abstracta/version.rb +10 -0
- data/lib/abstracta/world.rb +75 -0
- data/script/bootstrap.rb +17 -0
- data/spec/abstracta/developer_spec.rb +46 -0
- data/spec/abstracta/genome_spec.rb +18 -0
- data/spec/abstracta/occupant_spec.rb +22 -0
- data/spec/abstracta/territory_spec.rb +44 -0
- data/spec/abstracta/world_spec.rb +90 -0
- data/spec/abstracta_spec.rb +11 -0
- data/spec/spec_helper.rb +6 -0
- data/thoughts.rb.txt +63 -0
- metadata +182 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0043fdb30274ff1358134faba36ca65fe1971b0b
|
4
|
+
data.tar.gz: 5849ff0ba9bfdcbf456bcc8dc9064c0355fd4d85
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cf7f94c1a615120254e67fe7f9e300e8ce08e99049a7443caccf2ece1dd0b21d32a5b0ca486054b21b2fe01bec7541fcc9cdd0998c49a35dfff70e866d7debf4
|
7
|
+
data.tar.gz: e37136b39af3e646a512acdfdce15da0488cc7641028c6332b277113e8aa7ef78862f046e22381085f3763dabf08915e17d7aab51ef03e22d4d573f2cf6897ab
|
data/.coco.yml
ADDED
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.1.5
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "abstracta Documentation" --protected
|
data/ChangeLog.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Joseph Weissman
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
## abstracta
|
2
|
+
|
3
|
+
[](https://codeship.com/projects/54005)
|
4
|
+
[](https://codeclimate.com/github/jweissman/abstracta)
|
5
|
+
|
6
|
+
* [Homepage](https://rubygems.org/gems/abstracta)
|
7
|
+
* [Documentation](http://rubydoc.info/gems/abstracta/frames)
|
8
|
+
* [Email](mailto:jweissman1986 at gmail.com)
|
9
|
+
|
10
|
+
## Description
|
11
|
+
|
12
|
+
Cellular automata framework for ruby
|
13
|
+
|
14
|
+
## Features
|
15
|
+
|
16
|
+
## Examples
|
17
|
+
|
18
|
+
Again, intentionally abstract, so there's not much output you can
|
19
|
+
derive directly from interacting with it. It might go something like
|
20
|
+
this:
|
21
|
+
|
22
|
+
require 'abstracta'
|
23
|
+
world = Abstracta::World.new # creates a sim space
|
24
|
+
100.times { world.step } # iterates/grows organisms
|
25
|
+
|
26
|
+
In any particular case you'll want to extend from these classes and build on top of
|
27
|
+
them. A concrete example from the Biosphere game (probably the place
|
28
|
+
to go right now for something to look at around this/inspiration):
|
29
|
+
|
30
|
+
class Cell < Abstracta::Occupant
|
31
|
+
def coordinates(cell_size=1)
|
32
|
+
x, y = cell.x * self.cell_size, cell.y * self.cell_size
|
33
|
+
x1, y1 = x + self.cell_size, y + self.cell_size
|
34
|
+
[[x,y], [x1,y], [x, y1], [x1,y1]]
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(window, color=Gosu::Color::WHITE)
|
38
|
+
coords = coordinates(window.cell_size)
|
39
|
+
quad_args = coords.map{|c| c + [color] }
|
40
|
+
draw_quad(*quad_args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
## Requirements
|
45
|
+
|
46
|
+
Everything gosu needs, which is really not all that bad. But it's not exactly portable,
|
47
|
+
or easy stand up a dev environment through a simple provisioning script (though maybe a
|
48
|
+
little focused effort there could help containerize it.)
|
49
|
+
|
50
|
+
## Install
|
51
|
+
|
52
|
+
$ gem install biosphere
|
53
|
+
|
54
|
+
## Synopsis
|
55
|
+
|
56
|
+
$ biosphere
|
57
|
+
|
58
|
+
## Copyright
|
59
|
+
|
60
|
+
Copyright (c) 2014 Joseph Weissman
|
61
|
+
|
62
|
+
See {file:LICENSE.txt} for details. Abstracta
|
63
|
+
|
64
|
+
Cellular automata game :)
|
65
|
+
|
66
|
+
The idea is to have a game server where different players' organisms
|
67
|
+
could interact...
|
68
|
+
|
69
|
+
|
70
|
+
# Dependencies
|
71
|
+
|
72
|
+
You will need to
|
73
|
+
|
74
|
+
brew install sdl2 libogg libvorbis
|
75
|
+
|
76
|
+
for Gosu's dependencies. Then bundle and rake to play.
|
77
|
+
|
78
|
+
Ideally we'll get vagrant setup to build a dev environment for us...
|
79
|
+
|
80
|
+
(Eventually we'll want to containerize the server application too,
|
81
|
+
although that can be headless... A containerized dev environment
|
82
|
+
makes sense eventually too once that is reified a bit further. Something
|
83
|
+
like boxen may not be the worst thing to think about eventually there too.)
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
rescue LoadError => e
|
8
|
+
warn e.message
|
9
|
+
warn "Run `gem install bundler` to install Bundler."
|
10
|
+
exit(-1)
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Bundler.setup(:development)
|
15
|
+
rescue Bundler::BundlerError => e
|
16
|
+
warn e.message
|
17
|
+
warn "Run `bundle install` to install missing gems."
|
18
|
+
exit e.status_code
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake'
|
22
|
+
|
23
|
+
require 'rubygems/tasks'
|
24
|
+
Gem::Tasks.new
|
25
|
+
|
26
|
+
require 'rspec/core/rake_task'
|
27
|
+
RSpec::Core::RakeTask.new
|
28
|
+
|
29
|
+
task :test => :spec
|
30
|
+
task :default => :spec
|
31
|
+
|
32
|
+
require 'yard'
|
33
|
+
YARD::Rake::YardocTask.new
|
34
|
+
task :doc => :yard
|
35
|
+
|
data/abstracta.gemspec
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gemspec = YAML.load_file('gemspec.yml')
|
7
|
+
|
8
|
+
gem.name = gemspec.fetch('name')
|
9
|
+
gem.version = gemspec.fetch('version') do
|
10
|
+
lib_dir = File.join(File.dirname(__FILE__),'lib')
|
11
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
12
|
+
|
13
|
+
require 'abstracta/version'
|
14
|
+
Abstracta::VERSION
|
15
|
+
end
|
16
|
+
|
17
|
+
gem.summary = gemspec['summary']
|
18
|
+
gem.description = gemspec['description']
|
19
|
+
gem.licenses = Array(gemspec['license'])
|
20
|
+
gem.authors = Array(gemspec['authors'])
|
21
|
+
gem.email = gemspec['email']
|
22
|
+
gem.homepage = gemspec['homepage']
|
23
|
+
|
24
|
+
glob = lambda { |patterns| gem.files & Dir[*patterns] }
|
25
|
+
|
26
|
+
gem.files = `git ls-files`.split($/)
|
27
|
+
gem.files = glob[gemspec['files']] if gemspec['files']
|
28
|
+
|
29
|
+
gem.executables = gemspec.fetch('executables') do
|
30
|
+
glob['bin/*'].map { |path| File.basename(path) }
|
31
|
+
end
|
32
|
+
gem.default_executable = gem.executables.first if Gem::VERSION < '1.7.'
|
33
|
+
|
34
|
+
gem.extensions = glob[gemspec['extensions'] || 'ext/**/extconf.rb']
|
35
|
+
gem.test_files = glob[gemspec['test_files'] || '{test/{**/}*_test.rb']
|
36
|
+
gem.extra_rdoc_files = glob[gemspec['extra_doc_files'] || '*.{txt,md}']
|
37
|
+
|
38
|
+
gem.require_paths = Array(gemspec.fetch('require_paths') {
|
39
|
+
%w[ext lib].select { |dir| File.directory?(dir) }
|
40
|
+
})
|
41
|
+
|
42
|
+
gem.requirements = gemspec['requirements']
|
43
|
+
gem.required_ruby_version = gemspec['required_ruby_version']
|
44
|
+
gem.required_rubygems_version = gemspec['required_rubygems_version']
|
45
|
+
gem.post_install_message = gemspec['post_install_message']
|
46
|
+
|
47
|
+
split = lambda { |string| string.split(/,\s*/) }
|
48
|
+
|
49
|
+
if gemspec['dependencies']
|
50
|
+
gemspec['dependencies'].each do |name,versions|
|
51
|
+
gem.add_dependency(name,split[versions])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if gemspec['development_dependencies']
|
56
|
+
gemspec['development_dependencies'].each do |name,versions|
|
57
|
+
gem.add_development_dependency(name,split[versions])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/bin/abstracta
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
root = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
4
|
+
if File.directory?(File.join(root,'.git'))
|
5
|
+
Dir.chdir(root) do
|
6
|
+
begin
|
7
|
+
require 'bundler/setup'
|
8
|
+
rescue LoadError => e
|
9
|
+
warn e.message
|
10
|
+
warn "Run `gem install bundler` to install Bundler"
|
11
|
+
exit(-1)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'abstracta'
|
17
|
+
|
18
|
+
# TODO sim process...
|
data/features/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Feature: Simulating the game of life...
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
@wip
|
data/gemspec.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
name: abstracta
|
2
|
+
summary: "a cellular automation and simulation services library in Ruby"
|
3
|
+
description: "a modular cellular automata engine with the goal of providing a simulation service framework"
|
4
|
+
license: MIT
|
5
|
+
authors: Joseph Weissman
|
6
|
+
email: jweissman1986@gmail.com
|
7
|
+
homepage: https://rubygems.org/gems/abstracta
|
8
|
+
|
9
|
+
dependencies:
|
10
|
+
activesupport: ~> 4.2.0
|
11
|
+
straightedge: ~> 0.1.0
|
12
|
+
|
13
|
+
development_dependencies:
|
14
|
+
bundler: ~> 1.0
|
15
|
+
rake: ~> 0.8
|
16
|
+
rspec: ~> 2.4
|
17
|
+
rubygems-tasks: ~> 0.2
|
18
|
+
yard: ~> 0.8
|
data/lib/abstracta.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
require 'straightedge'
|
4
|
+
|
5
|
+
require 'abstracta/version'
|
6
|
+
require 'abstracta/occupant'
|
7
|
+
require 'abstracta/territory'
|
8
|
+
require 'abstracta/genome'
|
9
|
+
require 'abstracta/world'
|
10
|
+
require 'abstracta/developer'
|
11
|
+
require 'abstracta/extend/range'
|
12
|
+
|
13
|
+
module Abstracta
|
14
|
+
include Straightedge
|
15
|
+
def self.bootstrap!(opts={},&blk)
|
16
|
+
Engine.boot(opts,&blk)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Abstracta
|
2
|
+
class Developer
|
3
|
+
extend Forwardable
|
4
|
+
def initialize(entity)
|
5
|
+
@entity = entity
|
6
|
+
end
|
7
|
+
|
8
|
+
def collection; [] end
|
9
|
+
|
10
|
+
def tick
|
11
|
+
@entity.age!
|
12
|
+
end
|
13
|
+
|
14
|
+
def step(*args)
|
15
|
+
tick
|
16
|
+
develop(*args)
|
17
|
+
collection.map(&:step)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class WorldDeveloper < Developer
|
22
|
+
def world; @entity end
|
23
|
+
def collection; world.territories end
|
24
|
+
|
25
|
+
def develop
|
26
|
+
# now a no-op...
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TerritoryDeveloper < Developer
|
31
|
+
def_delegators :territory, :age, :size, :period, :limit, :[], :each, :first,
|
32
|
+
:occupy!, :adjacent, :growth, :cull!
|
33
|
+
|
34
|
+
def territory; @entity end
|
35
|
+
def collection; territory.occupants end
|
36
|
+
|
37
|
+
def develop(targets: adjacent)
|
38
|
+
grow(targets) if growth_indicated?
|
39
|
+
cull!
|
40
|
+
end
|
41
|
+
|
42
|
+
def growth_indicated?
|
43
|
+
in_cycle = age % territory.period == 0
|
44
|
+
under_bound = size <= territory.limit
|
45
|
+
in_cycle && under_bound
|
46
|
+
end
|
47
|
+
|
48
|
+
def grow(targets, n=growth)
|
49
|
+
targets.sample(n).map(&method(:occupy!))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Abstracta
|
2
|
+
class Genome < OpenStruct
|
3
|
+
def self.default
|
4
|
+
@default_genome ||= OpenStruct.new(
|
5
|
+
#growth_cycle: (2..10).sample,
|
6
|
+
#growth_limit: (4..20).sample,
|
7
|
+
#growth_radius: (1..4).sample,
|
8
|
+
#growth_rate: (15..100).sample / 10.0,
|
9
|
+
|
10
|
+
growth: OpenStruct.new(
|
11
|
+
cycle: 2, #(2..4).sample,
|
12
|
+
limit: 100, #(20..80).sample,
|
13
|
+
#radius: (1..2).sample,
|
14
|
+
rate: OpenStruct.new(
|
15
|
+
additive: 2,
|
16
|
+
multiplicative: 1.05
|
17
|
+
)
|
18
|
+
#2, #(1..4).sample #(150..200).sample / 100.0,
|
19
|
+
),
|
20
|
+
|
21
|
+
# TODO make these do something!
|
22
|
+
age_bound: 20 #(3..9).sample,
|
23
|
+
#influence_radius: 10,
|
24
|
+
#sterile: false,
|
25
|
+
#sticky: false,
|
26
|
+
#vision_radius: 100,
|
27
|
+
#mobile: true,
|
28
|
+
)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Abstracta
|
2
|
+
class Occupant
|
3
|
+
extend Forwardable
|
4
|
+
attr_reader :age
|
5
|
+
attr_reader :location
|
6
|
+
def_delegators :location, :zip, :x, :y
|
7
|
+
|
8
|
+
def initialize(location=[0,0])
|
9
|
+
@location = location
|
10
|
+
@age = 0
|
11
|
+
@size = 1
|
12
|
+
end
|
13
|
+
|
14
|
+
def position; [@x,@y] end
|
15
|
+
|
16
|
+
def step
|
17
|
+
@age = @age + 1
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Abstracta
|
2
|
+
class Territory
|
3
|
+
include Enumerable
|
4
|
+
extend Forwardable
|
5
|
+
|
6
|
+
def_delegators :occupants, :[], :size, :each, :include?, :adjacent
|
7
|
+
def_delegator :developer, :step
|
8
|
+
|
9
|
+
alias :occupy? :include?
|
10
|
+
|
11
|
+
attr_reader :dna
|
12
|
+
attr_reader :occupants
|
13
|
+
attr_reader :compass, :developer
|
14
|
+
attr_reader :age
|
15
|
+
attr_reader :period, :limit
|
16
|
+
|
17
|
+
def initialize(locations=[[0,0]],genome=Genome.default)
|
18
|
+
@compass = Compass.default
|
19
|
+
@occupants = locations.map(&method(:occupant_at))
|
20
|
+
|
21
|
+
@dna = genome.tap do |my|
|
22
|
+
@period = my.growth.cycle
|
23
|
+
@rate = my.growth.rate
|
24
|
+
@limit = my.growth.limit
|
25
|
+
@max_age = my.age_bound
|
26
|
+
end
|
27
|
+
|
28
|
+
@age = 0
|
29
|
+
|
30
|
+
@developer = TerritoryDeveloper.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def age!
|
34
|
+
@age = @age + 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def projected_size
|
38
|
+
(@rate.additive + size) * @rate.multiplicative
|
39
|
+
end
|
40
|
+
|
41
|
+
def growth
|
42
|
+
projected_size - size
|
43
|
+
end
|
44
|
+
|
45
|
+
def occupant_class
|
46
|
+
Occupant
|
47
|
+
end
|
48
|
+
|
49
|
+
def occupant_at(point)
|
50
|
+
occupant_class.new([point.x, point.y])
|
51
|
+
end
|
52
|
+
|
53
|
+
def occupy!(target)
|
54
|
+
@occupants << occupant_at(target)
|
55
|
+
end
|
56
|
+
|
57
|
+
def cull!
|
58
|
+
@occupants.reject! do |occupant|
|
59
|
+
occupant.age >= @max_age
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Abstracta
|
2
|
+
class World
|
3
|
+
extend Forwardable
|
4
|
+
attr_reader :age, :grid, :territories, :developer
|
5
|
+
|
6
|
+
def_delegators :grid, :width, :height
|
7
|
+
def_delegators :developer, :step
|
8
|
+
#def_delegators :compass, :distance_from
|
9
|
+
|
10
|
+
def initialize(geometry=[100,100], opts={})
|
11
|
+
@grid = Grid.new(geometry)
|
12
|
+
#@compass = Compass.default
|
13
|
+
@density = opts.delete(:density) { 0.05 }
|
14
|
+
|
15
|
+
@territory_count = opts.delete(:territory_count) { width * height * @density }
|
16
|
+
@territories = []
|
17
|
+
@territories = create_territories(@territory_count)
|
18
|
+
update_map
|
19
|
+
|
20
|
+
@age = 0
|
21
|
+
|
22
|
+
@developer = WorldDeveloper.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def update_map
|
26
|
+
@occupied = compute_occupied
|
27
|
+
end
|
28
|
+
|
29
|
+
def territory_class; Territory end
|
30
|
+
def create_territories(n=1)
|
31
|
+
seeds = @grid.sample(n)
|
32
|
+
Array.new(n) { territory_class.new([seeds.pop]) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def age!
|
36
|
+
@age = @age + 1
|
37
|
+
end
|
38
|
+
|
39
|
+
#def step
|
40
|
+
# old_size = occupied.size
|
41
|
+
# update_territories
|
42
|
+
# occupied.size - old_size
|
43
|
+
#end
|
44
|
+
|
45
|
+
#def update_territories
|
46
|
+
# @territories.each do |territory|
|
47
|
+
# update_map
|
48
|
+
# targets = compute_projected_targets(territory)
|
49
|
+
# territory.step(targets)
|
50
|
+
# end
|
51
|
+
#end
|
52
|
+
|
53
|
+
def occupied
|
54
|
+
@occupied ||= compute_occupied
|
55
|
+
end
|
56
|
+
|
57
|
+
def compute_occupied
|
58
|
+
territories.map(&:occupants).flatten.map(&:location)
|
59
|
+
end
|
60
|
+
|
61
|
+
def occupied?(xy)
|
62
|
+
@occupied.include?(xy)
|
63
|
+
end
|
64
|
+
|
65
|
+
def compute_projected_targets(territory, n=territory.growth)
|
66
|
+
available_adjacent(territory).take(n)
|
67
|
+
end
|
68
|
+
|
69
|
+
def available_adjacent(territory)
|
70
|
+
@grid.clip territory.adjacent.reject(&method(:occupied?)) # { |xy| occupied.include?(xy) } # & available # - occupied
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
#
|
data/script/bootstrap.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/enb ruby
|
2
|
+
|
3
|
+
#
|
4
|
+
root = File.expand_path(File.join(File.dirname(__FILE__),'..'))
|
5
|
+
if File.directory?(File.join(root,'.git'))
|
6
|
+
Dir.chdir(root) do
|
7
|
+
begin
|
8
|
+
require 'bundler/setup'
|
9
|
+
require 'abstracta'
|
10
|
+
Engine.boot
|
11
|
+
rescue LoadError => e
|
12
|
+
warn e.message
|
13
|
+
warn "Run `gem install bundler` to install Bundler"
|
14
|
+
exit(-1)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'abstracta'
|
3
|
+
|
4
|
+
include Abstracta
|
5
|
+
|
6
|
+
describe TerritoryDeveloper do
|
7
|
+
|
8
|
+
let(:territory) { Territory.new }
|
9
|
+
subject { TerritoryDeveloper.new(territory) }
|
10
|
+
|
11
|
+
describe "#step" do
|
12
|
+
it "should age occupants" do
|
13
|
+
expect { subject.step }.to change { subject.first.age }.by 1
|
14
|
+
end
|
15
|
+
|
16
|
+
#it "should grow to a specific location" do
|
17
|
+
# expect { subject.step([[0,1]]) }.to change { subject.size }.by(1)
|
18
|
+
#end
|
19
|
+
|
20
|
+
let(:cycle) { subject.period }
|
21
|
+
|
22
|
+
it "should grow total size" do
|
23
|
+
expect { cycle.times { subject.step } }.to change { subject.size }.by(subject.growth.to_i)
|
24
|
+
end
|
25
|
+
|
26
|
+
context "longterm growth behaviors" do
|
27
|
+
before { cycle.times { subject.step }}
|
28
|
+
|
29
|
+
it 'should generate occupants with valid positions' do
|
30
|
+
subject.each do |occupant|
|
31
|
+
expect(occupant.x).to be_an(Integer)
|
32
|
+
expect(occupant.y).to be_an(Integer)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should grow a little bit" do
|
37
|
+
expect(subject.size).to be > 1
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should not grow too much" do
|
41
|
+
expect(subject.size).not_to be > subject.limit
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'abstracta/extend/range'
|
4
|
+
require 'abstracta/genome'
|
5
|
+
|
6
|
+
describe Abstracta::Genome do
|
7
|
+
subject { Abstracta::Genome.default }
|
8
|
+
|
9
|
+
context "defaults" do
|
10
|
+
it 'should have sane options' do
|
11
|
+
#expect(subject.mobile).to be(true)
|
12
|
+
#expect(subject.sterile).to be(false)
|
13
|
+
expect(subject.growth.limit).to be_an(Integer) #eql(10)
|
14
|
+
#expect(subject.growth.radius).to be_an(Integer) #eql(10)
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'straightedge'
|
3
|
+
require 'abstracta/occupant'
|
4
|
+
|
5
|
+
describe Abstracta::Occupant do
|
6
|
+
it 'should have a position' do
|
7
|
+
expect(subject.x).to be_an Integer
|
8
|
+
expect(subject.y).to be_an Integer
|
9
|
+
end
|
10
|
+
|
11
|
+
context "#age" do
|
12
|
+
it 'should be a year older' do
|
13
|
+
expect { subject.step }.to change { subject.age }.by 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
#it 'should have a genetic code' do
|
18
|
+
# expect(subject.dna).to be_a Abstracta::Genome
|
19
|
+
#end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'abstracta'
|
3
|
+
|
4
|
+
include Abstracta
|
5
|
+
|
6
|
+
describe Territory do
|
7
|
+
subject { Territory.new([[0,0]]) }
|
8
|
+
|
9
|
+
it 'should have dna' do
|
10
|
+
expect(subject.dna).to be_a OpenStruct
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#step" do
|
14
|
+
it "should age occupants" do
|
15
|
+
expect { subject.step }.to change { subject.first.age }.by 1
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:cycle) { subject.dna.growth.cycle }
|
19
|
+
|
20
|
+
it "should grow total size" do
|
21
|
+
expect { cycle.times { subject.step } }.to change { subject.size }.by(subject.growth.to_i)
|
22
|
+
end
|
23
|
+
|
24
|
+
context "longterm growth behaviors" do
|
25
|
+
before { cycle.times { subject.step }}
|
26
|
+
|
27
|
+
it 'should generate occupants with valid positions' do
|
28
|
+
subject.occupants.each do |occupant|
|
29
|
+
expect(occupant.x).to be_an(Integer)
|
30
|
+
expect(occupant.y).to be_an(Integer)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should grow a little bit" do
|
35
|
+
expect(subject.size).to be > 1
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should not grow too much" do
|
39
|
+
expect(subject.size).not_to be > subject.dna.growth.limit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'abstracta'
|
3
|
+
|
4
|
+
include Abstracta
|
5
|
+
|
6
|
+
describe World do
|
7
|
+
let(:width) { 100 }
|
8
|
+
let(:height) { 100 }
|
9
|
+
let(:geometry) { [width, height] }
|
10
|
+
|
11
|
+
subject { World.new(geometry, territory_count: 1) }
|
12
|
+
|
13
|
+
#let!(:field) { subject.field }
|
14
|
+
let!(:territory) { subject.territories.first }
|
15
|
+
#let!(:occupant) { subject.occupants.first }
|
16
|
+
|
17
|
+
it 'should have an age' do
|
18
|
+
expect(subject.age).to be_zero
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should generate a territory' do
|
22
|
+
expect(territory).to be_a(Territory)
|
23
|
+
expect(territory.size).to eql(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should indicate what spaces is available' do
|
27
|
+
expect(subject.occupied.size).to eql(1)
|
28
|
+
end
|
29
|
+
|
30
|
+
#it "should construct a field" do
|
31
|
+
# expect(field.any?).to eql(true)
|
32
|
+
# expect(field.width).to eql(width)
|
33
|
+
# expect(field.height).to eql(height)
|
34
|
+
#end
|
35
|
+
|
36
|
+
context "territory creation" do
|
37
|
+
context "with a given density" do
|
38
|
+
let(:density) { 0.3 }
|
39
|
+
let(:projected_count) { (width * height * density).to_i }
|
40
|
+
subject { World.new(geometry, density: density) }
|
41
|
+
let!(:actual_territory_count) { subject.territories.count }
|
42
|
+
|
43
|
+
it "should have territories" do
|
44
|
+
expect(subject.territories).not_to be_empty
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should have the projected number of territories" do
|
48
|
+
expect(actual_territory_count).to eql(projected_count) #width*height*density)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should generate Territory objects with nonzero size" do
|
52
|
+
expect(territory).to be_a(Territory)
|
53
|
+
expect(territory.size).to be > 0
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "with a specified count" do
|
58
|
+
N = 5
|
59
|
+
subject { World.new(geometry, territory_count: N) }
|
60
|
+
it "should have #{N} territories" do
|
61
|
+
expect(subject.territories.count).to eql(N)
|
62
|
+
expect(territory).to be_a(Territory)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
#it "should be occupied" do
|
68
|
+
# expect(subject.occupants).not_to be_empty
|
69
|
+
# expect(occupant).to be_a(Occupant)
|
70
|
+
#end
|
71
|
+
|
72
|
+
context "#step" do
|
73
|
+
it 'should age the world' do
|
74
|
+
expect { subject.step }.to change { subject.age }.by(1)
|
75
|
+
end
|
76
|
+
|
77
|
+
context "growth" do
|
78
|
+
let(:n) { 10 }
|
79
|
+
|
80
|
+
before do
|
81
|
+
n.times { subject.step }
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should not have grown outside the territorial boundary' do
|
85
|
+
within_bounds = subject.occupied.all? { |p| subject.grid.include?(p) }
|
86
|
+
expect(within_bounds).to be(true)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'abstracta'
|
3
|
+
include Abstracta
|
4
|
+
|
5
|
+
describe Abstracta do
|
6
|
+
describe "constants" do
|
7
|
+
it "should have a VERSION constant" do
|
8
|
+
expect(subject.version).to eql("v0.1.0-prealpha \"aqua-prism\", (c) 2014 Joseph Weissman <jweissman1986@gmail.com>")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/thoughts.rb.txt
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# A discrete (ideally tuple-like) value at the
|
2
|
+
# boundaries between adapters and reactors
|
3
|
+
#class AbstractAdapter
|
4
|
+
# def initialize(rx)
|
5
|
+
# @reactor = rx
|
6
|
+
# end
|
7
|
+
|
8
|
+
# def drive
|
9
|
+
# @reactor.engine.turn
|
10
|
+
# end
|
11
|
+
|
12
|
+
# def handle(event)
|
13
|
+
# @reactor.react(event)
|
14
|
+
# end
|
15
|
+
#end
|
16
|
+
|
17
|
+
#class ConsoleAdapter
|
18
|
+
# def handle(event)
|
19
|
+
|
20
|
+
# end
|
21
|
+
#end
|
22
|
+
|
23
|
+
#class AbstractDisruptor
|
24
|
+
# include Disruptor::Processor
|
25
|
+
|
26
|
+
# def initialize(adapter)
|
27
|
+
# @adapter = adapter
|
28
|
+
|
29
|
+
# end
|
30
|
+
|
31
|
+
# def absorb(events)
|
32
|
+
# @adapter.handle(event)
|
33
|
+
# process_event(event)
|
34
|
+
# end
|
35
|
+
|
36
|
+
# def process_event(event)
|
37
|
+
# raise "implement in subclass"
|
38
|
+
# end
|
39
|
+
#end
|
40
|
+
|
41
|
+
#class SimpleDisruptor < AbstractDisruptor
|
42
|
+
|
43
|
+
#end
|
44
|
+
|
45
|
+
|
46
|
+
#class SimpleActor < AbstractReactor
|
47
|
+
# def perform(story) #name, *args, &blk)
|
48
|
+
# process(story.events)
|
49
|
+
# story.events.each do |event|
|
50
|
+
# @engine.adapt do |event|
|
51
|
+
# @disruptor.absorb(event)
|
52
|
+
# end
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#end
|
56
|
+
|
57
|
+
#class WebsocketReactor < SimpleReactor
|
58
|
+
# def react(name, *args, &blk)
|
59
|
+
# raise 'not implemented'
|
60
|
+
# end
|
61
|
+
#end
|
62
|
+
|
63
|
+
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: abstracta
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joseph Weissman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-12-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 4.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 4.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: straightedge
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.1.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.8'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.8'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.4'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.4'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubygems-tasks
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.8'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.8'
|
111
|
+
description: a modular cellular automata engine with the goal of providing a simulation
|
112
|
+
service framework
|
113
|
+
email: jweissman1986@gmail.com
|
114
|
+
executables:
|
115
|
+
- abstracta
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files:
|
118
|
+
- ChangeLog.md
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- thoughts.rb.txt
|
122
|
+
files:
|
123
|
+
- ".coco.yml"
|
124
|
+
- ".document"
|
125
|
+
- ".gitignore"
|
126
|
+
- ".rspec"
|
127
|
+
- ".ruby-version"
|
128
|
+
- ".yardopts"
|
129
|
+
- ChangeLog.md
|
130
|
+
- Gemfile
|
131
|
+
- LICENSE.txt
|
132
|
+
- README.md
|
133
|
+
- Rakefile
|
134
|
+
- abstracta.gemspec
|
135
|
+
- bin/abstracta
|
136
|
+
- features/.gitkeep
|
137
|
+
- features/abstracta.feature
|
138
|
+
- features/step_definitions/.gitkeep
|
139
|
+
- features/step_definitions/abstracta_steps.rb
|
140
|
+
- gemspec.yml
|
141
|
+
- lib/abstracta.rb
|
142
|
+
- lib/abstracta/developer.rb
|
143
|
+
- lib/abstracta/extend/range.rb
|
144
|
+
- lib/abstracta/genome.rb
|
145
|
+
- lib/abstracta/occupant.rb
|
146
|
+
- lib/abstracta/territory.rb
|
147
|
+
- lib/abstracta/version.rb
|
148
|
+
- lib/abstracta/world.rb
|
149
|
+
- script/bootstrap.rb
|
150
|
+
- spec/abstracta/developer_spec.rb
|
151
|
+
- spec/abstracta/genome_spec.rb
|
152
|
+
- spec/abstracta/occupant_spec.rb
|
153
|
+
- spec/abstracta/territory_spec.rb
|
154
|
+
- spec/abstracta/world_spec.rb
|
155
|
+
- spec/abstracta_spec.rb
|
156
|
+
- spec/spec_helper.rb
|
157
|
+
- thoughts.rb.txt
|
158
|
+
homepage: https://rubygems.org/gems/abstracta
|
159
|
+
licenses:
|
160
|
+
- MIT
|
161
|
+
metadata: {}
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.4.3
|
179
|
+
signing_key:
|
180
|
+
specification_version: 4
|
181
|
+
summary: a cellular automation and simulation services library in Ruby
|
182
|
+
test_files: []
|