React Revisited after version 16.8

React というのは DOMの内容を簡単に操作しようという JavaScriptのライブラリーの一つだが、Shadow DOMという概念を導入し、JQueryのようにコマンドごとの実行でDOMを書き換えることはせず、Shadow DOMにある程度書き込んでおいてから描画する直前にまとめてDOMに書き込む。なので軽快に動作する、というのが売り。

数年前に覚えようとしたのだが、 二点腑に落ちないところがあって、結局、Vue.jsという他のLibraryを愛用(溺愛)するに至った。

その二点というのが

1.JSXという新しい概念の導入。 一見するとHTMLなのだがHTMLではなく、XML書式をJavaScriptで理解できるようにしたプリプロセッサ。この一見HTMLというのがくせもので、class= はJSの予約用語ゆえに使えずclassName=にしなければならないなど、細かいところで違っていて気持ちがわるい。(これに比べてVueのTemplateはまごうことなきHTMLの拡張)

2.コンポーネントで操作できる変数(State)を使うためにはコンポーネントをClass表記してsetState()という関数を使って管理しなければならない。

というもの。

その後 「JSXは単なるJavaScriptの関数表現」という開発チームの解説をYoutubeでみて ストンと納得できるものがあったものの、 二つ目のJavaScriptにClassが必要というのがなんとも納得いかない(個人の意見です)身としては今いち手が出せずにいた。 Class表記をするあまり、 thisの多用を行い、しかもそのContextを明示するためにClass Constructorに this.function=this.function.bind(this) を書きまくるというのがなんとも切ない。

しかしながら、 Reactの勢いは侮りがたいものがある。 特にMicrosoftのOffice開発系の方たちは Office UI GraphicsのComponentに「Reactは正義!」という感じで使いまくっている。 SharePoinitの JavaScript Frame workも Reactびいきがすごい。

で、最近React-client-appというCLIでReactのScaffoldingが簡単にできます、という記事をよく見るので、実際どれくらい簡単なんだいと試してみたら、 生成されるScaffoldingのComponentがClass表記ではなくfunction表記になっている。 あれ、これって、Stateful Componentは Class表記しなければいけないんじゃなかったっけ、と思ったが、 実はReactも進化していた。

Version 16.8から Hooksという機能が追加されて Functional ComponentでもStateの管理ができるようになっていた。 そして今まで、 Functional Component = Stateless Component, Class Componnet=Stateful Componentとなっていたものが、 どちらを使ってもよいようになっている。 Reactのサイトにも将来の開発にはFunctional Componentを使うのがおすすめと書いてある。 なので、React-client-appもDefaultで生成するコードはクラスレスになっていたわけだ。

紹介ページ https://reactjs.org/docs/hooks-intro.html にあるコードをみれば一目瞭然だが、クラス表記のようなConstructorもなければ、this. でメンバー指定をすることもない。 非常に簡単。 ただ、このHookを使ったクラスレスのステートフルコンポーネントの作り方、オフィシャルサイト以外で解説しているサイトはまだ少ない。

この

const [ var1, setVar1]=useState('this is variable one');

で気をつけなければいけないのは Var1が複数メンバーから構成されるオブジェクトだった場合、

const [obj1,setObj1]=useState({member1:"this is member 1", member2:"this is member 2"})

のように Obj1を宣言したときに、

setObj1({member2:"this is member 2 modified"})

とやってもsetState()と違って、オブジェクトの他のメンバー(Key)とマージしてくれない。上の例ではmember1が消えてしまう。

セット関数には元のstateがパラメータとして渡されているのを利用して、

setObj1(s=>({ ...s, member2:"this is member 2 modified"}))

とスプレッド表記を使ってマージしてあげる必要がある。(上のサイトにはObject.createを使うのも可と書いてある)

また、いわゆる ComponentWillMountなどのLife Cycle hookの代わりには描画毎に発火するuseEffect()を使えと書いてある。 このHookは発火を限定するために二つ目のパラメータ―で発火する条件を指定できる。 ファイルデータを読み込むなど、あるいはイベントリスナーを設定するなど、一回だけ発火させたい場合、 このパラメーターを空のアレイで渡してあげることで実現できる。(なんとなくハックっぽいが)

useEffect(()=>{Data を読み込む云々}, [])

他にも何種類かのHooksが用意されているが、 演習用にアプリを書いてみると、上の二つのHookだけで、それなりのものができる。

すでにJSXへのアレルギーはなくなっているので、クラスを使わなくてよいReactという選択は結構魅力的です。

すべてにVue.jsを使いたいのはやまやまなれど、SharePoint関連においてMicroSoft が Reactありきの開発を推しているというのはやはり大きい。

Raspberry Pi でパソコン電源をInternet上から操作

