2010-11-09 優しいScalaの育て方 [長年日記]

[Scala] play scala with scalate の設定

Play framework の1.1がリリースされました。また各モジュールの安定性も増し、いよいよ全体的に実用レベルになってきた感があるので、RoR経験者の視点から現時点でのplayの状況および利用方法をまとめます。

追加するplayモジュール

名前version更新日説明
scala0.82010/11/01play上でScalaを利用する(必須)
scalate0.7.22010/07/29Scala用テンプレートライブラリ(erb,haml風)

インストール

  1. play 本体
  2. 追加モジュール
  • play本体

% wget http://download.playframework.org/releases/play-1.1.zip
% unzip play-1.1.zip

展開すると play-1.1/ ができるので、そこにパスを通す。(play-1.1/playが実行するplayコマンドになる)

  • 追加モジュール

% play install scala
% play install scalate

どちらもconfirmされるので、yで。インストール内容は、先程のplayインストールディレクトリ下のmodules/に展開されるだけなので、手動でDL&展開してもよい。(firewallでproxyが必要な環境下の場合など)

アプリケーションの雛形作成

% play new foo --with scala,scalate

でfooというアプリケーション(ディレクトリ名もfoo)が作成される。と同時にwithで指定したモジュールが有効になり、conf/application.confに設定が追加される。具体的には、

module.scalate=${play.path}/modules/scalate-0.7.2
...
scalate=ssp

といった内容になり、この設定を熟知していれば、withを使わずに作成して、configをいじっても同じ状態にできる。しかし、withの場合は設定ファイルの追加に加えて初期ファイルの配置も行ってくれるので、やっぱりwithでやるのがよい。具体的な初期ファイルは、scalateの場合であればapp/views/default.ssp(layout.rhtmlに相当)等が配置される。以下、その辺の詳細設定を行う。

scalateの設定(scamlを使う)

Scalateライブラリ(1.3.1)では以下の4種類の形式をサポートしているが、

scalateのファイル形式

形式内容
sspERbぽい記述<p><%= body %></p>
scamlhamlぽい記述%p!= body
jadescamlから%を削除p!= body
mustacheMustacheテンプレート互換<p>{{body}}</p>

playのscalateモジュール(0.7.2)ではjadeが未サポートであるため、残りの3つから選択することになる。以下はscamlを利用する時の設定方法である。(デフォルトはsspなので、sspを使う場合は2を変更するだけでよい)

  1. configファイルの変更
  2. コントローラの変更
  3. レイアウトファイルの配置
  • configファイルの変更

% vi conf/application.conf
scalate=scaml

  • コントローラの変更

% vi app/controllers.scala
object Application extends ScalateController {
  def index = render()
}

  • レイアウトファイルの配置

デフォルトのレイアウトファイル(default.ssp)はssp用なので、scaml用のレイアウトを新規作成する。

% vi app/views/default.scaml
-@ var body: String
-@ var title: String = "タイトル"

%html
  %head
    %title= title
  %body
    != body

アプリケーション作成

あとは、indexアクション用のビューファイルを作成すればOK。

% vi app/views/Application/index.scaml
Hello Scaml

Webサーバ起動

% play run

runで9000ポートで起動する。

% w3m http://localhost:9000/
Hello Scaml

ポートを変更する

% vi conf/application.conf
http.port=3000

プロダクションモードで起動する

% play run --%prod

プロダクションモードで起動すると毎回ソースファイルを見ないのでレスポンスが速くなる。また、prodは予約語ではなくコンフィグファイルの中で利用されている(モードの)識別子に過ぎないので、自由に変更できる。デフォルトのモードの識別子はdevであり、例えばdevでファイルのリロードを行わないようにするには、以下のように設定する。

% vi conf/application.conf
%dev.application.mode=prod

「%(モード名).」を付けると、各変数に対してそのモード特有の値を設定できる。また、ここのprodはプロダクションモードを意味する値(予約語)なのが若干紛らわしい。

参考

次回予告

次回は play scala + scaml の詳しい使い方です。


2010-10-28 [長年日記]

[Scala] neo4jで遊ぶ

前提知識

グラフをつくろう

  • エッジの追加はどうするの?
    • node1.createRelationshipTo(node2, relation) でnode1からnode2へrelationを表すエッジが作成されます
  • relationて何?
    • エッジは単なる矢印でなく、それに種類を定義できます。
  • で、relationて何?
    • つまりノード間に意味が発生するので、そのつながり方を「関係」として捉えているのです
  • どうやって定義するの?
    • DynamicRelationshipType.withName(name)で動的に作ることが出来ます
  • nameには何を指定するの?
    • 関係を意味する文字列です。好きな文字列で構いません。ラベルと思ってOKです

