1

私はRuby で Game of Life を実装しています。これまでのところ、次のようなものがあります。

class Cell

  attr_accessor :world, :x, :y

  def initialize(world=World.new, x=0, y=0)
    @world = world
    @world.cells << self
    @x = x
    @y = y
  end

  def neighbours
    @neighbours = []
    world.cells.each do |cell|

      # Detects neighbour to the north
      if self.x == cell.x && self.y == cell.y - 1
        @neighbours << cell
      end
      # Detects neighbour to the north-east
      if self.x == cell.x - 1 && self.y == cell.y - 1
        @neighbours << cell
      end
      # Detects neighbour to the east
      if self.x == cell.x - 1 && self.y == cell.y
        @neighbours << cell
      end
      # Detects neighbour to the south-east
      if self.x == cell.x - 1 && self.y == cell.y + 1
        @neighbours << cell
      end
      # Detects neighbour to the south
      if self.x == cell.x && self.y == cell.y + 1
        @neighbours << cell
      end
      # Detects neighbour to the south-west
      if self.x == cell.x + 1 && self.y == cell.y + 1
        @neighbours << cell
      end
      # Detects neighbour to the west
      if self.x == cell.x + 1 && self.y == cell.y
        @neighbours << cell
      end
      # Detects neighbour to the north-west
      if self.x == cell.x + 1 && self.y == cell.y - 1
        @neighbours << cell
      end

    end
    @neighbours
  end

  def alive?
    self.world.cells.include?(self)
  end

  def dead?
    !self.world.cells.include?(self)
  end

  def die!
    self.world.cells.delete(self)
  end

  def revive!
    self.world.cells << self
  end

end

class World
  attr_accessor :cells

  def initialize
    @cells = []
  end

  def tick!
    self.cells.each do |cell|
      # Rule 1
      if cell.neighbours.count < 2
        cell.die!
      end
    end
  end

end

私はしばらく Rails でコーディングを行ってきましたが、次のことを行う方法について混乱しています。

  1. 世界の 1 つのフィールドに 1 つの Cell オブジェクトしか存在できないことを検証して確認しますか?
  2. 物事をDBに保存する方法(postgresqlなど)?この場合、そうしなければなりませんか、それともそのままにしてメモリ内で実行できますか?
  3. ライフ ゲームのグラフィック出力を次のようにするにはどうすればよいですか?

人生ゲームGIF

私が混乱している理由は、Rails がすぐにこれを実行できるためです。Ruby だけでこれを行う方法を理解する助けが必要なだけです。

編集:

検証メソッドを使用して Cell クラスを更新しましたが、オブジェクトが初期化された後にしか実行できません。初期化中に実行する方法はありますか?コードは次のとおりです。

  5   def initialize(world=World.new, x=0, y=0)                                                           | 53       neighbour_cell = Cell.new(subject.world, -1, 0)                                                 
  6     @world = world                                                                                    | 54       subject.neighbours.count.should == 1                                                            
  7     @world.cells << self # if self.valid? < this after if doesn't work                                         | 55     end                                                                                               
  8     @x = x                                                                                            | 56                                                                                                       
  9     @y = y                                                                                            | 57     it 'Detects cell to the north-west' do                                                            
 10   end                                                                                                 | 58       neighbour_cell = Cell.new(subject.world, -1, 1)                                                 
 11                                                                                                       | 59       subject.neighbours.count.should == 1                                                            
 12   def valid?                                                                                          | 60     end                                                                                               
 13     @valid = true                                                                                     | 61                                                                                                       
 14     self.world.cells.each do |cell|                                                                   | 62     it 'Creates a live cell' do                                                                       
 15       if self.x == cell.x && self.y == cell.y                                                         | 63       cell.should be_alive                                                                            
 16         @valid = false                                                                                | 64     end                                                                                               
 17         self.world.cells.delete(self)                                                                 | 65                                                                                                       
 18       end                                                                                             | 66     it 'Kills a cell' do                                                                              
 19     end                                                                                               | 67       cell.die!                                                                                       
 20     @valid                                                                                            | 68       cell.should be_dead                                                                             
 21   end                                        
4

3 に答える 3

1

ここでは、いくつかの大きな一般的な質問をしていますが、それぞれに多くの答えがあります。それぞれの最初の見解を示します。

1: 検証valid?- (レールから来て)オブジェクトに対して呼び出すことができるような何らかのメソッドが必要なようです。ただし、純粋な Ruby では、「有効な」オブジェクトが実際に何であるかを明示的に定義する必要があります。また、検証をどこで行うべきか、つまり、どのオブジェクトが検証を行っており、どのオブジェクトが検証されているかを決定する必要もあります。

上記のコードから、少なくとももう 1 つのクラスが必要になることをお勧めします。Gameたとえば、それを呼び出して、世界とその中のすべてのセルを制御します。現在、多くのロジックをCellクラスにロードしており、単一責任の原則を見直して、これが有効な (笑) 設計上の選択であるかどうかを確認することをお勧めします。

2. 永続性- 純粋な Ruby で ActiveRecord を ORM として使用することを妨げるものは何もありません。実装例については、この SO の質問を確認できます: how-to-use-active-record-without-rails

3. グラフィックス- Ruby 用のゲーム ライブラリには、使用しているプラ​​ットフォームに関係なくグラフィックス API が組み込まれているものが多数あります。私は個人的に彼らと一緒に仕事をしたことはあまりありませんが、gosuは良い選択肢のようです.