家の外部からタブレットなりノートパソコンなりを使って自宅のパソコンにリモートアクセスするためのアプリというのは色々ある。Internet経由でアクセスする、というもので、Linuxでは当たり前のように実装されているし、 WindowsでもRemote Desktopというアプリケーションが同梱されているし、ファイアウオールでRemote Desktopが使えない環境でもWebベースのTeamViewer とかlogmeinというサードパーティの製品を使えばセキュアな接続がいとも簡単にできてしまう。 Chromeをブラウザとして使用しているのならChrome remote desktopというアプリが今の旬である。

 なぜそんなことが必要か、という疑問が湧くかもしれないが自分の場合、外部からお気に入りの開発環境にログインし、コード開発を一つのマシン上で行う、という使い方がメインになっている。 もちろん将来的にすべての作業がブラウザ上でできる、という環境が構築されればそんな必要はなくなるわけだが、

問題はパソコンにアクセスするには当然のことながら当該パソコンの電源が入っていなくてはならない、という事。
 エコの観点からみると年がら年中つけっぱなしになっているパソコン、というのはエネルギーの浪費に見える。 WEBサーバーを自宅のパソコンを使って構成していた時には電源をつけっぱなしにするしかなかったのだが、アイドル状態でも常に80Wくらいの消費電力が発生していた。
 現在はCubieboardという、手のひらに収まるサイズのArmcortexの開発ボードをサーバーとして使って賄っており、消費電力は3W以下だ。

で、パソコンの電源を使うときだけ遠隔操作でオンする方法がないか調べてみた。

 InstructableにElectric Impというモジュールを使って電源のオンオフをする方法が紹介されている
このモジュール、SDカードと同じform factorだが、内部にWifiモジュールとArm Coretex M3を内臓したれっきとした開発ボードであって、クラウドサービスに接続してパソコンやタブレット上から専用のコントロールパネルアプリを使って接続されたデバイスのリモートコントロールができるようになっている。

InstructableではこのElectric Impに簡単なトランジスタを追加して、PCのPower switch lineを瞬間的にショート状態にする。(つまりパソコンのスイッチを押したのと同じ状態にする)ことによってPCを起動する方法が述べられている。 Electric Impの値段は25ドル程度だが、サポート用の部品(Imp用のブレークアウトなど)を買い足すと40ドルくらいの出費になりそうだ。
自宅でcubieboardのWEBサーバーを随時動かしているのであえてアカウントを作ってクラウドを使ってコントロールする必要はないわけで、 そうすると似たようなことはArduinoでもできるはずと思うわけだが、これまたArduino本体にWifi、あるいはEthernetシールドを追加すると40ドルくらいの出費になる。 CubieBoardのGPIOをパソコンのスイッチに接続して操作することも可能だろうが、部屋が2階と地下室に分かれているし、メインサーバーになっているカードのGPIOで他のデバイスをハードウエアコントロールというのは安全性の面から心理的抵抗がある。

そこで遊び終わって引出しに眠っていたRaspberry Piを使うことにした。Desktop用のLinuxが走るほどのグラフィック性能を持つボードをただの電源スイッチとして使う、というといかにももったいない感じがするのではあるが Piの値段も含めて40ドルくらいで済んでしまうのでコストパーフォマンスの面から考えると他の方法に比べても高くはない、というか他の手間を考えるとこちらのほうが安上がりの感じ。

調べてみるとデバイス制御をPythonベースのhttp サーバーから実行できるWEBIOPIというPi用のライブラリーを発見した。 これをインストールするとGETとかPOSTのHTTP用のプロトコルを使ったRESTという手法でハードウエアのコントロールができるようになる。 つまりWEBサーバー上にページを作り、そのページ上にボタンを描画して、WEBIOPIで提供されているRESTコールを紐つけるという方法でPiのGPIOピンの遠隔操作ができるようになる。 このRESTという手法、非常に単純で、urlのパスに模してパラメーターをサーバーに送ってあげるというもの。GETはアドレスをブラウザで指定するだけで実行できるのでたとえば APIですべてのGPIOの状況を取得するAPIを使うには webiopiをバックグラウンドで走らせている状態で、

https://example.com/GPIO/*

とアドレスをブラウザに打ち込むだけで、Jason書式でGPIOの各ピンの状況が返ってくる。 ただし、これではページの表示自体がJasonのレスポンスになるので実用的ではなく、実際にはJavaScriptでコードを書くことになるが、webiopiにはうwebiopi.jsというヘルパーファイルが同梱されているので、使う難易度は低くなっている。

