Programmer's Nightmare 1回目

ルールはこちら: http://www.silcom.com/~tomjolly/pnrules.htm
とりあえずカードを配るところだけコードを書いてみた。一人でプレイ過程がどうなるか考えてみよう。配られたカード:

BIT_MOVER 
REVERSE_PROGRAM 
OVERWRITE 
GOTO
OVERWRITE 
COPY
ERASE
FUTURE
POWER_SURGE
ZAP
DELETE 
ACQUIRE
COPY 
BUG 

ふむ。で、2人対戦ということにして交互に6個のBitを置く。最初に何を置くかな。このプログラムでポイントを上下しうるのはACQUIRE, POWER_SURGE, BUG、ZAPの4枚。だけどBIT_MOVERとか押さえておくと相手の都合の悪いBitを移動できるからこれかな。OVERWRITEでBitの色を書き換えられるけど、2枚あるしBIT_MOVERの方が先だから大丈夫だろう。BIT_MOVERを選択する。

後手、さてどうするか。これかなり手の打ちようがないんじゃないか。OVERWRITEに置いてもBIT_MOVERで動かされるだけだし。かといって次の手でOVERWRITEを取られてもそれはそれで困るか。先のOVERWRITEを選択。

先手、明らかに有利だなぁ。いや、しかしBIT_MOVERは一度に1つしか動かせないから順当に考えて一周目につぶしきれなかったいくつかの機能が発動するわけか。DELETEを押さえておこう。

後手。今気づいたんだが、COPYを押さえるとBIT_MOVERまでコピー範囲じゃないか!COPYを選択。(注釈:COPYの機能を勘違いしていることに後で気づく)

先手。うむむ、1周目にOVERWITEとCOPYの両方をBIT_MOVERで無効化することはできないぞ。後手にOVERWRITEされたものをOVERWRITEし直すか、それとも…REVERSE_PROGRAMでいきなり順番を逆にすれば相手に手番が回る前に2回BIT_MOVERを発動できるか。

後手。ふむ…。先手がREVERSEを使うと仮定して、後ろのOVERWRITEを押さえるか。これをBIT_MOVERで動かす際に、COPYの上に動かすわけにはいかないからGOTOに動かすことになって、それ以降でBIT_MOVERが使えなくなる。

現状(先手ビット: +, 後手ビット: -):

BIT_MOVER +
REVERSE_PROGRAM +
OVERWRITE -
GOTO
OVERWRITE -
COPY
ERASE
FUTURE
POWER_SURGE
ZAP
DELETE +
ACQUIRE
COPY -
BUG 

あと3個ずつ置く。今までのところどちらも得点カードにビットを置いていなんだけど、たぶんそれどころじゃないよね。制御能力を得る方が先。

先手。後手の思考が見えてしまうのは一人でやっているから仕方ないんだけど、本来は後手がミスをしたとしてもミスかどうか自信が持てない場合は悩むことになるんだよね。後手はBIT_MOVERで無効化されることを前提に後のOVERWEITEに置いたけども、DELETEを押さえてあるのでそれを使わえばいい気がするね。GOTOを押さえてジャンプされないようにするか?いやジャンプしない場合、先のOVERWRITEが有効だから無効化する必要があるな。これをBIT_MOVERで無効化すると、GOTOに乗せないならREVERSEに乗せることになる。それはまずいか?REVERSEされてまた下向きに実行されると、、いやCOPYに到達する前にDELETEがあるからこれでCOPYのビットを消せばいいのか。いや、ダメだダメだ。そもそもさきにCOPYを無効化しておかないとDELETEの効果をコピーされてしまう。初手でBIT_MOVERが使えるという先手のアドバンテージを守るためには序盤の処理内容を固めてしまうべきだ。BIT_MOVERはCOPYのビットをBUGの上に移動する。そして2回目のBIT_MOVERで上のOVERWRITEのビットをREVERSEの上に移動し、DELETEで下のOVERWRITEのビットを消す。これでいいはずだ。うむ、これ以上処理の流れをかき回す選択肢を後手に与えないためにもGOTOを押さえて取れなくしよう。

