ガチャみたいに設定した確率で抽選を行うPHPライブラリを作った

くじ引きやガチャガチャのように、あるものはよく出る、あるものはあまり出ない、というように確率に差がある上で一つ抽出する、というプログラムをたまに書くことがあるので、その部分だけを行うPHPライブラリを作った。

インストール

Composerを使う場合は、composer.jsonのrequireに

"cloned/luckybox": "$VERSION"

を追加。$VERSIONに入れられるバージョンはPackagistを参照。今のところは 0.9.* としておくと良いでしょう。

Composer推奨だけど、Composerを使わない場合はGitHubからソースコードをダウンロードしてrequire_onceしてもOK。

使い方

コインは60%、キノコは35%、スターは5%、という設定で1回くじを引いてみる例はこんな感じ。

<?php
use LuckyBox\LuckyBox;
use LuckyBox\Card\IdCard;
// Items
$items = array(
1 => array('name' => 'Coin',     'rate' => 60), // 60%
2 => array('name' => 'Mushroom', 'rate' => 35), // 35%
3 => array('name' => 'Star',     'rate' => 5),  //  5%
);
// Setup
$luckyBox = new LuckyBox();
foreach ($items as $id => $item) {
$card = new IdCard();
$card->setId($id)
->setRate($item['rate']);
$luckyBox->add($card);
}
// Draw
$card = $luckyBox->draw();
$item = $items[$card->getId()];
echo "You got {$item['name']}" . PHP_EOL;

LuckyBoxに確率(rate)を設定したCardを詰めて、LuckyBox#draw()するとその確率に応じた割合で一つ取得できる。
バンドルされているIdCardクラスは、rate以外にidのみ保持できるクラスで、より複雑なCardを作りたい場合は、Cardインタフェースを実装したものであれば自作しても利用可能。

デフォルトでは無限に LuckyBox#draw() できるけれど、LuckyBox#setConsumable(true) するとadd()したCardの分しか引けなくなる。こんな感じで利用できる。

<?php
$luckyBox = new LuckyBox();
// Add some cards.
$luckyBox->setConsumable(true);
while (!$luckyBox->isEmpty()) {
$card = $luckyBox->draw();
// Do something.
}

0.5% みたいなより精度の高い確率を設定したい場合は、より大きな数をrateに設定すると可能。

<?php
$card1 = new IdCard();
$card2 = new IdCard();
$card1->setRate(1023); // 10.23%
$card2->setRate(8977); // 89.77%

ここでは説明しやすいように合計を100とか10000にしているけれど、実際には全てのrateの合計に対する比率になっているので、「1:1:3」みたいな指定も可能。「1:1:3」とすると「20%:20%:60%」という確率になる。

同じような要件があって、まだコーディングしていなければ、是非使ってみてください。

ウェブサイトで英数字のIDを表示するときに最適なフォント

注文IDや招待IDなど英数字の羅列を表示することがあるとして、ユーザーがその文字列をコピペできない(もしくはコピペが面倒、難しい)場合、各文字を間違わずに識別できる必要がある。具体的にはゼロとオーなどの似ている文字を勘違いせずに済むようにしたい。

一般的にウェブサイトで良く利用されるフォントであるArial、Verdanaでゼロとオーを表示すると下記のようになる。次に勘違いしやすそうなイチとエルも書いてある。
左から、ゼロ オー イチ エル。

こうして見ると細長い方がゼロだなと区別は付く。とはいえ、ゼロにスラッシュがあればより勘違いする可能性が低いだろうから、ゼロにスラッシュのあるフォントを探してみた。

OS X Mountain Lion:フォントリストより、Macにインストールされているフォント(ただし英語フォントのみ)でゼロにスラッシュのあるものを探してみた。こんなにたくさんのフォントがインストールされているのに、ゼロにスラッシュが入っているフォントは下記のみだった。

今度はList of Microsoft Windows fontsからWindowsにあるフォントでゼロにスラッシュのあるものを探してみた。

え、Consolasだけ?あんなにもフォントがあるのに。

結論

font-family: "Monaco", "Consolas";

MacをMonacoにしたのはイチとエルの区別も分りやすいというのがありますが単に好みです。Consolasはイチとエルが似ているのが微妙ですね。Linuxは手元にGUIが動く環境を持っていないので検証していません。また、Webフォントはブラウザが対応していない可能性があるので検証していません。