GPIOの使い方だが、
出力用に設定したGPIOピンをトランジスタを介してパソコンの電源スイッチとパラレルに接続し、パルス上の信号を出力してやるとパワースイッチを押したのと同様のことになり電源が立ち上がる。(電源が入っているときには逆に電源が落ちる)。電源スイッチへ接続されるピンはパソコンのマザーボード上のフロントパネルコネクター部分(パソコンケースのケーブル類を接続するコネクタ部分)に接続ピンがあるのでワイヤーをスプライスしてつなげてやればよい。。
また信号を出す前にパソコンの電源が入っているかどうかをわかっている必要があるのだが、フロントパネルコネクターには電源表示用のLEDへ接続されるピンも存在している。 そこでこのピンをGPIOでモニターすることによってパソコンに電源が入っているかどうかの判定をすることにした。

実際に組んだ回路。使ったGPIOピンは25をパワースイッチ用の出力に、24をLED電圧モニター用の入力にそれぞれ使用。 IntelのPC Interface 仕様(下に抜粋を載せます)を読む限り、パワースイッチ端子はトランジスタに直接接続(PWR+をコレクタ側に、PWR-をEmitter側に)しても大丈夫と思うが、5Vの小型リレーがたまたま手元にあったので使ってみた。NPNトランジスタのかわりに2N7000のようなFETを使えば、R1は必要ない。入力ピンのほうはLEDのVfでクランプされる電圧になるので規定の3.3V以下。万が一LEDがオープンとなっ場合5Vかかってしまうが、GPIOには保護用のクランプダイオードくらいはいっているのだろうと勝手に思い込んでいるので,保険は電流制限のR2のみ。

Table 5. Switch/LED Front Panel Electrical Connection

Pin Signal Description
2 FP PWR/SLP MSG LED pull-up (330 ohm) to +5 V
6 PWR_SW_P Power Switch high reference pull-up (10000 ohm) to +5 V
8 PWR_SW_N Power Switch Low reference pull-down (100 ohm) to GND

贅沢の極み?

贅沢の極み?


html pageの記述


<!DOCTYPE html>
<html lang="en">
<head>
<base href="https://example.com/" />
<title>PC power </title>
<!--[if IE]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
</head>

<body>
    <h2>Turn On Basement PC</h2>
    
パソコンの電源操作ボタンでやんす。!
<form class="buttonBar"></form>

<script type="text/javascript" src="//code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript" src="poweronbutton.js"></script>
</body>
</html>

参照されているJavascriptの記述

/**
 * Created on 5/4/14.
 * poweronbutton.js となっているが、実際にはGPIOのモニターも行う総括的なコード
 * powerbutton element id='pb'
 * html form tag class = buttonBar.
 * GPIO #25 connected to 1n2222 which drives spdp relay to momentariry close power switch.
 * PC power switch is momentary switch.  if PC is off, it will turn on PC.  if PC is on, it will turn off pc
 * relay connected to power connections of PC system panel terminals.
 * GPIO24 connected to Power LED terminal.
 * webiopi (python httms and coap server) installed on target raspberry pi
 *
 */
function setupGPIO(){   // set GPIO25 to out for pc power switch connection.
                        // and  GPIO24 to in  for monitoring PC power LED terminal status
    $.post( 'https://example.com/GPIO/24/function/in',
        function(data) {
            if (data==='IN')setTimeout(monitorPower(),1000);
                  });
    $.post( 'https://example.com/GPIO/25/function/out',
        function(data) {
            data =(data=='OUT')?'PC Power':'setup failed';
            $('#pb').attr("value",data);

        });
}

// }
function turnon(){ // RET command to output pulse
    $.post(
        'https://example.com/GPIO/25/sequence/1000,010',
        function(data) {
            data=(data==0)?'Power switch pushed':'NG';
            $('#pb').attr("value",data); }

    );
}
function monitorPower(){//monitor GPIO 24 status every one second
    $.get('https://example.com/GPIO/24/value',
        function(data){
            data= (data==='0')?'PC is OFF':'PC is ON';
            $('#powerstatus').html(data);
    setTimeout(monitorPower(),1000); //call myself every 1second

})

}
$(function(){
    $('<input type="button" class="power" id="pb" />')
        .appendTo('.buttonBar')
        .attr("value", "Initializing")
        .attr("title", "Turn on Somethin'")
        .click(function () {
            turnon();
        });
    $('<div></div>')
        .appendTo('.buttonBar')
        .attr('id','powerstatus');
      setupGPIO();
    }
);

ターゲットのRaspberry Piにwebiopiが導入され起動されていることが前提になる。 また、上の方法では同梱されてくるwebiopi.jsは使わずに自前のJavaScriptでREST APIを直接アクセスしている。

ページの仕組み:
ブラウザーがページを読み込み終わった時点で、上のjavascriptが以下の作業を実行する。
ボタンを描画し、ボタンのラベルを”Initializing” と表示する。
GPIO25を出力ピンに設定。 ボタンのラベルを”PC Power”と書き換え、ボタンのクリック動作にパルスを送信するRESTコマンドを紐つける。
GPIO24を入力ピンに設定。同時に入力ピンの1秒ごとのモニターをスタート。WEBIOPIからRESTで返された状況に応じてページ上に”Power is ON” または ”Power is OFF”を一秒ごとに更新表示