scala> import org.neo4j.graphdb.{Node,DynamicRelationshipType}
scala> val love = DynamicRelationshipType.withName("Love")

  • キャー、Love作っちゃった!岡井ちゃんに向けていい?
    • ご自由にどうぞ

rm -rf /tmp/neo1

import org.neo4j.kernel.EmbeddedGraphDatabase
import org.neo4j.graphdb.{Node,DynamicRelationshipType}

val db = new EmbeddedGraphDatabase("/tmp/neo1")
val tx = db.beginTx

// 舞波
val maiha = db.createNode
maiha.setProperty("name", "舞波")

// 岡井ちゃん
val okai = db.createNode
okai.setProperty("name","岡井")

// Love
val love = DynamicRelationshipType.withName("Love")
maiha.createRelationshipTo(okai, love)

tx.success
tx.finish
db.shutdown

     Love
 [舞波]  --->  [岡井] 

現在のDB内のグラフ(想像)

  • 本当にグラフできてるの?
    • node.getRelationships でそのノードが持つ関連(エッジ)のイテレータを取得できます
import org.neo4j.kernel.EmbeddedGraphDatabase
import scala.collection.JavaConversions._
val db = new EmbeddedGraphDatabase("/tmp/neo1")
val n1 = db.getNodeById(1)

scala> n1.getRelationships foreach(println)           
Relationship[0]

  • うほ!中身はどうやってみるの?
    • TABろう

scala> val r = n1.getRelationships head    
r: org.neo4j.graphdb.Relationship = Relationship[0]

scala> r.

asInstanceOf        compareTo           delete
getEndNode          getGraphDatabase    getId
getNodes            getOtherNode        getProperty
getPropertyKeys     getPropertyValues   getStartNode
getType             hasProperty         isInstanceOf
isType              removeProperty      setProperty
toString

  • これがどういう関連(エッジ)かを見るには?
    • getType

scala> r.getType
res8: org.neo4j.graphdb.RelationshipType = Love

  • Loveキタ!誰に向いてるかは?
    • getEndNode

scala> r.getEndNode
res9: org.neo4j.graphdb.Node = Node[2]
scala> r.getEndNode.getProperty("name")
res10: java.lang.Object = 岡井

  • グラフを全部表示して
import org.neo4j.kernel.EmbeddedGraphDatabase
import scala.collection.JavaConversions._
def inspectNeo(path:String) {
  val db = new EmbeddedGraphDatabase(path)
  for (n1 <- db.getAllNodes) {
    for (r <- n1.getRelationships) {
      val n2 = r.getEndNode
      if (n1 != n2) printf("[%s] --(%s)--> [%s]\n",
        n1.getProperty("name"), r.getType, n2.getProperty("name"))
    }
  }
}

scala> inspectNeo("/tmp/neo1")
[舞波] --(Love)--> [岡井]

  • グラフの作り方はわかったけど、createほげほげとか面倒だね
    • そんなあなたにneo4j-scala

http://github.com/jawher/neo4j-scala

  1. エッジの作成を "-->" でできるようになる
  2. node(key) = value でプロパティを扱えるようになる
  3. execInNeo4jでトランザクション処理を行う

rm -rf /tmp/neo1

import org.neo4j.kernel.EmbeddedGraphDatabase
import org.neo4j.scala.Neo4jWrapper

object world extends Neo4jWrapper {
  implicit val neo = new EmbeddedGraphDatabase("/tmp/neo1")
  execInNeo4j { neo =>
    val chisa = neo.createNode; chisa("name") = "岡井"
    val mai   = neo.createNode; mai("name")   = "萩原"
    val airi  = neo.createNode; airi("name")  = "鈴木"
    val momo  = neo.createNode; momo("name")  = "嗣永"

    chisa --> "疎遠" --> airi <-- "危機感" <-- momo
    airi  --> "疎遠" --> chisa
    chisa --> "親密" --> mai
  }
  neo.shutdown
}
world

scala> inspectNeo("/tmp/neo1")
[岡井] --(親密)--> [萩原]
[岡井] --(疎遠)--> [鈴木]
[鈴木] --(疎遠)--> [岡井]
[嗣永] --(危機感)--> [鈴木]

  • こ、これがコードなのか!Scalaの表現力はバケモノか!
    • Scalaの演算子定義は上手く使うと楽しいですね


