conway_deathmatch 0.6.0.1 → 0.6.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: e231c26991bcf255abd1bf05004d79183d025dc8
4
- data.tar.gz: 5d96b4380756b46bd477e4e0baadeae27a9a36d3
2
+ SHA256:
3
+ metadata.gz: d20c966d1ffe3ac886d2315514779065ff4e17a3fd8c3927401eae066642bdba
4
+ data.tar.gz: de732dcf01de06570287852844cd87a6d629b448c27a73b111e4c4cc2cdcdad8
5
5
  SHA512:
6
- metadata.gz: e11495b48f8611b99287e3a77c14d2fe4fdd966c97833d1af41983694642e81729ac3740b9fcf3be1c3b60742f3f922b2562fed8276cef7ef9be367dc24edb84
7
- data.tar.gz: f4fb473dc800d24f7f82bd03af235007dd2f00db3c122e861277b8065f053cafa8852177e0167e0bc4597b582c804a92f8b2ccaffe3e39afab08c38c43bdf634
6
+ metadata.gz: 81dbf4124a1cc0a97410221405f52aad977a378f455f38eddc49896f387891731d32f37d45e0d9bf686cd8453faa6441363e20deb99affdb64c2ed8d4d372743
7
+ data.tar.gz: cce9be6dd2fb3a4dc2b047d39ec56dc14dbc81f2eb3301e4e8822ba11fefd5d3c291d187ede915fac6cf12d547fecfab4a565d88cbe3fc03cb7a7eadebd965e3
data/README.md CHANGED
@@ -1,8 +1,5 @@
1
- [![Build Status](https://travis-ci.org/rickhull/conway_deathmatch.svg?branch=master)](https://travis-ci.org/rickhull/conway_deathmatch)
1
+ [![CI Status](https://github.com/rickhull/conway_deathmatch/actions/workflows/ci.yaml/badge.svg)](https://github.com/rickhull/conway_deathmatch/actions/workflows/ci.yaml)
2
2
  [![Gem Version](https://badge.fury.io/rb/conway_deathmatch.svg)](http://badge.fury.io/rb/conway_deathmatch)
3
- [![Code Climate](https://codeclimate.com/github/rickhull/conway_deathmatch/badges/gpa.svg)](https://codeclimate.com/github/rickhull/conway_deathmatch)
4
- [![Dependency Status](https://gemnasium.com/rickhull/conway_deathmatch.svg)](https://gemnasium.com/rickhull/conway_deathmatch)
5
- [![Security Status](https://hakiri.io/github/rickhull/conway_deathmatch/master.svg)](https://hakiri.io/github/rickhull/conway_deathmatch/master)
6
3
 
7
4
  Introduction
8
5
  ===
data/Rakefile CHANGED
@@ -1,20 +1,20 @@
1
1
  require 'rake/testtask'
2
+
2
3
  desc "Run tests"
3
4
  Rake::TestTask.new do |t|
4
5
  t.name = "test"
5
- t.pattern = "test/test_*.rb"
6
- # t.warning = true
6
+ t.pattern = "test/*.rb"
7
+ t.warning = true
7
8
  end
8
9
 
9
10
  desc "Run benchmarks"
10
11
  Rake::TestTask.new do |t|
11
12
  t.name = "bench"
12
- t.pattern = "test/bench_*.rb"
13
- # t.warning = true
13
+ t.pattern = "test/bench/*.rb"
14
+ t.warning = true
14
15
  end
15
16
 
16
- task default: %w[test bench]
17
-
17
+ task default: :test
18
18
 
19
19
  #
20
20
  # METRICS
@@ -37,8 +37,8 @@ end
37
37
  begin
38
38
  require 'flay_task'
39
39
  FlayTask.new do |t|
40
- t.verbose = true
41
40
  t.dirs = ['lib']
41
+ t.verbose = true
42
42
  end
43
43
  metrics_tasks << :flay
44
44
  rescue LoadError
@@ -58,7 +58,7 @@ task code_metrics: metrics_tasks
58
58
 
59
59
 
60
60
  #
61
- # PROFILING TASKS
61
+ # PROFILING
62
62
  #
63
63
 
64
64
  desc "Show current system load"
@@ -66,29 +66,34 @@ task "loadavg" do
66
66
  puts File.read "/proc/loadavg"
67
67
  end
68
68
 
69
- rubylib = "RUBYLIB=lib"
70
- rubyprof = "ruby-prof -m1"
71
- scriptname = "bin/conway_deathmatch"
72
- scriptargs = "-n 100 -s 0 --renderfinal"
69
+ def lib_sh(cmd)
70
+ sh "RUBYLIB=lib #{cmd}"
71
+ end
72
+
73
+ def rprof_sh(script, args, rprof_args = '')
74
+ lib_sh ['ruby-prof', rprof_args, script, '--', args].join(' ')
75
+ end
76
+
77
+ rprof_args = "-m1"
78
+ xname = "bin/conway_deathmatch"
79
+ xargs = "-n 100 -s 0 --renderfinal"
73
80
 
74
81
  desc "Run ruby-prof on bin/conway_deathmatch (100 ticks)"
75
82
  task "ruby-prof" => "loadavg" do
76
- sh [rubylib, rubyprof, scriptname, '--', scriptargs].join(' ')
83
+ rprof_sh xname, xargs, rprof_args
77
84
  end
78
85
 
79
86
  desc "Run ruby-prof with --exclude-common-cycles"
80
87
  task "ruby-prof-exclude" => "ruby-prof" do
81
- sh [rubylib,
82
- rubyprof, '--exclude-common-cycles', scriptname, '--',
83
- scriptargs].join(' ')
88
+ rprof_sh xname, xargs, "#{rprof_args} --exclude-common-cycles"
84
89
  end
85
90
 
86
91
  task "no-prof" do
87
- sh [rubylib, scriptname, scriptargs].join(' ')
92
+ lib_sh [xname, xargs].join(' ')
88
93
  end
89
94
 
90
95
  #
91
- # GEM BUILD / PUBLISH TASKS
96
+ # GEM BUILD / PUBLISH
92
97
  #
93
98
 
94
99
  begin
@@ -109,4 +114,4 @@ end
109
114
  #
110
115
 
111
116
  desc "Rake tasks for travis to run"
112
- task travis: %w[test bench no-prof]
117
+ task travis: %w[test no-prof]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0.1
1
+ 0.6.2.1
@@ -1,37 +1,19 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'conway_deathmatch'
3
- s.version = File.read(File.join(__dir__, 'VERSION')).chomp
4
- s.summary = "Conway's Game of Life"
5
- s.description = "Deathmatch"
3
+ s.summary = "Conway's Game of Life, Deathmatch Edition (tm)"
4
+ s.description = "Several distinct populations competing for survival"
6
5
  s.authors = ["Rick Hull"]
7
6
  s.homepage = 'https://github.com/rickhull/conway_deathmatch'
8
7
  s.license = 'GPL'
9
- s.files = [
10
- 'conway_deathmatch.gemspec',
11
- 'VERSION',
12
- 'Rakefile',
13
- 'README.md',
14
- 'lib/conway_deathmatch.rb',
15
- 'lib/conway_deathmatch/shapes.rb',
16
- 'lib/conway_deathmatch/shapes/classic.yaml',
17
- 'lib/conway_deathmatch/shapes/discovered.yaml',
18
- 'bin/conway_deathmatch',
19
- 'bin/proving_ground',
20
- 'test/bench_grid.rb',
21
- 'test/spec_helper.rb',
22
- 'test/test_grid.rb',
23
- 'test/test_shapes.rb',
24
- ]
8
+
9
+ s.required_ruby_version = ">= 2"
10
+ s.version = File.read(File.join(__dir__, 'VERSION')).chomp
11
+
12
+ s.files = %w[conway_deathmatch.gemspec VERSION Rakefile README.md]
13
+ s.files += Dir['lib/**/*.rb']
14
+ s.files += Dir['test/**/*.rb']
15
+ s.files += Dir['bin/**/*.rb']
25
16
  s.executables = ['conway_deathmatch']
26
- s.add_runtime_dependency "slop", "~> 4.0"
27
- s.add_development_dependency "buildar", "~> 3"
28
- s.add_development_dependency "minitest", "~> 5"
29
- s.add_development_dependency "ruby-prof", "~> 0"
30
- s.add_development_dependency "flog", "~> 4.0"
31
- s.add_development_dependency "flay", "~> 2.0"
32
- s.add_development_dependency "roodi", ">= 4", "< 6"
33
17
 
34
- # uncomment and set ENV['CODE_COVERAGE']
35
- # s.add_development_dependency "simplecov", "~> 0.9.0"
36
- s.required_ruby_version = "~> 2"
18
+ s.add_runtime_dependency "slop", "~> 4.0"
37
19
  end
@@ -1,15 +1,8 @@
1
- #require 'conway_deathmatch/shapes'
2
- #require 'lager'
3
-
4
1
  # Provides a 2d array for the grid
5
2
  # Implements standard and deathmatch evaluation rules
6
3
  # Boundaries are toroidal: they wrap in each direction
7
4
  #
8
5
  class ConwayDeathmatch
9
- #extend Lager
10
- #log_to $stderr
11
- class BoundsError < RuntimeError; end
12
-
13
6
  DEAD = '.'
14
7
  ALIVE = '0'
15
8
 
@@ -27,7 +20,6 @@ class ConwayDeathmatch
27
20
  @height = height
28
21
  @grid = self.class.new_grid(width, height)
29
22
  @deathmatch = deathmatch
30
- #@lager = self.class.lager
31
23
  end
32
24
 
33
25
  # Conway's Game of Life transition rules
File without changes
data/test/grid.rb ADDED
@@ -0,0 +1,127 @@
1
+ require_relative 'helper'
2
+ require 'conway_deathmatch'
3
+
4
+ describe ConwayDeathmatch do
5
+ before do
6
+ @alive = ConwayDeathmatch::ALIVE
7
+ @dead = ConwayDeathmatch::DEAD
8
+ end
9
+
10
+ describe "an empty grid" do
11
+ before do
12
+ @x = 5
13
+ @y = 5
14
+ @grid = ConwayDeathmatch.new(@x, @y)
15
+ end
16
+
17
+ it "consists entirely of dead cells" do
18
+ expect(@grid.population[@dead]).must_equal @x * @y
19
+ expect(@grid.population.keys.length).must_equal 1
20
+ end
21
+
22
+ it "stays dead after a tick" do
23
+ expect(@grid.tick.population[@dead]).must_equal @x*@y
24
+ expect(@grid.population.keys.length).must_equal 1
25
+ end
26
+
27
+ it "can be populated by a 2x2 block" do
28
+ @grid.populate 1,1
29
+ @grid.populate 1,2
30
+ @grid.populate 2,1
31
+ @grid.populate 2,2
32
+
33
+ expect(@grid.population[@dead]).must_equal @x * @y - 4
34
+ expect(@grid.population[@alive]).must_equal 4
35
+
36
+ 0.upto(4) { |x|
37
+ 0.upto(4) { |y|
38
+ if x.between?(1, 2) and y.between?(1, 2)
39
+ expect(@grid.value(x, y)).must_equal @alive
40
+ else
41
+ expect(@grid.value(x, y)).must_equal @dead
42
+ end
43
+ }
44
+ }
45
+ end
46
+ end
47
+
48
+ describe "aggressive deathmatch" do
49
+ it "allows survivors to switch sides" do
50
+ # don't risk an infinite loop if a bug is present
51
+ 99.times {
52
+ @grid = ConwayDeathmatch.new(5, 3, :aggressive)
53
+ @grid.populate(1, 1, :team) # friendly
54
+ @grid.populate(2, 1, :team) # friendly; eventual survivor
55
+ @grid.populate(3, 1, :hostile) # enemy
56
+
57
+ @grid.tick
58
+
59
+ # exit the loop when the survivor switches sides
60
+ break if @grid.value(2, 1) == :hostile
61
+ }
62
+
63
+ # expect to fail once per 2^99 runs
64
+ expect(@grid.population.fetch(:team)).must_equal 2
65
+ expect(@grid.population.fetch(:hostile)).must_equal 1
66
+
67
+ # survivors
68
+ team = [[2, 0], [2, 2]]
69
+ hostile = [[2, 1]]
70
+
71
+ 0.upto(4) { |x|
72
+ 0.upto(2) { |y|
73
+ val = team.include?([x, y]) ? :team :
74
+ (hostile.include?([x, y]) ? :hostile : @dead)
75
+ expect(@grid.value(x, y)).must_equal(val)
76
+ }
77
+ }
78
+ end
79
+ end
80
+
81
+ describe "defensive deathmatch" do
82
+ it "won't allow survivors to switch sides" do
83
+ 16.times {
84
+ @grid = ConwayDeathmatch.new(5, 3, :defensive)
85
+ @grid.populate(1, 1, :team) # friendly
86
+ @grid.populate(2, 1, :team) # survivor
87
+ @grid.populate(3, 1, :hostile) # enemy
88
+ @grid.tick
89
+
90
+ expect(@grid.population.fetch(:team)).must_equal 3
91
+ 0.upto(4) { |x|
92
+ 0.upto(2) { |y|
93
+ if x == 2 and y.between?(0, 2)
94
+ expect(@grid.value(x, y)).must_equal :team
95
+ else
96
+ expect(@grid.value(x, y)).must_equal @dead
97
+ end
98
+ }
99
+ }
100
+ }
101
+ end
102
+ end
103
+
104
+ describe "friendly deathmatch" do
105
+ it "allows survivors even with excess hostiles nearby" do
106
+ @grid = ConwayDeathmatch.new(5, 5, :friendly)
107
+ @grid.populate(1, 2, :team) # friendly
108
+ @grid.populate(2, 2, :team) # friendly, eventual survivor
109
+ @grid.populate(3, 2, :team) # friendly
110
+ @grid.populate(2, 1, :hostile) # enemy
111
+ @grid.populate(2, 3, :hostile) # enemy
112
+ expect(@grid.population.fetch(:team)).must_equal 3
113
+ expect(@grid.population.fetch(:hostile)).must_equal 2
114
+
115
+ @grid.tick
116
+
117
+ # (2,2) alive despite 4 neighbors (2 friendly); now all else @dead
118
+ expect(@grid.population.fetch(:team)).must_equal 1
119
+ 0.upto(4) { |x|
120
+ 0.upto(4) { |y|
121
+ expect(@grid.value(x, y)).
122
+ must_equal(x == 2 && y == 2 ? :team : @dead)
123
+ }
124
+ }
125
+ end
126
+ end
127
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,50 @@
1
+ if ENV['CODE_COVERAGE'] and
2
+ !%w[false no].include?(ENV['CODE_COVERAGE'].downcase)
3
+
4
+ require 'simplecov'
5
+ require "simplecov_json_formatter"
6
+
7
+ class SimpleCov::Formatter::TextFormatter
8
+ FILENAME = 'metrics/coverage'
9
+
10
+ def format(result)
11
+ tot = result.files
12
+ rpt = ["Coverage: %0.1f%%" % tot.covered_percent,
13
+ "Strength: %0.2f" % tot.covered_strength,
14
+ " Lines: %i" % tot.lines_of_code,
15
+ " Covered: %i" % tot.covered_lines,
16
+ " N/A: %i" % tot.never_lines,
17
+ ]
18
+ if tot.missed_lines > 0
19
+ rpt << "Missed: %i" % tot.missed_lines
20
+ end
21
+ if tot.skipped_lines > 0
22
+ rpt << "Skipped: %i" % tot.skipped_lines
23
+ end
24
+ rpt << result.files.map { |sfile|
25
+ "%i%% (%i/%i)\t%s" % [sfile.covered_percent,
26
+ sfile.covered_lines.length,
27
+ sfile.lines_of_code,
28
+ sfile.filename]
29
+ }.join("\n")
30
+ rpt = rpt.join("\n")
31
+
32
+ puts
33
+ puts rpt
34
+ if File.writable?(FILENAME)
35
+ File.open(FILENAME, 'w') { |f|
36
+ f.write(rpt + "\n")
37
+ }
38
+ puts "wrote #{FILENAME}"
39
+ end
40
+ end
41
+ end
42
+
43
+ SimpleCov.formatters = [
44
+ SimpleCov::Formatter::TextFormatter,
45
+ SimpleCov::Formatter::JSONFormatter,
46
+ ]
47
+
48
+ SimpleCov.start
49
+ end
50
+ require 'minitest/autorun'
data/test/shapes.rb ADDED
@@ -0,0 +1,9 @@
1
+ require_relative 'helper'
2
+ require 'conway_deathmatch/shapes'
3
+
4
+ describe ConwayDeathmatch::Shapes do
5
+ it "recognizes acorn" do
6
+ expect(ConwayDeathmatch::Shapes.classic.fetch("acorn")).
7
+ must_be_instance_of Array
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'helper'
2
+ require 'conway_deathmatch'
3
+ require 'conway_deathmatch/shapes'
4
+
5
+ describe "Shapes on the grid" do
6
+ before do
7
+ @alive = ConwayDeathmatch::ALIVE
8
+ @grid = ConwayDeathmatch.new(20, 20)
9
+ ConwayDeathmatch::Shapes.add(@grid, "acorn 0 0")
10
+ end
11
+
12
+ it "recognizes \"acorn 0 0\"" do
13
+ ConwayDeathmatch::Shapes.classic.fetch("acorn").each { |xy_ary|
14
+ expect(@grid.value(*xy_ary)).must_equal @alive
15
+ }
16
+ expect(@grid.population.fetch(@alive)).must_equal 7
17
+ end
18
+
19
+ it "ticks correctly" do
20
+ @grid.tick
21
+ new_points = [
22
+ [0, 1],
23
+ [1, 1],
24
+ [2, 1],
25
+ [4, 1],
26
+ [4, 2],
27
+ [5, 1],
28
+ [5, 2],
29
+ [5, 3],
30
+ ].each { |xy_ary|
31
+ expect(@grid.value(*xy_ary)).must_equal @alive
32
+ }
33
+ expect(@grid.population.fetch(@alive)).must_equal new_points.length
34
+ end
35
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: conway_deathmatch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0.1
4
+ version: 0.6.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-11 00:00:00.000000000 Z
11
+ date: 1980-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: slop
@@ -24,98 +24,8 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
- - !ruby/object:Gem::Dependency
28
- name: buildar
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '3'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3'
41
- - !ruby/object:Gem::Dependency
42
- name: minitest
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '5'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '5'
55
- - !ruby/object:Gem::Dependency
56
- name: ruby-prof
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: flog
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '4.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '4.0'
83
- - !ruby/object:Gem::Dependency
84
- name: flay
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "~>"
88
- - !ruby/object:Gem::Version
89
- version: '2.0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - "~>"
95
- - !ruby/object:Gem::Version
96
- version: '2.0'
97
- - !ruby/object:Gem::Dependency
98
- name: roodi
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '4'
104
- - - "<"
105
- - !ruby/object:Gem::Version
106
- version: '6'
107
- type: :development
108
- prerelease: false
109
- version_requirements: !ruby/object:Gem::Requirement
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- version: '4'
114
- - - "<"
115
- - !ruby/object:Gem::Version
116
- version: '6'
117
- description: Deathmatch
118
- email:
27
+ description: Several distinct populations competing for survival
28
+ email:
119
29
  executables:
120
30
  - conway_deathmatch
121
31
  extensions: []
@@ -125,27 +35,25 @@ files:
125
35
  - Rakefile
126
36
  - VERSION
127
37
  - bin/conway_deathmatch
128
- - bin/proving_ground
129
38
  - conway_deathmatch.gemspec
130
39
  - lib/conway_deathmatch.rb
131
40
  - lib/conway_deathmatch/shapes.rb
132
- - lib/conway_deathmatch/shapes/classic.yaml
133
- - lib/conway_deathmatch/shapes/discovered.yaml
134
- - test/bench_grid.rb
135
- - test/spec_helper.rb
136
- - test/test_grid.rb
137
- - test/test_shapes.rb
41
+ - test/bench/grid.rb
42
+ - test/grid.rb
43
+ - test/helper.rb
44
+ - test/shapes.rb
45
+ - test/shapes_on_grid.rb
138
46
  homepage: https://github.com/rickhull/conway_deathmatch
139
47
  licenses:
140
48
  - GPL
141
49
  metadata: {}
142
- post_install_message:
50
+ post_install_message:
143
51
  rdoc_options: []
144
52
  require_paths:
145
53
  - lib
146
54
  required_ruby_version: !ruby/object:Gem::Requirement
147
55
  requirements:
148
- - - "~>"
56
+ - - ">="
149
57
  - !ruby/object:Gem::Version
150
58
  version: '2'
151
59
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -154,9 +62,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
154
62
  - !ruby/object:Gem::Version
155
63
  version: '0'
156
64
  requirements: []
157
- rubyforge_project:
158
- rubygems_version: 2.6.8
159
- signing_key:
65
+ rubygems_version: 3.4.4
66
+ signing_key:
160
67
  specification_version: 4
161
- summary: Conway's Game of Life
68
+ summary: Conway's Game of Life, Deathmatch Edition (tm)
162
69
  test_files: []
data/bin/proving_ground DELETED
@@ -1,136 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'set'
4
- require 'slop'
5
- require 'conway_deathmatch'
6
- require 'conway_deathmatch/shapes'
7
-
8
- # process cmdline options
9
- #
10
- opts = Slop.parse do |o|
11
- o.banner = "Usage: proving_ground [options]"
12
- o.int '-w', '--width', 'Grid width'
13
- o.int '--height', 'Grid height'
14
- o.int '-n', '--num_ticks', 'Max number of ticks to generate'
15
- o.int '-p', '--num_points', 'Number of points to generate'
16
- o.int '-m', '--max_collisions', 'Max number of generation collisions'
17
- o.on '-h', '--help' do
18
- puts o
19
- exit
20
- end
21
- end
22
-
23
- num_points = opts[:num_points] || 5
24
- width = opts[:width] || num_points * 5
25
- height = opts[:height] || num_points * 5
26
- num_ticks = opts[:num_ticks] || num_points * 5
27
- max_c = opts[:max_collisions] || num_points ** 2
28
-
29
- # choose center point
30
- # choose next point within 2 units randomly
31
- def generate_points(num_points, width, height)
32
- points = [[width / 2, height / 2]]
33
- count = 0
34
- while points.length < num_points
35
- raise("sanity check failed") if count > num_points * 2
36
- count += 1
37
- next_p = next_point(points.sample)
38
- if next_p[0].between?(0, width - 1) and next_p[1].between?(0, height - 1)
39
- points << next_p unless points.include?(next_p)
40
- else
41
- # debug
42
- puts "#{next_p.inspect} out of bounds"
43
- end
44
- end
45
- points
46
- end
47
-
48
- def next_point(prev_point)
49
- prev_point.map { |dim| random_within(dim, 2) }
50
- end
51
-
52
- def random_within(x, dist)
53
- new_x = x - dist + rand(dist * 2)
54
- new_x += 1 if new_x >= x
55
- new_x
56
- end
57
-
58
- def shape_str(points)
59
- points.map { |point| "p #{point[0]} #{point[1]}" }.join(' ')
60
- end
61
-
62
- def conclude!(results, w, h)
63
- results.each { |k, res|
64
- puts "#{k} population: #{res[0]}"
65
- puts "shape_str: #{shape_str(res[1])}"
66
- puts
67
- }
68
- puts "Grid: #{w}x#{h}"
69
-
70
- exit 0
71
- end
72
-
73
- results = { final: [0, []], peak: [0, []], score: [0, []], }
74
- seen = Set.new
75
- collisions = 0
76
-
77
- Signal.trap("INT") { conclude!(results, width, height) }
78
-
79
- loop {
80
- # generate a random shape
81
- points = generate_points(num_points, width, height)
82
-
83
- # have we seen these points before?
84
- hsh = points.hash
85
- if seen.member? hsh
86
- collisions += 1
87
- puts "X" * collisions
88
- break if collisions > max_c # exit the loop, stop generating points
89
- next
90
- end
91
- seen << hsh
92
-
93
- # initialize grid with generated shape
94
- b = ConwayDeathmatch.new(width, height)
95
- b.add_points(points)
96
-
97
- current = peak = score = 0 # track population (results)
98
- static_count = 0 # detect a stabilized grid
99
-
100
- # iterate the game of life
101
- num_ticks.times { |i|
102
- b.tick
103
-
104
- # evaluate grid
105
- last = current
106
- current = b.population[ConwayDeathmatch::ALIVE]
107
- peak = current if current > peak
108
- score += current * i
109
-
110
- # cease ticks for static or (soon-to-be) empty grids
111
- break if current < 3
112
- static_count = (current == last ? static_count + 1 : 0)
113
- break if static_count > 3
114
- }
115
-
116
- puts "#{current} (#{peak}) [#{score}]"
117
-
118
- # track the highest populators
119
- #
120
- if current > results[:final][0]
121
- results[:final] = [current, points]
122
- puts "\tLargest final: #{current}"
123
- end
124
-
125
- if peak > results[:peak][0]
126
- results[:peak] = [peak, points]
127
- puts "\tLargest peak: #{peak}"
128
- end
129
-
130
- if score > results[:score][0]
131
- results[:score] = [score, points]
132
- puts "\tLargest score: #{score}"
133
- end
134
- }
135
-
136
- conclude!(results, width, height)
@@ -1,175 +0,0 @@
1
- acorn:
2
- - [0, 2]
3
- - [1, 0]
4
- - [1, 2]
5
- - [3, 1]
6
- - [4, 2]
7
- - [5, 2]
8
- - [6, 2]
9
-
10
- beacon:
11
- - [0, 0]
12
- - [0, 1]
13
- - [1, 0]
14
- - [2, 3]
15
- - [3, 2]
16
- - [3, 3]
17
-
18
- beehive:
19
- - [0, 1]
20
- - [1, 0]
21
- - [1, 2]
22
- - [2, 0]
23
- - [2, 2]
24
- - [3, 1]
25
-
26
- blinker:
27
- - [0, 1]
28
- - [1, 1]
29
- - [2, 1]
30
-
31
- block:
32
- - [0, 0]
33
- - [0, 1]
34
- - [1, 0]
35
- - [1, 1]
36
-
37
- block_engine_count:
38
- - [0, 5]
39
- - [2, 4]
40
- - [2, 5]
41
- - [4, 1]
42
- - [4, 2]
43
- - [4, 3]
44
- - [6, 0]
45
- - [6, 1]
46
- - [6, 2]
47
- - [7, 1]
48
-
49
- block_engine_space:
50
- - [0, 0]
51
- - [0, 1]
52
- - [1, 0]
53
- - [1, 3]
54
- - [2, 0]
55
- - [2, 3]
56
- - [2, 4]
57
- - [3, 2]
58
- - [4, 0]
59
- - [4, 2]
60
- - [4, 3]
61
- - [4, 4]
62
-
63
- block_engine_stripe:
64
- - [0, 0]
65
- - [1, 0]
66
- - [2, 0]
67
- - [3, 0]
68
- - [4, 0]
69
- - [5, 0]
70
- - [6, 0]
71
- - [7, 0]
72
- - [9, 0]
73
- - [10, 0]
74
- - [11, 0]
75
- - [12, 0]
76
- - [13, 0]
77
- - [17, 0]
78
- - [18, 0]
79
- - [19, 0]
80
- - [26, 0]
81
- - [27, 0]
82
- - [28, 0]
83
- - [29, 0]
84
- - [30, 0]
85
- - [31, 0]
86
- - [32, 0]
87
- - [34, 0]
88
- - [35, 0]
89
- - [36, 0]
90
- - [37, 0]
91
- - [38, 0]
92
-
93
- boat:
94
- - [0, 0]
95
- - [0, 1]
96
- - [1, 0]
97
- - [1, 2]
98
- - [2, 1]
99
-
100
- diehard:
101
- - [0, 1]
102
- - [1, 1]
103
- - [1, 2]
104
- - [5, 2]
105
- - [6, 0]
106
- - [6, 2]
107
- - [7, 2]
108
-
109
- glider:
110
- - [0, 2]
111
- - [1, 0]
112
- - [1, 2]
113
- - [2, 1]
114
- - [2, 2]
115
-
116
- loaf:
117
- - [0, 1]
118
- - [1, 0]
119
- - [1, 2]
120
- - [2, 0]
121
- - [2, 3]
122
- - [3, 1]
123
- - [3, 2]
124
-
125
- lwss:
126
- - [0, 1]
127
- - [0, 3]
128
- - [1, 0]
129
- - [2, 0]
130
- - [3, 0]
131
- - [3, 3]
132
- - [4, 0]
133
- - [4, 1]
134
- - [4, 2]
135
-
136
- rpent:
137
- - [0, 1]
138
- - [1, 0]
139
- - [1, 1]
140
- - [1, 2]
141
- - [2, 0]
142
-
143
- swastika:
144
- - [0, 0]
145
- - [0, 1]
146
- - [0, 2]
147
- - [0, 3]
148
- - [0, 6]
149
- - [1, 3]
150
- - [1, 6]
151
- - [2, 3]
152
- - [2, 6]
153
- - [3, 0]
154
- - [3, 1]
155
- - [3, 2]
156
- - [3, 4]
157
- - [3, 5]
158
- - [3, 6]
159
- - [4, 0]
160
- - [4, 3]
161
- - [5, 0]
162
- - [5, 3]
163
- - [6, 0]
164
- - [6, 3]
165
- - [6, 4]
166
- - [6, 5]
167
- - [6, 6]
168
-
169
- toad:
170
- - [0, 1]
171
- - [0, 2]
172
- - [1, 3]
173
- - [2, 0]
174
- - [3, 1]
175
- - [3, 2]
@@ -1,112 +0,0 @@
1
- # discovered via proving_ground
2
- a4: # boner
3
- - [0, 2]
4
- - [1, 0]
5
- - [1, 1]
6
- - [2, 2]
7
-
8
- a5:
9
- - [0, 1]
10
- - [1, 3]
11
- - [2, 2]
12
- - [3, 0]
13
- - [3, 2]
14
-
15
- b5:
16
- - [0, 0]
17
- - [1, 0]
18
- - [1, 1]
19
- - [1, 2]
20
- - [2, 1]
21
-
22
- c5:
23
- - [0, 1]
24
- - [1, 0]
25
- - [1, 2]
26
- - [2, 2]
27
- - [3, 3]
28
-
29
- d5: # peace
30
- - [0, 2]
31
- - [1, 0]
32
- - [1, 1]
33
- - [2, 1]
34
- - [3, 0]
35
-
36
-
37
- e5: # cross (peace + 1)
38
- - [0, 1]
39
- - [1, 0]
40
- - [1, 1]
41
- - [1, 2]
42
- - [2, 1]
43
-
44
- a6: # zed
45
- - [0, 2]
46
- - [1, 0]
47
- - [1, 1]
48
- - [2, 3]
49
- - [2, 4]
50
- - [3, 2]
51
-
52
- b6: # trinary
53
- - [0, 2]
54
- - [1, 1]
55
- - [2, 2]
56
- - [2, 3]
57
- - [3, 2]
58
- - [4, 0]
59
-
60
- c6: # cardioid
61
- - [0, 0]
62
- - [0, 1]
63
- - [1, 1]
64
- - [1, 2]
65
- - [2, 0]
66
- - [2, 1]
67
-
68
- a7:
69
- - [0, 3]
70
- - [1, 4]
71
- - [1, 1]
72
- - [2, 0]
73
- - [2, 1]
74
- - [3, 2]
75
- - [4, 1]
76
-
77
- b7: # rocket
78
- - [0, 0]
79
- - [0, 1]
80
- - [0, 2]
81
- - [1, 3]
82
- - [2, 0]
83
- - [2, 1]
84
- - [2, 2]
85
-
86
- c7: # urawizardarry
87
- - [0, 0]
88
- - [1, 1]
89
- - [2, 2]
90
- - [3, 2]
91
- - [4, 3]
92
- - [5, 4]
93
- - [5, 5]
94
-
95
- d7: # A
96
- - [0, 2]
97
- - [1, 1]
98
- - [1, 3]
99
- - [2, 0]
100
- - [2, 1]
101
- - [2, 3]
102
- - [2, 4]
103
-
104
- a8: # A
105
- - [0, 2]
106
- - [1, 1]
107
- - [1, 2]
108
- - [2, 0]
109
- - [2, 1]
110
- - [3, 1]
111
- - [3, 2]
112
- - [4, 2]
data/test/spec_helper.rb DELETED
@@ -1,12 +0,0 @@
1
- if ENV['CODE_COVERAGE']
2
- require 'simplecov'
3
- SimpleCov.start
4
- end
5
-
6
- require 'minitest/autorun'
7
- require 'conway_deathmatch'
8
- require 'conway_deathmatch/shapes'
9
-
10
- ALIVE = ConwayDeathmatch::ALIVE
11
- DEAD = ConwayDeathmatch::DEAD
12
- Shapes = ConwayDeathmatch::Shapes
data/test/test_grid.rb DELETED
@@ -1,141 +0,0 @@
1
- require_relative './spec_helper'
2
-
3
- describe ConwayDeathmatch do
4
- describe "an empty grid" do
5
- before do
6
- @x = 5
7
- @y = 5
8
- @grid = ConwayDeathmatch.new(@x, @y)
9
- end
10
-
11
- it "must have dead population" do
12
- @grid.population[DEAD].must_equal @x * @y
13
- @grid.population.keys.length.must_equal 1
14
- end
15
-
16
- it "must still be dead after a tick" do
17
- @grid.tick.population[DEAD].must_equal @x*@y
18
- @grid.population.keys.length.must_equal 1
19
- end
20
-
21
- it "must accept a block" do
22
- @grid.populate 1,1
23
- @grid.populate 1,2
24
- @grid.populate 2,1
25
- @grid.populate 2,2
26
-
27
- @grid.population[DEAD].must_equal @x * @y - 4
28
- @grid.population[ALIVE].must_equal 4
29
-
30
- 0.upto(4) { |x|
31
- 0.upto(4) { |y|
32
- if x.between?(1, 2) and y.between?(1, 2)
33
- @grid.value(x, y).must_equal ALIVE
34
- else
35
- @grid.value(x, y).must_equal DEAD
36
- end
37
- }
38
- }
39
- end
40
- end
41
-
42
- describe "adding shapes" do
43
- before do
44
- @grid = ConwayDeathmatch.new(40, 40)
45
- Shapes.add(@grid, "acorn 0 0")
46
- end
47
-
48
- it "must recognize \"acorn 0 0\"" do
49
- Shapes.classic.fetch("acorn").each { |xy_ary|
50
- @grid.value(*xy_ary).must_equal ALIVE
51
- }
52
- @grid.population.fetch(ALIVE).must_equal 7
53
- end
54
-
55
- it "must tick correctly" do
56
- @grid.tick
57
- new_points = [
58
- [0, 1],
59
- [1, 1],
60
- [2, 1],
61
- [4, 1],
62
- [4, 2],
63
- [5, 1],
64
- [5, 2],
65
- [5, 3],
66
- ].each { |xy_ary|
67
- @grid.value(*xy_ary).must_equal ALIVE
68
- }
69
- @grid.population.fetch(ALIVE).must_equal new_points.length
70
- end
71
- end
72
-
73
- describe "aggressive deathmatch" do
74
- it "must allow survivors to switch sides" do
75
- 32.times {
76
- @grid = ConwayDeathmatch.new(5, 3, :aggressive)
77
- @grid.populate(1, 1, '1') # friendly
78
- @grid.populate(2, 1, '1') # survivor
79
- @grid.populate(3, 1, '2') # enemy
80
-
81
- @grid.tick
82
- break if @grid.value(2, 1) == '2'
83
- }
84
-
85
- @grid.population.fetch('1').must_equal 2
86
- @grid.population.fetch('2').must_equal 1
87
- 0.upto(4) { |x|
88
- 0.upto(2) { |y|
89
- if x == 2 and y.between?(0, 2)
90
- @grid.value(x, y).must_equal(y == 1 ? '2' : '1')
91
- else
92
- @grid.value(x, y).must_equal DEAD
93
- end
94
- }
95
- }
96
- end
97
- end
98
-
99
- describe "defensive deathmatch" do
100
- it "must not allow survivors to switch sides" do
101
- 16.times {
102
- @grid = ConwayDeathmatch.new(5, 3, :defensive)
103
- @grid.populate(1, 1, '1') # friendly
104
- @grid.populate(2, 1, '1') # survivor
105
- @grid.populate(3, 1, '2') # enemy
106
- @grid.tick
107
-
108
- @grid.population.fetch('1').must_equal 3
109
- 0.upto(4) { |x|
110
- 0.upto(2) { |y|
111
- if x == 2 and y.between?(0, 2)
112
- @grid.value(x, y).must_equal '1'
113
- else
114
- @grid.value(x, y).must_equal DEAD
115
- end
116
- }
117
- }
118
- }
119
- end
120
- end
121
-
122
- describe "friendly deathmatch" do
123
- it "must allow survivors with excess hostiles nearby" do
124
- @grid = ConwayDeathmatch.new(5, 5, :friendly)
125
- @grid.populate(1, 2, '1') # friendly
126
- @grid.populate(2, 2, '1') # survivor
127
- @grid.populate(3, 2, '1') # friendly
128
- @grid.populate(2, 1, '2') # enemy
129
- @grid.populate(2, 3, '2') # enemy
130
- @grid.tick
131
-
132
- @grid.population.fetch('1').must_equal 1
133
- # (2,2) alive despite 4 neighbors, only 2 friendly; all else DEAD
134
- 0.upto(4) { |x|
135
- 0.upto(4) { |y|
136
- @grid.value(x, y).must_equal (x == 2 && y == 2 ? '1' : DEAD)
137
- }
138
- }
139
- end
140
- end
141
- end
data/test/test_shapes.rb DELETED
@@ -1,15 +0,0 @@
1
- require_relative './spec_helper'
2
-
3
- describe Shapes do
4
- it "must recognize acorn" do
5
- Shapes.classic.fetch("acorn").must_be_instance_of Array
6
- end
7
-
8
- it "must confirm acorn on the grid" do
9
- @grid = ConwayDeathmatch.new(20, 20)
10
- Shapes.add(@grid, "acorn 0 0")
11
- Shapes.classic.fetch("acorn").each { |xy_ary|
12
- @grid.value(*xy_ary).must_equal ALIVE
13
- }
14
- end
15
- end