JavaScriptの部分はRefactoringやnamespacing をやっていないので改善の余地あり。またPower LEDのモニターだけではパソコンがオンしていることはわかっても、実際にWindowsが立ち上がっているかどうかはわからないので、何らかのインターネットサービスをPi側からモニターする手法を併用するほうがよさそうだし、CSSの記述も追加してページの見栄えもよくしたいと思うが、一応初期の目的はこれにて達成。

なお、WEBIOPI自体にログイン形式のセキュリティ機能が実装されており、ページの最初のロード時にログインネームとパスワードを聞いてくる仕組みになっている。ログインとパスワードの初期値がそれぞれ”webiopi” と”raspberry” となっているので、マニュアルの手順に従って変更しておく必要がある。

また、Raspberry Piへの電源だが、パソコンの電源に5V standbyが存在しているため、ここから引き込んでいる。24p/20pコネクタの紫の線だが、マザーボードの電源側への要求スペックが500mAで電源側の仕様が2Aなので余裕のようだ。Raspberry PiもPCの筐体に入れてしまったので見た目もすっきりである。

<後記> 電力消費計を入手したので、実際のところ、どれだけエコになっているのか測定してみた。 パソコンを普通に電源投入すると80Wくらの消費になっていた。 パソコンをオフにすると7Wだ。パソコン用の電源が動いていて5Vのスタンバイ電源からPiとその他に供給しているということだ。 その差73Wだが、電気代で考えるとまったくパソコンをオンしなかった場合、年間約50ドルちょっとの節約になる。パソコンの稼働率が10パーセントくらいと仮定すると45ドルの節約なので1年でもとが取れる計算になる。

IIS のurls rewrite を使ってmodxのfriendly url を有効にする。

たとえIISサーバーを使っていてもHelicon Ape というツールをIISに組み込めば、modxについてくる.htaccess をそのまま使っのfriendly url を活用することはできているわけだが、今回はIISのURL rewrite module (IIS 7 の場合は別途ダウンロードで組み込む)を使って同じことをやってみた。

IISのGUIでルールを作る作業になるが、.htaccess の記述を参考にしてやってみたら動くようになった。

modx に付属する.htfile でfurl に関連するところは 以下の部分と思われる

RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]

これを解釈すると
条件として、要求されているファイル名がファイルとして存在しない、かつ
フォルダーとしても存在しない場合、それはつまり動的に生成されるHTMLページなので、
以下の書き換えルールを適用する。
書き換えルール: 渡された文字列$1を以下のパターンにして加工する
“index.php?q=$1”
さらに 後ろにクエリーがついている場合はそのまま渡し、 下流の処理を行わない。

IISのGUI上のRuleでは これを Inbound rule として追加するが、その内容は以下のごとくなる。

Matches Pattern, “Regular expression”
“^(.*\.html)$” <= RewriteRule の最初の部分*1 Conditions : "Matches All" {REQUEST_FILENAME} "file does not exist"  {REQUEST_FILENAME} "Directory does not exist" Action type "rewrite" index.php?q={R:1} <= RewriteRule index.php?q=$1 に匹敵 Append ques : yes <= [QSA] に匹敵 Stop Processing: yes <= [L] に匹敵 当たり前ながら、同じ条件を記述しているので、それほど迷わずにできた。 はまりそうになったところはRegular Expression の部分で.htmlを明示している部分。これをやらないとloopしてしまう。 ほかのところの条件付けでもっと綺麗に記述できるかもしれない。 とりあえず、これでOK

JavaScript, Object literal で Private Variable を埋め込む

New とかを使わず、Object literal で object を記述するのがクール.。で、プライベート変数を仕込むには Function を使ってLiteral をReturnするパターンが使える。

var myobje=(function() {
// private変数
var name = "my, oh,my";
//この機能定義は即実行され、以下がまとめてオプジェクトリテラルとして返される。
return {
getName : function () {
return name;
}
};
}());

myobje.getName(); //"my, oh my"

おぷじぇくとの外部からはこのname という変数は直接いじれない。 function 内部の変数はファンクションが実行された後でも、内部に残っている、というClosure と呼ばれる性質を使った技。

JavaScriptで動く将棋盤のページを作る。コンセプトの整理

あらためて、考え方を整理してみる。

Webページは HTMLファイル、CSSファイル、JavaScriptファイルという三つの構成要素から成り立っている。そこで今回試作したページのすみわけを考えると以下のようになる。

