Hatena::ブログ(Diary)

エンジニアのソフトウェア的愛情 このページをアンテナに追加 RSSフィード Twitter

2015-07-29

駒の移動できる位置をビット演算で求める

King’s Vallery というゲームがあります。

これの駒の移動できる位置をビット演算で求めてみようと思います。

細かくは、一手目は家臣駒を動かさないといけないとか、家臣駒は King's Vallery に入れないとかありますが、今回はそれらは置いておいて。



実装。

#include <iostream>
#include <sstream>
#include <string>

// 番兵
static const unsigned long long SENTINEL_BITS = 0x000007f04104107fLLU;

// 盤の状態を番兵付きのビット列に変換する
unsigned long long cells_to_bits(const int cells[5][5])
{
  unsigned long long cell_bits = SENTINEL_BITS;
  for(int row = 0; row < 5; ++row)
  {
    for(int col = 0; col < 5; ++col)
    {
      if(cells[row][col] != 0)
      {
        cell_bits |= (1LLU << (35 - (row * 6 + col)));
      }
    }
  }
  return cell_bits;
}

// 盤 cells 上の (row, col) の位置の駒が移動できる位置をビット列で取得する
unsigned long long can_move(const int cells[5][5], int row, int col)
{
  if(cells[row][col] == 0)
  {
    return 0;
  }

  static const int DIRECTIONS[] = { 1, 5, 6, 7 };

  unsigned long long cell_bits          = cells_to_bits(cells);
  unsigned long long can_move_cell_bits = 0;
  unsigned long long piece_bit          = 1LLU << (35 - (row * 6 + col));

  // 左、右上、上、左上への移動
  for(int i = 0; i < 4; ++i)
  {
    unsigned long long searching_cell_bit = piece_bit;
    // 他の駒か番兵に当たるまで進む
    while(((searching_cell_bit << DIRECTIONS[i]) & cell_bits) == 0)
    {
      searching_cell_bit <<= DIRECTIONS[i];
    }
    // 最後まで進んだ位置を記録する
    can_move_cell_bits |= searching_cell_bit;
  }

  // 右、左下、下、右下への移動
  for(int i = 0; i < 4; ++i)
  {
    unsigned long long searching_cell_bit = piece_bit;
    // 他の駒か番兵に当たるまで進む
    while(((searching_cell_bit >> DIRECTIONS[i]) & cell_bits) == 0)
    {
      searching_cell_bit >>= DIRECTIONS[i];
    }
    // 最後まで進んだ位置を記録する
    can_move_cell_bits |= searching_cell_bit;
  }

  // 最後まで進んだ位置の重ね合わせから、最初の位置を引いたものを返す
  return can_move_cell_bits & ~piece_bit;
}

// 盤の状態と移動できる位置を文字列にする
std::string bits_to_str(const unsigned long long can_move_cell_bits, const int cells[5][5], int piece_row, int piece_col)
{
  std::ostringstream cell_stream;
  for(int row = 0; row < 5; ++row)
  {
    for(int col = 0; col < 5; ++col)
    {
      if((row == piece_row) && (col == piece_col))
      {
        // 動かしたい駒
        cell_stream << "@ ";
      }
      else if(cells[row][col] != 0)
      {
        // その他の駒
        cell_stream << cells[row][col] << ' ';
      }
      else
      {
        // 移動可能な位置に x を出力
        cell_stream << ((can_move_cell_bits >> (35 - (row * 6 + col)) & 1u) ? "x " : ". ");
      }
    }
    cell_stream << '\n';
  }
  return cell_stream.str();
}

int main(int argc, char *argv[])
{
  // 盤面の様子
  int cells[5][5] =
  {
    { 1, 1, 5, 1, 1 },
    { 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0 },
    { 2, 2, 6, 2, 2 }
  };

  // 盤上の各駒について移動できる場所を調べる
  for(int row = 0; row < 5; row += 4)
  {
    for(int col = 0; col < 5; ++col)
    {
      std::cout << '\n';
      std::cout << bits_to_str(can_move(cells, row, col), cells, row, col);
    }
  }

  return 0;
}

実行結果。

$ g++ --std=c++11 -o can_move can_move.cpp 
$ ./can_move 

@ 1 5 1 1 
. . . . . 
. . . . . 
x . . x . 
2 2 6 2 2 

1 @ 5 1 1 
x . . . . 
. . . . . 
. x . . x 
2 2 6 2 2 

(中略)

1 1 5 1 1 
x . . x . 
. . . . . 
. . . . x 
2 2 6 @ 2 

