読者です 読者をやめる 読者になる 読者になる

100コインで1UPするデーモン

何かのコマンドが標準出力に"Error"と出力したら音を鳴らすプログラムには多々問題がありました。

  • コインをゲットする音を再生するのにmplayerを起動しているので、大変重い。`yes ok`とやってしまうと大変なことになる。
  • それ以前にmplayerを起動するのはカッコ悪い
  • コインの音が「コイン、コイン、コイン」とぎこちなく、「ココココイーン」と気持ちよく鳴らない
  • 100コインで1UPしない

というわけで、音を鳴らす部分をRuby/SDLにしてみました。それからコインをゲットするプログラムをデーモン化して、確実に100コインで1UPするようにしてみました。

  • Ruby/SDLを初期化
  • 音声ファイルをロード
  • SIGUSR1を受け取ったら…
    • コインをゲット
  • SIGUSR2を受け取ったら…
    • ノコノコにやられる

という寸法で動きます。
適当なプログラムでシグナルを送ってやれば、コインをゲットできます。for((i=0; i<100; i++)); do killall -USR1 ruby; sleep 0.1; done など(bash拡張文法)。

  • coind.rb
#!/usr/bin/env ruby

require 'rubygems'
require 'sdl'
require 'pp'

# Ruby/SDLのAudioだけ初期化
SDL.init(SDL::INIT_AUDIO)
SDL::Mixer.open

sound_coin = SDL::Mixer::Wave.load("coin.wav")   # コイン
sound_up = SDL::Mixer::Wave.load("up.wav")       # 1UP
sound_fail = SDL::Mixer::Wave.load("fail.wav")   # ノコノコにやられる

# デーモン化
## IO.daemon   # Ruby 1.9
#exit! 0 if fork
#Process.setsid
#exit! 0 if fork
#Dir.chdir('/')
#File.umask(0)
#STDIN.reopen  '/dev/null', 'r'
#STDOUT.reopen '/dev/null', 'a'
#STDERR.reopen '/dev/null', 'a'

COIN_CHANNEL = 0
UP_CHANNEL   = 1
FAIL_CHANNELNNEL = 2

coin = 0   # コインの数

Signal.trap(:USR1) {   # SIGUSR1
  coin += 1
  if coin % 100 == 0    # 100コインごとに1UP
    SDL::Mixer.play_channel(UP_CHANNEL, sound_up, 0)
  else
    unless SDL::Mixer.play?(UP_CHANNEL)  # 1UP中はコインの音を鳴らさない
      SDL::Mixer.play_channel(COIN_CHANNEL, sound_coin, 0)
    end
  end
}

Signal.trap(:USR2) {   # SIGUSR2
  SDL::Mixer.play_channel(FAIL_CHANNEL, sound_fail, 0)
}

while 1
  sleep
end


Mac OS Xでデーモン化すると「The process has forked and you cannot use this CoreFoundation functionality safely.」と怒られてしまうので、コメントアウトしてあります。