html ファイル:将棋盤の入れ物を定義し、CSS,JavaScriptファイルにリンクさせる。
CSSファイル:将棋盤と駒の表示の仕組みを定義する。これをHTMLのエレメントの属性に対するスタイル定義で行う。
JavaScriptファイル: 将棋盤の初期配置と駒の動きのデータ、および、駒を初期配置の通りに並べて表示し、駒を動かす関数およびボタンの表示。 データ部分と機能部分を分けて二つのファイルで構成するようにする。
JavaScriptでも”描画”作業は行っていない。WebのDOM(DataObjectModel)を操作してHTMLのエレメント属性を変更させているだけで、あとはブラウザにお任せである。
一度画像データをサーバーから読み込めばデータがローカルにキャッシングされる。ブラウザとしては初期画面で盤と駒のデータを読み込んだあとは時々成り駒のデータをダウンロードしにいくだけなので、作画速度で目に見えるような遅れはない。
Web page を表示するためには将棋の盤、マス目、各コマの画像データの他に
filename.html
shogiboard.css
boarddata.js
boardfunction.js
の4つのファイルが必要、boarddata.jsに棋譜データを書き込んで動きを指定する、ということになる。実際にはboarddata01.js, boarddata02.js などと作っておいて、html からのリンク先指定を変えてやるような操作になる。
HTML内部、JavaScriptへのリンク部分
<script src="http://code.jquery.com/jquery-1.7.2.min.js"></script> 
<script src="boardfunction.js"></script> 
<script src="boarddata01.js"></script>
HTML内部、CSSファイルへのリンク部分
<link href="shogiboard.css" rel="stylesheet">
ところで、
この将棋盤、自分のサイトに表示するには少し大きすぎる。
もっと小さい画像データがないかなあ、と思っていたのだが、CSSでイメージをスケーリングすれば解決できることに気が付いた。 Cascaded Style Sheet は変更部分だけ記述すれば、最後に置かれた記述が依然の記述を書き換えるので
約65%のスケールダウンには以下のような内容のCSSファイルをリンク先に追加すればよい。
.board {width:286px; height: 312px;} 
.komadai {width:100px; height:150px;} 
.komadai img,.koma, .marker {width:30px; height:33px;} 
.comment {padding-left:7px; width:450px;min-height:100px;}
#boardbase {width:323px; height:337px;} 
.c1 {left:258px;} .c2 {left: 228px;} .c3 {left:198px;} 
.c4 {left:168px;} .c5 {left: 138px;} .c6 {left:108px;} 
.c7 {left:78px;} .c8 {left: 48px;}  .c9 {left: 18px;} 
.r1 {top:17px;}   .r2 {top:50px;}    .r3 {top:83px;} 
.r4{top:116px;}   .r5{top:149px;}    .r6 {top:182px;} 
.r7{top:215px;}   .r8{top:248px;}    .r9{top:281px;}
このファイル名、shogiboard-small.css とすれば HTML内部、CSSファイルへのリンク部分は
<link href="shogiboard.css" rel="stylesheet">
<link href="shobiboard-small.css rel="stylesheet">
となる。
動くサンプルはこちら
下にファイルと機能のまとめをマインドマップで整理したものをリンクしておく

将棋盤に戻しボタンをつけてみる(JavaScript)

将棋盤の上の駒がボタンを押すごとに一手づつうごくようになって喜んだのもつかの間、手を戻すボタンがないのがひどく問題に見えてきた。 これは機能を追加せねば、と考えたのだが、最初の設計時点で履歴をとるような構想は一切なかったのではたと困った。動きを逆になぞってみても、駒を取る、というアクションがデータ構造上明示されていないので、もとに戻すのが無理なんである。 何か方法があるはずだと思いつつ2~3日、ほおっておいた。

いっそのこと、一手ごとに将棋盤全部のスナップショットを取ってそれを変数配列に格納してはどうだろうと考えついた。 スナップショットとは言っても、画像データをコピーするのではなく、JavaScriptが精製したhtmlのツリー構造をそのまま変数にコピーしてしまおうというわけだ。JavaScript ではinnerHTMLといタグ内の文字列をそっくりコピーできる関数がある。JQueryでは.html()メソッドにファクタリングされているので、これを使えばなんとかなるのではないかと実験してみたらあっさり動いてしまった。
ちなみに40枚駒を並べた将棋盤のスナップショットは文字列の大きさで5キロバイト程度だった。 これなら200手動かしてもでも1メガバイトで収まる。 Z80でプログラミングしている時代なら、5キロバイトとわかった時点で、間抜け扱いだったが、時代は変わったのだ。これでいいのだと自分を納得させる。 ブラウザ上の JavaScript で動いているわけだから、データがネットワークを移動するわけでもなく、単にブラウザの使用メモリーがそれだけ増えるだけ、ということになる。
まずはデータを格納する配列変数を用意する。
var board {
.
.
history:[],
}
という具合にGloval variable に Array としてhistory[]を定義しておき、次に<div id=”snapshot”>…</div> 内のhtml構文をboard.history[]にセーブする関数 takesnapshot()を定義する。
function takeSnapshot(i){
board.history[i]=$('.forSnapshot').html();
}
次に、このboard.history[]配列から現状の<div id=”forsnapshot”>…</div>内に内容をロードする関数を定義する。 同時に”一手進めるボタン”が無効化されていれば、それを有効にする作業も行う。
function setBoardToHistory(i){
$('#aButton').removeAttr("disabled");
$('.forSnapshot').empty().html(board.history[i]);
}