1 1 5 1 1 
. x . . x 
. . . . . 
. . . . . 
2 2 6 2 @ 


解説はまた後日…。

2015-06-24

this is not global

めも。

A = 10;
console.log('A = ' + A);
console.log('this.A = ' + this.A);
console.log('global.A = ' + global.A);

実行結果。

$ node what-is-global.js 
A = 10
this.A = undefined
global.A = 10

ただし。

$ node
> A = 10
10
> this.A
10
> global.A
10
> this == global
true

CoffeeScriptでグローバルな変数を扱わざるをえないばあいは、globalを使うとグローバルにするすることが可能な様子。



このトップレベルの thisglobal の関係がわからずかなりの時間を費やしました。

2015-05-10

JavaScriptでEvernoteを操作する、ただしYosemiteにかぎる

OS XYosemite になって、標準で JavaScript を扱えるようになりました。

使い方について、こちらの方が詳しくまとめられています。


JavaScriptAppleScript と同等のことができるというのなら。

以前このブログに書いたように AppleScriptEvernote を操作することができるのだら、JavaScript でも操作できるだろうと思い、やってみました。



ノートブックを作る


Evernote アプリケーションオブジェクトを取得し、createNote メソッドを呼ぶことでノートを作ることができます。

#!/usr/bin/osascript -l JavaScript

// アプリケーションオブジェクトを取得する
var evernote = Application('Evernote');

// 新しいノートを作る
// ノートブックは、なければノートと一緒に作られます
evernote.createNote({withText: '新しいテキスト', notebook: 'JSで作ったノート', title: 'JSで作りました'});

実行

$ chmod +x create_note 
$ ./create_note 
Application("Evernote").notebooks.byName("JSで作ったノート 1").notes.byId("x-coredata://B95C0294-C338-4778-937B-76D3050F2609/ENNote/p16263")

あるいは

$ osascript -l JavaScript create_note
Application("Evernote").notebooks.byName("JSで作ったノート").notes.byId("x-coredata://B95C0294-C338-4778-937B-76D3050F2609/ENNote/p16265")

あたりまえですが osascript コマンドで実行する場合は1行目の shebang は不要です。

スクリプトの言語の指定 -l JavaScript は大文字小文字まで一致させないとエラーになりました。

なお、ファイル明に拡張子 .js をつけると言語指定をしなくても JavaScript として実行してくれるようです。


osascript コマンドは、実行したスクリプトの最後の値を結果として表示します。このばあいは作成されたノートのオブジェクトが表示されます。x-coredata://〜 が作成されたノートの ID です。


ノートブックをしなかった場合デフォルトのノートブックにノートが作られます。また指定したノートブックがなかった場合にはノートが作成されるのと同時にノートブックも作られます。

withText の代わりに withHtml を使うと HTMLマークアップした内容のノートを作ることができます。



ノートを見つける

Evernote アプリケーションオブジェクトには findNote というメソッドがあります。…あるのですが。

メソッドの仕様を見てみると*1引数に note link を取るようになっています。

f:id:E_Mattsan:20150510144407p:image:w360


具体的にはこんな感じ。

var evernote = Application('Evernote');
evernote.findNote('evernote:///view/1350007/s11/fa134265-f675-4278-a294-ed1a8df8a008/fa145665-f675-4278-a294-ed1a8ca8a008/');

つまり Evernote でノート間のリンクを作るための文字列引数として渡す必要があります。

f:id:E_Mattsan:20150510150411p:image:w360 f:id:E_Mattsan:20150510150410p:image:w360

検索という用途で考えるとあまりうれしくありません。


そこでもう一つのメソッド findNotes を利用します。findNotes メソッド引数に検索文字列を取ります。この文字列Evernote でノートを検索するときの検索文字列とおなじです。

var evernote = Application('Evernote');
var notes = evernote.findNotes('notebook:JSで作ったノート intitle:JSで作りました');

for(var i in notes) {
  console.log('No.' + i + ' タイトル: ' + notes[i].title() + ' リンク: ' + notes[i].noteLink());
}

実行。

$ osascript find_note.js 
No.0 タイトル: JSで作りました リンク: evernote:///view/1350007/s11/fa134265-f675-4278-a294-ed1a8df8a008/fa145665-f675-4278-a294-ed1a8ca8a008/

検索文字列での検索ですから複数のノートがヒットする可能性があります。ピンポイントで一つのノートを得たいときは、一旦 findNotes メソッドで該当するノートを見つけ、ノートのプロパティ noteLink でリンクの文字列を取得しておいて、findNote メソッドでノートを取得する、といった感じになりそうです。


