ラベル Ruby の投稿を表示しています。 すべての投稿を表示
ラベル Ruby の投稿を表示しています。 すべての投稿を表示

2016年8月31日水曜日

Ruby Warrior をやってみる(Level9)

はい、Ruby Warrior の Level9 です。

説明を意訳する

毎度の事ながら、説明文を意訳します。

Time to hone your skills and apply all of the abilities that you have
learned. Watch your back.

これまで学んだ全てのスキルを使って頑張って。
後ろの攻撃に注意。

こんな感じですかね。
先に書いてしまいますが、この Level9 で一旦のクリアになるようです。
なので、こういう文面になっているようです。

前レベルのコードをそのまま実行

これまで通り、前レベルクリアのコードをそのまま実行してみます。。。
見事にフルボッコにあって敵一人倒さずに終了してしまいました。さすが最終レベル。

さて、どうする?

開始直後に前後 3 マスに敵が配置されています。
そして、開始時点で戦士は右に向いています。
右の緑の敵は、目の前に行かないと攻撃してきませんが、後ろの弓持ちは、最初の位置からでも攻撃してきます。
ここの判定が難しいです。

攻撃された方向がメソッド等で取得できるのであれば良いのですが、ただダメージを貰ったという内容だけではどの方向に向けば良いか判定する方法が分かりませんでした。

なので、ここは説明文にあった忠告通り、最初は後ろを向いてそこからゲームをスタートする様にしました。
最初の向きを変える部分は、クラス変数を用意して最初の一度だけ方向を転換する実装としました。
ここの部分がちょっとかっこ悪いような気がしますが、勝てば官軍ということで。。。

さあ!反撃だ!?

初期の方向転換ができたので、早速攻撃を開始していきます。
弓持ちについては、開始位置からこちらも弓で攻撃すれば倒すことが出来ます。

しかし、その後前回のコードのままにすると、左に進み、捕虜を一人助けて、そのまま階段へすすみクリアしてしまいます。。。

どうやら、階段へ進みさえすればこのゲームはクリアとなるので、いいっちゃ良いんですが、右にも捕虜がいるので、全員助けてクリアしたい物です。

どうすれば良いか考えた所、汎用性は無い気がしますが、目の前の敵を倒した後に再度周り 3 マスを検索して敵がいれば方向を転換するようにしました。

これで、最初の位置で弓持ちを倒したら後ろを向いて緑の敵の方向を総なめし、壁にあたった所で向きを替え、最終的には階段へ向かうようになりました。

これがコードだ

class Player
  def initialize
    @health = 20
    @first_pivot = false
  end
  
  def play_turn(warrior)
    @warrior = warrior
    
    unless @first_pivot
      @first_pivot = true
      return warrior.pivot!
    end
    
    return warrior.pivot! if feel_wall?
    return warrior.rescue! if feel_captive?

    three_space_captive?

    if decrease_health?
      if pinch_health?
        warrior.walk!(:backward)
      else
        if check_enemy?
          warrior_attack!
        elsif three_space_enemy?
          warrior_pivot? ? warrior.pivot! : warrior_arrow_attack!
        else
          warrior_walk!
        end
      end
    else
      if max_health?
        if three_space_captive?
          warrior_walk!
        elsif three_space_enemy?
          warrior_pivot? ? warrior.pivot! : warrior_arrow_attack!
        else
          warrior_walk!
        end
      else
        warrior.rest!
      end
    end
    @health = warrior.health
  end
  
  def three_space_captive?
    spaces = @warrior.look
    spaces.each do |space|
      return true if space.captive?
    end
    false
  end

  def three_space_enemy?
    spaces = @warrior.look
    spaces.each do |space|
      return true if space.enemy?
    end
    spaces = @warrior.look(:backward)
    spaces.each do |space|
      return true if space.enemy?
    end
    false
  end
  
  def warrior_pivot?
    spaces = @warrior.look
    spaces.each do |space|
      return false if space.enemy?
    end
    spaces = @warrior.look(:backward)
    spaces.each do |space|
      return true if space.enemy?
    end
    false
  end

  def decrease_health?
    @health > @warrior.health
  end
  
  def pinch_pinch_health?
    @warrior.health <= 5
  end
  
  def pinch_health?
    @warrior.health <= 5
  end
  
  def max_health?
    @warrior.health == 20
  end
  
  def feel_captive?
    @warrior.feel.captive?
  end
  
  def feel_wall?
    @warrior.feel.wall?
  end
  
  def check_enemy?
    @warrior.feel.enemy? || @warrior.feel(:backward).enemy?
  end
  
  def warrior_walk!
    @warrior.walk!
  end

  def warrior_attack!
    @warrior.attack!
  end
  
  def warrior_arrow_attack!
    @warrior.shoot!
  end
end

まとめ

前にも書いたとおり、もっと汎用性をもたせれるのかもしれませんが、その都度の実装でなんとかクリアすることができました。
一旦更新を途絶えさせてからの再開でしたので、モチベーションが若干下がっていたのですが、一旦の区切りを付けることができ、良かったかと思っています。

あらたなレベルに進むかは決めていませんが、また余裕ができたらやってみたいと思います。それでは。

Written with StackEdit.

2016年8月28日日曜日

Ruby Warrior をやってみる(Level8)

Ruby Warrior Level8 です。

説明部分意訳します。

You hear the mumbling of wizards. Beware of their deadly wands! Good
thing you found a bow. Use warrior.look to determine your
surroundings, and warrior.shoot! to fire an arrow.

魔術師が詠唱をしている声が聞こえます。
彼らは広範囲の致命攻撃をして来るので注意してください。
幸運にも、弓矢を発見しました。
warrior.look メソッドにより環境を確認して warrior.shoot! で矢を放ってください。

ですかね。

と、言うわけで新しいメソッドが 2 つ増えました。
warrior.look メソッドは、前方 3 マス分の SPACE オブジェクトを配列の形で取得できるメソッドです。
このメソッドを使うことではターンを消費しないので、このメソッドの後に、移動や攻撃のメソッドを呼ぶことが出来ます。

もう一つは、warrior.shoot! メソッド。
どうやら弓矢を手に入れたらしいので、これを使う事により遠距離攻撃ができるようになります。
このメソッドは、通常の攻撃同様 1 ターン消費します。

これらを駆使してクリアを目指します。

まず、恒例の前回のソースそのまま実行の結果ですが、捕虜を助けた後に魔法使いの無慈悲な攻撃を受けます。その後1体は倒すのですが、次ターンでオーバキルされてしまいました。
やはり、上記のメソッドを駆使するしかなさそうです。

一番楽なのは、look メソッドを使わずに、shoot! をした次のターンで一歩進むという方式を取れば良いのですが、これでは捕虜ものとも倒して進むといった非常な戦士になってしまいます。