>

次に上記の関数を呼び出して、一手だけ戻る関数 stepback()を定義する。ここでは一応board.index が0以下にならないようにチェックを入れている。
function stepback(){
if(board.index>0)
setBoardToHistory(--board.index);
}
そして、animateBoard() function に太字部分を追加。
function animateBoard(){
var zAction=board.moves[board.index];
takeSnapshot(board.index);
parseAction(zAction);
board.index+=1;
if (board.moves[board.index].charAt(0)=='x') $('#aButton').attr("disabled","disabled")
;

}
ボタン機能を追加
function setupButton() {
$("#aButton").click(function () {animateBoard()}).attr("value","Forward for solution");
$("<input type='button'>").click(function() {stepback()}).attr('value','Step back').appendTo('#buttonBar');
}

 
上のルーチンで、

<div id='buttonBar'>
<input  type="button"  id="aButton" value="Javascript did not load" />
</div>

というエレメントにStep back というボタンが動的に追加される。(aButton のほうも同じように動的に追加したほうがコードがコンパクトになりそうだ)

これでもとに戻るボタンを追加することができた。 デモはこちら

JavaScriptで将棋の駒を動かす2

前述のグローバルデータを読み込んで、駒の処理をする関数を定義していく。

function animateBoard(){
var zAction=board.moves[board.index];
parseAction(zAction);
board.index+=1;
if (board.moves[board.index].charAt(0)=='x') $('#aButton').attr("disabled","disabled")   ;
}

上がメインの関数になる。これは呼ばれるごとにboard.indexで指定されるboard.moves[]文字列を読み出し、parseAction関数に渡す。 ’x’ を読み込んだ時点で、駒を動かすボタンを無効化するようにしている。

function parseAction(aAction) {

if (aAction.charAt(0) == '*') postComment(aAction.slice(1));
else {
if (aAction.charAt(1) == 'd') makeAdrop(aAction.charAt(0), aAction.charAt(4), aAction.substr(2, 2));
else {
makeAmove(aAction.charAt(0).toUpperCase(), aAction.charAt(1), aAction.substr(4, 2), aAction.substr(2, 2));

}
if(aAction.indexOf('*')>0){postComment(aAction.slice(aAction.indexOf('*')+1))}
}
}

parseAction 関数では文字列を解析し、コマンドを実行する。コメントであれば、postComment,  駒を打つのであれば、makeAdrop, それ以外はmakeAmove関数にパラメーターを渡し、最後にもう一度コメントの有無を確認している。

function postComment(comment) {$('#scomment').empty().append(comment);}

postComment はid=”scomment”のhtml エレメントをまず空にしてから、コメントを張り付ける、というだけのもの。

function makeAdrop(side,koma,position) {
var png=side.toUpperCase()+komatopng(koma);
if (side.toUpperCase()=='S') side='#senteMochigoma';
else side='#goteMochigoma';
setMarker(position);
position=cordToClass(position);
emptyComment();
var selector=side+' [src$="'+png+'"]';
$(selector).first().addClass(position).appendTo('#boardbase');
}

駒をドロップする、ということは先手、または後手の駒台から当該する駒のイメージエレメントを探し出し、このイメージエレメントのクラスを変更して将棋板のブロックにぶら下げる(appendTo)という作業になる。appendTo自体はエレメントを移動するコマンドなので、駒台のイメージエレメントをdetachする必要はない。

function makeAmove(side,promote, from, to) {
emptyComment();
//if to position is already occupied, then capture that image element to 'side's mochigoma
//for this we check the lenth of selector. ie, if $(".c6 .r7").length>0 then there is an element.
if ($(cordToSelector(to)).length>0) captureKoma(side,to);
// then set a marker to "to" position
setMarker(to);
// then move the piece, it just involves the changing of class
$(cordToSelector(from)).attr('class', cordToClass(to));
// then check if the piece is promoted by checking the variable promote
if (promote=='+') {promoteKoma(side,to);}
}

