psqlで\dpコマンドが使えない場合の対処法

\dp コマンドは、PostgreSQL のテーブル、ビュー、シーケンスのアクセス権一覧を表示するためのコマンドである。 しかし、PostgreSQL 15 を利用している場合に以下のようなエラーが出て失敗してしまうことがある。

postgres=# \dp
ERROR:  operator is not unique: unknown || "char"
LINE 16:            E' (' || polcmd || E'):'
                          ^
HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

事象としては過去に書いた以下記事と同じようなものであるが、原因について調べてみた。

gumfum.hatenablog.com

原因

サーバ側の PostgreSQL のバージョンが 15 で、クライアント側の psql のバージョンがそれ以前の場合に発生することがある。私の手元で上記のエラーメッセージが出た際、サーバ側のバージョンは 15.3、クライアント側のバージョンは 14.7 であった。

% psql --version
psql (PostgreSQL) 14.7 (Homebrew)

接続する際にも、psql の機能が動かない可能性がある旨の WARNING が出る。

psql (14.7 (Homebrew), server 15.3 (Debian 15.3-1.pgdg120+1))
WARNING: psql major version 14, server major version 15.
         Some psql features might not work.
Type "help" for help.

サーバ側のログにも同様のエラーと詳細が出ている。

2023-11-28 18:43:35.801 UTC [241] ERROR:  operator is not unique: unknown || "char" at character 770
2023-11-28 18:43:35.801 UTC [241] HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.
2023-11-28 18:43:35.801 UTC [241] STATEMENT:  SELECT n.nspname as "Schema",
      c.relname as "Name",
      CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'm' THEN 'materialized view' WHEN 'S' THEN 'sequence' WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table' END as "Type",
      pg_catalog.array_to_string(c.relacl, E'\n') AS "Access privileges",
      pg_catalog.array_to_string(ARRAY(
        SELECT attname || E':\n  ' || pg_catalog.array_to_string(attacl, E'\n  ')
        FROM pg_catalog.pg_attribute a
        WHERE attrelid = c.oid AND NOT attisdropped AND attacl IS NOT NULL
      ), E'\n') AS "Column privileges",
      pg_catalog.array_to_string(ARRAY(
        SELECT polname
        || CASE WHEN NOT polpermissive THEN
           E' (RESTRICTIVE)'
           ELSE '' END
        || CASE WHEN polcmd != '*' THEN
               E' (' || polcmd || E'):'
           ELSE E':'
           END
        || CASE WHEN polqual IS NOT NULL THEN
               E'\n  (u): ' || pg_catalog.pg_get_expr(polqual, polrelid)
           ELSE E''
           END
        || CASE WHEN polwithcheck IS NOT NULL THEN
               E'\n  (c): ' || pg_catalog.pg_get_expr(polwithcheck, polrelid)
           ELSE E''
           END    || CASE WHEN polroles <> '{0}' THEN
               E'\n  to: ' || pg_catalog.array_to_string(
                   ARRAY(
                       SELECT rolname
                       FROM pg_catalog.pg_roles
                       WHERE oid = ANY (polroles)
                       ORDER BY 1
                   ), E', ')
           ELSE E''
           END
        FROM pg_catalog.pg_policy pol
        WHERE polrelid = c.oid), E'\n')
        AS "Policies"
    FROM pg_catalog.pg_class c
         LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
    WHERE c.relkind IN ('r','v','m','S','f','p')
      AND n.nspname !~ '^pg_' AND pg_catalog.pg_table_is_visible(c.oid)
    ORDER BY 1, 2;

これは PostgreSQL 内部で利用される文字の型が、PostgreSQL 15 から一部変わった影響のようである。このため以前のバージョンの psql からコマンドを実行した場合、型エラーとして処理されてしまう場合があり、これに \dp コマンドが該当してしまったためと言えそうでである。
参考: https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=07eee5a0dc642d26f44d65c4e6263304208e8583

対処法

1. クライアント側の psql のバージョンをサーバ側の PostgreSQL のバージョンと合わせる

基本的にはクライアント側のpsqlのバージョンを、サーバ側のPostgreSQLのバージョンと合わせることで解消するしかない。Homebrew でインストールしている場合は、@15 でバージョンを指定した上で、パスを通す必要がある。

% brew install postgresql@15
% export PATH=/usr/local/Cellar/postgresql@15/15.5_1/bin:$PATH
% psql --version
psql (PostgreSQL) 15.5 (Homebrew)