で、これらを考慮したコードが以下のとおり。

class Player
  def initialize
    @health = 20
  end
  
  def play_turn(warrior)
    @warrior = warrior
    
    return warrior.pivot! if feel_wall?
    return warrior.rescue! if feel_captive?

    three_space_captive?

    if decrease_health?
      if pinch_health?
        warrior.walk!(:backward)
      else
        check_enemy? ? warrior_attack! : warrior_walk!
      end
    else
      if max_health?
        if three_space_captive?
          warrior_walk!
        elsif three_space_enemy?
          warrior_arrow_attack!
        else
          warrior_walk!
        end
      else
        warrior.rest!
      end
    end
    @health = warrior.health
  end
  
  def three_space_captive?
    spaces = @warrior.look
    spaces.each do |space|
      return true if space.captive?
    end
    false
  end

  def three_space_enemy?
    spaces = @warrior.look
    spaces.each do |space|
      return true if space.enemy?
    end
    false
  end

  def decrease_health?
    @health > @warrior.health
  end
  
  def pinch_pinch_health?
    @warrior.health <= 5
  end
  
  def pinch_health?
    @warrior.health <= 5
  end
  
  def max_health?
    @warrior.health == 20
  end
  
  def feel_captive?
    @warrior.feel.captive?
  end
  
  def feel_wall?
    @warrior.feel.wall?
  end
  
  def check_enemy?
    @warrior.feel.enemy? || @warrior.feel(:backward).enemy?
  end
  
  def warrior_walk!
    @warrior.walk!
  end

  def warrior_attack!
    @warrior.attack!
  end
  
  def warrior_arrow_attack!
    @warrior.shoot!
  end
end

look メソッドを使って、前方 3 マスの SPACE オブジェクトを取得して、その中に捕虜が居るか確認して、居た場合は前方に進み救出に専念し、捕虜がおらず敵が居るのであれば、弓で攻撃するようにしました。

これで、このレベルではなんとかうまくいきました。
ダメージ受けた時の処理はそのままなので、その部分が次のレベルで出てくると、ちょっと面倒だなと。

ということで、今日はこれまで。

Written with StackEdit.

2016年8月22日月曜日

Ruby Warrior をやってみる(Level7-2)

はい、今回は前回の続きで Level7 の pivot! メソッドを使ったバージョンです。

説明文は前回と同じなので、省略と。

今回は、しつこいようですが pivot! メソッドを使用する形になります。
pivot! メソッドは、戦士の向いている方向を変更するメソッドになります。
前回の :backward と違って、pivot! は破壊的メソッドのため(!マークが付いているからね)、これだけで 1 ターン使うので、pivot! の後に、warrior 系のメソッドを使うとエラーになってしまいます。
(ここいらのルール無視するとずっと俺のターンができるからしょうがないね)

ここで分かったのは、:backward 付きの攻撃と、敵と正面向いての攻撃とで与えるポイントが違う点。なんと細かいのだ。

なので、前回のような体力低下による戦略的撤退を最初の敵で行う必要がなくなります。
が、結局弓矢持ちの敵の所で、引く処理が必要なので、まあよく考えられているゲームになっています。

で、実装した結果が以下のとおり

class Player
  def initialize
    @health = 20
  end
  
  def play_turn(warrior)
    @warrior = warrior
    
    return warrior.pivot! if feel_wall?
    return warrior.rescue! if feel_captive?

    if decrease_health?
      if pinch_health?
        warrior.walk!(:backward)
      else
        check_enemy? ? warrior_attack! : warrior_walk!
      end
    else
      if max_health?
        check_enemy? ? warrior_attack! : warrior_walk!
      else
        warrior.rest!
      end
    end
    @health = warrior.health
  end
  
  def decrease_health?
    @health > @warrior.health
  end
  
  def pinch_health?
    @warrior.health <= 5
  end
  
  def max_health?
    @warrior.health == 20
  end
  
  def feel_captive?
    @warrior.feel.captive?
  end
  
  def feel_wall?
    @warrior.feel.wall?
  end
  
  def check_enemy?
    @warrior.feel.enemy? || @warrior.feel(:backward).enemy?
  end
  
  def warrior_walk!
    @warrior.walk!
  end

  def warrior_attack!
    @warrior.attack!
  end
end

前回の方向をインスタンス変数に持つのをやめて、壁がある場合には、pivot! メソッドを使って向きを変える処理の追加と、ピンチの場合の判断体力と、後ろに下がる処理を :backward 付きの処理に変更したぐらい。
各動作については、メソッドラップの必要性がなくなったけど、あえて残しています(面倒くさかった)。

毎度のことながら、ひとまずクリア出来たので、めでたしめでたしと。

Written with StackEdit.

Ruby Warrior をやってみる(Level7-1)

さて、今回は Level7 です。
だいぶんしんどくなってきた。。。

とはいえ、blog を書く気になっている間にやってしまわないとまた時間経ちそうなのでやってしまわないと。

前置きは良いとして、説明文を意訳します。

You feel a wall right in front of you and an opening behind you. You
are not as effective at attacking backward. Use warrior.feel.wall? and
warrior.pivot! to turn around.

壁を目の前にして開始します。
通常の攻撃は、後ろに対して行えません。
warrior.feel.wall? メソッドと warrior.pivot! メソッドを使って向きを変えてください。

かな?
前回、:backward メソッドを使用して後ろ側への移動などを行って来ましたが、今回からなんと! warrior.pivot! メソッドを使うと戦士の向きを変えることができるようになるそうです。

ってか、最初から教えておいてくれたらよかったのに。
ということで、前回の資産を活かせるか試してみたところ、当然うまく行きませんでした。しかし、pivot! を使わずに :backward 引数だけで対応できるのでは無いかと、ちょっと弄るとクリア出来たので、今回はそのコードを載せておきます。
次の回で、実際に pivot! を使った内容を実装します。

コードは以下のとおり

class Player
  def initialize
    @health = 20
    @direction = :backward
  end
  
  def play_turn(warrior)
    @warrior = warrior
    
    return warrior.rescue!(@direction) if feel_captive?
    @direction = change_direction if feel_wall?
    
    if decrease_health?
      if pinch_pinch_health?
        @direction = change_direction
        warrior_walk!
      else
        check_enemy? ? warrior_attack! : warrior_walk!
      end
      @direction = change_direction if pinch_health?
    else
      if max_health?
        check_enemy? ? warrior_attack! : warrior_walk!
      else
        warrior.rest!
      end
    end
    @health = warrior.health
  end
  
  def decrease_health?
    @health > @warrior.health
  end
  
  def pinch_pinch_health?
    @warrior.health <= 2
  end
  
  def pinch_health?
    @warrior.health <= 3
  end
  
  def max_health?
    @warrior.health == 20
  end
  
  def feel_captive?
    @warrior.feel(@direction).captive?
  end
  
  def feel_wall?
    @warrior.feel(@direction).wall?
  end
  
  def check_enemy?
    @warrior.feel.enemy? || @warrior.feel(:backward).enemy?
  end
  
  def warrior_walk!
    @warrior.walk!(@direction)
  end

  def warrior_attack!
    @warrior.attack!(@direction)
  end
  
  def change_direction
    @direction === :forward ? :backward : :forward
  end