駒を動かすmakeAmoveという関数は、駒の移動先にすでに他の駒がいるかどうかを判定し、もし存在すればそのコマを自分の駒台におくという作業(captureKoma)を行う。 次に駒を移動させ、もし、成る、という行為が指定されていれば駒を昇進させる(promoteKoma) 駒の移動自体前述したごとくClass属性を変更するだけという非常に簡単な作業。

function captureKoma(side,cord){
var komaban,koma;
komaban=(side=='S')?'#senteMochigoma':'#goteMochigoma';
koma=$(cordToSelector(cord)).data("koma");
$(cordToSelector(cord)).first().attr("class","").attr("src",board.pathname+side+koma).appendTo(komaban);
}

captureKoma では先手か後手かを判断して、どちらの駒台に駒を移動するかを決める。駒の移動は当該イメージエレメントのClass属性を削除して駒台エレメントにappendするだけ。

function promoteKoma(side,cord) {
var koma;

koma = $(cordToSelector(cord)).data("koma");
koma = (koma == "hi.png") ? "ryu.png" : koma = (koma == "kaku.png") ? "uma.png" : 'n' + koma;
$(cordToSelector(cord)).first().attr("src", board.pathname + side + koma);
}

promoteKoma では、画像ファイルの名前を変更する。 なお、駒の画像ファイル名をイメージエレメント自体に記録しておくために”data-koma”という属性をエレメント作成時に追加しているのだが、これはjquery では.data(“koma”) というメソッドで読むことができる。駒がなっても、この画像ファイル名自体は変更していない。(駒を取った時に、このデータを読み込んで駒台に置かれる駒の画像としている。竜を取っても、駒台に置かれるのは飛車、というわけ)

上記の関数に使われるサブ関数の定義は以下の通り。

function emptyComment() {$('#scomment').empty();} コメント欄を空に
function cordToClass(cord){ return 'koma c'+cord.charAt(0)+' r'+cord.charAt(1);}

位置情報をクラス属性に変換(例:34→”koma c3 r4″)

function cordToSelector(cord){return ('.koma.c'+cord.charAt(0)+'.r'+cord.charAt(1));}

位置情報をjQueryのセレクター情報に変換(例:34→’.koma.c3.r4 ‘)

function setMarker(cord){
var markerClass;
markerClass="marker c"+cord.charAt(0)+' r'+cord.charAt(1);
$('#marker').attr("class",markerClass);
}

これは、動いた駒を示すために、駒の背景色を変えてるためのmarkerセットするルーチン。

 

動かすためのボタンを設定

function setupButton() {
$("#aButton").click(function () {animateBoard()}).attr("value","Forward for solution");
}

 

初期設定にボタン設定を追加

$(function () {
initializeBoard();
setupButton();

});

 

これで一応ボタンをクリックすることにより、駒が一手づつ動く将棋盤ができた。(デモサンプル) とたんに気が付いたのが、手を戻す機能がほしい、ということ。 というわけで次回は戻しボタンの実装をする。

 

将棋駒をJavaScriptで動かす

前回作った将棋盤で、初期画面を表示することができたので、ここから駒を動かすしかけをJavaScriptで作成していく。

駒の動きを表すデータ形式をまず決める。やはり、boardというグローバルオブジェクトの中にmoves というString配列 として用意することにする。

一文字目:先手・後手の指示、s (先手)またはg(後手)の一文字で表記。先手が2手続けて指す、ような場合にも対応、  ‘x’で終了。
二文字目:駒を打つ場合にはd(drop) ,駒をなる場合には’+’, それ以外は読み込まない。
三、四文字目:駒の移動先の座標 3四の場合は34 etc.,
五、六文字目:二文字目、drop指定以外は駒の移動元の座標 3三の場合は33 etc.,
二文字目がDrop指定の場合、五文字目は駒の指定一文字jとなる。(p,l,n,s,g,r or b)

たとえば、後手3三の駒が3四に行く場合の表記は “g-3433″となる。 コマの種類に無頓着に3三にあった駒を3四に移動する、という考え方である。

また、後手側が歩を2三に打つ場合は”gd23p”となる。

駒をとる、という表記がないが、これは移動した先に駒があれば、それを取るということにする。よって明示的には記述しない。

また、’*’以下の文字列をコメントとして認識し、コメント欄に表示できるようにする。 一文字目にあれば、駒を動かさず、コメントのみ更新する。

 

配列のポインターとしてboard.index という変数を指定しておく。 初期値は当然0.

これらのルールによって、一連の駒の動きをあらわすとたとえば以下のようなデータ配列となる。


object={
.
.
.

moves:[
"*this is a comment that should go to comment line",
"s-2627 *here, the sente moves a piece from 27 to 26",//2六歩
"g-8483", //8四歩
"s-2526", // 2五歩
"g-7261", // 7二金
"s-2425", // 2四歩
"g-2423 *capturing the piece is implied", //同歩
"s-2428", // 同飛車
"gd23p", // 2三歩打
"s+2324",// 同飛車成り
"g-3241", //3二金
"s-2223", // 2二竜
"g-2231", //同銀
"x" //終了
],
index:0,
.
.
}