クライアント側とサーバ側のメジャーバージョンが合ったことにより、\dp コマンドが実行できるようになった。

% psql -h localhost -p 6432 -U postgres
ユーザー postgres のパスワード: 
psql (15.5 (Homebrew)、サーバー 15.3 (Debian 15.3-1.pgdg120+1))
"help"でヘルプを表示します。

postgres=# \dp
                         アクセス権限
 スキーマ | 名前 | タイプ | アクセス権限 | 列の権限 | ポリシー 
----------+------+--------+--------------+----------+----------
(0 行)

2. システムカタログを直接参照する

\dp コマンドで確認できるテーブル、ビュー、シーケンスのアクセス権一覧を、PostgreSQL のシステムカタログから直接参照する方法である。pg_class.relname がテーブル、インデックス、ビューなどの名前 、pg_namespace.nspacl がアクセス権限を表すカラムとなる。

SELECT
    relname, relacl
FROM
    pg_class c
JOIN
    pg_namespace ns ON (ns.oid = c.relnamespace)
WHERE
    ns.nspname = '<スキーマ名>';

スキーマ名を指定しない場合は、システムにより自動作成されたTOASTテーブルも一覧に出てきてしまうため、ある程度の絞り込みとしてスキーマ名は指定するのが良さそうである。また、テーブルのみなどのより詳細な絞り込みを行いたい場合は、pg_namespace.relname で指定するか、他のシステムカタログと組み合わせるなどする必要がある。

\dp コマンドの情報も大元はシステムカタログで管理されているものであるため、SQL を組み立てることで同様の情報を参照することはできるが、\dp コマンドの方が手軽かつ情報が見やすく整理されているため、可能であればクライアント側のバージョンを上げてしまうのが良さそうである。

山口宇部空港から最寄りの草江駅まで歩いてみる

山口宇部空港は、山口県宇部市にある空港で、本州では最も西に位置している空港である。 新山口駅へはバスで30分ほどで到着できるのだが、空港周辺の歩ける距離に駅があるということで実際に歩いてみた。

空港周辺

タクシー乗り場の横を通り過ぎると、案内の中に「草江駅」の表記がある。そのまま駐車場の横の歩道を進んでいく。

駐車場横のスペースは公園になっており、休日ということもあって子供を連れた家族が多くいた。飛行機型の遊具があるのに加えて、実際の飛行機が比較的間近に見られるので写真撮影にも良さそうな場所である。

道中

空港の出口付近の道路案内板に、草江駅の表記がある。

この写真でも既に奥の方に踏切が見えており、交差点を渡るともう駅に到着してしまった。

実は草江駅までの距離は数百メートル程度であり、空港の駐車場からは400メートルとの案内が出ていた。

駅周辺

駅周辺は普通の住宅街であり、一見すると空港が近くにあるような感じはしない。駅前の看板には空港までのアクセス方法がしっかり記載はされている。

駅舎や看板等の写真を撮っていると、駅近くの地元の方(と思われる方)から「何か珍しいんですか?」と声をかけられたが、「空港の最寄り駅なので…」と答えるしかなく少し気まずい感じになってしまった。そのくらい普通の住宅に囲まれた場所にある駅であった。

草江駅から新山口駅まで移動する

草江駅から乗車する宇部線はワンマン列車での運行のため、乗車時に整理券を取って降りる駅で精算する必要がある。草江駅から新山口駅までの料金は420円で、新山口駅で降りる場合は改札で駅員さんに整理券と運賃を渡せば良い。所要時間は40分程度。

空港から新山口駅までのバスは料金が910円で所要時間が30分のため、費用面では電車の方に分がある。ただし、バスの時刻表は飛行機のダイヤに合わせて時刻が設定されているという大きなメリットがある。電車の本数は1時間に1本程度なので、利用する場合は事前に時刻表を確認しておく必要があると言えそうである。

まとめ

空港から徒歩数分程度の場所に位置しているため、大きめの荷物を持っていても利用できるような距離感であった。空港にはレストランもあり、草江駅まで電車で移動して空港で時間を潰す、というパターンで利用するのも良さそうに思う。

【ボードゲーム】アフター・アス(After Us)をプレイした感想を書いてみる

ボードゲームの「アフター・アス(After Us)」をボードゲームアリーナ(BGA)でプレイしたので、感想をまとめてみる。