検索文字列については Evernote のサイトの「Evernote の高度な検索シンタックスの使い方 - Evernote ナレッジベース」を参照してみてください。



ノートを更新する

ノートの内容を更新するには3つの方法があります。

ENML というのは Evernote 固有のマークアップ形式のようです。まだ調べてませんが。

ENML 、 HTML でどのように格納されているかはプロパティの値を表示するとわかります。


var evernote = Application('Evernote');
var note = evernote.findNote('evernote:///view/1350008/s12/fa145665-f675-4278-a294-ed1a8ca8a008/fa145665-f675-4278-a294-ed1a8ca8a008/');

console.log('ENML: ' + note.enmlContent());
console.log('HTML: ' + note.htmlContent());

実行。

$ osascript show_note.js 
ENML: <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
<en-note>新しいテキスト</en-note>

HTML: 
<div id="en-note">新しいテキスト</div>

プロパティに値を代入するとノートの内容を更新できます。

var evernote = Application('Evernote');
var note = evernote.findNote('evernote:///view/1350008/s12/fa145665-f675-4278-a294-ed1a8ca8a008/fa145665-f675-4278-a294-ed1a8ca8a008/');

console.log('before: ' + note.htmlContent());

note.htmlContent = '更新したテキスト'

console.log('after: ' + note.htmlContent());

実行。

$ osascript update_note.js 
before: 
<div id="en-note">新しいテキスト</div>

after: <div id="en-note">
更新したテキスト
</div>

これで確かに更新できるのですが。ですが。

わたしの環境ではこのスクリプトを続けて実行しようとするとエラーになります。原因はまだ調べていません。

 $ osascript update_note.js 
before: <div id="en-note">
更新したテキスト
</div>
update_note.js:225:254: execution error: Error on line 6: Error: この操作により月間アップロード許容量を超過します。 (1)

これを次のようにすると更新できるようになります。理由はまだ調べていません。

var evernote = Application('Evernote');
var note = evernote.findNote('evernote:///view/1350008/s12/fa145665-f675-4278-a294-ed1a8ca8a008/fa145665-f675-4278-a294-ed1a8ca8a008/');

console.log('before: ' + note.htmlContent());

note.htmlContent = ''
note.append({text: '更新したテキスト'})

console.log('after: ' + note.htmlContent());

実行。

$ osascript update_note.js 
before: <div id="en-note">
更新したテキスト
</div>
after: <div id="en-note">
 更新したテキスト</div>

また append メソッドの説明をみると次のようになっています。

append&#8194;method : Append data to an existing note. You must specify exactly one of 'text', 'html', or 'attachment' parameters.
  append Note : The note to modify.
    [text: text] : Text to append.
    [html: text] : HTML to append.
    [attachment: file] : Attachment file to append.

text の代わりに html を利用すれば HTMLマークアップで装飾ができます。

var evernote = Application('Evernote');
var note = evernote.findNote('evernote:///view/1350008/s12/fa145665-f675-4278-a294-ed1a8ca8a008/fa145665-f675-4278-a294-ed1a8ca8a008/');

console.log('before: ' + note.htmlContent());

note.htmlContent = ''
note.append({html: '<font color=red>更新</font>したテキスト'})

console.log('after: ' + note.htmlContent());

実行結果。

f:id:E_Mattsan:20150510144932p:image:w360



いつか読むはずっと読まない:ただの虫

2012年の刊行ですが先日存在を知り購入。15年ぶりの新刊だそうで。

パラダイスバード (BUNCH COMICS)

パラダイスバード (BUNCH COMICS)


先の2作品も新装版が出ていたみたいですね。

楽園通信社綺談 (キュンコミックス)

楽園通信社綺談 (キュンコミックス)

ビブリオテーク・リヴ (キュンコミックス)

ビブリオテーク・リヴ (キュンコミックス)

*1スクリプトエディタを起動し、メニューから「ファイル > 用語説明を開く... 」を選択します。アプリケーションを選択するダイアログが開くので Evernote を選択すると用語説明のウィンドウが表示されます。言語を JavaScript に設定することで JavaScript で呼び出せる形式でメソッドが表示されるようになります。

2015-04-12

海老で PDF を釣る

単票を作るために、RubyPDF を生成するライブラリ Prawn を試してみました。


以下、Prawn を使って PDF ができるまでの様子です。


Prawn を導入する

RubyGem で提供されているので、それインストールします。

$ gem install prawn

PDF ファイルを作成してみます。

require 'prawn'

Prawn::Document.generate('sample.pdf', page_size: 'A4') do |pdf|
  pdf.text 'Hello, Prawn!'
end