将棋盤と駒をWEB上に表示する1

まずは静的に将棋盤と駒をWEBページに表示してみる。
HTMLの記述は以下のごとくなる。
当該部分のみ

<link href="boardstyle.css"  rel="stylesheet" >
.
.

<div class="table"> <!-- outer table -->

<div class="row">
<div id="boardbase" class="cell">
<img class="board" src="assets/images/shogiboards/ban_kaya_a.png" alt=""/> <!-- 盤を表示-->
<img class="board" src="assets/images/shogiboards/masu_dot_xy.png" alt=""/><!-- マス目を表示 -->
<img class="koma c5 r9" src="assets/images/shogiboards/Sou.png" alt=""/><!--先手王を 59に -->
<img class="koma c5 r1" src="assets/images/shogiboards/Gou.png" alt=""/><!-- 後手王を 51に-->
<img class="koma c3 r7" src="assets/images/shogiboards/Ggin.png" alt=""/><!--後手銀を 37に -->
<img class="koma c9 r7" src="assets/images/shogiboards/Sfu.png" alt=""/><!--先手の歩を97に-->
<img class="koma c4 r9" src="assets/images/shogiboards/Skin.png" alt=""/><!--先手の金を49に-->
</div>

<div class="table"><!-- inner table -->
<div class="row">
<div id="goteMochigoma" class="komadai cell"> <!-- 後手の駒台-->
<img src="assets/images/shogiboards/Gou.png" alt=""/>
<img src="assets/images/shogiboards/Ggin.png" alt=""/>
<img src="assets/images/shogiboards/Gfu.png" alt=""/>
<img src="assets/images/shogiboards/Gfu.png" alt=""/>
<img src="assets/images/shogiboards/Gkin.png" alt=""/>
</div>
</div>
<div class="row">
<div class="cell empty" ></div>
</div>
<div class="row">
<div id="senteMochigoma" class="komadai cell"><!--先手の駒台-->
<img src="assets/images/shogiboards/Sgin.png" alt=""/>
<img src="assets/images/shogiboards/Sfu.png" alt=""/>
<img src="assets/images/shogiboards/Sfu.png" alt=""/>
<img src="assets/images/shogiboards/Skyo.png" alt=""/>

</div>
</div>
</div><!-- innter table -->
</div><!-- shogirow for the outer css-table-->
</div><!--shogitable-->

一方、boardstyle.cssは

.forSnapshot {position:relative;}
.table {display:table;}
.row {display:table-row;}
.cell {display:table-cell;}
.empty {height:25px;}
#boardbase {position:relative; width: 440px; height:490px;}
#senteMochigoma {vertical-align:bottom;}
.komadai {height:220px; width:160px; background-image:url(assets/images/shogiboards/ban_kaya_a.png);}
.board {position: absolute; left:10px; top:9px; }
.comment {padding-left:10px; width:590px;min-height:120px;}
.koma {position: absolute ; width:43px; height:48px;}
.marker {position:absolute; width:43px; height:48px;}
.lostworld {left:-20000px; top: -20000px;}
.c1 {left:365px;} .c2 {left: 322px;} .c3 {left:279px;}
.c4 {left:236px;} .c5 {left: 193px;} .c6 {left:150px;}
.c7 {left:107px;} .c8 {left: 64px;}  .c9 {left: 23px;}
.r1 {top:20px;}   .r2 {top:68px;}    .r3 {top:116px;}
.r4{top:164px;}   .r5{top:212px;}    .r6 {top:260px;}
.r7{top:308px;}   .r8{top:356px;}    .r9{top:404px;}
画像ファイルはすべてassets/images/shogiboards フォルダーに格納しておく。
ポイントとしては


<div id=boardbase> で将棋板ブロックのコンテクストを作り、この中に画像を放り込む。この画像の位置については koma属性でabsolute とし、列と行の位置をcx, rx でそれぞれ指定する。 r1=20px, c9=23px を起点に cxは横幅43px  lxは 縦高さ48pxづつ変化させている。駒台のブロックには単純に駒のイメージファイルを放り込んでおくと順番に表示される。

html において、将棋板と駒台をアレンジするのに、CSSのdisplay:table 属性を使っている。 htmlの一連のtable tag類 を使うことも考えたが、これはデータをリストアップするのとはちょっと違ったような気がしたので。cssで処理をすることにした。

結果はこちら

 

一応表示できるようになったが、これで将棋の駒40枚をならべるのは大変なので、 次回はたとえば文字列 ”s44g”,”g65p” などの羅列をつくり、これをJavascriptで処理して駒の配置表示する方法をかんがえる。