Le jeu de la vie en ruby (opal) - partie 1
Après la version Javascript du jeu de la vie, je débute la version Ruby/Opal.
Obtenir une première génération au hasard
$ touch app/generation.rb
$ touch spec/generation_spec.rb
$ tree
.
├── app
│ └── generation.rb
└── spec
└── generation_spec.rb
Mon premier test consiste à spécifier l’interface publique.
require './app/generation.rb'
describe Generation do
before do
@width = 4
@height = 3
end
specify { expect(Generation.new(@width, @height)).to respond_to :create }
endEt on le fait passer facilement.
class Generation
def initialize(width, height)
end
def create
end
endUn test en plus pour s’assurer que le tableau possède le bon nombre de lignes.
describe '#create' do
it 'returns an array with the right height' do
generation = Generation.new(@width, @height).create
expect(generation.size).to eq @height
end
endEncore une fois, le code vient facilement.
class Generation
def initialize(width, height)
@height = height
end
def create
Array.new(@height)
end
endMême chose maintenant avec le nombre de colonnes.
describe '#create' do
...
it 'returns an array with the right width' do
generation = Generation.new(@width, @height).create
expect(generation.first.size).to eq @width
end
endPour faire passer ce test, on crée un tableau à deux dimensions.
class Generation
def initialize(width, height)
@height = height
@width = width
end
def create
Array.new(@height) { Array.new(@width) }
end
endOn remarque l’expression Generation.new(@width, @height), en commun dans
chaque test. C’est notre premier refactoring.
require './app/generation.rb'
describe Generation do
before do
@width = 4
@height = 3
@generation = Generation.new(@width, @height)
end
specify { expect(@generation).to respond_to :create }
describe '#create' do
it 'returns an array with the right height' do
expect(@generation.create.size).to eq @height
end
it 'returns an array with the right width' do
expect(@generation.create.first.size).to eq @width
end
end
endDu hasard maitrisé
Je veux être sûr que la méthode create remplit bien le tableau avec soit des
0, soit des 1. Pour tester ça facilement, je vais figer le générateur de
nombre aléatoire à l’aide de srand.
it 'creates random cells' do
srand(0)
expect(@generation.create.first).to eq [0, 1, 1, 0]
end def create
Array.new(@height) { Array.new(@width) { rand(2) } }
endAfficher une génération
C’est la partie que je crains le plus avec certains langages. Avec Javascript par exemple, pas de problème, mais avec Haskell, Rust ou Julia je n’ai aucune idée des bibliothèques/frameworks/wrappers à employer.
Avec Ruby le souci est ailleurs. Je considère que l’écosystème Ruby est horrible dès qu’on touche de près ou de loin au GUI. Je vais donc tricher quelque peu et utiliser opal.rb.
La structure de l’application va bien changer :
$ tree
.
├── app
│ ├── application.rb
│ ├── canvas.rb
│ └── generation.rb
├── build.js
├── Gemfile
├── index.html
├── Rakefile
└── spec
└── generation_spec.rb
gem "opal", ">= 0.6.2"require 'opal'
desc "Build our app to build.js"
task :build do
Opal::Processor.source_map_enabled = false
env = Opal::Environment.new
env.append_path "app"
File.open("build.js", "w+") do |out|
out << env["application"].to_s
end
end<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Game of Life</title>
</head>
<body>
<canvas width="100" height="100" id="canvas"></canvas>
<script src="build.js"></script>
</body>
</html>Dans Canvas j’écris une sorte d’adaptateur pour utiliser un canvas
Javascript.
class Canvas
attr_reader :width, :height
def initialize
@canvas = `document.getElementById('canvas')`
@context = `#@canvas.getContext('2d')`
@height = `#@canvas.height`
@width = `#@canvas.width`
end
def clear
draw_rect(0, 0, @width, @height, 'black')
end
def pixel(x, y)
draw_rect(x, y, 1, 1, 'white')
end
private
def draw_rect(x, y, w, h, color)
`#@context.fillStyle = #{color}`
`#@context.fillRect(#{x}, #{y}, #{w}, #{h})`
end
endDans app/application.rb je peux maintenant afficher une génération.
require 'opal'
require 'canvas'
require 'generation'
canvas = Canvas.new
canvas.clear
generation = Generation.new(canvas.width, canvas.height)
cells = generation.create
cells.each_with_index do |line, y|
line.each_with_index do |cell, x|
canvas.pixel(x, y) if cell == 1
end
endAprès un rake build, on peut lancer l’application avec see index.html.
La suite la prochaine fois.