2010-10-27 [長年日記]

[Scala] 試行錯誤で学ぶ neo4j

グラフデータベースのneo4j をドキュメントをろくに読まずに、REPL 上から淡々と試行錯誤して覚える

  • DBへの接続は?
    • 多分 EmbeddedGraphDatabase クラスを使う
import org.neo4j.kernel.EmbeddedGraphDatabase
val db = new EmbeddedGraphDatabase("/tmp/neo1")                 
  • DBで何ができるの?
    • REPLでTABってみる

scala> db.

asInstanceOf                        beginTx
createNode                          enableRemoteShell
getAllNodes                         getConfig
getManagementBean                   getNodeById
getReferenceNode                    getRelationshipById
getRelationshipTypes                getStoreDir
index                               isInstanceOf
isReadOnly                          registerKernelEventHandler
registerTransactionEventHandler     shutdown
toString                            unregisterKernelEventHandler
unregisterTransactionEventHandler

  • グラフにノードを追加するには?
    • createNodeがそれっぽい

scala> db.createNode
org.neo4j.graphdb.NotInTransactionException

  • なんかでたよ?
    • createNodeはトランザクション内で実行する必要があるっぽい
  • トランザクションの開始は?
    • beginTxがそれっぽい

scala> db.beginTx   
res1: org.neo4j.graphdb.Transaction = org.neo4j.kernel.TopLevelTransaction@329a0466
scala> db.createNode
res2: org.neo4j.graphdb.Node = Node[2]

  • ノード作成できた!
    • おつかれちゃん
  • 一度終了して、REPL立ち上げなおして確認しよう
    • そうしましょう

scala> import org.neo4j.kernel.EmbeddedGraphDatabase  
scala> val db = new EmbeddedGraphDatabase("/tmp/neo1")
org.neo4j.graphdb.TransactionFailureException: Could not create data source [nioneodb],
 see nested exception for cause of error

  • あれ、繋がらなくなった?
    • 正常にシャットダウンしないとこうなるらしい(ぐぐった)
  • どうやって修復するの?
    • 接続中のJVMプロセスが残っているのでkillする
  • はい、殺して REPL を立ち上げなおしました
  • さっき作ったノードはどうやって取るの?
    • getNodeById がそれっぽい

scala> db.getNodeById()
:8: error: not enough arguments for method getNodeById: (x$1: Long)org.neo4j.graphdb.Node.
Unspecified value parameter x$1.
       db.getNodeById()
                     ^

  • いやいや、IDとか覚えてねーし
    • どうせシーケンシャルだろうから1じゃね?

scala> db.getNodeById(1)
org.neo4j.graphdb.NotFoundException: Node[1]
        at org.neo4j.kernel.impl.core.NodeManager.getNodeById(NodeManager.java:388) 

  • ちょw
    • 2回実行したから2かも

scala> db.getNodeById(2)
org.neo4j.graphdb.NotFoundException: Node[2]

  • ちょww
    • じゃあ、3...
  • もう騙されないぞ!
    • getAllNodes てのがあるから、そっちやってみよう

scala> db.getAllNodes
res9: java.lang.Iterable[org.neo4j.graphdb.Node] = org.neo4j.kernel.EmbeddedGraphDbImpl$2@24174be7

  • Nodeクラスだってよ
    • 変数に入れてTABろう

scala> val nodes = db.getAllNodes
scala> nodes.

asInstanceOf   isInstanceOf   iterator       toString

  • iteratableか、面倒だな。全部ガツーンと返せよ!
    • 1億件あったらお前文句言うじゃん
  • ですよねー
    • foreachで見てみよう

scala> nodes foreach println
:11: error: value foreach is not a member of java.lang.Iterable[org.neo4j.graphdb.Node]
       nodes foreach println
       ^

  • あぁ、Javaのクラスだから互換ないのか。どうするんだっけ?
    • import scala.collection.JavaConversions._

scala> import scala.collection.JavaConversions._
scala> nodes foreach println                    
Node[0]

  • うわぁ、0オリジンかよー!
    • ついてないね
  • ノードの情報を見るには?
    • TABろう

scala> val node = nodes head
node: org.neo4j.graphdb.Node = Node[0]

scala> node.