boardgamearena.com

基本的なルール

デッキをビルドして点数を稼いでいくゲーム。

ゲームは3つのフェーズに分かれており、それぞれのフェーズを各プレイヤー同時に進めていく*1。初期の山札8枚は各プレイヤーとも同一のもので、カードを獲得したり削除したりすることでデッキの強化を行なっていく。

  • フェーズ1. カードの並び替え
  • フェーズ2. カードの獲得
  • フェーズ3. 次のラウンドの準備
フェーズ1. カードの並び替え

山札から4枚のカードを引いて公開し、それらを並べ変える。リソースのアイコンが描かれている枠が完成した(図中では枠が緑色になっている)場合、枠内のリソースや勝利点の獲得や交換を行うことができる。

リソースや勝利点の獲得は左から右、上から下の順に解決していく。2段目、3段目にはリソースの支払いが必要なものがあるが、同じターンで先に獲得しているリソースはそのターン内ですぐ使うことができる。

リソースには以下の5種類がある。また、電球のアイコンは勝利点を表している*2。激怒のリソースのみ保有できる上限が12個と言う制限があるが、その他のリソースには上限はない。

  • 花(水色のアイコン): マンドリルのカード獲得に必要
  • 果物(オレンジ色のアイコン): オランウータンのカード獲得に必要
  • 穀物(黒色のアイコン): ゴリラのカード獲得に必要
  • エネルギー(バッテリー型のアイコン): 道具の使用に必要
  • 激怒(ゴリラの顔のアイコン): デッキからのカード削除に必要

全プレイヤーのリソース獲得の処理が完了するとフェーズ2. カードの獲得に進む。

フェーズ2. カードの獲得

マンドリル、オランウータン、ゴリラ、チンパンジーのいずれかを選択し、ボーナスとカードの獲得を行う。

カードにはレベル1とレベル2があり、獲得するためにレベル1のカードは3つ、レベル2のカードは6つのリソースを必要とする。 レベル2のカードの方が枠数が多く、得られるリソースの数も多い。

  • マンドリル
    • 消費するリソース: 花
    • ボーナス: 2勝利点を獲得する。
    • カード: 勝利点を獲得するのに役立つものが多い。
  • オランウータン
    • 消費するリソース: 果実
    • ボーナス: 2エネルギーを獲得する。
    • カード: エネルギーを獲得するのに役立つものが多い。
  • ゴリラ
    • 消費するリソース: 穀物
    • ボーナス: 2激怒を獲得する。
    • カード: 激怒を獲得するのに役立つものが多い。
  • チンパンジー
    • 消費するリソース: 花、果実、穀物のいずれか1種
    • ボーナス: カードの効果をもう一度利用することができる。資源を必要とする場合は消費しなければならない。
    • カード: ボーナスの内容と同じ効果を得られるカードが多い。

また、カードの獲得の際に、任意のリソース1種(花、果実、穀物、バッテリーのいずれか)を2つ消費することで、両隣のプレイヤーが獲得したボーナスと同じものを獲得することができる。

ボーナスの獲得はカードの獲得の前に行うことができるので、カード獲得のためのリソースが足りない場合にはボーナスで獲得したリソースを充てることができる。

獲得したカードは山札の一番上に置き、次のターンですぐ使うことができる。全プレイヤーがボーナスとカードを獲得し終わったら、フェーズ3. 次のラウンドの準備に進む。

フェーズ3. 次のラウンドの準備

BGAでは特にフェーズとして表示はされないが、フェーズ2. カードの獲得の後に「ターン終了」をするまでの間であれば、以下の行動ができる。

これらの行動はフェーズ1.からフェーズ3.までの任意のタイミングで行うことができるが、道具によっては使用できるフェーズに制限がある。

道具の使用

道具カードが3種類公開されており、バッテリーを消費することで効果を得ることができる。いずれの道具も使用できるのは1ターンに1回までである。

道具カードは複数種類存在するが、例えば以下のようなものがある*3

  • ラジカセ
    • フェーズ1. でのみ使用可能
    • 消費バッテリー: 2
    • カードの並び替えの前に、公開されたカードのうち1枚を捨て山に置き、代わりに山札から1枚カードを公開する。
  • パソコン
    • フェーズ1. 2. 3. で使用可能
    • 消費バッテリー: 5
    • 即時に5勝利点を獲得する。
  • スクーター
    • フェーズ2. でのみ使用可能
    • 消費バッテリー: 6 もしくは 9
    • 好きなカードを1種類(マンドリル、オランウータン、ゴリラ、チンパンジーのいずれか)選択し、山札の一番上におく。レベル1のカードなら6、レベル2のカードなら9のバッテリーを消費する。

