「精霊の箱」の貧者の像と富者の像のようなものを書きました

精霊の箱 上: チューリングマシンをめぐる冒険』の1章で登場する貧者の像と富者の像のような動作をする関数rich_and_poorを書きました。のようなと言っているのは、最後の状態の処理で@posが逆に移動するのですが、そこは無視しているからです。そのまま2章の文字列の比較も書こうとしましたが、ループのところ、えーっと正規表現で言うところのe*をオートマトンで表現した部分で、eが複雑な式の場合がうまく書けてないので、コンパクトに書けずにいます。

#!/usr/bin/env ruby

class Machine
  def initialize(state)
    @pos = 0
    @init = @state = state
  end
  def step(str)
    next_states(@state).#tap{|a| puts a.inspect}.
    each do|s|
      if s.type == :start
        @state = s.out1
        return
      elsif s.type == :end
        @state = s
        return
      elsif s.b == str[@pos]
        s.exec(@pos, str)
        @state = s.out1
        @pos += s.steps
        return 
      end 
    end  
    raise "failed" 
  end
  def exec(str)
    while @state.type != :end
      step(str)
    end
    @state = @init
    @pos = 0
    str 
  end
  def next_states(state)
    nstates = []
    return [] if !state
    case state.type
    when :split
       nstates << next_states(state.out1)
       nstates << next_states(state.out2)
    else
       nstates << state
    end
    nstates.flatten.compact
  end
end

class State
  attr_reader :b, :steps, :type, :a, :id
  attr_accessor :out1, :out2
  def initialize(id, type, b, a, steps)
    @id = id
    @type = type
    @b = b
    @a = a 
    @steps = steps
  end
  def inspect
    "S(#{@id}, #{@type}, #{@b.inspect}, #{@a.inspect})"
  end
  def to_s
    "S(#{@id},#{@b},#{@a})"
  end
  def exec(i, str)
    #puts "S(#{@id}).exec(#{i}, #{str})"
    str[i] = @a
  end
  def push(o)
    s = self
    while s.out1 != nil do
      s = s.out1
    end
    s.out1 = o 
  end
end

def star(id, before, after, step)
  s1 = State.new(id, :split, nil, nil, 1)
  s2 = State.new("#{id}:star", :value, before, after, step)
  s1.out2 = s2
  s2.out1 = s1
  s1
end

def connect(s1, before, after, step, s2)
  s3 = State.new("#{s1.id}:#{s2.id}", :value, before, after, step)
  s = s1
  while s do
    if !s.out1
      s.out1 = s3
      break
    elsif s1.out1.type == :value
      s4 = State.new("", :split, nil, nil, nil)
      t = s.out1
      s.out1 = s4
      s4.out1 = t
      s4.out2 = s3
      break
    end
    s = s.out2
  end
  s3.out1 = s2
end

def rich_and_poor
  rich =      star(1, '0', '0', 1)
  poor =      star(2, '0', '0', 1)
  goal = State.new(3, :end, nil, nil, nil)
  connect(rich, '1', '1', 1, poor) 
  connect(rich, ' ', '0', 1, goal)
  connect(poor, '1', '1', 1, rich)
  connect(poor, ' ', '1', 1, goal)

  m = Machine.new(rich)
  puts "0101101" == m.exec("010110 ")
  puts "0000000" ==  m.exec("000000 ")
  puts "0001001" == m.exec("000100 ")
end

rich_and_poor

数独板をAsciiコードで表示するの作りました。


数独ソルバー作っていくときに、数列で1行でわっと表示したり、9文字ずつ改行するのとかでも、ちょっと見えにくいので、ぱっと見てわかる表示に変更しました。

もっと複雑な画面の場合、Jupyter Notebook使うしかなさそうです。今回もJupyter Notebookにした方が良かったです、多分。

ブロッケンで決まる一番自明な場合


上のような場合、灰色で塗りつぶしている部分(以下カーソルのある場所と言います)がブロッケンで決まるとすぐ分かるパターンです。このカーソルの位置のブロックの上下左右のブロック全てに2が含まれています。なので、これらの2でカーソルの位置のブロックで2が入れる場所は必ず1マスに限定されます。というわけで、ここのブロックが空ブロックでもこの状況では2がブロッケンで決まります。

SudokuLogの使い方(ゲスト向け)

現在、herokuで公開中のSudokuLogの使い方についてです。主にゲスト向けに操作方法のみ解説します。ログインユーザーとしては、いつどんな問題をどのように解いたのかを記録として残す機能がメインになりますが、ゲストとしては、数独解くのに使う紙と鉛筆の代わりのようなものです。初心者が数独を解くハードルをいくらか下げてくれるとは思います。

とりあえず使ってみる

新規数独の入力方法 ゲスト(ログインしていない状態)の方の場合、トップページを開くと、すでに数独が入力された状態から始まります。 [start]ボタンを押すと、ローディング終了後に数字が青に変わります。ここから数独を解いていくことができます。