asInstanceOf            compareTo               createRelationshipTo
delete                  getGraphDatabase        getId
getProperty             getPropertyKeys         getPropertyValues
getRelationships        getSingleRelationship   hasProperty
hasRelationship         isInstanceOf            removeProperty
setProperty             toString                traverse

  • お、getIdがある
    • もちろん0なんだろうな

scala> node.getId
res5: Long = 0

  • おういえ!
    • getProperty てのもあるよ

scala> node.getProperty
:15: error: ambiguous reference to overloaded definition,
both method getProperty in trait PropertyContainer of type (x$1: java.lang.String,x$2: Any)java.lang.Object
and  method getProperty in trait PropertyContainer of type (x$1: java.lang.String)java.lang.Object
match expected type ?
       node.getProperty
            ^

  • 引数が必要だって
    • まぁ、そうだろうね
  • 有効なプロパティ名は?
    • nameぐらいはあるだろJK

scala> node.getProperty("name")
org.neo4j.graphdb.NotFoundException: name property not found for NodeImpl#0.

  • お前、意外と適当だな
    • getPropertyKeys があるよ(無視しながら)

scala> node.getPropertyKeys
res8: java.lang.Iterable[java.lang.String] = []

  • 何も入ってねーぞ!
    • 何も設定してないからね
  • 名前入れるぞー!
    • よっしゃ、いったれー

scala> node.setProperty("name", "maiha")
org.neo4j.graphdb.NotInTransactionException
        at org.neo4j.kernel.impl.core.LockReleaser.getAndSetupPrimitiveEleme

  • 俺達・・・
    • 学習しないよね

scala> db.beginTx
scala> node.setProperty("name", "maiha")

  • お、エラーでない!
    • 中身の確認を早く!

scala> node.getPropertyKeys
res12: java.lang.Iterable[java.lang.String] = [name]

scala> node.getProperty("name")         
res13: java.lang.Object = maiha

  • キター!
    • キテルー!!
  • 一旦終了して、再REPLで保存されてるか確認しよう
    • 参照だからTXは不要で、ノードIDは0と
import org.neo4j.kernel.EmbeddedGraphDatabase
val db = new EmbeddedGraphDatabase("/tmp/neo1")   
val node = db.getNodeById(0)
println(node.getProperty("name"))

org.neo4j.graphdb.NotFoundException: name property not found for NodeImpl#0.

  • んな、バカな!
    • nodeのkeysはどうなってる?

scala> node.getPropertyKeys
res1: java.lang.Iterable[java.lang.String] = []

  • 空じゃねーか!
    • そもそも保存されてないってことか
  • さっきは見えてたじゃん?
    • transactionだからcommitする必要があったのかも
  • あぁ、そのままREPL閉じたからabort扱い?
    • 恐らく
  • てことは、このID=0のノードはどうやって保存したの?
    • 多分、初期ノード?
  • ふーん。新規に保存してみるか。コミットはどうするの?
    • dbオブジェクトにはメソッドないね
  • ちょw
    • TXオブジェクトにあるみたい(ぐぐった)
  • てことは、変数に入れて
    • TAB!

scala> val tx = db.beginTx 
tx: org.neo4j.graphdb.Transaction = org.neo4j.kernel.TopLevelTransaction@106f29c8

scala> tx.

asInstanceOf   failure        finish         isInstanceOf   success
toString

  • failureがabortとして、finishとsuccessってどっちだ?
    • finishはcloseぽい(ぐぐった)
メソッド役割
successcommit
failureabort
finishclose
  • もっかいREPL再起動
    • よっしゃいくぞー
import org.neo4j.kernel.EmbeddedGraphDatabase
val db = new EmbeddedGraphDatabase("/tmp/neo1")
val tx = db.beginTx
val n1 = db.createNode
n1.setProperty("name", "maiha")
tx.success
tx.finish
db.shutdown
  • 保存できたー!(多分)
    • 再REPLで内容確認だー!
import org.neo4j.kernel.EmbeddedGraphDatabase
import scala.collection.JavaConversions._
val db = new EmbeddedGraphDatabase("/tmp/neo1")
db.getAllNodes foreach println

Node[0]
Node[1]

  • おぉ!Node[1]キター!
    • 名前!名前!

scala> db.getNodeById(1).getProperty("name")
res8: java.lang.Object = maiha

  • キター!
    • キテター!


  [(root node?)]         [name:maiha]    

現在のDB内のグラフ(想像)

(以下、淡々と勉強中)


2010-10-19 [長年日記]

[Scala] 優しいAkkaの育て方 (level cap:19)

Scalaネット界でまことしやかに囁かれている噂がある。