後手。今気づいたんだがCOPYの機能はExecute the *previous* 2 instructions in order.だぞ。先手も後手も根本的に間違えているぞ。先手はREVERSEを使うのも使わないものどっちでも対処できるか。使うならCOPYを無効化すればいいし、使わないならGOTOを発動させればいい。GOTOを使われるならZAPまでジャンプするから間のを持ってもあまり意味がない。使われないならREVERSEが起きるから…ふむ。FUTUREを押さえよう。実行順が逆順になっている場合、FUTUREがERASEとCOPYを発動させるのでPOWER_SURGEが消えた上でCOPYの効果でDELETEとZAPが発動する。悪くない。

先手。さて、あと2つのビットを置くわけだが。うむ。FUTUREでDELETEを発動されるのを阻止しようとするとOVERWRITEの発動を止められないかな。いや、GOTOで上のOVERWRITEを飛ばしてしまえばいいんだろうか。逆順実行状態でGOTOを使った場合ACQUIREまで飛ぶことになるから、自分はBIT_MOVERを使えないけどDELETEは使える。DELETEでOVERWRITEを無効化したうえでGOTOをやめればいいんじゃないか。というかDELETEってBIT_MOVERより強力な無効化手段だな今気づいた(ぉぃ) というわけで特に問題はないように見える。まあ有利だと思い込んで油断することにしてACQUIREを押さえよう。二人の間に2点の点差を付けられるカードだし。もう1枚の2点の差を付けるPOWER_SURGEは消えるし。

後手。後手がかつ方法は残っていないんだろうか。なにかまだ考えられていないような制御方法が。あ、あるじゃん。FUTUREを実行しないでスルーしてCOPYでFUTUREをコピーする。そうするとGOTOをコピーできるのでどうなる?あまりうれしくない?いや、1回目のCOPYでERASEが消えるからPOWER_SURGEを発動できるぞ。COPYを選択。

先手。後手としては先手には油断してほしいところだけど、まあさすがにPOWER_SURGEが消されない可能性には気づく。かといってPOWER_SURGEに置くのもみすみすERASEされる危険性があるな。一歩引いてZAPにしておこう。悪くはないはず。

後手。最後のビットだ。選択肢としてはERASE, POWER_SURGE, BUGか。まあ、POWER_SURGEだろうな。

ビット配置完了(先手ビット: +, 後手ビット: -, 区別のために同名のカードに番号を振った):

BIT_MOVER +
REVERSE_PROGRAM +
OVERWRITE1 -
GOTO +
OVERWRITE2 -
COPY1 -
ERASE
FUTURE -
POWER_SURGE -
ZAP +
DELETE +
ACQUIRE +
COPY2 -
BUG 

さてここからやっとプログラムの実行が始まるわけだ。まあ最初の方はすでに先手が読み切ってあるようにBIT_MOVER, REVERSE, BIT_MOVERだ。先手はCOPY2のビットをBUGに移し、OVERWRITE1のビットをREVERSEに移す。

BIT_MOVER +
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
OVERWRITE2 -
COPY1 -
ERASE
FUTURE -
POWER_SURGE -
ZAP +
DELETE +
ACQUIRE +
COPY2
BUG -↑

後手はBUGを発動しない。先手はACQUIREを発動。点数 12対10。先手DELETEでOVERWRITE2のビットを削除。

BIT_MOVER +
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
OVERWRITE2 
COPY1 -
ERASE
FUTURE -
POWER_SURGE -
ZAP +
DELETE +↑
ACQUIRE +
COPY2
BUG -

