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

GTK+ で落書き 8(Ruby)

Gem 'oekaki' で落書きです。
oekaki | RubyGems.org | your community gem host
GTK+でお絵かきしてみた(Ruby) - Camera Obscura

 
再帰的な tree を描いてみました。画像では動きはないですが、じつはアニメーションです。

 

require 'oekaki'

Width, Height = 400, 300
Turn = 20
Length = 40
Ratio = 0.9
Depth = 8

θ = PI * Turn / 180
left  = Matrix[[cos( θ), -sin( θ)], [sin( θ), cos( θ)]]
right = Matrix[[cos(-θ), -sin(-θ)], [sin(-θ), cos(-θ)]]


Oekaki.app width: Width, height: Height, title: "tree" do
  draw do
    color(0, 0, 0)
    rectangle(true, 0, 0, Width, Height)
  end
  
  ar = [[Vector[Width / 2, 1], Vector[0, Length]]]
  branch_num = 0
  
  id = timer(80) do
    ob = ar.shift
    v1 = left  * ob[1]
    v2 = right * ob[1]
    p1 = ob[0] + v1
    p2 = ob[0] + v2
    
    color(0, 65535, 0)
    line(ob[0][0], Height - ob[0][1], p1[0], Height - p1[1])
    line(ob[0][0], Height - ob[0][1], p2[0], Height - p2[1])
    
    ar << [p1, v1 * Ratio]
    ar << [p2, v2 * Ratio]
    
    branch_num += 2
    Gtk.timeout_remove(id) if branch_num >= 2 ** (Depth + 1) - 2
    true
  end
end

いわゆる「スライドパズル(15パズル)」もどきを Ruby で解いてみる

スライドパズルっていうおもちゃがありますよね。4×4 のマス目に空白がひとつあって(残りはコマ)、コマは空白にスライドさせて動かすしかなく、それを特定のパターンに揃えるというものです。ここではルールを少し改変して、特定のマスを別の特定のマスに移動させるのに最短で何手かかるかを考えます。

例えば 3×2 のパズルでいちばん右下のマスが空白であり、いちばん左上のマスにあるコマをいちばん右下へもってくるには、最短で 9手かかります。

●●●
● ○

●●●
●○ 

●● 
●○●

● ●
●○●

●○●
● ●

●○●
 ●●

 ○●
●●●

○ ●
●●●

○● 
●●●

○●●
●● 

9手で終了

ただしこれは、下の方から見ていって下さい(プログラムの関係でそうなっています)。

僕が見たアルゴリズムパズルの本では、10×10 のパズルでやるのと同等な問題が与えてありました(駐車場で車を脱出させるという体裁になっていました)。これは自分の書いたプログラムだと、69手ということになります(調べたパターンの数は 8927個)。計算時間はこれだと(自分の環境で)およそ 41秒と、かなり微妙な数字になりました。瞬殺は無理でしたね。これ以上広いパズルだと、根本から考えなおさないとダメかも知れません。

コードは以下です。幅優先探索で求めています。また、既に出たパターンに逢着した場合はスキップしています。

class Parking
  def initialize
    start = Pattern.new
    start.car.x, start.car.y = 0, 0
    start.space.x, start.space.y = Width - 1, Height - 1
    @@patterns = [start]
  end
  
  def solve
    buffer = @@patterns.dup
    while (po = buffer.shift)
      [po.up, po.down, po.left, po.right].each do |i|
        next unless i
        if i[1]    #true ならば終了
          @@co = 0
          i[0].show
          puts "#{@@co - 1}手で終了"
          puts "調べたパターン数は#{@@patterns.size}"
          exit
        end
        buffer << i[0]
      end
    end
  end
end

class Pattern < Parking
  class Car   < Struct.new(:x, :y); end
  class Space < Struct.new(:x, :y); end
  
  def initialize
    @car   = Car.new
    @space = Space.new
    @parent = nil
  end
  attr_accessor :car, :space, :parent
  
  def up
    x, y = space.x, space.y - 1
    return false if y < 0
    check(x, y)
  end
  
  def down
    x, y = space.x, space.y + 1
    return false if y >= Height
    check(x, y)
  end
  
  def left
    x, y = space.x - 1, space.y
    return false if x < 0
    check(x, y)
  end
  
  def right
    x, y = space.x + 1, space.y
    return false if x >= Width
    check(x, y)
  end
  
  def check(x, y)
    nxt = Pattern.new
    nxt.space.x, nxt.space.y = x, y
    nxt.car = (@car.x == x and @car.y == y) ? @space : @car
    
    @@patterns.each do |pa|
      return false if nxt.car == pa.car and nxt.space == pa.space 
    end
    nxt.parent = self
    
    @@patterns << nxt
    [nxt, (nxt.car.x == Width - 1 and nxt.car.y == Height - 1)]
  end
  private :check
  
  def putout
    Height.times do |i|
      st = "" * Width
      st[@car.x]   = "" if @car.y   == i
      st[@space.x] = " " if @space.y == i
      puts st
    end
    puts
  end
  
  def show
    @@co += 1
    putout
    @parent.show if @parent
  end
  
  def inspect
    "<Pattern:car(#{@car.x},#{@car.y}), space(#{@space.x},#{@space.y})>"
  end
end

Width = 4; Height = 4

Parking.new.solve

なお、このプログラムは Pattern#putout で String の破壊的変更を行っているので、たぶん Ruby 2.4 以上ではエラーが出るのではないかと思います。
 
ちなみに「15パズル(4×4)」の場合、21手で終了します。時間は約 0.1秒です。

Ubuntu 17.04 でインターネット接続できない