これで sample.pdf というファイル名の A4 サイズの PDF ファイルが作成されます。


直接ファイルを生成するのではなく、レンダリングしたデータを生成することもできます。

次の例では、Prawn::Document#renderレンダリングしたデータを IO.write でファイルに保存しています。

require 'prawn'

pdf = Prawn::Document.new page_size: 'A4')
pdf.text 'Hello, Prawn!'
IO.write('sample2.pdf', pdf.render)

文字列の出力位置を変更します。

text の代わりに draw_text を使います。

pdf.draw_text 'Hello, Prawn', at: [100, 100]

Prawn では印字範囲の左下が原点となり、そこから [ 右方向の距離, 上方向の距離 ] という形で位置を指定します(第一象限の (x, y) を指定する形です)。単位はポイント(72分の1インチ)です。


表示領域を指定し、その領域内の配置を指定することもできます。

そのばあいは text_box を使います。

次の例では、右から 100 ポイント、下から 100 ポイント、幅 100 ポイント、高さ 100 ポイントの範囲に、縦横とも中央寄せで出力します。

pdf.text_box 'Hello, Prawn!', at: [100, 100], width: 100, height: 20, align: :center, valign: :center

高さ (height) を指定するばあい、指定した位置から下方向の位置になるので注意が必要です


f:id:E_Mattsan:20150412103335p:image


また「ポイントの計算が面倒臭い」というばあいのために、メートル法、ヤード法を使うための手段が用意されています。

prawn/measurement_extensions を使うと、.mm.cm といったメソッドが使えるようになります。

require 'prawn'
require 'prawn/measurement_extensions'

Prawn::Document.generate('sample.pdf', page_size: 'A4') do |pdf|
  pdf.draw_text 'Hello, Prawn', at: [100.mm, 100.mm]
end

標準では日本語フォントを持っていないので、日本語を表示させるばあいはフォントファイルを読み込ませる必要があります。

IPAフォントなどを用意します。

たとえば明朝体フォント ipaexm.ttf を設定するばあい次のように記述します。

# フォントの読み込みと設定
pdf.font('ipaexm.ttf')

あるいは

# フォントファルの読み込み
pdf.font_families.update('ipa-mincho' => {normal: {file: 'ipaexm.ttf'}})
# フォントの設定
pdf.font('ipa-mincho')

細かな設定は Prawn のドキュメントを参照してみてください。



捺印欄を作ってみる

これらを踏まえて。スタンプラリーの台紙 捺印欄のある書類を作成してみます。

f:id:E_Mattsan:20150412100026p:image


require 'prawn'
require 'prawn/measurement_extensions'

# ファイル名 sample.pdf 、 用紙サイズ A4 、マージン を上 15mm 、右 15mm 、下 20mm 、左 25mm に設定した PDF ファイルを作る
# マージンの数字の並び(上右下左の並び)は CSS と同じ、また値が1つ、2つ、3つの場合の動作も CSS と同じ
Prawn::Document.generate('sample.pdf', page_size: 'A4', margin: [15.mm, 15.mm, 20.mm, 25.mm]) do |pdf|

  # 捺印欄の位置と幅高さ
  # 印字領域の右から 9cm 、上から 5cm の位置、幅 9cm 、高さ 2.5cm の領域に設定
  pdf.bounding_box([pdf.bounds.width - 90.mm, pdf.bounds.height - 50.mm], width: 90.mm, height: 25.mm) do
    # このブロック内では捺印欄の左下が原点

    # 太線の幅を指定
    pdf.line_width(1)

    # 枠を書く(bounding_box で指定した領域を覆う線を描く)
    pdf.stroke_bounds

    # 技術本部と技術部を分ける線
    pdf.horizontal_line(0.mm, 90.mm, at: 20.mm) # 横線:左から 0mm〜90mm までの横線を、高さ 20mm の位置に描く
    pdf.vertical_line(0.mm, 25.mm, at: 30.mm)   # 縦線:高さ 0mm〜25mm までの縦線を、左から 30mm の位置に描く

    # ここまでの線を描画
    pdf.stroke

    # 細線の幅を指定
    pdf.line_width(0.2)

    # 役職を分ける線
    pdf.horizontal_line(0.mm, 90.mm, at: 15.mm)
    pdf.vertical_line(0.cm, 20.mm, at: 15.mm)
    pdf.vertical_line(0.cm, 20.mm, at: 45.mm)
    pdf.vertical_line(0.cm, 20.mm, at: 60.mm)
    pdf.vertical_line(0.cm, 20.mm, at: 75.mm)

    # ここまでの線を描画
    pdf.stroke

    # フォントファルの読み込み
    pdf.font_families.update('ipa-mincho' => {normal: {file: 'fonts/ipaexm.ttf'}})

    # フォントの設定
    pdf.font('ipa-mincho')

    # フォントサイズ指定
    pdf.font_size(8)

    # 部門名
    pdf.bounding_box([0.mm, 25.mm], width: 90.mm, height: 5.mm) do
      pdf.text_box('技術本部', at: [0.mm, 5.mm], width: 30.mm, height: 5.mm, align: :center, valign: :center)
      pdf.text_box('技術部', at: [30.mm, 5.mm], width: 60.mm, height: 5.mm, align: :center, valign: :center)
    end

    # 役職名
    pdf.bounding_box([0.mm, 20.mm], width: 90.mm, height: 5.mm) do
      %w(本部長 副本部長 部長 課長 係長 担当).each_with_index do |pos, i|
      pdf.text_box(pos, at: [15.mm * i, 5.mm], width: 15.mm, height: 5.mm, align: :center, valign: :center)
      end
    end
  end