PHPカンファレンス関西2013のLTでPHPUnitに関する発表をしてきました

PHPカンファレンス関西2013のLT(ライトニングトーク)セッションでPHPUnitを使ってCoverage 100%を目指すためのちょっとしたTipsについて発表してきました。

はてダにSlideShareをうまく貼付ける方法が判らないので、発表資料は以下のリンクからどうぞ。

一人でゲームをリリースするための自動化 PHPUnit編 ~目指せCoverage 100%~

「ヒッグス粒子の謎」を読んだ

ヒッグス粒子の謎(祥伝社新書290)

ヒッグス粒子の謎(祥伝社新書290)

  • 作者: 浅井祥仁
  • 出版社/メーカー: 祥伝社
  • 発売日: 2012/09/03
  • メディア: 新書

難しい。専門的な用語を使わずに、使う場合も説明を添えて使っているので、素粒子と量子力学にバックグラウンドがある人が読んだら凄く平易な本なのかもしれないけれど、今の僕にはどうも釈然としないまま本が終わってしまった。
もしかしたら平易な説明にするために平易な表現になりすぎているため、本来理解に必要な難しい理屈が記載されていないのが原因かもしれない。
とはいえ、「真空」「反粒子」「場」といったテーマについて今までよりも知ることができたし、何よりヒッグス粒子が質量の起源として考えられているという基本的なことも本書で初めて知ることできたのは良かった。

Symfony2+Doctrine2.3でSharding(水平分割)を実現する