end

今回だけの無理やりコードなので、攻撃にも方向シンボルの指定とメソッド化、ピンチ体力の調整、そして本格的にやばくなった時の動作を無理やり実装しています。

本格的にやばくなった時のコードが何故必要になったかというと、最初の緑の敵ですが、フル体力で殴りあっても勝てないようになっています。。。一旦退いて体力回復後殴らないと勝てない。なんて糞仕様素敵な的なんだ。

一応これで、なんとかクリアできます。
ちょっと無理やりですかね。まあ、本番は次回になるので良いとしましょうか。

Written with StackEdit.

2016年8月20日土曜日

Ruby Warrior をやってみる(Level6)

はい。また Ruby Warrior です。
前回に引き続き Level6 です。

だんだん難しくなってきました。
例によって、説明文を意訳します。

The wall behind you feels a bit further away in this room. And you
hear more cries for help. You can walk backward by passing ‘:backward’
as an argument to walk!. Same goes for feel, rescue! and attack!.
Archers have a limited attack distance.

背後に壁があるのを感じる。
そして、助けを求める声も聞こえる。
:backward を walk! に引数にすることで後退することができる。
これを使って感じ取り、捕虜を助け、そして敵を攻撃せよ。
ここにいるアーチャーは範囲攻撃を持っている。

って感じですかね。
どうやら feel 系(warrior.feel(:backward).xxx)のメソッドの引数や、warrior のメソッド(e.g. warrior.walk!(:backward) など)に :backward シンボルを渡すことで、戦士の背後のチェックや行動ができるようになるみたいです。
ちなみに、:backward の反対は :forward を使うこともできます。
ココらへんは、デフォルト引数(xxx(direction=:forward)みたいな感じ)でも使ってるんでしょうか。

さて、ここいら辺の情報を持ってしても適当に実装するとクリアできませんでした。
相変わらずロジックがショボイからでしょうか。
悲観的なことは置いていて、クリアしたコードは以下のとおり。

class Player
  def initialize
    @health = 20
    @direction = :backward
  end
  
  def play_turn(warrior)
    @warrior = warrior

    return warrior.rescue!(@direction) if feel_captive?
    @direction = change_direction if feel_wall?

    if decrease_health?
      check_enemy? ? warrior.attack! : warrior_walk!
      @direction = change_direction if pinch_health?
    else
      if max_health?
        check_enemy? ? warrior.attack! : warrior_walk!
      else
        warrior.rest!
      end
    end
    @health = warrior.health
  end
  
  def decrease_health?
    @health > @warrior.health
  end
  
  def pinch_health?
    @warrior.health <= 8
  end
  
  def max_health?
    @warrior.health == 20
  end
  
  def feel_captive?
    @warrior.feel(@direction).captive?
  end
  
  def feel_wall?
    @warrior.feel(@direction).wall?
  end
  
  def check_enemy?
    @warrior.feel.enemy? || @warrior.feel(:backward).enemy?
  end
  
  def warrior_walk!
    @warrior.walk!(@direction)
  end
  
  def change_direction
    @direction === :forward ? :backward : :forward
  end
end

デフォルトである「# cool code goes here」のコメントはクールなコードな気分がなんとなくしなくなったので外しました。

説明文にあったとおり、このレベルから方向を指定できるようになったので、方向を保持するインスタンス変数を使うようにしました。これ使って救助や攻撃、移動の方向を指定するようにします。

あと、if 文が多用に使われて見難いので判定系はメソッドに分けてみました。メソッド化に当たり、play_turn() の warrior を各メソッドの引数で渡そうかとも思いましたが、ゲーム内だけなので、これもインスタンス変数を用いるようにしました。

終盤のアーチャー 2 体部分がいやらしいのですが、攻撃を一定量もらうと攻撃の当たらない位置まで一旦退いて回復後出直す方法をとっています。

うーん。今回は、敵の配置等を見て、このレベル専用に実装したんですが、これ汎用的な実装できるんでしょうか?
終わったら他の人のコードも見てみましょうか。。。

Written with StackEdit.

2016年8月18日木曜日

Ruby Warrior をやってみる(Level5)

はい、今回は Level5 です。
これまで同様に内容を意訳してみます。

You hear cries for help. Captives must need rescuing. Use
warrior.feel.captive? to see if there is a captive and warrior.rescue!
to rescue him. Don’t attack captives.

捕虜が助けを呼んでいる。
warrior.feel.captiove? メソッドで捕虜の存在を確認して、warrior.rescue! メソッドで捕虜を助けてください。
捕虜を攻撃しちゃダメ。

とのこと。
檻に入って不安そうな感じの目をしているのが捕虜のようです。
見た目が悪いのでゲームがゲームならミミック系の敵で倒してしまいそう。。。

そんな話は置いといて、今回も説明文にあるとおり、新しいメソッドを使って捕虜を助けつつ攻略していく形になります。

前回のそのまま起動したけど、捕虜の檻で足踏みして先に進まないので、改良が必要ですね(当たり前だけど)。

手っ取り早く、捕虜救出の部分だけを書いて実行すると、最後の緑の敵であっさり倒されてしまいます。

具体的には敵を倒した後に、次のターンで前進してしまい、そこにまた敵がいるので、殴り合いの末 K.O されるようになっていました。

で、色々模索してコードをすっきりさせる方法を探ってみたのですが、結果ぐちゃぐちゃに。
ひとまず、クリアはできたのでコード載せておきます。


class Player
  def initialize
    @health = 20
    @damage = false
  end
  
  def play_turn(warrior)
    # cool code goes here
    return warrior.rescue! if warrior.feel.captive?

    if @health > warrior.health
      if @damage
        warrior.feel.enemy? ? warrior.attack! : warrior.walk!
      else
        warrior.feel.enemy? ? warrior.attack! : warrior.rest!
      end
      @damage = true
    else
      if warrior.health <= 12
        warrior.rest!
      else
        warrior.feel.enemy? ? warrior.attack! : warrior.walk!
      end
      @damage = false
    end
    @health = warrior.health
  end
end

かっちょ悪いですが、@damage というインスタンス変数を設け、体力低下確認に合わせて 1 回だけは攻撃を踏ん張るようにしてみました。
これで、敵を倒した直ぐに前に進まず回復をするようになりクリアできます。

あんだけ if のネストが気持ち悪いと書いていたのにさらにネストがパワーアップしている。。。

