Commit 3d2a6da
Changed files (4)
spec/cell_spec.rb
@@ -0,0 +1,159 @@
+class Cell
+ attr_accessor :x, :y
+ def initialize(populated: false)
+ @populated = populated
+ end
+
+ def spawn(neighbors)
+ populated_neighbors = neighbors.find_all { |x| x.populated? }
+ if populated?
+ Cell.new(populated: (2...4).include?(populated_neighbors.count))
+ else
+ Cell.new(populated: populated_neighbors.count == 3)
+ end
+ end
+
+ def neighbor?(other_cell)
+ if matches_x?(other_cell) && one_cell_away(other_cell.y - self.y)
+ return true
+ elsif matches_y?(other_cell) && one_cell_away(other_cell.x - self.x)
+ return true
+ end
+ false
+ end
+
+ def populated?
+ @populated
+ end
+
+ private
+
+ def matches_x?(other_cell)
+ other_cell.x == x
+ end
+
+ def matches_y?(other_cell)
+ other_cell.y == y
+ end
+
+ def one_cell_away(difference)
+ difference.abs == 1
+ end
+end
+
+describe Cell do
+ let(:populated_neighbor) { Cell.new(populated: true) }
+ let(:unpopulated_neighbor) { Cell.new(populated: false) }
+
+ context "when populated" do
+ subject { Cell.new(populated: true) }
+
+ it "is populated at creation" do
+ expect(subject.populated?).to be_truthy
+ end
+
+ context "when it has a single populated neighbor" do
+ let(:neighbors) { [populated_neighbor, unpopulated_neighbor] }
+
+ it "dies of isolation" do
+ expect(subject.spawn(neighbors)).to_not be_populated
+ end
+ end
+
+ context 'when it has two populated neighbors' do
+ let(:neighbors) { [populated_neighbor] * 2 }
+
+ it "remains populated" do
+ expect(subject.spawn(neighbors)).to be_populated
+ end
+ end
+
+ context 'when it has three populated neighbors' do
+ let(:neighbors) { [populated_neighbor] * 3 }
+
+ it "remains populated" do
+ expect(subject.spawn(neighbors)).to be_populated
+ end
+ end
+
+ context 'when it has more than three neighbors' do
+ let(:neighbors) { [populated_neighbor] * 4 }
+
+ it "becomes unpopulated" do
+ expect(subject.spawn(neighbors)).to_not be_populated
+ end
+ end
+ end
+
+ context "when unpopulated" do
+ subject { Cell.new(populated: false) }
+ let(:populated_neighbors) { [populated_neighbor] * 2 }
+ let(:neighbors) { populated_neighbors + [unpopulated_neighbor] }
+
+ context "when it has two populated neighbors" do
+ it "remains unpopulated" do
+ expect(subject.spawn(neighbors)).to_not be_populated
+ end
+ end
+ end
+
+ describe "neighbor?" do
+ subject { create_cell(3, 3) }
+
+ context "when other cell is one cell north" do
+ it "returns true" do
+ expect(subject.neighbor?(create_cell(3, 4))).to be_truthy
+ end
+ end
+
+ context "when the other cell is two cells north" do
+ it "returns false" do
+ expect(subject.neighbor?(create_cell(3, 5))).to be_falsey
+ end
+ end
+
+ context "when other cell is one cell east" do
+ it "returns true" do
+ expect(subject.neighbor?(create_cell(4, 3))).to be_truthy
+ end
+ end
+
+ context "when other cell is two cells to the east" do
+ it "returns true" do
+ expect(subject.neighbor?(create_cell(5, 3))).to be_falsey
+ end
+ end
+
+ context "when the other cell is one cell to the south" do
+ it "return true" do
+ expect(subject.neighbor?(create_cell(3, 2))).to be_truthy
+ end
+ end
+
+ context "when the other cell is two cells to the south" do
+ it "returns false" do
+ expect(subject.neighbor?(create_cell(3, 1))).to be_falsey
+ end
+ end
+
+ context "when the other cell is one cell to the west" do
+ it "returns true" do
+ expect(subject.neighbor?(create_cell(2, 3))).to be_truthy
+ end
+ end
+
+ context "when the other cell is two cells to the west" do
+ it "returns true" do
+ expect(subject.neighbor?(create_cell(1, 3))).to be_falsey
+ end
+ end
+
+ def create_cell(x, y)
+ Cell.new.tap do |cell|
+ cell.x = x
+ cell.y = y
+ end
+ end
+ end
+end
+
spec/spec_helper.rb
@@ -0,0 +1,78 @@
+# This file was generated by the `rspec --init` command. Conventionally, all
+# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
+# The generated `.rspec` file contains `--require spec_helper` which will cause this
+# file to always be loaded, without a need to explicitly require it in any files.
+#
+# Given that it is always loaded, you are encouraged to keep this file as
+# light-weight as possible. Requiring heavyweight dependencies from this file
+# will add to the boot time of your test suite on EVERY test run, even for an
+# individual file that may not need all of that loaded. Instead, make a
+# separate helper file that requires this one and then use it only in the specs
+# that actually need it.
+#
+# The `.rspec` file also contains a few flags that are not defaults but that
+# users commonly want.
+#
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+# The settings below are suggested to provide a good initial experience
+# with RSpec, but feel free to customize to your heart's content.
+=begin
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = 'doc'
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # Enable only the newer, non-monkey-patching expect syntax.
+ # For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ expectations.syntax = :expect
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Enable only the newer, non-monkey-patching expect syntax.
+ # For more details, see:
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ mocks.syntax = :expect
+
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended.
+ mocks.verify_partial_doubles = true
+ end
+=end
+end
spec/world_spec.rb
@@ -0,0 +1,33 @@
+class World
+ def initialize(cells)
+ @cells = cells
+ end
+
+ def neighbors_for(cell)
+ @cells.find_all { |x| cell.neighbor?(x) }
+ end
+end
+
+describe World do
+ subject { World.new(cells) }
+ let(:cells) { [neighbor, other_cell] }
+ let(:neighbor) { Object.new }
+ let(:other_cell) { Object.new }
+
+ context "#neighbors_for" do
+ let(:cell) { Object.new }
+
+ before :each do
+ cell.stub(:neighbor?).with(neighbor).and_return(true)
+ cell.stub(:neighbor?).with(other_cell).and_return(false)
+ end
+
+ it "returns the neighboring cells" do
+ expect(subject.neighbors_for(cell)).to include(neighbor)
+ end
+
+ it "does not return cells that are not neighbors" do
+ expect(subject.neighbors_for(cell)).to_not include(other_cell)
+ end
+ end
+end
.rspec
@@ -0,0 +1,3 @@
+--color
+--warnings
+--require spec_helper