「Akkaフレームワークの Actor はScalaの生Actorより高機能かつ高速」

そんな話を聞いてじっとしていれるほど、俺達大人じゃないんだ。

取得条件

レベル1. Akkaて何?

スケーラビリティがあり、障害耐性(fault-tolerant)を持つような並列型アプリケーションを簡単に書くことを目的としているのが、Akka Projectです。Akkaは、非同期(イベントドリブン)でシンプルで軽量な独自のアクタープロセス機構をベースにしており、障害管理やSTMおよびトランザクション、リモートアクターといった高レベルな機能も提供します。それ以外にも、永続的ストレージ、RESTインタフェース、Cometといったモジュールも具備しており、並列プログラミングを強力に支援するオールインワンフレームワークとなっています。

(オープンソフトウェア: Apache 2 License)

レベル2. 要するに何?

便利なjarです

レベル3. もう少し専門的に言うと?

se.scalablesolutions.akka.actorパッケージで定義され...

レベル4. 名前長いよw

ですよねー。この件に関して作者は、「ver 1.0では短い名前にするかも(約束はできないけど)」と言っています。(現在はver 0.10)

レベル5. サンプルきぼんぬ

pingにpongを返すアクターです。

import se.scalablesolutions.akka.actor.Actor
class Foo extends Actor {
  def receive = {
    case "ping" => println("pong")
   }
}

レベル6. うほっ、シンプル!

はい。"act { loop { react" なんて飾りです。偉い人にはそれがわからんのですよ。で、アクターの作成方法は...

レベル7. ストップ、みなまで言うな。基本レベル50はダテじゃない!

scala> new Foo
se.scalablesolutions.akka.actor.ActorInitializationException
        [maiha_1287811363519]
        ActorRef for instance of actor [Foo] is not in scope.
        You can not create an instance of an actor explicitly using 'new MyActor'.
        You have to use one of the factory methods in the 'Actor' object to create a new actor.
        Either use:
                'val actor = Actor.actorOf[MyActor]', or
                'val actor = Actor.actorOf(new MyActor(..))', or
                'val actor = Actor.actor { case msg => .. } }'
        null
        at se.scalablesolutions.akka.actor.Actor$class.$init$(Actor.scala:337)
...

レベル7. ちょw。newでけへん

Akka Actorは直接newすることはできません。上記のいずれかの方法で、作成して下さい。

レベル8. なんでnewではダメなの?

Akkaではアクターを直接操作せずに、そのアクターをwrapしたActorRefオブジェクトを経由して操作を行います。したがって、actorOfで作成されたものは、ActorではなくActorRefとなります。

scala> val foo = Actor.actorOf[Foo]
foo: se.scalablesolutions.akka.actor.ActorRef = Actor[Foo:1287771048941]

レベル9. ActorRefを被せると何が嬉しいの?

ローカルアクターとリモートアクターの差異を吸収しています。これにより、ユーザはローカルとリモートの意識をすることなく、ActorRefにメッセージを投げるだけで、分散可能なアクタープログラミングを実現できます。

レベル10. よーし、メッセージいくぞー!

scala> val foo = Actor.actorOf[Foo]
scala> foo ! "ping"
se.scalablesolutions.akka.actor.ActorInitializationException
        [maiha_1287813604216]
        Actor has not been started, you need to invoke 'actor.start' before using it
        null
        at se.scalablesolutions.akka.actor.ScalaActorRef$class.$bang(ActorRef.scala:1566)

レベル11. ちょい、ちょい、ちょーい!

Akka Actor では、起動していないActorにはメッセージを送ることができません。先にstartさせる必要があります。

レベル12. えー、start前に初期化処理をストックさせておくのが楽しかったのに。なぜダメなの?

不明 情報求む

レベル13. ま、ええとしよ。メッセージ、よっしゃ、いくぞー!

import se.scalablesolutions.akka.actor.Actor
class Foo extends Actor {
  def receive = {
    case "ping" => println("pong")
   }
}

scala>  val foo = Actor.actorOf[Foo]
scala> foo.start
scala> foo ! "ping"
pong

レベル14. キター。よーし、もいっちょ、行くぞー!

scala>  val foo = Actor.actorOf[Foo]
scala> foo.start
scala> foo ! "ping"
pong
scala> foo ! "ping"
pong

レベル15. キター。って、あれ?何でreceive定義しただけで何度もreplyできるの?