Written with StackEdit.

2016年8月17日水曜日

Ruby Warrior をやってみる(Level4)

さて今回は、Level4 です。

You can hear bow strings being stretched. No new abilities this time,
but you must be careful not to rest while taking damage. Save a
@health instance variable and compare it on each turn to see if you’re
taking damage.

説明文を読むと、弓持ちの敵がいるので注意と言うことと、ダメージを受けている時に回復したらアカンということ。これを判断するには @health インスタンス変数を使用しろということのようです。

明らかに弓を持ったおっさんと、でかくなった敵がいます。
前回のコードをそのまま実行しましたが、最後の敵でやられてしまいました。普通に考えたら成功するわけないですよね。。。

と、言うことで説明に有ったとおり @health インスタンス変数を使用してダメージを受けているかを判定して処理を分岐するようにします。

@health のインスタンス変数については最初は定義されていないので、どこかで定義をする必要があるのでとりあえず、Player クラスの initialize メソッドで定義するようにします。
その後は、play_turn() メソッドの最後の部分でターン終了時の戦士の体力を代入するようにします。

これで、自分のターン後に敵からダメージを受けていた場合、先程保持した値よりも戦士の体力が減っていればダメージを受けているかどうか判断がつくようになります。

ダメージを受けている場合は、のんきに回復などをしていると、消耗戦でやられてしまうので、さっさ前に進み敵を倒しに行くようにします。

ダメージに変動がなくなり、前方に敵がいない場合に限り回復処理を行うようにします。

ここで、回復のポイント数ですが、緑の敵がでかくなって5ターンの攻撃を行う必要があるので前回の回復目安 6pt ではやられてしまうため、4 ターン分耐え切る体力(3 x 4 = 12 以上)を確保するようにします。

これを踏まえて実装したコードが以下のようになります。

class Player
  def initialize
    @health = 20
  end
  
  def play_turn(warrior)
    # cool code goes here
    return warrior.attack! if warrior.feel.enemy?
    if @health > warrior.health
      warrior.walk!
    else
      if warrior.health <= 12
        warrior.rest!
      else
        warrior.feel.enemy? ? warrior.attack! : warrior.walk!
      end
    end
    @health = warrior.health
  end
end

if 文のネストが増えているのがちょっと嫌な感じですが、ひとまずクリアはできました。

2016年8月16日火曜日

Ruby Warrior をやってみる(Level3)

大分放置してた。。。
さて、Level3 です。

ここでは、敵の数が増えるので、今まで通りに進むと体力が足りずに詰みます。

説明文を読むと

The air feels thicker than before. There must be a horde of sludge.
Be careful not to die! Use warrior.health to keep an eye on your health, and warrior.rest! to earn 10% of max health back. 

warrior.health メソッドと warrior.rest! メソッドがキーと書いています。

warrior.health メソッドは、自分の体力を確認できるメソッドで、warrior.rest! メソッドが 体力最大の 10% を回復するメソッドになります。

こちらの攻撃が 1 回に付き 5 を与えて、3 ターンで倒せるので、体力は 15 ぐらいで、相手の攻撃で -3 をもらい、倒しきるまでに 2 ターンあるので、体力的には 6 より多くないと厳しいということになります。

ということで、必要条件の体力が 6 より小さくなった場合に、rest! メソッドで体力を回復させるようにします。

class Player
  def play_turn(warrior)
    # cool code goes here
    return warrior.rest! if warrior.health <= 6
    warrior.feel.enemy? ? warrior.attack! : warrior.walk!
  end
end

で、単純に体力確保だけを考えて実装後に実行をすると、途中で敵が 2 連続の部分でやられてしまいました。

具体的には、2 匹目の部分で、体力低下により回復(2pt)するけど、相手の攻撃(3pt)をもらい、消耗戦の結果、体力尽きてゲームーオーバーというパターンです。

これを回避するには、ゲームオーバー時に表示されるヒントをみると、

敵が目の前にいない時に回復するようにしろ!と書いてあります。
敵が目の前かどうかは、SPACES オブジェクトの enemy? メソッドを使えば判断できます。
space 系のオブジェクトは、warrior.feel から取得できるようになっているので、warrior.feel.enemy? と記述すれば判断が付きそうです。
最終的には、以下のようになりました。

class Player
  def play_turn(warrior)
    # cool code goes here
    return warrior.attack! if warrior.feel.enemy?
    return warrior.rest! if warrior.health <= 6
    warrior.feel.enemy? ? warrior.attack! : warrior.walk!
  end
end

うーんいい感じ

2014年8月10日日曜日

Ruby Warrior をやってみる(Level3)

前回に引き続き、今回は Level3 をやっていきます。

画面を見てみると、敵の数が増えています。


そして、例のごとく説明文

The air feels thicker than before. There must be a horde of sludge.
Be careful not to die! Use warrior.health to keep an eye on your health, and warrior.rest! to earn 10% of max health back. 


毎度の意訳

空気がさっきよりも重たく感じる。どうやら敵の大群が居る感じがする。
殺られたらだめだ!warrior.health を使えば、戦士の体力を確認できる。
そして warrior.rest! で最大体力の 10% の体力を回復する事ができる。

とのこと。

戦士の初期体力が 20 で、敵は 3 回の攻撃で倒す事ができる。
つまり 2 回は敵の攻撃を貰う事になる。
敵の 1 回の攻撃で -3 のダメージなので、 (-3 x 2) x 4 = -24 となり、Level2 のままではクリアできない。

敵一体であれば、6 より大きい体力があれば倒す事ができる。
この判定を行うためには、自分の体力を見て判定するとよいので、今回の warrior.health を使えば乗り切れる体力かどうかの判断が出来る。
そして、体力的にまずい場合は warrior.rest! を使用すれば、最大体力の 10 % 回復できるということで、今回の場合は最大体力 20 の 10% で 2 回復できる事になる。

これを踏まえた上でプログラムを書いていく。

当然のことながら、目の前に敵がいるのに回復などしているとダメージ量と回復量の差によってクリアができないので、目の前に何も居ない事を見てから回復動作を行う必要がある。

これを踏まえて書いたコードがこちら

class Player
  def play_turn(warrior)
    if warrior.feel.empty? then
      # 目の前が空いている
      if warrior.health <= 6 then
        # 体力が 6 以下の場合は回復
        warrior.rest!
      else
        warrior.walk!
      end
    else
      # 敵がいる
      warrior.attack!
    end
  end
end

上手くいきました。
ここでは、条件分岐での関係演算子の使い方が分かりました。

まだなんとか行けるね。

RMagick を Windows にインストールしてみた

今回は Windows への RMagick のインストールを行ってみた。
RMagick というのは、ruby 用の画像加工ライブラリで、トリミングやサイズ変更などを行う事ができる物。