Symfony2 + Doctrine2.3の環境でデータベースのSharding(水平分割)を行う際の実装方法など。ここで言うShardingは、例えば10台データベースを利用するとしてユーザーIDなどを基準に利用するデータベースを各10台のどれかに振り分けるような場合(参考: 分割 (データベース) – Wikipedia

確認環境

下記手順でSymfonyを展開。DocumentRootがSymfony/web。

% wget "http://symfony.com/download?v=Symfony_Standard_Vendors_2.1.1.tgz" .
% tar zxvf Symfony_Standard_Vendors_2.1.1.tgz
% chmod 777 Symfony/app/cache Symfony/app/logs

データベースはMySQL 5.5を利用。mysqld_multiを使ってlocalhostに3インスタンス起動(ポートは3306、3307、3308)。
3つのインスタンス全てに下記SQLを実行しておく。今回はuserテーブルをそれぞれのデータベースで振り分けるという例。

CREATE DATABASE `sharding` DEFAULT CHARSET=utf8;
GRANT ALL PRIVILEGES ON `sharding`.* TO 'sharding'@'127.0.0.1' IDENTIFIED BY 'sharding'
USE sharding;
CREATE TABLE `user` (
`id` int(11) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Bundle作成手順を省略するために確認はAcmeDemoBundleを変更して行う。下記URLにアクセスしているイメージ。

http://localhost/app_dev.php/demo/

自前でConnectionを切り替える

app/config/config.ymlに下記のようなDoctrineの設定を行う(内容をわかりやすくするためにparameters.ymlの値を使ってません)

doctrine:
dbal:
connections:
db1:
driver:   pdo_mysql
host:     127.0.0.1
port:     3306
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8
db2:
driver:   pdo_mysql
host:     127.0.0.1
port:     3307
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8
db3:
driver:   pdo_mysql
host:     127.0.0.1
port:     3308
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8

同じホストだけどそれぞれポートが3306、3307、3308と分かれて設定されている。この状態でコントローラに下記のようなコードを書くと各MySQLにそれぞれデータが挿入される。

<?php
class DemoController extends Controller
{
public function indexAction()
{
$conn = $this->get('doctrine')->getConnection('db1');
$conn->insert('user', array('id' => 1));
$conn = $this->get('doctrine')->getConnection('db2');
$conn->insert('user', array('id' => 2));
$conn = $this->get('doctrine')->getConnection('db3');
$conn->insert('user', array('id' => 3));

3306ポートにはuserテーブルにid:1のデータ。3307にはid:2、3308にはid:3。これで一番基本的なコネクションの切り替えは出来るのだけど、当然のことながらDoctrineを使っているのにこのようなコードは書きたくない。

EntityManagerで切り替える

Symfonyのドキュメントにあるようなデータの取得方法をしようとする場合は素のConnectionを扱わずにEntityManagerを使いたい。
下記コマンドでEntityクラスを作成する(src/Acme/DemoBundle/Entity/User.phpが作られる)。

% php app/console doctrine:mapping:import AcmeDemoBundle annotation
% php app/console doctrine:generate:entities AcmeDemoBundle

※今回はプライマリキーに値を自分で設定するので、上記コマンドでUser.phpを生成したあとにidに対してのsetterを追加している(idがstrategy=”IDENTITY”なので上記コマンドだけだとidに対するsetterが生成されない)。

<?php
use Acme\DemoBundle\Entity\User;
class DemoController extends Controller
{
public function indexAction()
{
$user = new User();
$user->setId(1);
$em = $this->get('doctrine')->getEntityManager();
$em->persist($user);
$em->flush();

ドキュメントにあるように普通にこのようにすると3306ポートのMySQLにデータを保存してしまう。そこでapp/config/config.ymlに下記のようなORMの設定を追加する。

doctrine:
dbal:
~上に同じなので省略~
orm:
auto_generate_proxy_classes: %kernel.debug%
entity_managers:
db1:
connection: db1
mappings:
AcmeDemoBundle: ~
db2:
connection: db2
mappings:
AcmeDemoBundle: ~
db3:
connection: db3
mappings:
AcmeDemoBundle: ~

PHP側のコードはこんな感じ。

<?php
use Acme\DemoBundle\Entity\User;
class DemoController extends Controller
{
public function indexAction()
{
$user = new User();
$user->setId(5);
$em = $this->get('doctrine')->getEntityManager('db2');
$em->persist($user);
$em->flush();

これで3307にid:5が挿入される。データを取得する場合も同じようにEntityManagerを指定して取得すれば該当のMySQLに接続してデータを取得できる。

<?php
use Acme\DemoBundle\Entity\User;
class DemoController extends Controller
{
public function indexAction()
{
$em = $this->get('doctrine')->getEntityManager('db2');
$repos = $em->getRepository('AcmeDemoBundle:User');
$user = $repos->find(5);

ShardManagerを使う

ここまでの情報をうまく使って実装してもSharding自体は可能だけど、Shardingのためのオリジナリティあふれる自前コードを書かなければならないし、Doctrineのコード全体に渡ってコネクション名を管理するのは避けたい。
そこで、Doctrine2.3で提供されているShardManagerインタフェースを使ってShardingを実現するというのがこのエントリの本旨。ここまでに書いたデータベースの切り替え方法は他でもよく書かれていることなのだけど、土台から書かないとよくわからない感じになりそうだったので書いてみた。

13. Sharding — Doctrine DBAL 2.1.0 documentation

このページは英語だけどDoctrineに限らないShardingをする上での検討事項がわかりやすく書いてあるので原文を参照するのがおすすめ。主にShardingすることによる制約、Sharding対象の全データベースにまたいで一意になるプライマリキーをどのように生成するか、ShardManagerインタフェースの使い方が書かれている。

データベースにまたがって一意になるプライマリキーの生成

13.1.2. Table Generatorの方法をここではやってみる。IDをインクリメントするだけの管理テーブルを作成して採番する方法。Single point of failureなどのこの方法に起因する欠点も原文に書かれているので参照のこと。

下記のようにテーブルを作成する。ここでは3306ポートのMySQLに下記SQLを実行(Doctrine\DBAL\Id\TableGeneratorのdocコメントにこのテーブル定義が書いてある)。

CREATE TABLE `sequences` (
`sequence_name` varchar(255) NOT NULL,
`sequence_value` int(11) NOT NULL DEFAULT '1',
`sequence_increment_by` int(11) NOT NULL DEFAULT '1',
PRIMARY KEY (`sequence_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

あとはPHPでTableGeneratorを利用すればすぐに採番を開始できる。

<?php
use Doctrine\DBAL\Id\TableGenerator;
class DemoController extends Controller
{
public function indexAction()
{
$conn = $this->get('doctrine')->getConnection('db1');
$tableGenerator = new TableGenerator($conn, 'sequences');
$id = $tableGenerator->nextValue('user');

sequencesテーブルは下記のようになる。nextValueする度にsequence_valueがsequence_increment_by分だけインクリメントする。

mysql> select * from sequences;
+---------------+----------------+-----------------------+
| sequence_name | sequence_value | sequence_increment_by |
+---------------+----------------+-----------------------+
| user          |              1 |                     1 |
+---------------+----------------+-----------------------+
1 row in set (0.00 sec)

ShardManagerを使ってShardingする

プライマリキーの生成ができるようになったので、いよいよShardManagerを使ってShardingをしてみる。ShardManagerはインタフェースなので今回はDoctrine\DBAL\Sharding\PoolingShardManagerクラスを使って実装する。実装結果を先に見た方がわかりやすいのでまずはPHPから。

<?php
use Doctrine\DBAL\Id\TableGenerator;
use Doctrine\DBAL\Sharding\PoolingShardManager;
use Acme\DemoBundle\Entity\User;
class DemoController extends Controller
{
public function indexAction()
{
$conn = $this->get('doctrine')->getConnection();
$shardManager = new PoolingShardManager($conn);
// globalとして設定しているデフォルトの接続先に接続
$shardManager->selectGlobal();
$tableGenerator = new TableGenerator($conn, 'sequences');
$id = $tableGenerator->nextValue('user');
// $idを基準にして接続すべきデータベースへ接続
$shardManager->selectShard($id);
$user = new User();
$user->setId($id);
$em = $this->get('doctrine')->getEntityManager();
$em->persist($user);
$em->flush();

大体こんな感じになる。selectGlobal、もしくはselectShardするとその後のクエリは全て同じデータベースに流される。つまり、適切にselectShardさえできれば個々のクエリの接続先は意識しなくても大丈夫。データベースをまたがってのトランザクションは制約として使えないので、1トランザクションで1shardということを意識してShardManagerを扱う。

selectGlobalはデフォルトとして設定されている接続先(後述)に接続する。selectShardは引数を基準にしてSharding先に接続する。
データを取得するときも同じで、事前にselectShardさえしていれば特定のEntityManagerを呼び出したりしなくても大丈夫。

<?php
use Doctrine\DBAL\Id\TableGenerator;
use Doctrine\DBAL\Sharding\PoolingShardManager;
use Acme\DemoBundle\Entity\User;
class DemoController extends Controller
{
public function indexAction()
{
$conn = $this->get('doctrine')->getConnection();
$shardManager = new PoolingShardManager($conn);
$shardManager->selectShard($id); // $idはリクエストなどから取得した仮定
$em = $this->get('doctrine')->getEntityManager();
$repos = $em->getRepository('AcmeDemoBundle:User');
$user = $repos->find($id);

ShardManager#selectShardしたときの接続先を決定する

selectShardに渡される基準値を使って接続先を決める必要がある。これはDoctrine\DBAL\Sharding\ShardChoser\ShardChoserインタフェースを実装したクラスを作成して行う。selectShardしたときに内部ではShardChoserが呼び出される。

<?php
namespace Acme\DemoBundle;
use Doctrine\DBAL\Sharding\PoolingShardConnection;
use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser;
class SimpleShardChoser implements ShardChoser
{
public function pickShard($distributionValue, PoolingShardConnection $conn)
{
return ($distributionValue % 2) + 1;
}
}

ここではあくまでも例なので偶数だったら1、奇数だったら2を返す単純なもの。このpickShardの戻り値が各shardのid(後述)になる。つまりselectShardの引数を使ってshardのidを返すように実装する。

ShardManagerが動作するように設定を行う

ここが難所で、今のところ正攻法な解決ができていない(というかわからない)。普通にapp/config/config.ymlに設定しようとするとShardManagerの設定をDoctrine\Bundle\DoctrineBundle\DependencyInjection\Configurationが想定していないので、設定エラーとみなされてしまう。この辺はおいおい対応されるのかなという感じ。今のところは13.8. Generic SQL Sharding Supportにあるように直接PHPコードで設定してしまうしかないのかもしれない。
よろしくないけど、一応こういうことをすると設定が書けるようになるっちゃなる(真似しないようにしましょう)。

--- Configuration.php    2012-09-11 08:24:24.000000000 +0000
+++ vendor/doctrine/doctrine-bundle/Doctrine/Bundle/DoctrineBundle/DependencyInjection/Configuration.php    2012-09-19 14:25:52.615927030 +0000
@@ -139,6 +139,17 @@
->booleanNode('profiling')->defaultValue($this->debug)->end()
->scalarNode('driver_class')->end()
->scalarNode('wrapper_class')->end()
+                ->scalarNode('shardChoser')->end()
+                ->arrayNode('global')
+                    ->useAttributeAsKey('key')
+                    ->prototype('scalar')->end()
+                ->end()
+                ->arrayNode('shards')
+                    ->prototype('array')
+                        ->useAttributeAsKey('key')
+                        ->prototype('scalar')->end()
+                    ->end()
+                ->end()
->booleanNode('keep_slave')->end()
->arrayNode('options')
->useAttributeAsKey('key')

この変更を行った上でのapp/config/config.ymlはこんな感じ。

doctrine:
dbal:
connections:
default:
wrapper_class: Doctrine\DBAL\Sharding\PoolingShardConnection
shardChoser:   Acme\DemoBundle\SimpleShardChoser
global:
driver:   pdo_mysql
host:     127.0.0.1
port:     3306
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8
shards:
-
id:       1
driver:   pdo_mysql
host:     127.0.0.1
port:     3307
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8
-
id:       2
driver:   pdo_mysql
host:     127.0.0.1
port:     3308
dbname:   sharding
user:     sharding
password: sharding
charset:  UTF8
orm:
auto_generate_proxy_classes: %kernel.debug%
auto_mapping: true

PHPで設定するにしてもYamlで設定するにしても、ポイントはwrapper_classでConnectionクラスをラッパークラスに切り替えること。上記設定はあくまでもDoctrine\DBAL\Sharding\PoolingShardConnectionに準じた設定になっていて、この辺もおいおい変更されていく可能性がありそうだなと思う。

横浜まで来たら是非立ち寄りたいビアカフェHOPMAN

神奈川県の茅ヶ崎にあるBeer Cafe HOPMAN。東京からだと少し遠いのだけど、横浜や小田原からだとそれぞれ30分で行ける。茅ヶ崎駅からは歩いて5分。

ハンドポンプ@HOPMAN
ハンドポンプ@HOPMAN posted by (C)cloned

日本の地ビールや世界のビールを樽生で30種類(ハンドポンプ2本)用意しております

Beer Cafe HOPMAN

30種類の樽生というと都内のビアバー激戦区でも指折りの数だと思う。【国内ビアバー】樽生ビール取扱い数ランキング【1位〜12位編】によるとHOPMANは堂々4位ということらしい。

志賀高原ビール・AFPA@HOPMAN
志賀高原ビール・AFPA@HOPMAN posted by (C)cloned

ビールの種類が多いというだけでもメリットだけど、特に注目したいのはお料理。「安くて旨い」というのはこういうお店を言うものだなと思った。

しいたけの炭火焼@HOPMAN
しいたけの炭火焼@HOPMAN posted by (C)cloned

運ばれて来た瞬間にしいたけの香りが広がり、口にすると炭火の香りと味がぐっとくるのに300円。

ヘルシー野菜グラタン@HOPMAN
ヘルシー野菜グラタン@HOPMAN posted by (C)cloned

大口な野菜がたっぷり入った熱々のグラタンが480円(写真はSサイズ)。

きゅうりの天ぷら@HOPMAN
きゅうりの天ぷら@HOPMAN posted by (C)cloned

甘みたっぷりのキュウリがカラっと揚げたてで380円。

食べログを見ると、星が3.52というのも凄いけど、「夜(平均)¥3,000〜¥3,999」というのも地味に凄いと思う。他のビアバーだと5,000円を超えることが多いと思う。

柔らかくなるまで何時間も炒めた玉ねぎが美味しいカレーライスも530円と、とてもビアカフェという値付けではない。旬の野菜を仕入れてくれているのでつい野菜ばかり注文してしまったけれど、もちろんチキン&チップスや手羽先などのお肉なメニューもある。

HOPMANのカレーライス@HOPMAN
HOPMANのカレーライス@HOPMAN posted by (C)cloned

お店は照明が明るくてスペースが(席の間隔も)広い。また整理整頓・清掃がきっちりされていてきれいだった。化粧室前は雰囲気が違ってこんな感じでお洒落。

化粧室前@HOPMAN
化粧室前@HOPMAN posted by (C)cloned

オーナーは「気軽に来れる店にしたい」ということを言っていたけど、この価格設定でこの種類のビールでこのお店の雰囲気ですからすでに実現しているじゃないですかと感じた。もちろん今後も引き続き期待しています(オーナー写真の掲載も快くOKしてくれた)。

オーナー@HOPMAN
オーナー@HOPMAN posted by (C)cloned

僕は独身だったころ結構一人で下北沢のビアバーうしとらに通っていたけど、うしとらに通えたのは一人でも行けるくらい雰囲気が良くてお客さんがフレンドリーで、そして何よりスタッフがお客さんの会話を聞き漏らさずにちゃんと話をつないでくれるお店だったからだと思う。HOPMANにもその良さがきっちりあって、オーナーを含めスタッフの方と会話を楽しむために来ているお客さんも多そうな雰囲気だった。

お隣さんのお誕生日サイズ@HOPMAN
お隣さんのお誕生日サイズ@HOPMAN posted by (C)cloned

お店に伺った昨日はちょうどオーナーの誕生日だったのだけど、なんとお隣のカウンターに座った方も誕生日ということで、お誕生日サイズのビールを召し上がっていた。おめでとうございました。

1年半前にHOPMANに行ったときは、どちらかというと無事オープンおめでとうということで書いたけど、オープンからもうすぐ2年を迎えるに当たってきっちり理想のお店を目指して経営されているなと心から感じた。

CDに表記されている(再)02.06.04といった表記と再販売価格維持

CDに「(再)年.月.日まで」と表記されているのを見かけるけれど、これが何を意味しているのかずっと知らなかった。

「ドラゴンクエスト」ゲーム音源大全集1
「ドラゴンクエスト」ゲーム音源大全集1 posted by (C)cloned

これは再販売価格維持の時限再販が適用されているもので、表記されている日付までは小売店で勝手に価格を変えてはいけないようになっている。このため、表記期間内のCDはどこの店でも値引きされることがない。

再販行為は,不公正な取引方法(再販売価格の拘束,一般指定12項)に該当し,原則として,独占禁止法第19条の違反に問われるものであるが,同法第24条の2の規定により,おとり廉売の防止等の観点から,当委員会が指定する特定の商品(以下「再販指定商品」という。)及び著作物を対象とするものが例外的に同法の適用除外とされている。

再販売価格維持契約 – 公正取引委員会

公正取引委員会にこのように記載されている通り、あくまでもこの再販売価格維持は公正取引委員会により指定された特定の商品のみに適用されるもの。例えば、書籍、雑誌、新聞、音楽CDといったものがあるみたい。
豆知識としてはDVDなどの映像媒体はこの対象になっていないので、初回限定版などでDVD付きの商品だと定価で売らなくても良くなる(つまり割引してもOK)。激安!Amazonで初回限定版のDVD付きCDを買うとお得な理由(わけ)にわかりやすく書いてあったので参考に。

Wikipediaの再販売価格維持を読み進めると、

音楽ソフトの再販行為を容認している国は日本だけである。

再販売価格維持 – Wikipedia

とあり、書籍などに比べると特に珍しい状況の様子。

最後に思ったことなど。
再販売価格維持の是非について色々とウェブで読んでみたけど、なかなかこうあるべきだという考えを持つのは難しかった。というのも、こと音楽に絞って考えても、再販売価格維持に限らずレンタルの存在だとか、そもそも単価がほぼ均等であるだとか、その辺も全て一緒に考えないと一概に再販売価格維持だけの問題でもないように思えた。
個人的には一番レンタルが問題だなと思う。レンタル後にそのまま実物と同じ価値を継続できる(もちろん歌詞カードなどの実物は手に入らないけれど音楽データとしては完璧に同等という意味で)という時点で、もはやレンタルという言葉が間違えているように思う。著作権料がレンタルを通して支払われているにしても価格破壊はしてしまっていると思う。レンタルを禁止してもCDが売れるようになるとは思わないけど、CDの価格を是正する上では一番関連してしまうと思う。とはいえ、CDの売り上げの内訳(著者者、レーベル、小売店、輸送費など)を知ったら考え方がぐっと変わるかもしれないけど。

志賀高原ビールと南信州ビールを飲んでみた感想

id:n0tsさんに志賀高原ビールを退職祝いにと頂きました。なんと専用グラスまで付いておりました。これは堪りませんね。
志賀高原ビール Miyama Blonde
志賀高原ビール Miyama Blonde posted by (C)cloned
注ぎきってもボトル一杯にならないのはご愛嬌。
サイト上ではアルコール度数7.00%と表記されているけれど、ボトルの表記は6.5%。ときによって違うのかもしれない。さて、飲んだ感想の前にラベルの解説が良い感じだったので引用。

蔵元のある志賀高原の麓で、蔵人達自らが自家栽培した酒造好適米美山錦と、厳選した麦芽・ホップを用い、志賀高原の湧水で手作りした黄金色のストロングエールです。味わい豊でありながらも、すっきりとした飲み口をどうぞお楽しみください。

飲んだ瞬間、苦みは穏やかですっきりしながらも薄い感じは全くない。味は個性的で地ビールならではという感じ。しばらく飲んでいると口に苦みが残るので実際は結構苦いのかもしれない。そこそこ尖った味なのでバランスが良い感じではなかったけど、ビールとしての選択肢の幅を感じさせてくれる。
お次は南信州ビールの駒ヶ岳エール。これもid:n0tsが絶賛してたので買ってみた。
南信州ビール 駒ヶ岳エール
南信州ビール 駒ヶ岳エール posted by (C)cloned
こちらは非常にバランスが良く、ふんわりしたやさしい味なのにしっかりしていてフルーツっぽくない甘さがじっくり感じられる。とても良い仕上がりだなと感じた。この駒ヶ岳エールは季節限定ビールということで5月・6月しか販売していないみたい。11月・12月の季節限定ビールになっているクリスマスエールが気になるのでその次期が来たら買おうと思う。
豆鼓蒸鱈魚
豆鼓蒸鱈魚 posted by (C)cloned
ちなみに上のビールのお供は奥さんが作った豆鼓蒸鱈魚。中華は美味しい!

チェスのルールを覚えた

チェス入門を読んでチェスのルールを覚えてみた。このサイトは一つ一つの説明に試しに駒を動かせるFlashがあるので、とてもわかりやすかった。サイトの管理人の方、ありがとうございます。

さて、なぜチェスのルールを覚えようと思ったかというと、映画や小説などで稀にチェスの用語を使った例えや説明が出てくるので、知っておいた方がそういったコンテンツに出会ったときにより楽しめるだろうなと思ったからというのが大きい。あとは将棋が好きなので、単純にどういったゲームなのか知ってみようという気も少しあった。

チェス
チェス posted by (C)cloned

やってみての感想。将棋と駒の動きは確かに似ているのだけど、特殊なルールがあって驚いた。キャスリングやアンパサンあたり。あとは引き分けになるケースが多い(実戦でなりやすいということではなくてゲームルール上という意味で)。ステールメイトという名前がついているものまである。

それと、いくつか駒の動きが名前から直感的にイメージできなくて少し覚えるのに苦戦した。ルークは飛車と同じく上下左右一直線に動けるのだけど、駒の形は塔(もしくは城)のような形をしている。意味は戦車ということらしいのだけど、形が塔だからなぁ。キングは将棋の王と同じ(上下左右もしくは斜めに1コマだけ移動できる)なんだけど、クイーンが飛車+角の動きで、どんだけ動き回れるんだ女王様という感じ。キングとのギャップが大きい。
ナイトは馬に乗って跳ねている騎士を想像すれば腑に落ちたし、ポーンも単純な人の形で歩兵をイメージできるんだけど、ビショップが斜め一直線に移動できる(角と同じ動き)のはなんでだろうと思ってしまう。と思って調べてみたら、ビショップは元々ゾウだったみたい。

新人という意味のルーキーという言葉はチェスのルークから来ているみたいだけど、これもなんだか直感的に繋がらない。