激怒

激怒のリソースを4つ使用することで、このターンに自分が公開していたカードのいずれかをデッキから削除することができる。 削除した場合、カード右上に記載のあるリソースを獲得することができる*4

1ターンの間に何枚も削除することができるが、激怒のリソース上限が12個であるため、1ターンに削除できるのは3枚までが限界である。基本的に初期山札のカードはゲーム中に獲得できるカードよりも弱いため、デッキ圧縮の意味でも積極的に削除していけると良さそうである。

ゲームの終了

いずれかのプレイヤーが80点を獲得した場合、そのフェーズの処理を完了した時点でゲームが終了する。

例えば、フェーズ1. カードの並び替えの段階で点数が80点に到達した場合、フェーズ2. カードの獲得には進まず、全プレイヤーのフェーズ1. での処理が完了した時点でゲームが終了する。

最終的に点数が最も高いプレイヤーが勝利となるため、処理内容によっては最初に80点に到達したプレイヤー以外が逆転勝利する場合もある。

プレイしてみての考察

プレイ中に感じたことや、プレイ後の感想戦で話した内容を書いておく。

最初の2ターンの重要性

獲得したカードは次のターンに使用可能なため、1ターン目にゴリラのカードが獲得できた場合とそうでない場合で、デッキの圧縮スピードにそれなりに差が出るように感じた*5

ゴリラのカード獲得に限らず、最初の2ターンの引きが悪いと資源の獲得がままならず、圧倒的に不利な展開になってしまう場合があるが、こればかりはゲームの性質上仕方ないように思える*6

オランウータンとバッテリーの使いどころ

序盤はゴリラで激怒を獲得してデッキを圧縮しつつ、チンパンジーで必要な効果を再利用、中盤以降はマンドリルで勝利点稼ぎ、と言うのが基本的な動き方になってくるように思う。 そうなると、オランウータンのカードを獲得するタイミングがあまりなく、必要があまりないように感じてしまった。

また、オランウータンのカードで獲得できるバッテリーも、カードの獲得には使えないこと、道具の効果もそこまで強力なものと思えなかったこと、さらにはオランウータン以外のカードでもバッテリーが獲得できてしまうことから、ますます有用性が感じにくくなってしまっていた。 道具にはいろいろな種類があるため、場によっては有効活用できるのかもしれないが、そのためにあえてオランウータンのカードを取るか、と言われると微妙なように感じる。

レベル1のカードを獲得するタイミング

カードにはレベル1とレベル2があるのは前述した通りであるが、レベル2のカード獲得に必要な資源6つが比較的早い段階で揃うことから、レベル1のカードを買うタイミングが早々になくなってしまう。

あえてレベル1のカードを買うメリットは「資源を3つ節約できる」ことになると思うが、資源を多く残すことが有効なタイミングがあまりなく、多くの資源を消費したとしても次のターンにレベル2のカードを使えるので比較的すぐに回収することもできる。レベル1のカードは枚数が多い*7にも関わらず、最初の2ターン程度で誰にも買われなくなってしまっていたのは少々残念に感じた。

行動をキャンセルした場合の挙動

BGAでプレイする場合、「直前の行動をキャンセル」と「N回前までの行動をキャンセル」を選べるのだが、「N回前までの行動をキャンセル」を選んだ場合、カードの並びが最初の状態に戻されてしまう。時間をかけて最大効率で並べ替えたものがリセットされてしまうだけでなく、自分だけ時間消費が大きくなってしまうことにもつながるため少し注意が必要。

感想とまとめ

プレイ時間は5人で1ゲーム50分程度。全員で一斉に行動を処理していくタイプのゲームであるため、他のプレイヤーのターンが終わるのを長時間待つようなことはあまりなく、比較的サクサクプレイすることができた。

デッキビルド系のゲームとしては「ドミニオン」や最近は「チャレンジャーズ!」などが有名なものとしてあるが、デッキを組んだ上でターン毎に引いたカードを並べ替えて行動を変えると言う部分が中々頭を使って面白くプレイできたように思う。