元々は、ImageMagick という Unix 系でのライブラリのようだ。

さて、発祥が Unix 系なので Linux 等の機械にインストールするのが筋なのだろうが、手持ちが Windows 機なので、こちらでインストールできるかやってみる事にする。
仮想環境で、Linux を入れて行う方法もあるが、PC が非力なために極力ネイティブで行いたかった。

どんな時でもそうだが、Unix 系やその他の環境からの Windows 機への対応は良くない印象で、今回も案の定だった。
結局、トライアンドエラーでインストールできたので(しかも今ひとつどれが有効だったのか核心がない。。。)、記録だけ残しておく。

まず、インストール時の環境確認をした。
Ruby 自体がインストールされているかを確認した。
ruby -v
ruby 1.9.3p392 (2013-02-22) [i386-mingw32]
少し古いが、たぶん去年 Rails の確認を取る際に使用したままの物だろう。
フォルダを見ると RailsInstaller とかいうフォルダが存在していたので間違いない。

次に、gem の update を行った。
正直 ruby にまだ入門したばかりなので、このアップデート方法が有っているかは分からない。
gem update
rails の rdoc で恐ろしく時間がかかった。
止まったのかと思って、強制終了使用かと思ったぐらいだったが、放置して別の事していたら終わっていた。それでも1時間はかかったと思う。

時は来たという事で、さっそく RMagick のインストールをと思ったが、よく分からないので、以下のページを参考にさせていただいた。

君の瞳はまるでルビー - Ruby 関連まとめサイト
RMagick を Windows 7 にインストールする方法

http://www.ownway.info/Ruby/index.php?rmagick%2Fhowtoinstall%2Fwindows

欲しいタイトルそのままで良かった。
このページを追いながら、省けるところは省いて作業をしてみたが。。。ダメでした。。。
手間を惜しんでせこくせこく小手先だけでの対応で色々やってみたけどダメだったので、一通り上から作業していく事で成功した。

やった事と言えば、

1. Development Kit の更新
上記に書いているとおり、インストールしているのは RailsInstaller を使用した Ruby ということもあり、Development Kit が古いのでは無いか?という推測の元更新を行ってみた。

RubyInstaller のページから Development Kit をダウンロードする。

RubyInstaller for Wndows
http://rubyinstaller.org/downloads/

現在使用している Ruby は、1.9.3 だったので、それに対応した物をダウンロード。
(DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe 2014/08/10時点)

ダウンロードしたファイルを展開し、RailsInstaller のフォルダの DevKitへコピー。
ここを最初別の場所に置いて、後からフォルダ参照されない無い事に気がついてやり直したのは言うまでもない。

ファイルを置き換えたら、セットアップが必要とのことなので、フォルダまで移動してコマンドを実行。
cd {Development Kit のインストールディレクトリ}
ruby dk.rb init
ruby dk.rb install
2.ImageMagick のバージョンとインストール先ディレクトリの指定
RMagick をインストールするにあたり、ImageMagick のインストールが必要らしい。
そこで、インストールするバージョンだが、公式から最新版を取得して、またそのままインストーラのデフォルトのまま作業をした。
インストールを成功されている方の情報をみると、今のはバージョンが有っていないので、成功例があるバージョンを使用するようにした。

ImageMagic公式(今回ここは使わないけど参考に)
http://www.imagemagick.org/script/binary-releases.php#windows

ちなみに今回失敗した時のバージョンは、ImageMagick-6.8.9-6-Q16-x86-dll.exeだった。

今回は、参考サイトにある旧バージョン取得ページから成功例のある  ImageMagick-6.8.5-8-Q8-x86-dll.exe を使用してインストール。

SUNNET(スエーデンの大学?)
http://ftp.sunet.se/pub/multimedia/graphics/ImageMagick/binaries/

インストールの時には、インストール先フォルダを C:\ 直下にして
オプションは、
・ Add application directory to your system path
・ Install development headers and libraries for C and C++
にチェックを入れてインストールをする。

インストール後は、システム環境変数をセットする。
CPATH = {ImageMagick のインストールディレクトリ}/include
LIBRARY_PATH = {ImageMagick のインストールディレクトリ}/lib
セット後は、設定を反映させるために再起動をかけておく。

3.RMagick インストール時に gem にバージョンを与える
成功されている方のインストールでは、gem install の時にバージョンの指定を行っていたので、参考にしてバージョン指定してみた。
gem install rmagick - v 2.13.2
これで RMagick のインストールが成功した。


X.失敗の振り返り
ちなみに失敗したときはログを見ろと言う事で、指定されたログを見てみた。
....
======================================================================
Sun 10Aug14 13:52:13
This installation of RMagick 0.0.0 is configured for
Ruby 1.9.3 (i386-mingw32) and ImageMagick 6.8.9
======================================================================



make
generating RMagick2-i386-mingw32.def
compiling rmagick.c
In file included from rmagick.c:13:0:
rmagick.h:81:2: error: #error Specified QuantumDepth is not supported.
rmagick.c: In function 'Magick_colors':
rmagick.c:40:5: warning: implicit declaration of function 'GetExceptionInfo'
rmagick.c:42:5: warning: passing argument 2 of 'GetColorInfoList' from incompatible pointer type
C:\Program Files\ImageMagick-6.8.9-Q16\include/magick/color.h:75:5: note: expected 'size_t *' but argument is of type 'long unsigned int *'
rmagick.c: In function 'Magick_fonts':
rmagick.c:90:5: warning: passing argument 2 of 'GetTypeInfoList' from incompatible pointer type
C:\Program Files\ImageMagick-6.8.9-Q16\include/magick/type.h:98:5: note: expected 'size_t *' but argument is of type 'long unsigned int *'
rmagick.c: In function 'Magick_init_formats':
rmagick.c:178:5: warning: passing argument 2 of 'GetMagickInfoList' from incompatible pointer type
C:\Program Files\ImageMagick-6.8.9-Q16\include/magick/magick.h:135:5: note: expected 'size_t *' but argument is of type 'long unsigned int *'
make: *** [rmagick.o] Error 1
ハッキリ言って、これだけ見て分かったら何も困らないのだが、、、
今見ると、RMagick のバージョンが 0.0.0 になっていたりするのが気になる。
その他は、ImageMagick のバージョンが最新という点とインストール先が Program Files の下という点か?
Dev Kit も見当違いのフォルダに置いていたりとしていたので、結局核心を突く前にインストールが成功してしまった。
問題点の検証する気が起きないので、次出たら作業ログを取ってやっていこう。。。

参考ページには、インストールできたからと言ってRMgacik が動くわけではないとの事を書かれてあったので、サンプルページを作成、実行しエラーが出ない事を確認した。
(requrire でライブラリが読めるかの確認だけだけど)

はて、困っている方の参考になるであろうか心配である。。。

