ターミナルのカーソル位置を移動する方法を知って、ターミナルで動くものを書いてみたくなったのでライフゲームを書いてみました。
ライフゲームの詳細は Wikipedia 参照。
この解説動画も分かりやすくて面白いです! → ライフゲームの世界1【複雑系】
コード
ruby -e 'h,w=`stty size`.split.map &:to_i;trap 2,:exit;f=h.times.map{w.times.map{rand<0.5}};loop{print f.map{|r|r.map{|e|e ??*:" "}*""}*?\n+"\e[;H";f=h.times.map{|y|w.times.map{|x|n=(-1..1).inject(0){|s,i|s+(-1..1).count{|j| f[(y+i)%h][(x+j)%w]}};f[y][x]??a[n-4]:n==3}};sleep 0.05}'
少し見やすくするとこんな感じです。
h,w =`stty size`.split.map &:to_i #ターミナルの行数、列数を取得
trap 2,:exit; # Ctrl + Cしたら終了
f = h.times.map{w.times.map{rand<0.5}} #ターミナルと同じサイズの配列にランダムな真偽値を入れる
loop{
print f.map{|r|r.map{|e|e ??*:" "}*""}*?\n+"\e[1;1H" #ターミナルに出力して1行目に戻る
f=h.times.map{|y|
w.times.map{|x|
n=(-1..1).inject(0){|s,i|s+(-1..1).count{|j| f[(y+i)%h][(x+j)%w]}} #周囲3×3マスの生存セルの数 (自分を含む)
f[y][x]??a[n-4]:n==3 #周囲のセルの数によって生死が決まる (生きてたら2つor3つで生存, 死んでたら3つで復活)
}
}
sleep 0.05
}
終了はControl + Cです。
Macのターミナルで動作確認しましたが、他では動かないかもしれません。
普通に書いたライフゲーム をできる限り短くしたものです。
普通に書いたほうはステータス行を作ってターン数や生存セル数を表示したりしてます。
(ちなみに間違えてgistに匿名で送信してしまったので編集も削除もできません。)
1セルずつ描画する方法もやってみましたが重かったので一気に描画することにしました。
追記
配列作成の処理を統一したりして少し縮まりました。
ruby -e 'h,w,f=`stty size`.split.map &:to_i;loop{f=h.times.map{|y|w.times.map{|x|next rand<0.5 if !f;n=(a=-1..1).inject(0){|s,i|s+a.count{|j|f[(y+i)%h][(x+j)%w]}};f[y][x]??a[n-4]:n==3}};print f.map{|r|r.map{|e|e ??*:" "}*""}*?\n+"\e[;H";sleep 0.05}'
h,w,f=`stty size`.split.map &:to_i
loop{
f=h.times.map{|y|
w.times.map{|x|
next rand<0.5 if !f
n=(a=-1..1).inject(0){|s,i|s+a.count{|j|f[(y+i)%h][(x+j)%w]}}
f[y][x]??a[n-4]:n==3
}
}
print f.map{|r|r.map{|e|e ??*:" "}*""}*?\n+"\e[;H"
sleep 0.05
}
任意の場所にカーソルを移動する方法
\e[n;mH
のn
とm
の部分を書き換えて出力するとカーソルが移動します。
例えば、\e[1;2H
で1行目の2列目(2文字目)に移動します。
その後の文字列でカーソル位置の文字列が書き換えられます。
数値は1から始まります。
数値が1未満だったり省略して\e[;2H
のようになっている場合は1になります。
puts "\e[2J" #画面消去
print "\e[;H" #画面左上に移動
puts "XXX"
puts "XOX ←3秒後に「O」を「X」に書き換えます。"
puts "XXX"
sleep 3
puts "\e[2;2HX" #「O」を「X」に書き換え
puts
エスケープシーケンスでもっとめんどくさいことをする場合はcurses
ライブラリを使います。
参考
ANSI escape code - Wikipedia
ライフゲーム - Wikipedia
curses による端末制御
ターミナルでゆのっちが降る