いつもの得意技(?)のライフゲームです。Ruby 用のグラフィック・ライブラリ 'Ruby2D' を使っています。
コード。
lifegame_for_Ruby2D.rb
require 'ruby2d' include Ruby2D::DSL class LifeGame CellWidth = 10 Margin = 20 Space = 2 SideWidth = 35 W = CellWidth * SideWidth + Space * (SideWidth - 1) + Margin * 2 def initialize(num) set width: W, height: W, title: "LifeGame", fps_cap: 3 cells = SideWidth.times.map do |y| SideWidth.times.map do |x| Square.new x: Margin + (CellWidth + Space) * x, y: Margin + (CellWidth + Space) * y, size: CellWidth, color: Color.new([rand, rand, rand, 1.0]), z: 0 end end each_cell {|x, y| cells[y][x].remove} f = [1] * num + [0] * (SideWidth ** 2 - num) f.shuffle! @field = f.each_slice(SideWidth).to_a clear update do each_cell do |x, y| @field[y][x].nonzero? ? cells[y][x].add : cells[y][x].remove end next_field end end def each_cell SideWidth.times {|y| SideWidth.times {|x| yield(x, y)} } end def next_field tmp = Array.new(SideWidth) {Array.new(SideWidth, 0)} each_cell do |x, y| num = neighbor(x, y) if @field[y][x].nonzero? tmp[y][x] = 1 if num == 2 or num == 3 else tmp[y][x] = 1 if num == 3 end end @field = tmp end def neighbor(x, y) dirs = [[1, 0], [1, -1], [0, -1], [-1, -1], [-1, 0], [-1, 1], [0, 1], [1, 1]] dirs.map do |dx, dy| x1 = x + dx y1 = y + dy next if x1 < 0 or x1 >= SideWidth next if y1 < 0 or y1 >= SideWidth @field[y1][x1] end.compact.inject(&:+) end def go show end end LifeGame.new(250).go
ちょっとチュートリアルだけではわからないところがあったので、ソースコードを見る必要がありました。まずクラスの中で Ruby2D のメソッドをどう使ったらよいかで、これは最初に include Ruby2D::DSL
を宣言しておくことで解決(他の方法もあります)。それから、メソッド update のブロックの中で状態がその都度更新されるのですが、そのフレーム数の指定はどうすればよいか。これはメソッド set のキーワード引数で :fps_cap で指定してやればできることがわかりました。なお、ソースコードはとても読みやすいものでした。
'Ruby2D' については以下で紹介しています。
obelisk.hatenablog.com
上とは関係のないどうでもいいこと
Ruby は型の指定がないのでコードが読みにくいという根強い意見がありますが、たぶんこれではないかと。
- そもそもそのコード自体が読みにくい、クソなものである → Ruby のコードは一般にシンプルで短めなので、さっと読むのは楽な方です
- 全体のコードが長すぎて読みにくい → ソースファイルを分割する必要がある
- Ruby にはジェネリクスがない → Ruby の書き方を知らないにもほどがある
なお、これらはじつは他の言語にも応用できることです。別に Ruby は完璧な言語ではありませんが、実用的な(といってもまあいろいろでしょうけれど)言語としては非常によくできたものです。オブジェクト指向言語としてはもっともよくできたもののひとつでしょうし、クロージャ、第一級関数もサポートしており、関数型プログラミングのエッセンスも詰まっています。初心者にもやさしいですし、高度なプログラミングのできるプログラマにはそれなりの優れた書き方ができます。あんまり知らないでバカにするのはやめましょう。
僕は Ruby 以外では Go が結構好きなので(ソースコードの見た目が好きです笑)、Ruby と Go が連携できるような仕組みを作って欲しいのですけれど…。C言語がうまく書けるようになるのはなかなか大変…。
それから、「デザインパターンはオワコン」という意見も見ましたが、オワコンであろうが知っておいた方がよいと思います。純粋にプログラミング技術としておもしろいものです。最近はプロのプログラマの絶対数が増えたので、プロであり自信満々でもじつはよくわかっていない人も多いですから、我々素人のプログラミング好きは気をつけたいものです。