2014年8月8日金曜日

Ruby Warrior をやってみる(Level2)

昨日に引き続き Level2 です。
Level1 をクリアして次の画面へ進むとセーブの確認と、FaceBook に投稿が出来るっぽいのですが、FaceBook は使っていないのでここは SKIP LOGIN を選択します。
image
すると、次の Level2 が表示されます。
image
上部の説明文には
It is too dark to see anything, but you smell sludge nearby.
Use warrior.feel.empty? to see if there is anything in front of you, and warrior.attack! to fight it. Remember, you can only do one action (ending in !) per turn.
。。。意訳します。
とても暗く何も見えないが、臭いを感じ取る事はできる。
warrior.empty? を使えば、目の前に何かあるか確かめる事が出来る。そして warrior.attack! を使って戦う事が可能だ。
しかし忘れないでくれ、お前が動けるのは1ターンに一度だけだという事を。
こんな感じ?
要するに新しいメソッドが増えたって事ですね。
ここら辺は、画面の ABILITIES のボタンをクリックすると内容が書かれています。
image
これをみると、戦士の行動以外に周りの状況判断のメソッドも有る事が書かれています。
使い方は、warrior.feel.メソッド とやるみたいです。
(今回の .empty? も書かれています。)
画面の説明では、warrior.feel.empty? を使えばいけそうなので、早速使ってみたいと思います。
このメソッドは、目の前が空いているか?のチェックを行うもので、空いていれば true が返ってきて、何か障害物があれば false が返ってくる様になっています。
この判断を行うには if 文を使えば分岐が出来るので、今回は以下のような実装になります。
class Player
  def play_turn(warrior)
    if warrior.feel.empty? then
      warrior.walk!
    else
      warrior.attack!
    end
  end
end

見たまんまですけど、目の前が空いていれば歩き、空いていないなら敵がいる物として攻撃を行うようにしています。
あとは、特に説明する必要がないので、実行してみます。

道中を歩き、敵がいるところでは攻撃を行い、敵がない無くなったら再度歩き出してゴールまで行きクリアの動作となります。

image

今回も上手くいきました。
ここで得た内容は、if 文による分岐と、feel にぶら下がるメソッドです。

次回も頑張ります。

2014年8月7日木曜日

Ruby Warrior をやってみる(Level1)

Ruby を楽しみながら学習できるサイト「Ruby Warrior」がどこかで紹介されていたのでやってみようと思う。


Ruby Warrior
https://www.bloc.io/ruby-warrior/#/

検索すると、回答サイトなどもあるので、詰まっても大丈夫。。。

さて、サイトを進むと、戦士のセーブ情報が表示され、次に名前を付ける事が出来る様になっている。ひとまず名前は初期の物でいいとする。
次に、初心者か中級者かの選択がある。
Ruby は、あまり使ってないので、とりあえず初心者を選択し、次へすすむ。

すると、レベル1が表示された。


You see before yourself a long hallway with stairs at the end. There is nothing in the way.
Call warrior.walk! to walk forward in the Player 'play_turn' method.

ああ、いきなり英語が分からない。。。
Google 翻訳で読んでみよう、、、

あなたは自分の前に最後に階段の長い廊下を参照してください。の方法では何もありません。

・・・???
さらにわからん。
ひとまず意訳でいこう。

「あなたの前には長い廊下があり、最後に階段がある。ただそれだけ」
「Player の 'play_trun' メソッドで warrior.wark! を呼んで前に歩かせてくれ」

こんな感じか。まあ、画面見たまんまなんですけどね。
なので、早速コードを書いていきましょう。

画面の下の部分にコードを書くエディタがあるので、そこに記述して Run! ボタンを押せば、コードが実行されていきます。

今回は、歩くだけなので相当簡単。

class Player
    def play_turn(warrior)
        warrior.walk!
    end
end

こんな感じですね。
ちなみに一回の play_trun() で、戦士の動作を行う事は1回のみと言うルールみたいで、warrior.walk! を続けて書くと実行時にエラーで怒られます。


warrior.walk! を一回にして実行すると、ターンが繰り返され無事階段まで辿り着き
の表示が出て次のレベルへ。
一応意訳で、 「成功!次の旅へ Ruby 戦士」うーんいまいち。
もうすこしゲームっぽく「やったぜ!Ruby 戦士よ次の旅へ向かうんだ!」かな?

ひとまず、Level1 は簡単で良かったです。

ここでは、クラスと、メソッドの記述方法、引数は warrior で、消しちゃいましたが "#" でコメントが扱えると言う事がわかりました。

この調子で次回は Level2 をやってみたいと思います。


2013年10月13日日曜日

「楽天ブックス書籍検索API」を使ってみる

いきなりだが、楽天ウェブサービスの一つ「楽天ブックス書籍検索API」を使ってみようともう。
一応、無料の商用利用可と言う事となっている。
言語は、Rails 等で使えるかどうか分からないけど、Ruby で使えたら良いなと思っている。

毎度の事ながら既に、実現している方が居られた。

[Ruby]Ruby 1.9で楽天ブックス書籍検索API2を使ってみる
http://d.hatena.ne.jp/d-kami/20130409/1365483598

こちらでは、名前が「楽天ブックス書籍検索API2」になっているが、現行は「楽天ブックス書籍検索API」に変わった模様。

「楽天ブックス書籍検索API」
https://webservice.rakuten.co.jp/api/booksbooksearch/

といっても、処理については同じで良い事がわかった。

準備
さて、まずこのサービスを使うには、「楽天ウェブサービス」に登録し、「楽天ブックス書籍検索API」と使うためのアプリ申請をする必要がある。

これは、特に注意点はなく、登録すればよい(コールバックドメインについては example.com にした)。
この登録によって、今回のプログラムで使用するアプリIDを取得する。

1. コードを書く
もう終盤に当たるが、参考にさせて頂いたサイトのコードを写す。

ポイントとしては、ここでは、https の通信を行うので 「net/https」を使っている。

また、参考先では、CA証明書ファイルについて指定があるが、
この部分「nil」に設定して、
「https.verify_mode = OpenSSL::SSL::VERIFY_NONE」をしてしても問題なく動いたので今回はこちらの方法を使った。
    require 'json'
    require 'net/https'
    
    app_id = 'ここにアプリIDをセット'
    searchTitle = 'Java'
    
    https = Net::HTTP.new('app.rakuten.co.jp', 443)
    https.use_ssl = true
    https.ca_file = 'nil'
    https.verify_mode = OpenSSL::SSL::VERIFY_NONE
    https.verify_depth = 5
    
    https.start {
      response = https.get("/services/api/BooksBook/Search/20130522?format=json&applicationId=#{app_id}&title=#{searchTitle}")
      document = JSON.parse(response.body);
      
      document['Items'].each{|item|
        puts item['Item']['title']
      }
    }