先手ZAPを発動。点数12対9。後手 POWER_SURGEを発動。点数9対8。
後手 FUTUREを発動するか?
発動するとPOWER_SURGEは失われるが、DELETEとZAPが発動できる。発動しないで POWER_SURGE を残しても先手が3点の差を付けている間に2点しか取り戻せない。発動しよう。 FUTUREの特殊効果でERASEとCOPY1が実行される。 ERASEで POWER_SURGEが削除される。次にCOPY1でDELETEとZAPが発動される。
何のビットをDELETEする?
今気づいたけども REVERSE_PROGRAMに後手マーカーが乗っているので先手の判断を後手が上書きできる。次の REVERSE_PROGRAMで実行順序が元通りにしたとすると、先手はCOPY1で OVERWRITE2を発動されるのを避けるためにGOTOでジャンプをする。ジャンプ先は先手のDELETEなので点差が一方的に広がるだけ。意味がないな。じゃあBIT_REMOVERのビットを消しておくか。
ZAPで点数8対8。同点に戻った。

BIT_MOVER 
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
OVERWRITE2 
COPY1 -
ERASE
FUTURE -↑
ZAP +
DELETE +
ACQUIRE +
COPY2
BUG -

プログラムカウンタはCOPY1へ。発動するか?発動するとFUTUREがコピーされて…あれ、OVERWRITE2が発動するじゃん。(上のログを見る)あああ、「GOTOがコピーできるけどあまりうれしくない」なんて書いてある!見落としだ。OVERWRITEが発動するならBIT_MOVERを残しておけばよかった。まあいい。これでも逆転の芽が見えるぞ。
COPY1を発動。FUTUREとERASEがコピーされる。FUTUREの効果によりOVERWRITE2とGOTOが発動。OVERWRITE2でDELETEのビットを書き換える。 OVERWRITE2は効果を失いデッキから引かれたカードに置き換えられる。そのカードは…ZAPだ。続いてGOTOのジャンプ先はACQUIREに固定される。プログラムカウンタはACUIREへ移動。さらにERASEの効果でERASEが消える。

BIT_MOVER 
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
ZAP2 
COPY1 -
FUTURE -
ZAP1 +
DELETE -
ACQUIRE +↑
COPY2
BUG -

ACQUIREの効果で点数10対8。後手DELETEの効果を発動してZAP1のビットを削除。

BIT_MOVER 
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
ZAP2 
COPY1 -
FUTURE -↑
ZAP1
DELETE -
ACQUIRE +
COPY2
BUG -

後手 FUTUREを発動してCOPY1とZAP2をコピー。COPY1の効果によりDELETEとZAP1が発動される。DELETEにより ACQUIREのビットを削除。ZAP2で点数9対8。

BIT_MOVER 
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
ZAP2 
COPY1 -↑
FUTURE -
ZAP1
DELETE -
ACQUIRE
COPY2
BUG -

後手さらにCOPY1を発動してZAP1とFUTUREをコピー。ZAP1により点数8対8。FUTUREによりZAP2とGOTOが発動される。点数7対8。ついに逆転した。GOTOによりプログラムカウンタは ACQUIREへ。

BIT_MOVER 
REVERSE_PROGRAM +-
OVERWRITE1
GOTO +
ZAP2 
COPY1 -
FUTURE -
ZAP1
DELETE -
ACQUIRE↑
COPY2
BUG -

DELETEによりGOTOのビットを削除。FUTUREを発動してCOPY1とZAP2をコピー。COPY1の効果によりDELETEとZAP1が発動される。DELETEにより REVERSE_PROGRAMのビットを削除。先手はすべてのビットを失い、後手の勝利。

BIT_MOVER 
REVERSE_PROGRAM -
OVERWRITE1
GOTO
ZAP2 
COPY1 -
FUTURE -
ZAP1
DELETE -
ACQUIRE
COPY2
BUG -

感想。やたら複雑だ、と思っていたけどCOPYとFUTUREってかなり重要だなぁ。BIT_MOVERなんかよりよっぽど大事だったんじゃないか。この同じプログラムで僕が先手、誰かが後手でもう一度やってみたいな。誰かと対戦するときも、1回戦って終わりじゃなくて、勝った方が負けた方の席についてプレイしてみると面白いかも。