ただ、1戦目は色々なカードを獲得して遊んでいたが、それ以降は前述のように「レベル1のカードとオランウータンのカードは取らない」で戦略がほぼ固まってしまったのは少し残念に感じた。もう少しデッキビルドの戦略性が広がるかどうかは、今後プレイしつつ確かめていきたいと思う。

また、このゲームはまだ日本語版が出ておらず、実物を購入したい場合は外国語版を海外のサイト等で入手する必要がある。道具のカードの内容やレベル1/2のカードの違いなどを確認してみたかったが、現時点ではBGAで何度もプレイして確認してみる必要がありそう。

boardgamearena.com

*1:ドミニオンで言うところの「アクション」「購入」「クリーンアップ」の順を、各プレイヤー一斉に行っていくイメージ

*2:勝利点を消費して何かを獲得するような行動はなかったため、リソースとは別枠として記載している

*3:これらはBGAでは「初心者向けのセット」として登録されており、この設定を使えば毎回これらの道具が使用される

*4:初期山札のカードは花、果実、穀物、バッテリーのいずれか1個、ゲーム中に獲得したカードの場合は3勝利点が獲得できるようである

*5:初期の山札が8枚、毎ターン4枚のカードを引いていくため、1ターン目に獲得したゴリラのカードは運が良ければ最短で3ターン目には再び使える

*6:例えば、ドミニオンにおいて2コインで獲得できるカードがないような場での初手2コインなど、探せば同じようなパターンは色々ありそうである

*7:レベル1のカードは18枚、レベル2のカードは12枚。ただ、10ターン程度でゲームとしては決着がつくので、レベル2のカードも売り切れることはほとんどなさそうではある

【ボードゲーム】ラクリモーサをプレイした感想を書いてみる

ボードゲームの「ラクリモーサ」をプレイしたので、感想と次回プレイ時にルールを思い出せる程度のまとめを書いておく。

参考記事

記事の作成にはこちらの記事にある和訳ルールを参考にさせていただきました。 note.com

製品版にもルールブックは付属していますが、以下記事中の用語も上記の記事に準拠しているため、製品版のルールブックと表記揺れや翻訳が異なる場合があることをご了承ください。

大まかなゲームルール

ゲームの大まかな流れは以下の通り。

  1. 9枚の個人デッキから4枚を手札にする。初期の個人デッキは各プレイヤーとも同じ。
  2. そこから1枚をアクションカードとして使用して、もう1枚をピリオド終了時の収入として使用する。
  3. 2.を4回繰り返すとピリオド終了となり、終了時の処理を行う。
  4. 5ピリオド終了時に勝利点を計算し、最も多くの勝利点を獲得したプレイヤーの勝利となる。

設定としては、プレイヤーはモーツァルトの未亡人であるコンスタンツェの後援者(いわゆるパトロン)となり、モーツァルトの未完の作品であるレクイエムを完成させることを目的として行動する、というもの。ちなみに、タイトルとなっている「ラクリモーサ」とはキリスト教聖歌「怒りの日」を構成する定型詩の一つであり、モーツァルトはこの一節をレクイエムの楽章として設定している。

ja.wikipedia.org

メインボード

基本的にはプレイしたアクションカードに応じて、メインボード上のアクションを実行する。アクションの実行には、各プレイヤーが持っているストーリーポイントおよびお金を支払う必要がある(コストの支払いが必要ないものもある)。

ストーリーポイントには「才能(黒)」「旅路(赤)」「作曲(白)」の3種類が存在し、「作曲」「曲の演奏 or 売却」アクションで「才能」ポイント、「旅路」アクションで「旅路」ポイント、「レクイエム」アクションで「作曲」のポイントをそれぞれ消費する。「経験」アクションの場合はどれでも良い。

[1]には「作曲」アクションで獲得できる作品カード、および「経験」アクションで獲得できるメモリーカードが置かれる。カードが獲得されたら空いているスペースを右詰めし、[2]のカード置き場から補充する。左側に置かれているカードを獲得するには、追加のコストを支払う必要があるようになっている。

[3]は各ピリオドでのボーナス勝利点の獲得条件、[4]には各プレイヤーの勝利点を記録するためのコマを置く。

[5]には各都市のタイルとモーツァルトコマが置かれており、「旅路」アクションで移動したタイルを獲得して効果を得ることができる。モーツァルトコマは全員で共通のものになっている。