2. コードの実行
これで、完了。
あとは、実行するだけ
    $ ruby BookSearch.rb 
    Javaで始めるOpenCVプログラミング
    ...
    速習JavaScript
と、まあ簡単に書籍のデータが取得できるようになった。
実際には、一つ前の記事のプロキシ設定で、悩み数時間かかったりだとか、Windows の環境では証明書関係のエラーが出て躓いた(OpenSSL::SSL::VERIFY_NONE で切り抜けた)ので、サクサク行けたわけではない。

今回は、タイトルだけだが当然他の値も取得できているので、今後に活かさせば良いかなと思う。

2013年9月7日土曜日

Ruby で APNS を行う(houston)

今日は、Ruby を使って、APNS(Apple Push Notification Service)を行ってみたいと思います。

今回も 1 から作っていくのではなく、mattt さんが作成してるライブラリの力を借りる事にします。
ライブラリの名前は、
houston[ヒューストン] (https://github.com/nomad/houston)

名前の由来等サイトに書かれている気がしますが、、、とりあえず作業に入りたいと思います。
あと、APNS での Push の仕組みなどは Apple のドキュメントを参照してください。

Apple Document: Local Notification および Push Notification プログラミングガイド
https://developer.apple.com/jp/devcenter/ios/library/documentation/RemoteNotificationsPG.pdf

また、今回は完全なシステムを立ち上げるのではなく、あくまで手動で huston を使って Push の確認を取るところまでを記載します。
言うまでもなく実システムに組み込む場合は、細かな処理に手を入れる必要があります。

また、今回は色々なサイトを見させてもらったのですが、PHP で Push を実現する事が出来る apns-php を使っておられる方が非常に分かりやすく解説されており、非常に勉強になりました。
houston 以外の部分(houston もほとんどサイト丸写しなんですけどね。。。)は、こちらのサイトを見たほうがわかりやすいかもしれません。

MonteCut さん
iOS + PHPでPush Notificationを実装する


以下が、作業内容です。
なんども言いますが、1.〜 4. までは確実に
「iOS + PHPでPush Notificationを実装する」
の方がわかりやすい。。。

1.iOS Developer から Push 用の IDs の取得を行う
Push を行うに当たり、端末の識別番号を取得するなどの処理を行うiPhone側(iOS)プログラムが必要となります。
このプログラムを作りに当たって ID を取得します。
自分は、これまでに IDs の登録時のApp ID Suffix の Bundle ID をワイルドカードの物(Wildcard App ID)で賄っていました。
しかし、Push を行うにはこの形式の物は使えないと、色々なサイトに書かれてあったので、Push テスト用に別途作成しました。


2. 証明書の取得
1. で作成した IDs から証明書ファイルを取得します。
取得した証明書をダブルクリックでキーチェインアクセスへ登録します。

登録後、p12 ファイルを取得します。
取得方法は、キーチェインアクセスで、今回追加した証明書を選択し(キーチェインを「ログイン」、分類を「自分の証明書」を選択すると出てくる)、左の三角を押して、自分の鍵を出した状態で、Ctrlキーで両方選択し、右クリックで p12 形式で書き出します。
(今回名前を AppNameDev.p12 とした) この p12 から pem ファイル形式へ変換します。
$ openssl pkcs12 -in AppNameDev.p12 -out AppNameDev.pem -nodes
そしてその pem ファイルを houston で使用・指定します。

3. プロビジョニングファイルの作成
これも1. 同様に Push で確認を行うためのプログラムを実機で試すために必要なプロファイルです。
どうやら、Push の確認については、実機でないと行えない様なので、これも iOS Developer で取得しておきます。

iOS Developer から Provisioning Profiles と辿り「+」ボタンで作成していきます。
Development は iOS App Development として、Select App ID は、1. で作ったアプリを選択します。
Select certificates. は、自分の証明書を選択
Select devices. は、動かす端末を選択
Name this profile and generate. は、任意のプロファイル名を入れ Generate で作成後、ダウンロードします。
ダウンロードしたものをダブルクリックでオーガナイザに登録します。

4. iOS で Push 対応のプログラムを作成する
XCode を立ちあげ、適当なプロジェクトを選択(Empty Applicationでもいい)し、プロジェクト設定画面を開きます。
PROJECT で、プロジェクトを選択し、Build Settings を選択し、Code Signing の項目の Code Signing Identity の Debug 用プロファイルを 3. で登録した物に設定します。

次に、プログラムを書いていきます。
...
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    ...

    // アプリケーションが起動するたびに、デバイストークンを要求してそれをプロバイダに渡すことで、
    // プロバイダが最新のデバイストークンを持つことを保証
    [application registerForRemoteNotificationTypes: (UIRemoteNotificationTypeBadge|UIRemoteNotificationTypeSound|UIRemoteNotificationTypeAlert)];
    
    return YES;
}

- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *) devToken
{ 
    NSLog(@"deviceToken: %@", devToken);
    // 自分のサーバーにトークンを送信する
}

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *) err
{ 
    NSLog(@"Errorinregistration.Error:%@", err); 
}
実装が問題なくでき、ビルドも通ったら、実行してみてください。
上手くいけば、デバイストークンが取得できるはずです(ログに出力されている)。
ここで、表示されるデバイストークンをメモっておきます。

実際には、ここで取得したトークンを、サーバへ送信して、サーバー側で値を管理する様にしないと行けません。

5. Ruby(huston) のプログラムを作成する
ここでようやく houston の出番です。
houston のサイトから zip ファイルを取得します(git で取得しても良い)
取得したファイルを展開し、任意の場所へ移動します(以降 houstonフォルダとする)。

houstonフォルダ内に、証明書を入れるフォルダを作成します(ここでは、certificateフォルダとする)。
作成したフォルダに、2. で作成した証明書ファイルを入れます。

次に、houstonフォルダに、Push を実行するためのスクリプトファイルを作成します(名前は任意。今回は pushTest.rb とした)
パスの関係上で、Web のサンプル通りだとエラーになったので、各プログラムの読み込むパスを変更しました。
require './lib/houston'  ← ./lib/ を追加
# Environment variables are automatically read, or can be overridden by any specified options. You can also
# conveniently use `Houston::Client.development` or `Houston::Client.production`.
APN = Houston::Client.development
APN.certificate = File.read("./certificate/AppNameDev.pem")

# An example of the token sent back when a device registers for notifications
token = "<4.で取得したトークンを貼り付ける>"

# Create a notification that alerts a message to the user, plays a sound, and sets the badge on the app
notification = Houston::Notification.new(device: token)
notification.alert = "Hello, World!"

# Notifications can also change the badge count, have a custom sound, indicate available Newsstand content, or pass along arbitrary data.
notification.badge = 57
notification.sound = "sosumi.aiff"
notification.content_available = true
notification.custom_data = {foo: "bar"}