かなり深刻なバグだと思います。新規インストール、アップグレードの双方で起こるようです。また、派生のフレーバーすべてでも起きるようです(Ubuntu Budgie でも発生しました)。発生した PC は ThinkPad T410i です。

特徴は一見ネット接続ができているように表示されることです。ネット接続のところを見ると「接続」になっています。それなのに、Firefox やシステムのアップデートで接続できません。


対策としては、/etc/resolv.conf を編集します。

nameserver xx.xx.xx.xx

となっているところ(xx..云々のところには具体的なアドレスが入っています)を、$ sudo vi /etc/resolv.conf などで

nameserver 8.8.8.8

に編集し直します(元のアドレスは控えておいた方がよいでしょう)。これで [logout]→[login] でネット接続すれば OK です。このとき、同じローカルネットワークに接続している他の PC のネット接続ができなくなると思われるので、ルーターの電源を入れ直すなどしてネットワークを更新して下さい。これで他の PC からもネット接続できればおしまいです。


※参考
Bug #1654918 “No Internet access with default of Automatic (DHC...” : Bugs : network-manager package : Ubuntu
Bug #1679535 “! Cannot connect to internet in Ubuntu 17.04 (DNS ...” : Bugs : network-manager package : Ubuntu

RubyPico でクラスのリファレンスを便利に

サンプルコードの class_help.rb を少し変えただけですが、だいぶ使いやすくなったと思います。まずクラスの一覧が出るので、調べたいクラスをタッチすれば詳細が表示されます。[戻る] でループします。
 
コードは Gist に。
Created by RubyPico at Tue May 02 20:17:08 2017 · GitHub

RubyPico でファイルを Gist にアップロードする

GitHubAPI用の token を取得して下さい(参照)。それを以下のコードの「***」のところに入れます。あとは実行するだけ。コードの保存用にも使えますね。

upload_to_gist.rb

def main
  fname = Popup.input("ファイル名を入力して下さい")
  return unless fname
  unless File.exist?(fname) and File.file?(fname)
    puts "ファイルが存在しないか、ディレクトリです"
    return
  end
  
  file_content = File.open(fname) {|io| io.read}

  # https://developer.github.com/v3/gists/#create-a-gist
  json = {
    description: "Created by RubyPico at #{Time.now}",
    public: true,
    files: {
      fname => {content: file_content}
    }
  }
  
  puts "アップロード中..."
  
  result =  Browser.post(
    "https://api.github.com/gists",
    header: { "Authorization" => "token ***" },
    json: json
  )
  
  if result.length < 100
    puts result
  else
    puts fname + "を Gist にアップロードしました"
  end
end

main

mruby を Linux Mint(Ubuntu)で使ってみる

Linux Mint 18 で確認しました。

まずはここの Download から mruby をダウンロードします。自分は「mruby Stable版 v1.2.0 Linux/Mac版」を選択しました。

解凍して適当な場所に置き、ディレクトリに入って

$ make

を実行。gcc と CRuby と Bison が必要です。Bison は入っていなければ $ sudo apt-get install bison で入りますが、僕の環境にはすでに入っていました。gcc が入っていないということはないでしょうが、なければ $ sudo apt-get install build-essential で。実行すると
mruby_build.txt · GitHub
という風にビルドされました。これでおしまいです。


mruby の Hello, World! は CRuby とはだいぶちがいます。おおよそ
Hello World · mruby/mruby Wiki · GitHub
に書いてあるようにやればいいのですが、一部(C に慣れている人(自分にあらず)には自明であろう)修正が必要です。

まず

#include <stdio.h>
#include <mruby.h>
#include <mruby/compile.h>

int main(void)
{
  mrb_state *mrb = mrb_open();
  if (!mrb) { /* handle error */ }
  puts("Executing Ruby code from C!");
  mrb_load_string(mrb, "p 'hello world!'");
  mrb_close(mrb);
  return 0;
}

のコードを例えば hello.c で保存します。それで書かれているとおりに

$ gcc -Iinclude hello.c build/host/lib/libmruby.a -lm -o hello.out

とやると

gcc: error: build/host/lib/libmruby.a: そのようなファイルやディレクトリはありません

とおこられます。なのでそれに対応して

$ gcc -Imruby/include hello.c mruby/build/host/lib/libmruby.a -lm -ohello.out

と修正、そして

$ ./hello.out
Executing Ruby code from C!
"hello world!"

とうまくいきました。


では helloworld.rb みたいな rbファイルはどうやって実行するのかなあ。ここが初心者である。mruby はバイナリにコンパイルして実行するのだよね。さて。


※追記
わかった。bin/mruby が mruby のインタプリタだから、例えば

$ ./mruby/bin/mruby helloworld.rb

とかでいいわけだ。

コンパイルは bin/mrbc で行うので、

$ ./mruby/bin/mrbc helloworld.rb

バイトコードコンパイルされ、helloworld.mrb が出来る。これを実行するには

$ ./mruby/bin/mruby -b helloworld.mrb
Hello, World!

とオプションをつけて実行すればよい。

※参考
mruby概論(2):“組み込みRuby”こと「mruby」をセットアップしてみよう (2/3) - MONOist(モノイスト)
mrubyをとりあえず動かしてみただけ - ねこのて

PubyPico でじゃんけんゲーム

20170502034956

 
以前作った Ruby でのオブジェクト指向じゃんけんゲームを、iOS アプリの RubyPico 向けに書き直しました。操作性を iPad 用に変えただけで、あとはほぼもとのままです。iPadRuby スクリプト(正確には mruby)が動くとはじつに楽しいですね。RubyPico、もっと遊んでみたいと思います。

コードは Gist に置いておきました。
m_jankenasobi.rb · GitHub
 
iPad mini で確認しました。