[6]は未完成のレクイエムのパートになっており、支援者として2人の作曲家のうちどちらかに依頼をするというアクションになる。「レクイエム」アクションで各プレイヤーが支援したい作曲家のマーカーを置き、タイルを獲得する。各楽章に置いたマーカーの個数および多数派かどうか応じて、ゲーム終了時に勝利点を得ることができる。

上記で出てきた「作曲」「思い出」「移動」「レクイエム」アクションの他に、「曲の演奏 or 売却」アクションが存在しており、獲得している作品カードを選択して、使用もしくは廃棄することで勝利点やお金を得ることができる。

超ざっくりと説明すると、上記の処理を各プレイヤーが順次実行していくことでゲームが進行していく。このほかにも獲得したタイルによる追加効果が存在するが、数が多く処理内容もある程度複雑なため本記事では省略する。

個人ボード

使用するアクションカードをボード上段の[1]の部分、ピリオド終了時の収入となるカードをボード下段の[2]に挿せるようになっている。[3]はストーリーポイントのキューブ、[4]はチップ置き場になっている*1

[5]には大まかなターンおよびピリオド終了時の処理が記載されている*2。[6]はピリオド終了時に受け取ることができるお金、勝利点、ストーリーポイントを管理する場所で、[7]はレクイエムのアクションを実行する際にメインボードに置くトークン置き場、およびメインボードから獲得したタイルの置き場になっている。

ハマったポイント

全員初めてプレイするという状況だったため、初プレイ時にハマったポイントの記録のために書いておく。

ボードやカードの処理内容が一目で分かりにくい

これが一番大きいのだが、ボードおよびカードのデザインが非常におしゃれな影響で、実際の処理内容が一眼で分かりにくいのが難点のように感じた。カードがどのアクションに対応するのか、獲得したカードやタイルがどのような効果を持つのかなど、初見では把握することが難しいと思われるため、処理に迷った場合は適宜ルールブックを参照して確認するのが良さそうである。

個人ボードやトークンの名称がややこしい

記事作成中にも感じたことだが、要素が多いこともありどうしても用語が多く登場することになる。そのためインスト中に用語を聞いてもどうしても一発で覚えることができずプレイ中に何度か確認することにはなる。

例えば個人ボードだと、「経験セクション」が上段のカードを挿す部分、「ストーリーセクション」が下段のカードを挿す部分、「ストーリーポイントトラック」が中段のキューブを動かす部分を表しているのだが、途中からは「上段」「下段」と呼んでいた*3。ストーリーポイントに関しても、それぞれの色で呼んだりマーカーに描いてある絵柄の「車輪」「インク」で呼んだりしていたので、プレイヤー間で分かりやすい言い方で進めても良いとは思う。

「経験」アクションの処理

途中までルールを勘違いしていたのだが、「経験」アクションとして出したカードそのものではなく、ストーリーセクション(下段)に出したカードを交換する。上段のカードは思い出のカードで確定しているので、交換するカードによっては今後「経験」アクションができなくなってしまうのである意味当然ではあるのだが、注意が必要。

感想とまとめ

4人プレイで150分程度で1ゲーム終了。 初めの方はルールを細かく確認しながら、後半の方は1ターン中に処理する内容が増えてくため、1ピリオドあたり30分程度で進行した。

手持ちのリソースをどのように効率的に使っていくかを考えるのがメインになるが、各ターンに選べるアクションは手札の4枚からしか選べないので、手札運によっては非常に残念な進め方しかできないターンも出てくる*4

作品カードを演奏または売却してお金やポイントを稼いでいく必要があるが、曲のジャンルによっては獲得コストが非常に高かったり、通常の演奏/売却時のコストが安すぎるが特定のタイルと組み合わせることで大量得点を獲得できたりするため、それを事前に把握しておくと戦略が立てやすいように感じた。

最初の方は要素および用語が多いこともありなかなか戸惑うが、後半になるにつれてアクション時のコンボが繋がるようになり面白くなってくる。一度プレイするとゲームの進め方やアクション時のコンボの狙い方などがある程度わかってくるので、機会があればルールや処理内容を忘れないうちにまたプレイできればと思う。

ラクリモーサcmonjapan.shop

*1:くぼみにはなっていないため、個人ボード上にチップが散らばってしまいがちなのは少々注意が必要