# And... sent! That's all it takes.
APN.push(notification)
lib フォルダの houston.rb も同様に
module Houston
  VERSION = "0.3.0"
end

require './lib/houston/client'        ← ./lib/ を追加
require './lib/houston/notification'  ← ./lib/ を追加
require './lib/houston/connection'    ← ./lib/ を追加
ここまで、できれば後は実行するだけです。
$ ruby pushTest.rb
これで、iPhone に Push 通知が飛んできます。

駆け足で書いているため(絵がないからわかりづらい)、抜けているかもしれませんが、また自分でも使うことがあると思うので、その際に修正していきたいと思います。



番外
この huston も、先日のdubai と同様、bin フォルダに apn と言うプログラムが用意されています。
これを使えば、コマンドラインから Push を行う事が出来ます。
(パスは自分の環境に対応してください)
$ apn push "4.で取得したトークン" -c /certificate/AppNameDev.pem -m "Hello from the command line!"

用途に合わせて、使い分けをすれば、軽作業で Push 処理が実現できるかと思います。


2013年9月6日金曜日

Ruby で Passbook を作る(Dubai)

iPhone に標準搭載されている Passbook。
みんな使っていますか?

今回は、Ruby を使用して Passbook を作成する方法を記載します。
とは言っても、1 からの作成ではなく、既にライブラリとして用意されている物を利用させて貰います。

ライブラリ名前は Dubai(ドバイ)
https://github.com/nomad/dubai 作者は Mattt Thompson 氏。
Heroku の方らしい。

ライブラリの名前の由来がダジャレっぽかった気がするが、気にしない。
(ドバイとか分かりやすい名前は良いのだが、情報を調べる際に関係ない物が多くかかるので、こういうネーミングはあんまり好きじゃない)

さて、今回は Passbook を一度作成した事がある方を対象とする。
細かな説明については、他の方が書かれているから。

さて作成に入る。

1. Pass 作成用の p12 ファイルを作成する
iOS Developer から Passbook 用の IDs を作成して、証明書を取得する。
証明書をキーチェインアクセスで選択して p12 形式で書き出す。
(この時のパスワードを覚えておくこと)
このファイルを後ほど使用する。

2. プログラムの取得
github から取得する(今回はGitクライアントを使わずに zip ファイルを落とした)。

次に、zip ファイルを展開する。
dubai フォルダが作成されるので任意の場所へ移動する。

次に、ターミナルで dubai フォルダまで移動する。
ここで、必要ファイルを取得するために
bundle install
をする。

3. 証明書ファイルのフォルダを作成
本当は、どこでもいいんだろうけど、今回は dubai フォルダに
「certificates」 というフォルダを作成し、1. で作った p12 ファイルを格納する。

4. Passbook のフォルダを格納する
Pass が作成できる前段階まで仕上げた物(フォルダ)を dubai フォルダに格納する。
(ここで言う、前段階まで仕上げた物というのは、Appleから取得したテンプレートフォルダに有った signpass プログラムで Passbook が作成できる段階のものを指す)

5. ruby スクリプト作成
dubai フォルダに、適当な ruby ファイルを作成する(今回は passbookCreate.rb と言う名前のファイルにした)

後は、Web のサンプル通りにすれば良いが、パスの指定で、エラーが出たので、
lib フォルダの dubai の読み込みと、lib/dubai.rb のファイルの読み込み部分を変更した。
(ここの辺が、ruby が分かっていない点)
require './lib/dubai' ← "./lib/"を追加

Dubai::Passbook.certificate, Dubai::Passbook.password = "./certificates/証明書.p12", "証明書パスワード"

# Example.pass is a directory with files "pass.json", "icon.png" & "icon@2x.png"
File.open("作成するPassの名前.pkpass", 'w') do |f|
  f.write Dubai::Passbook::Pass.new("Passの元フォルダ.raw").pkpass.string
end
require './lib/dubai/pass'    ← "./lib/"を追加
require './lib/dubai/server'  ← "./lib/"を追加
require './lib/dubai/version' ← "./lib/"を追加
6. 実行
上記まで作業が完了したら、作成したスクリプトを実行する。
$ ruby passbookCreate.rb
これで、.pkpass のファイルが作成されている。
出来たPassbook ファイルをダブルクリックすれば Mac なら Pass を開いて見ることができる。
また、iPhone へ Pass をメールで添付すれば、iPhone に保存することも出来る。
iOS シミュレータなら Pass をドラッグアンドドロップでも行ける。
Web での配信もできるが、面倒なのでここではしない。


番外? pk プログラム
dubai フォルダ内の bin には pk と言うプログラムがある。

これは、
・ Passbook 雛形作成
・ Passbook 生成
・ 配信サーバー機能
が出来る。

Passbook 雛形作成
以下のコマンドで、Passbook 用の雛形フォルダ及び、データが作成される。
$ pk generate [Pass データフォルダ] -T [Pass の種類]
Pass の種類は以下の通り
・ boarding-pass
・ coupon
・ event-ticket
・ store-card
・ generic

これで、作成された pass.json を編集・画像ファイルを準備すれば Pass 作成の準備が完了となる。


Passbook 生成
Passbook 生成前のファイルが準備出来たら、以下のコマンドで Pass の作成を行う
$ pk build Example.pass -c /path/to/certificate.p12
Web ページには、上記の様に書いているが、この通り(Pass名や証明書ファイルとかは当然変える)すると、
Error: PKCS12_parse: mac verify failure
You may be getting this error because the certificate password is either incorrect or missing
とエラーが出る(エラーでも空パスが作成される)。
以下のように -p オプションを付与すると、パスワードを聞いてくるようになる。
$ pk build Example.pass -c ./certificate/pass_certificate.p12 -p
ここのパスワードは、証明書を作成した時のパスワード。
これで、パスが作成される。


配信サーバー機能
※ 正直動いていないのでよくわかない。

以下のコマンドで、パスの作成と配信用のサーバ(Sinatra)が起動できる。
ここでも後ろに -p オプションを付けないとエラーになる。
$ pk serve Example.pass -c /path/to/certificate.p12 -p
Enter certificate password:

== Sinatra/1.4.2 has taken the stage on 4567 for development with backup from Thin
>> Thin web server (v1.5.0 codename Knife)
>> Maximum connections set to 1024
>> Listening on localhost:4567, CTRL+C to stop
これで、localhost の 4567 番ポートにアクセスすればパスが取得できるはずだが、上手く行かなかった。
今回は使わない機能だったので、これ以上の深追いをしないこととした。
ひょっとしたら、localhost ではなくドメインの設定とかも出来るかもしれない。。。

長々と、書いたが非常に簡単なステップで Passbook  の作成ができるので、興味のある方はどうぞ。