于 2013-03-12T15:11:53.803 に答える
1

Rails から抜け出して、何か違うこと、特に Game of Life のような古典的な課題に挑戦するのは良いことです。

質問に関しては、選択したシステム設計によってすべて異なります。しかし、簡単な実装のために、ここにいくつかの指針があります。

  1. Setクラスの利用はいかがですか?これは、重複を許可しないオブジェクトの配列です。
  2. アプリケーションの個別の実行間で状態を維持したい場合にのみ、DB が必要です。それが必要ない場合は、すべてをメモリに保管してください。
  3. これはここで答えるには大きすぎる質問ですが、多くの選択肢があります。極端な例は次のとおりです。
    • 画面の更新を管理する 自分でピクセルをレンダリングし、セルが変化したときに文字を端末に出力する
    • スプライト、GUI、および描画の概念を持つruby​​gameのようなものを使用する
于 2013-03-12T14:57:44.777 に答える
0

やった!しかし、ここで採用されたアプローチではありません。以下に私のコードを示します。あとは、グラフィック出力を実装するだけです。また、ここにGithubのコード全体があります。チェックしてコメントし、改善に役立ててください。私を助けてくれてありがとう:)

この'live_neighbours_around_cell(cell)'メソッドをリファクタリングすることはできますが、方法がわかりません。あなたはそれを手伝ってもらえますか?また、「Cell.new」を「cells」配列にすぐに追加すると便利ですが、そのための個別の関数がある現在のようにはなりません。

class Game
  attr_accessor :world

  def initialize(world=World.new, seeds=[[1, 1]])
    @world = world
    seeds.each do |seed|
      world.cell_board[seed[0]][seed[1]].alive = true
    end
  end

  def tick!
    next_round_live_cells = []
    next_round_dead_cells = []

    world.cells.each do |cell|
      # Rule 1: 
      # Any live cell with fewer than two live neighbours dies
      if world.live_neighbours_around_cell(cell).count < 2
        next_round_dead_cells << cell
      end
      # Rule 2:
      # Any live cell with two or three live neighbours lives on to the next generation
      if cell.alive? && world.live_neighbours_around_cell(cell).count == (2 || 3)
        next_round_live_cells << cell
      end
      # Rule 3:
      # Any live cell with more than three live neighbours dies
      if cell.alive? && world.live_neighbours_around_cell(cell).count > 3
        next_round_dead_cells << cell
      end
      # Rule 4:
      # Any dead cell with exactly three live neighbours becomes a live cell
      if cell.dead? && world.live_neighbours_around_cell(cell).count == 3
        next_round_live_cells << cell
      end
    end

    next_round_live_cells.each do |cell|
      cell.revive!
    end
    next_round_dead_cells.each do |cell|
      cell.die!
    end
  end
end

class World
  attr_accessor :rows, :cols, :cell_board, :cells

  # Scheme of default initialized world matrix
  #------------------------
  #     0     1     2
  # 0 [ dead, dead, dead ]
  # 1 [ dead, alive, dead ]
  # 2 [ dead, dead, dead ]
  #-----------------------

  def initialize(rows=3, cols=3)
    @rows = rows
    @cols = cols
    @cells = []

    @cell_board = Array.new(rows) do |row|
      Array.new(cols) do |col|
        Cell.new(row, col)
      end
    end

    cell_board.each do |row|
      row.each do |element|
        if element.is_a?(Cell)
          cells << element
        end
      end
    end
  end

  def live_cells
    cells.select { |cell| cell.alive }
  end

  def live_neighbours_around_cell(cell)
    live_neighbours = []
    live_cells.each do |live_cell|
      # Neighbour to the North
      if live_cell.x == cell.x - 1 && live_cell.y == cell.y
        live_neighbours << live_cell
      end
      # Neighbour to the North-East
      if live_cell.x == cell.x - 1 && live_cell.y == cell.y + 1
        live_neighbours << live_cell
      end
      # Neighbour to the East
      if live_cell.x == cell.x && live_cell.y == cell.y + 1
        live_neighbours << live_cell
      end
      # Neighbour to the South-East
      if live_cell.x == cell.x + 1 && live_cell.y == cell.y + 1
        live_neighbours << live_cell
      end
      # Neighbour to the South
      if live_cell.x == cell.x + 1 && live_cell.y == cell.y
        live_neighbours << live_cell
      end
      # Neighbour to the South-West
      if live_cell.x == cell.x + 1 && live_cell.y == cell.y - 1
        live_neighbours << live_cell
      end
      # Neighbour to the West
      if live_cell.x == cell.x && live_cell.y == cell.y - 1
        live_neighbours << live_cell
      end
      # Neighbour to the North-West
      if live_cell.x == cell.x - 1 && live_cell.y == cell.y - 1
        live_neighbours << live_cell
      end
    end
    live_neighbours
  end

end

class Cell
  attr_accessor :x, :y, :alive

  def initialize(x=0, y=0)
    @x = x
    @y = y
    @alive = false
  end

  def alive?
    alive
  end

  def dead?
    !alive
  end

  def die!
    @alive = false
  end

  def revive!
    @alive = true # same as > self.alive = true
  end
end
于 2013-03-14T13:45:03.930 に答える