end


Ruby on Rails のレスポンスで PDF を返す

Prawn::Document.newPrawn::Document#render を使うことで Rails のレンスポンスとして生成した PDF を返すことができます。


  def show
    respond_to do |format|
      format.pdf do
        pdf = Prawn::Document.new(page_size: 'A4', margin: [15.mm, 15.mm, 20.mm, 25.mm])
        # PDF を構築(省略)
        send_data pdf.render, filename: "sample.pdf", type: "application/pdf", disposition: 'inline'
      end
    end
  end


いつか読むはずっと読まない:がんばれカミナリ竜

ブロントサウルスはアパトサウルスとは別属だったらしいという記事。

歩けば雷鳴が轟くごときだっただろうという想像から、雷竜〜ブロントサウルスという名称がつけられ、広く知られていたもののアパトサウルスと同属と判断され先名権により姿を消していた名称。

ここにきて復活するとは思いませんでした。


雷竜といえば、この書籍。

今回調べてみて、入手困難になっていることを知り、少なからずショックを受けました。


そしてグールド博士の最後のエッセイ集。日本語訳版がでてすぐに購入したのですが、これでおしまいかと思うと、もったいない感じがしてしまい、いまだに読めずにいます。

2015-03-30

RailsとHamlでSVG

ふつうに

ブラウザがインラインのSVG表示に対応していれば、HamlSVGを書くとそのまま表示してくれます。

app/controllers/circles_controller.rb

class CirclesController < ApplicationController
  def index
    @circles = 30.times.map { {x: rand(500), y: rand(500), r: rand(100)} }
  end
end

app/views/circles/index.html.haml

%svg{'xmlns' => 'http://www.w3.org/2000/svg', 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', 'viewBox' => '0 0 500 500', 'width' => '100%', 'height' => '100%'}
  - @circles.each do |circle|
    %circle{cx: circle[:x], cy: circle[:y], r: circle[:r], stroke: 'red', fill: 'none'}

Rails を起動して http://localhost:3000/circles を開くとこんな感じ。

f:id:E_Mattsan:20150330225649p:image


フォーマットにSVGを指定して

フォーマットを指定して、http://localhost:3000/circles.svg とアクセスしたい場合には、いくつか設定が必要になります。


MIME タイプを設定する

config/initializers/mime_types.rb に SVG を設定します。

次の1行を追加します。

Mime::Type.register 'image/svg+xml', :svg

Haml のフォーマットを設定する

Haml のフォーマットを XHTML にします。これをしておかないとXMLのDOCTYPE宣言が出力されません。

具体的には config/initializers/haml.rb を作成して次の内容を記述します。

Haml::Template.options[:format] = :xhtml

ビューを書く

拡張子SVGにしたHamlを書きます。

内容はインラインで書いたものにDOCTYPE宣言を追加したものになります。

app/views/circles/index.svg.haml

!!! XML
%svg{'xmlns' => 'http://www.w3.org/2000/svg', 'xmlns:xlink' => 'http://www.w3.org/1999/xlink', 'viewBox' => '0 0 500 500', 'width' => '100%', 'height' => '100%'}
  - @circles.each do |circle|
    %circle{cx: circle[:x], cy: circle[:y], r: circle[:r], stroke: 'red', fill: 'none'}

img タグで表示する

MIME タイプを指定しているので、img タグを使っても表示することができます。

index.html.haml を次のように書き換えて http://localhost:3000/circles にアクセスすると通常の画像と同じように表示されます。

app/views/circles/index.html.haml

= image_tag circles_path(format: :svg)