Akkaでは、メールボックスのメッセージ処理はアクター自身でなくDispatcherと呼ばれるオブジェクトが担当します。Dispatcherは内部にループを持っており、その中からActor#receiveの処理が呼び出されています。

レベル16. てか、生Actor脳だとreceive怖いんだけど、reactもある?

Dispatcher機構では、処理の主体はDispatcherオブジェクトです。従って、Actorのreceiveは処理内容が定義されたただのエントリ(handler)に過ぎず、処理のループに関する情報は含まれません。

レベル17. で、receiveはブロックするの?

ループ処理の方法は、利用するDispatcherの種類で切り替えます。デフォルトでは ExecutorBasedEventDrivenDispatcher が利用され、このDispatcherはブロックしません。

レベル18. ブロックするDispatcherもあるの?

ReactorBasedSingleThreadEventDrivenDispatcher などがあります。

レベル19. Dispatcherはどうやって指定するの?

利用されるDispatcherの優先順位は

  1. ActorRef#dispatcher = による指定 (start前に実行する必要あり)
  2. コンフィグファイルの "akka.actor.default-dispatcher"
  3. ExecutorBasedEventDrivenDispatcher (デフォルト)

となります。Scala Actorのreceiveに近いブロック型を実現するには、例えば以下のようにします。

scala> val foo = Actor.actorOf[Foo]
scala> foo.dispatcher = Dispatchers.globalReactorBasedSingleThreadEventDrivenDispatcher
scala> foo.start
scala> foo ! "ping"
pong

以下、淡々と勉強中。


2010-10-15 [長年日記]

[将棋] 盤上からのメッセージ

2010年10月14,15日に行われた第23期竜王戦七番勝負<第1局>。

広く深い読みと驚異の終盤力を武器にしながらも、竜王戦ではさらなる異様な強さを発揮し君臨し続ける渡辺竜王。それに挑戦するのは、史上最強の棋士である事に恐らく誰も異論を唱えないであろう、羽生名人。竜王vs名人という名に恥じない、棋界2強によるガチの頂上決戦である。

1日目の渡辺優勢で始まったこの歴史的一局は、2日目の羽生の粘りと大局観によって押し戻され、午後を迎えて一手ごとに優勢評価が変化するまさに名局となった。が、その衝撃的なラストによって、将棋ファンの間ではさらに伝説の一局へと昇華することとなる。

最終盤

後手の羽生玉は、周囲にオーラのような空間を残しつつも、絶体絶命の状況。しかし、後手はここまで力を溜めに溜め、大駒3枚と桂馬で先手玉を一気に打ち取る形を取っている。手番は羽生。今まさに手負いの羽生による最後の猛攻がはじまろうとしていた。そして90手目。羽生は誰も予想しない5五香を打つ。

20101016-n75byijnp1aq2g86mb4j6dur4h.jpg

一見、素人目にも簡単に歩で止められそうな香打ち。この時の驚きは、棋譜中継でも伝えられている。

羽生の△5五香から△5六同香に、控室は「どういう意味があるんだ?」と大混乱。

終局

その懸念通り、羽生の香は渡辺の歩打ちによってただで取られてしまう。直後に羽生が投了。

この局面で羽生が投了を告げた。突然の出来事に、控室も大混乱。

「なぜ投了!?まだイーブンだろ!?」「小ヨセといっても極端に細かい!僕にはサッパリだ!」

2日間の熱戦。クライマックスに突入した瞬間のあっけない投了に、見るもの全てが驚愕し、思考停止した。ある意味、名局の品位を落としかねない香打ちからの突然の投了。

佐藤康光九段 > 何か錯覚があったのでしょうか? (10/15-18:05:04)
烏@長崎 > 突然の投了に、現地も大混乱です。 (10/15-18:04:49)
佐藤康光九段 > えーっ?投了ですか? (10/15-18:03:34) 

トッププロですら理解できないこの奇行の謎を解いたのは94氏だった。

94 :名無し名人 :sage :2010/10/15(金) 17:59:01 ID:x3CQVOGB
  盤上にハートマークきたーーーーーーーーー
第23期竜王戦 Part41 http://toki.2ch.net/test/read.cgi/bgame/1287131722/94

終局図

20101016-tgtijqpd427bmm86nr9dshhwa2.jpg

2010年。棋界の現人神たる羽生の戦いはもう別次元に突入している

参考


サイト内検索 (by Google)

| JRuby | Rails | Berryz | ℃-ute | エッグ | jQuery |

過去

2014年
7月
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31

未来

コンタクト