*2:ただ、処理すべき内容の箇条書きの最後が「次のピリオドの準備」になっているなど、これを見るだけで一通りの処理が把握できるわけではない

*3:これはサイズ -大鎌戦役- を何度かプレイしていて馴染みがある言い方だったというのも大きい

*4:体感としては「目当ての作品カード/アクションカードが獲得できない」のような状況が頻繁に起きていたように思う

gitでマージ済のブランチを一括で削除したい

git を利用して作業していると、マージ済のブランチがローカルに残ったままになってしまうことが多い。放置したままにしていると作業が進むにつれて増え続けてしまうため、できれば一括で削除したい。

前提

ローカルブランチを1つ削除したいのであれば、以下のコマンドで実行できる。

% git branch -d <ブランチ名>

ただ、現在のブランチにマージされていない場合は、警告が出て削除はされない。 強制的に削除したい場合は -D オプションを使う必要がある。

% git branch -D <ブランチ名>

マージ済のブランチを全て削除したい

ローカルブランチは git branch コマンドで表示できるが、--merged オプションを指定することで、現在のブランチにマージ済のブランチのみを表示させることができる。 これを xargs で引数として与えることで一括削除ができる。

% git branch --merged | xargs git branch -d

現在のブランチにマージ済のブランチのみを対象としているため、-D オプションを指定する必要はない。 また、git のドキュメントにも以下のような記載があり、マージ済のブランチは積極的に消してしまっても良さそうである。

このリストにあがっているブランチのうち先頭に * がついていないものは、通常は git branch -d で削除してしまって問題ないブランチです。 すでにすべての作業が別のブランチに取り込まれているので、何も失うものはありません。

git-scm.com

pythonでQRコードを作成する

タイトルの通り。python には qrcode という名前通りのパッケージが存在しており、これを使ってQRコードを作成してみる。

環境

python は anyenv + pyenv でインストールしたものを使用。

% sw_vers
ProductName:    macOS
ProductVersion: 12.6.3
BuildVersion:   21G419
% python --version
Python 3.11.3
% pip --version
pip 22.3.1 from /Users/gumfum/.anyenv/envs/pyenv/versions/3.11.3/lib/python3.11/site-packages/pip (python 3.11)

インストール

pip コマンドでインストールできる。

% pip install qrcode

使い方

qr コマンドに、QRコードを生成したい文字列を指定し、ファイルに出力する。

% qr "test" > test.png

コマンドを実行すると、以下のようなファイルが作成される。

基本的な使い方はこれだけ。

オプション

QRCode クラスを利用することで、生成するQRコードのオプションを指定することができる。

import qrcode

qr = qrcode.QRCode(
    version=1,
    error_correction=qrcode.constants.ERROR_CORRECT_L,
    box_size=10,
    border=4,
)
qr.add_data('test')
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")

version で指定しているのはQRコードのバージョン番号と呼ばれるもので、大きいほど多くの情報を格納することができる。詳細はデンソーウェーブが公開している以下のページを参照。 www.qrcode.com

error_correction には誤り訂正の種類を指定する。誤り訂正の種類には4種類あり、訂正能力が高くなるほど必要なデータ量が大きくなる。こちらも詳細はデンソーウェーブが公開している以下のページを参照。 www.qrcode.com

box_size border はそれぞれQRコード内の各マスの1辺のピクセル数を指定する。デフォルトは10となっており、これより小さな値を指定することで、同じバージョン番号のQRコードであってもより小さいサイズで出力させることが可能になる。border は外周の余白のピクセル数を指定できる。デフォルトは4 となっており、指定できる最小の値になっている。

fill_color back_color はそれぞれピクセルの色、背景の色を指定ができる。black white red のようなCSSでも指定できる名前での記述のほか、16進コードで指定することもできる。

プロジェクトページのリンク

上記の他にも、グリッドの形や塗りつぶし方を変えるオプション等が用意されている。どちらも同じ内容ではあるが、GitHubの方が画像が添付されているので分かりやすそう。

PostgreSQL で order by 2 と書くと2番目に指定したカラムでソートできる

タイトルの通り。PostgreSQL において SELECT 文で order by 2 を指定すると、2番目に指定したカラムでソートをすることができる。

試してみる

過去の記事でも利用した、都道府県コード(id)とローマ字表記(name)のテーブルで試してみる。 order by 2 を指定すると、2番目に指定されているローマ字表記(name)の順にソートされていることが分かる。