新規数独の入力する場合

  1. 既に数独は入力済みなので、まず数独をクリアします。
  2. 数独を入力します。
  3. 「とりあえず使ってみる」と同様に[start]ボタンを押すとスタートします。

入力済み数独を削除 する

[clear]ボタンを押すと画面がクリアされます。 入力済みの数独をクリアする

新規数独を入力する

数独の入力の仕方は矢印でカーソルを移動させて、数字を押すことでマスの数字が入ります。数字を取り消したいときは、カーソルを合わせて、そこに表示されている数字を入力すると空マスになり、他の数字を入れた場合は、その数字に切り替わります。 新規数独の入力方法

キーボードを使った操作

PCから使っている場合は、物理キーボードから操作できます。画面下のボタンは主にスマホから操作するときに使います。十字キーでもカーソル移動できますが、vimと同じようにも移動させることができます。

キー説明
1 2 3 4 5 6 7 8 9fixモード時カーソルの位置に数字を入力。deleteモード時はメモからその数字を消す
h左にカーソルを移動
左にカーソルを移動
j下にカーソルを移動
下にカーソルを移動
k上にカーソルを移動
上にカーソルを移動
l右にカーソルを移動
右にカーソルを移動
u1ステップ戻ります。間違えて入力した場合、この操作でのみ消せます
rアンドゥしたけどやっぱり戻したいとき、rを押すと戻れます。

天才になりたかったら「限界的練習」を身に付けようってことでWebアプリ作りました

超一流になるのは才能か努力か? (文春e-book)』によると、世の中の天才と呼ばれる人たちは「限界的練習」やそれに類するものを実践して、並の人では考えられないような能力を身につけているそうです。才能というものは、どうやら存在しないそうです。あったとしても、僕たちが聞かされたり、思い込んだりしている程にはないようです。僕たちが才能というものが存在すると信じて疑わなくなった原因は、スポーツや勉強などをはじめたときだと思います。特に説明しなくても、誰でも経験はあると思います。はじめて直ぐにその小学校の誰よりもうまくなったとか、逆に何年も続けているのに全くうまくならないとか。でも、この本で紹介されている調査によると、そういった個人差ごく初期のみにあらわれるもので、長続きはしないそうです。そういえば、器用になんでもある程度はこなすけれども、そこまでどれもうまくないって人もいますよね。

それでも「才能はない」なんて急に言われても、自分事として信じるってのは難しい話ですよね。だったら、この本の1章で事例として紹介されている短期記憶を驚異的に伸ばした事例を自分でも試してみたら良いと思ったんですね。その事例では短期記憶力を測定は、実験者が数列を1桁ずつ読み上げ、それを被験者が何桁まで復唱できるかを見ることでやってました。ルールの詳細は次のとおりです。

  • 使用する数列はランダムとします。
  • 実験者は毎秒1桁の速度で数字を読み上げます。
  • はじめは5桁からはじめます。
  • 被験者が正しく復唱できた場合は1桁ずつ増やしていきます
  • 被験者が間違えた場合は2桁ずつ減らします。

通常、人間が短期記憶を使って記憶できる桁数は7桁前後です。ところがこの事例で被験者は、2年間で82桁まで記録を伸ばしたそうです。ちょっと信じられないですよね。どのくらいの頻度で、1度にどれだけの時間練習したのかは正確には分からないのですが、簡単に試せるならば自分でもちょっと試したくなりました。人力でやるとなると、実験に協力してくれる人(数字を読み上げ、合っているか記録する人)がいるし、ランダムな数列を事前に用意したり、正しく復唱できているか判定するのも面倒だし、時間がかかります。

ということで「短期記憶チャレンジ」というWebアプリを作りました。ChromeのPC版とAndroid版で動作確認してます。測定結果はtwitterのシェアボタンで投稿するか、どこかにメモして記録します。使い方は次の動画でも確認できますよ。

ActivityにonTouchEventを書く時の注意

Viewに書いていたonTouchEventをActivityに書くように変更すると、y軸の値がおかしくなる場合があります。調査すると、ViewのonTouchEventの中のevent.getRawY()はViewの座標を基準に計算しますが、Activityはscreenサイズで計算するようです。そのため、下の図のようにフルスクリーンモードでない場合はActivityにonTouchEventをそのまま移すと動作がおかしくなります。
f:id:nabeyang:20130808081413p:plain
これを直すには、下の図のようにフルスクリーンモード(バッテリー残量などの部分が消える)にするか、screenの高さとviewの高さの差をy軸の値に足す必要があります。
f:id:nabeyang:20130808081419p:plain
このような面倒があるので、通常はViewの方へonTouchEventは書いた方が良いかもしれません。ただ、onTouchEventに対して、ロジックを変更するようなコードを書くので、Viewにロジックが混ぜないことを徹底するならば、Activityの方へ書くべきかもしれません。

フルスクリーンモードにする方法について

タイトルバーを消す方法」を参考にしました。