postgres=# select * from prefecture order by 2 limit 10;
 id |   name    
----+-----------
 23 | Aichi
  5 | Akita
  2 | Aomori
 12 | Chiba
 38 | Ehime
 18 | Fukui
 40 | Fukuoka
  7 | Fukushima
 21 | Gifu
 10 | Gunma
(10 rows)

当然ではあるが、指定したカラム位置に該当するものがない場合はエラーになる。

postgres=# select * from prefecture order by 3 limit 10;
ERROR:  ORDER BY position 3 is not in select list
LINE 1: select * from prefecture order by 3 limit 10;

これだけだとカラム名を指定した場合と変わらないが、例えば集約関数などを用いて計算した結果でソートしたい場合などに、AS で出力列名を指定せずにソートすることができる。 また、カラム位置を複数指定してその順でソートすることもできる。

postgres=# select *, length(name) from prefecture order by 3 limit 10;
 id | name  | length 
----+-------+--------
 24 | Mie   |      3
 41 | Saga  |      4
 21 | Gifu  |      4
 44 | Oita  |      4
 29 | Nara  |      4
 10 | Gunma |      5
 39 | Kochi |      5
 18 | Fukui |      5
 25 | Shiga |      5
  5 | Akita |      5
(10 rows)

postgres=# select *, length(name) from prefecture order by 3, 2 limit 10;
 id | name  | length 
----+-------+--------
 24 | Mie   |      3
 21 | Gifu  |      4
 29 | Nara  |      4
 44 | Oita  |      4
 41 | Saga  |      4
 23 | Aichi |      5
  5 | Akita |      5
 12 | Chiba |      5
 38 | Ehime |      5
 18 | Fukui |      5
(10 rows)

PostgreSQL文書( 7.5. 行の並べ替え(ORDER BY) )を見てみると以下のような記載がある。

sort_expressionは以下のように列ラベルもしくは出力列の番号で指定することができます。

DBMSのドキュメントも見てみる

PostgreSQL 以外の DBMS のドキュメントも見てみると、OracleMySQL でも同様の構文で同様の操作ができるとの記述がある。

ただし、MySQLのドキュメントには「カラム位置の使用は、この構文が SQL 標準から削除されたため非推奨です。」との記述がある。

標準SQLの仕様を見てみる

SQLANSI(米国国家規格教会)、現在は ISO(国際標準化機構) によって言語仕様の標準化が行われており、標準 SQL 規格として制定されている。 1986年に初版である SQL86 が制定されて以降、不定期ではあるが更新がなされており、2023年5月現在の最新版は SQL:2016 である。

標準 SQL 規格の内容を確認するためには、基本的にドキュメントを購入する必要がある。 規格検索結果 | 日本規格協会 JSA Group Webdesk で検索してみると、個人で購入するには価格がかなり高めであり、さらに分冊になっていることから全て揃えるのは難しいような印象を受ける。

文法レベルでの確認をしたいだけであれば、有志により公開されている SQL overview で確認すればほとんど事足りるのではないかと思う。

jakewheat.github.io

話を order by 2 に戻すと、SQL92 では order by の sort key として unsigned integer が指定可能になっている。

しかし、次の版である SQL:1999 からは記述が削除されている。この版から、標準 SQL ではカラム位置の指定は仕様の対象外になったものと思われる。

今回ドキュメントを確認した PostgrSQL/MySQL/Oracle はいずれも 1999年以前から存在している DBMS であり、作成済みの SQL の互換性を維持するために独自に構文をサポートしているという可能性は十分に考えられるのではないかと思う。

まとめ

PostgreSQL だけでなく、OracleMySQL でも order by 2 を指定することで、2番目に指定したカラムでソートをすることができる。 ただし、標準SQLで定められた記法ではなく、DBMSによっては非推奨とされているため、利用できるかどうかはドキュメント等で確認しておくのが良さそうである。

個人的な感想として、一見しただけでは処理内容が分かりにくいことから利用する機会が少なそうに感じていたが、集約関数などと組み合わせる時に別名を指定してなくて良いなどのメリットはありそうだということが分かった。ただ、読み手としてはカラム名をつけてもらえる方が分かりやすいかとは思うので、利用できるかどうかと合わせて場合に応じて使うのが良さそうである。