12A猫で学んだこと-Memoir-

...What are you learning now?

雑多な夏の日記の1ページ

こんにちは。StudentSです。
まだまだ暑い夏が続きますね。 季節の変わり目は体調が崩れやすい時期なのでお互い体に気を付けたいですよね。

人狼は...視点整理に難ありですねー 感想戦で振り返ると、論理パズル的に気づくことを失念していますね。
まぁ、気づくことができるようになっているのはいい傾向なのかもしれませんー

最近、はたらく細胞というアニメを見ました。

「雑菌め、この体の血球に手を出して生きてかえれると思うなよ!」
「白血球は自分の命を犠牲にしても、他の細胞を守るんだ!」
「お兄ちゃん助けてくれてありがとうございました。 また会えるかな。」

(はたらく細胞 第6話「赤芽球と骨髄球」より)

いや~ 赤芽球も骨髄球も可愛いです!
リピート再生しながら、ソースコードのバグとのたたかいですー

今月のブログのエントリはプログラミングの練習です。

の記事で

  • RMQ (Range Minimum Query)

    • 問題: 配列が与えられる。指定した区間内の最小値のインデックスを求めよ。
  • LCA (Least Common Ancestor)

    • 問題 : ツリーが与えられる。2つのノードの共通祖先の中で最もルートから遠いものを求めよ。

の効率的な解き方や、線形時間でそれぞれの問題を相互変換するアルゴリズムが紹介されていました。 整理をしていないですが、自分の実装を

以下雑感を...

  • 効率的なデータ構造を考えるとき、$\sqrt{N}$, $\log(N)$ のオーダーでの処理ができるかを考える視点は役に立つ時があるかも
  • 出てくる問題のサイズが限られているのであれば、メモ化は有効な選択なのだろうなー
  • 問題同士の変換を思いつくのはすごいなー

自分用のメモだけど、
何とか今月もブログを書くことができたー!

暑い夏、 ぬるぬるシステムを考えた (5月は夏、6月は夏、7月は夏)

エントリのまとめ

f:id:StudentS:20180730222737p:plain f:id:StudentS:20180730222745p:plain f:id:StudentS:20180730222751p:plain f:id:StudentS:20180730222858p:plain

はじめに

このエントリは下記のNulさんからのTweetへの回答です。

https://ruru-jinro.net/log5/log456479.html

上記の村で使用された「定跡」について、「完全な成立条件/最低使用条件/不成立条件、占い結果の出し方、4日目の占い結果を○にする条件、CCOを入れない理由、村陣営の反論への対策、負け筋」などについて、定跡体系を中心に考察せよ。どうでしょう。

Tweetを読んだあと、ログを読ませていただきました。

「Nulさんうまいなぁ...面白いプレーだったなぁ。」
さぁ、暖かいルイボスティーを飲んで寝ようー!
おやすみなさい!

これが最初の感想でした。
だけど、Nul先生から、きちんと解析せよ と言われてしまいました...これはレポートの宿題ですねー
生徒としては、レポートの提出は嫌な時もありますが、単位のためには頑張らなきゃいけない時もありますね。

人の発言から思考をトレースし、想像力を膨らませて色々なことを考えるのが人狼
私は人狼をします...おそらく、色々な意味で今回の内容は重くなるはず...

(2018-06-24 追記)

下記のようなヒントをいただきました。

将棋の木村定跡に似ている

wikipediaを読んだのですが、さっぱり分からなかったです。
将棋の経験を積んで実際に自分で並べてみないと理解できないのでしょうね。
将棋は分からないながらも、つかめたポイントは2つ。

  1. 先手勝利まで研究が終わっていることから、完成された定跡とも言われている
  2. ただし、問題点として、先手の疑問手に対して、後手が悪手で返すという前提が存在する

条件付きで狼の勝利が確定する戦略である
というのがNulさんからのメッセージだと思っています。

(2018-07-18 追記)

とここまで書いた6月....7月後半まで手が付けられていませんでした。
そして、NulさんのTweetに以下の言及がありました。

※某人に出した問題についてちょこっとだけ言及
①同じ○進行でも真の占い先の中身と狼の占い先の中身、○の位置によって成立不成立が分かれたり、成立が不完全になったりします
②2−1が前提ですが、ある条件を満たすと上記以外でも不成立になります
この辺はしっかり詰めた方がポイント高いかな?

実は勝ちの定跡より
①「この手を使うと村の〜という反論があるので、〜という定跡部分はこれを対策している」
②「この手を使うと〜という理由でまず負けるので、まず使われない」
③「この状況なら〜という理由で負けるので、この形を回避する必要がある」
とかの方が大事だったりします

上記のポイント全てに応対できてはいませんが、 以下が自分のレポートになります。

1. 「定跡」の定義について

まず、Nulさんの「定跡」の定義が分からない状況からのスタートです。
前に同村させていただいた時、「ぬるぬるシステム」という単語を聞いた覚えはありました。
しかし、内容はよくわかりません。

何かを解析するときには、定義が明確でないと、その解析結果は曖昧になります。
最初に、この記事中での「定跡」の定義を明確にする必要があります。
提示された村のログとNulさんからの問題、素朴な自分の第一感を統合すると、

  • [エントリ上での『定跡』の定義] 2-1初日〇進行の時、2日目の夜に霊能者を噛み、3日目に仲間に狼騙りが身内切りをする。猫/狩CCOはなし。

とするのがいいと感じました。

この定義がNulさんの言動に適合していることを確かめます。
Nulさんの参加村数は多いので、全ての村を調べるのは諦めます。
NulさんがPLとして参加した村のうち、「ぬるぬるシステム」という単語が使用された村のログから確認を行いました。

調査結果からのまとめは

  • 霊能者を噛んで、●を出す戦略であるということ (CO数2-1の場合)
  • 初期の「ぬるぬるシステム」は3日目の●に対して、猫又CCOを入れていたが、CCOをしないようになった
  • 投票情報などを使って、(霊能者死亡のなかで) 2日目に吊れたのは村人であるという主張を行う傾向が見られる

といったところでしょうか。

調査結果の詳細は 付録A.「ぬるぬるシステム」のワードの言及村について を参照ください。

少なくとも、上記の定義は現在の「ぬるぬるシステム」として、大きくずれている気がしなかったです。

2. 「定跡」の解析について

自分のスタンスから行くと、厳密な解析は無理 、 というのが最初の一言になります。

ぬるぬるシステムが勝率が高い定跡かどうかは、村の進行次第で決まります。
村の進行をfixした時の勝率の導出は論理パズルとして原理的には可能だけれど、 場合分けが多すぎてできません。(終)

(付録B. 本ブログの解析の基本的な方針を参照)

しかし、この主張では全く面白みがありません。
厳密 ではないけれど、語ることができる定性的な論点を考えます。
以下の議論は、自分の感じる「るる鯖の12A猫の進行傾向」に基づく考察です。

[最低使用条件]

最初に思いつくのは、「初日のグレランで狼が処刑されないこと」ですね。
LWの状態で猫潜伏にはできないと考えます。
他には、対抗の占い師に〇を出さないことです。
占い師が吊れなくなってしまう可能性が極めて高くなってしまうので。

また、2日目で狩人、霊能平和が出た時に、猫又CCOなしのまま戦略を使うことはかなり厳しいという感覚を持ちます。
出来なくはないと思いますが、
狩人/猫又関連で破綻する可能性は上がるし、3日目の夜以降、残った潜伏狼が猫噛みを行った場合、
狼騙りの占い候補視点LWになるのは、占い師候補吊りを行う強い要素になります。

だからこそ、逆をついて、霊能者平和の時に●を打つという手もあると思うのですが、

  • 村の進行役の思考がどれだけ霊能生存下での身内切り●に信用を置くか。
  • 猫又・狩人の位置を推測できるか?

という要素と天秤にかけることになるでしょうね。

[完全な成立条件] について

厳密な意味では"完全"ではないのですが、村陣営が「身内切り + COなしは"絶対に"ない」
という進行をするのであれば、この戦略は極めて有効でしょうね。
猫又や狩人・狂人関連の兼ね合いにおいて相当な不運が重ならない限り、
ほぼ、勝利確定といっていいでしょう。

村の進行と場合分けが多岐に渡るので、その"ほぼ"の境界線の引くのは非常に難しいのですが、
例えば、狂人死亡済みで霊能者を噛んで●を吊った後、

  • 狩人がすべて平和を出し、
  • 吊り余裕のある状態でその狩人を狼騙り視点●にしてしまい、
  • 潜伏狼が対抗狩人COをすると詰み進行になる。

というのが、 村陣営が「身内切り + COなしは"絶対に"ない」という前提で動いた場合に
狼陣営が負ける条件の一例だと思います。
このような、非常に発生しにくい事象の時のみだけだと考えています。

[不成立条件], [負け筋]について

逆に、この戦略を村側に読まれた場合、極めて不利な戦いを強いられますね。

  • 3日目占いの決め打ちで狼騙りが吊られる + 身内切り(or 狂人の誤爆)を読まれて、翌日に●先が処刑される

これが最悪のパターンでしょうか。 狩人の平和や猫噛みも若干の恐怖ですが、1平和だけの偶数進行だったりする場合はそこまで痛手ではないような場合も多いでしょうね。

[占い結果の出し方]

4日目の占い結果について

●出しを躊躇する一番の要因は狩人が平和を出すことでしょうね。 狩人を●にしてしまっては狂人がうまく立ち回ってくれないと勝利は苦しくなるでしょうね。

4日目素村に●を打って、自身が噛まれない違和感を作り出したいと思うところですが、 狩人に●を打つことは避けたいですよね。

なので、4日目に平和がでることは〇結果を打つ要因になると考えています。

逆に、非狩人を決め打てるところがあることや、4日目に平和が発生しないことは●打ちを行う要素になります。
狩人や猫に●を出す危険性と2連続●結果による信頼・噛み筋の違和感を消せる利得の計算は、村の状況を読みながらといったところでしょうか。

[村陣営の反論への対策]

「霊能者を噛んで仲間に●を出す狼騙りが少ない」ことを軸に、反論する材料はたくさんありますね。

  • 自分の●を吊ってゲームが続いている。初日のグレランで狼が吊れたCCOなしの身内切りを主張することになるけれど、どっちを主張するの?
  • 霊能者を噛んで、身内切りをする意味がない。
  • 身内切りなら、狩人CCOや猫又CCOが入っているはず。

人狼において、主張の材料は何でも使うと思います。
この戦略は比較的レアケースです。そして、狼の吊り1を村陣営に与える、心理的に行いにくい"味方を殺す"作戦を採用しています。
したがって、 統計面・心理面の両面から要素を引っ張ってくることができます。

(統計の観点から主張を作る時、 直接、"統計"というワードを使うことは少ないと思いますが。 "考えにくい" / "あり得ない"などレアケース想起させるワードを使うことは一般的だとみています。 )

逆に 村陣営の多くが

  • 「比較的レアケースである、狼の霊能噛み身内切りCOなしを、レアケースとして扱わない」
  • 人狼プレイヤーは心理的にも身内切りを躊躇なく行う、または積極的に行う」

という前提を持っている場合、システム特有の反論は効果的ではありません。

[CCOを入れない理由]

猫又/狩人CCOをすると、狼の身内切りを追うPLが多いから。 この一言に尽きると思っています。
Nulさん自身が霊界で言っているように、戦略を取る人が少ない = 狼陣営の作戦として身内切りCOなしを考慮する村陣営が少ないから成立する作戦です。
また、狩人CCOの場合に(展開次第ではありますが、)

  • (真狩人を噛んだ場合)狩人が死亡しているのにも関わらず、狼騙りが噛まれない違和感がある or 猫又に●を打ってしまう可能性がある の2拓
  • (真狩人を噛まない場合)真狩人が狼として認識される場合がある

などのリスクもあります。

真狂 - 真の2-1のケースが多くなり、村が真狂の可能性を強く追うようになるのは不利に働く要素

この戦略が機能しているのは、2-1 + 霊能者噛みでの村の進行が、非常に真狼に比重が置かれているからだと考えています。
狂人には誤爆という要素があります。 狼騙りの●が狂人の誤爆とみなされる可能性が高くなることは『定跡』にとって不利に働くでしょう。
(LW狼騙り、猫潜伏のケースはできるだけ狼視点できるだけ防ぎたいはず)
狩人が死亡していたりすればいいのですが、最後の最後で詰み進行を採用される可能性は上がると思います。

「Nulさんが入村している可能性が高い」ことは、この『定跡』は不利に働く要素

繰り返しになりますが、この『定跡』が成立している理由は「霊能噛み + 狼騙りの身内切りをするPLが少ない」からです。 Nulさん自身もログで言及されていますが、『定跡』を採用するプレイヤーの存在確率が高い場合、 『定跡』を読まれることが多くなるでしょう。

現実のるる鯖の村で採用される村の戦略をNulさんはよく知っていると思います。 村陣営が実際に多く採用する戦略の弱点を考えて、この戦略が有効だと考えている。 だからこそ、「Nulさんが参加している可能性が高い」という情報が出ることには警戒しているのだろうな。 と想像します。 Nulさんの取りうる戦略を知っている人が進行役になると、「身内切りCO無しはレアケース」という主張点が活かせません。

中の人読みが全くできない自分に対しては意味のない警戒ですが!

感想

勝率の高いプレイヤーの方々は

  1. ゲーム中の進行を読む力に長けている
  2. 読んだ進行を前提として"良い"戦略を導出できる

この2つの点に優れている方が多い気がします。
これは、論理力とるる鯖での経験・観察眼が組み合わされていると考えています。

じゃんけんで例えると、

  • 相手が何を出すかを事前にある程度予想できる
  • 相手の出す手を見てから自分の出す手を決定できる

というような感じでしょうか。
そういうじゃんけんができる人は強いはずです。

勝率の高いプレイヤーの方々がゲームの進行を読むことができる一因は、村の進行の定常性にあると考えています。
ある期間、村の進行のパターンの幅が限られていれば、その間、『定跡』のような考え方が成立すると思います。

しかし、村の進行も変化します。
例えば、2015年ぐらいには、初日〇展開で占い師が3人COしたときは占いローラーにする進行が定着していた記憶があります。
現在、2018年夏は、占いローラーの進行はほとんど採用されていないようです。

実は最初にNulさんから"定跡"という言葉を聞いた時、自分は違和感を感じました。
ある時期と環境を切り取れば、確かに"定跡"として成立する戦略は存在すると思うのですが、
将棋や囲碁などのゲームとは違って、"読みあい"の要素が強いこのゲームで不変の「定跡」は存在しないのではないだろうか。と。
高勝率の「定跡」が定着した場合、「定跡」の逆を突くような戦略が多く採用されるはずです。
そして、戦略の流行り廃りが生まれて、村/狼陣営の勝率は均衡解付近で振動する...
これが勝率という観点からの、自分の人狼ゲームの戦略の基本的な見方です。

この考え方にも一理あるとは思います。しかし、これは実際の人狼ゲームで高勝率を目指す姿勢とずれています。
上手いプレイヤーの方々は環境についての知見を深め、 その環境下で最適な行動を探求しているという印象を持ちました。
自分だって、(あんまりうまくいっているとは思いませんが、) 実際のゲームをするときには、村の進行パターンに適合するような勝ち筋を最初に考えます。

実際の人狼ゲームをするということは、環境に対する情報を利用するという側面は必ずある。 
そして、環境をFixしたときに有利になる戦略が"定跡"として考えられていると感じました。 

頭の体操にまた、自分も人狼に参加させていただきたいと思います。
自分たちが、上手い人たちの「定跡」に思考をめぐらし、戦略の可能性を十分に考慮することができれば、上手い人たちは別な手を編み出していくと思います。
鮮やかに騙されてばかりなのは、ちょっと悔しいところもありますが、勝敗にかかわらずエンターテイメントを感じるのが人狼ゲームの面白さだと信じています。

次はどんな風に騙されるのかなぁ? それとも、一矢報いることができるのかなぁ?
レアケースを追いすぎちゃって負けちゃうことがあってもいいよね!

なんて思いながら、また楽しませていただきたいと思います。

最後に

この機会を作ってくださったNulさんに感謝を。
色々なところで不足している部分があるのですが、これが今の自分の実力でしょうね。
それでも、今回は自分なりに全力で人狼をやったつもりです。

数年前、勝率重視進行のPLの方に対しては苦手意識がありました。
勝率探求の姿勢はどうしても、感想戦においてミスを責める姿勢ことにつながる傾向があるから。
その中で、「感想戦の穏やかさと勝利の探求は両立できる」ということを教えてくださったPLさん
という印象が一番強いです。
もちろん、強くて村中ではボコボコにされるわけで、その印象も強く残っていますー

また、同村させていただいたときはよろしくお願いします。
メタとかは関係ないですね。だって、特殊な状況じゃない限り、自分はNulさんかどうかを読むことができないので。
...自分も透けにくくなっているかもしれませんよ!!! (希望)

夏の終わりに

いや、8月はまだ夏で暑いですが...

7月の終わり、このエントリの追記は終わります。
人狼だって、このブログだって、自分勝手に気ままにやっているけれど、
誰かにとって、エンターテイメントを感じられる何かを作れたら楽しんだろうなー

でも、一番大切なのは、自分が面白いと思うことだと信じていますねー
このブログを書いているときには、色々な意味で人狼をやらせていただいている気分でした。

暑い日が続くけれど、体に気をつけていきましょうー!
おやすみなさいー 明日がいい日になりますように!

付録A.「ぬるぬるシステム」のワードの言及村について

2018年7月某日までにおいて、Nulさんが12A猫に参加した村を対象に調査を行いました。

ぬるぬるシステムってこんなもんだしね。結局は互角なんだよ。
システム使っていい? 霊噛み身内切りから入るやつ

この村では初日に身内票をしていますね。そして、「猫又CCOを入れて良い」と相方の方に提案しています

2-2型というのがあるんですね...結果的にこの村では使っていないようです。

コメントは差し控えさせていただきたいと思います。

3日目平和が出て同じところに占いから2●。そして猫又CCOが入りました。
その状況に対して、平和が出たら身内切りはしないほうが良いというコメントです。

意図が汲み取れなかったので、言及はなしです。

どうやら、最初は猫又CCOを使っていたようですが、それを使わないようにしたようですね。

「ぬるぬるシステム」が何なのか? という議論が霊界でなされています。
1人の参加者の方が 『「ぬるぬるシステムって2-1霊能噛み身内切り戦術」のことを言っているのかね』
という発言を残しています。

狼の占い騙りとして、ぬるぬるシステムを使おうとしたけれど、対抗占いに〇を出したので止めた村。
この2日目の村のNulさん(CN:世話好きミミ)さんの発言には特筆すべきものがありますね。

「ミミの真切るからミミ吊ります」じゃなくて「取り敢えず真狂主張してるから吊ります」ってなんのゲームしに来たの?って言いたくなるわ

まず、前提として、ぬるぬるシステム自体が 村の真狼重視進行という要素を利用しています。
なので、Nulさんは「統計的に2-1展開の時に真狼が多い/村が真狼読みの進行を行うことは多い」ことを熟知しており、
なおかつゲーム中にその事実を利用しているのです。
本来真狼が多い → 真狂主張しているミミさんが真とは考えにくい → 吊り
というロジックに対して、
統計的事実を使って、ゲームを進行を決めるのは面白くない という観点からの反論を行っています。

もちろん、どんな状況でも要素を拾って主張を作るのが人狼ゲームの醍醐味だと思うので、素直にうまいなーと思うのですが、
ログ収集でNulさんの発言を追ってきた身としては、
「Nulさん自身が、村の進行を(統計的に)読んで進行を考えているPLじゃないですかー」
というツッコミを入れたくなりました。

付録B. 本ブログの解析の基本的な方針

勝率の観点から「良い進行・戦略」を主張する方針として、2つあります。

  1. 他陣営の戦略を(確率分布として)Fixし、そのFixした状態で進行の勝率を算出しての比較
  2. 他陣営がどんな戦略を採用したとしても、自陣営にとって最も勝率の高くなる戦略であることを証明

1.の例は占い3COの時、「狼が3COしている」と仮定(Fix)するなら占い全吊りがよいという主張などです。(極端な例ですが)
2.の例としては、狩人の護衛指示による詰み進行です。

今回の「定跡」の良さについて、2.の方針では主張できないです。
反例を提示することは簡単で、「狼陣営が常にこの『定跡』を使う」ということが村陣営が分かっているならば、
「3日目の●は狼。そして、3日目に●を出した占い候補は狼」という前提で村陣営が進行できます。

1.からの証明が必要になるのですが、この解析には村の戦略のFixが必要になります。
Fixされた村の戦略ごとに、Nulさんの問題に対する答えが定まる。というが自分の返答になります。
(実際問題としては、戦略のFixも大変だし、Fixした後の計算も大変ですね...)

提出締め切り日の占いCO

今月は何も書けなかったですね。 Student風に言えば、締め切り間際のレポートですみませんと1枚書いて提出する感じですね。

占いCO レポート〇
月末ということでレポートを書こうと思いました。結果は〇。 純白の白!
純白ってキュート! ラブリーな漆黒ではありませんでした。
"レポートを書くという行為"、"カードをドローするという行為"、"占いを行う行為"というのは
全て"可能性を狭める"という共通点を持っています。観測は非可逆操作ですよね!
まだ、私は、自分の可能性を狭めたくない!!! だから、レポートをかけないんです!

試験のほうでも〇結果しか引けない気がしますが、結果は占ってみないと分かりません。
選択式の問題を多くしてくれると、こちらの陣営としても勝負ができそうです。

... 7月の後半週は頑張る....

(Python/Role Play) Brother's protection -Example of Descriptor-

こんにちは。StudentSです。
最近、Fluent Pythonという本を読んでいます。

自分の中の朧げな「こんなことがしてみたい」というのを楽にできるようになりたいですね。
ありがたいことに世界の方々のおかげで技術が進歩しているけれど、自分が技術を楽しんだり、面白いことを創り出すためには、自分の能力を上げなきゃいけないんだろうなと思う5月の夕方です。

人狼に参加させて感じていることは、色々なバックグランドの人がいるから面白いということです。
その人の人狼以外の趣味が、人狼のゲーム中の思考のパターンに反映されるんだろうなと感じます。

  • 論理性の強いゲームが趣味の人は始めに、詰み進行の探索や確率論に基づく最適進行を考えるだろうし。
  • 人間観察が得意な人は始めに、発言を精査するだろうし。

様々な要素があるゲームだからこそ、色々なアプローチを見ることができるんだと思います。

不器用な私は、残念ながらプレイヤーごとの思考体系を読んで戦略を立てるのが苦手ですが、 他の参加者やGMさんの考え方に触れて刺激をもらっています。

自分ができるようになりたい、と思ったことができるようになっていけば、 また、少し違った人狼プレーもできると信じて。 このブログは自分の個人的なメモだけれど、それでも定期的に書くことには何か意味があると思うのです。

話しがすごく脱線しましたが、 今月のブログの内容です。
PythonにDescriptorという仕組みがあって、今まで勘違いしていたので会話口調でメモを作成しました。
Fluent Pythonと下記のページが元ネタです。

さて、このエントリの対象者は次の人です

  • Pythonを知っている
  • 私のRole Playを楽しんでくれる慈悲深い人である

登場人物は、Pythonの課題が出された高校生。課題を解くためにChatで友達としゃべりながら取り組んでいるようです。

とある高校生の就寝前

今日もお疲れ様! わたしはまだ、Pythonの課題が終わっていないから眠れないんだ... 課題は妹を守るっていうってふざけた問題なんだけど、 たぶん、これはインスタンス属性へのアクセスをフックすることができればいいんだ。

まずさ、Pythonの基本として クラスのみんなで決めたことよりも、自分が決めたことが優先される が基本だよね。 だから、クラスの属性よりも自分が設定した属性のほうが優先されるよね。

class Player:
    description = "Player"
    def __init__(self):
        pass

if __name__ == "__main__":
    student = Player()
    assert  "description" not in student.__dict__, "attribute does not exist in __dict__"
    assert student.description ==  Player.description == "Player"

    #student.description = "StudentS"
    setattr(student, "description", "StudentS")
    assert  "description" in student.__dict__, "attribute exists in __dict__"

    del student.description

    assert  "description" not in student.__dict__, "attribute does not exist in __dict__"
    assert student.description ==  Player.description == "Player"

何もしないと、属性を自由に書き換えられてしまうんだけど、属性へのアクセスの仕方を変えることができるのがDesciptorなんだ。 今日の課題となって渡されているプログラムはこれ。

class Teacher:
    def __call__(self, girl):
        raise RuntimeError("検閲により削除")

class You:
    """ [Implement]
    """

class Imoto:
    boy_friend = You()

    def check(self):
        if isinstance(self.boy_friend, You):
            print("やっぱり、お兄ちゃんはまだ私がいないとだめだなぁ!")
        elif isinstance(self.boy_friend, Teacher):
            print("お兄ちゃんなんてどうでもいい!")
            self.act()
        elif isinstance(self.boy_friend, You):
            print("たどり着くのは難しいって分かっている。")
            print("この穏やかな関係を1回壊して、もう1回関係を作り直さなきゃいけないから。")
            print("でも、ここが、お兄ちゃんとの理想の関係。")
            print("私たちの到達点だよ!")

    def act(self):
        self.boy_friend(self)

if __name__ == "__main__":
    """ :今日の課題:
    君の妹はとても可愛い。幼いながらも知的で純粋な目を僕に向けてくれる。
    冷めた目で僕を嘲笑する君とは大違いだよ。兄弟で全然性格が違うね。
    僕は君の妹に僕だけをみていて欲しいと強く願うんだ...

    君は可愛い妹の兄だ。
    上記は職員の新年会で酒に酔ったとある教師がつぶやいていた文章である。
    教師の妹に対するアプローチをYouクラスを実装することで制限せよ。
    1. Runtime Error を発生させないようにせよ
    2. Imotoクラスcheck関数で"ここが、お兄ちゃんとの理想の関係。"が表示されるパスを通るようにせよ。
    """
    imoto = Imoto()
    teacher = Teacher() 
    imoto.boy_friend = teacher
    imoto.check()

問題設定がひどいよね。 なんで兄限定にしてんだか。 フィクションの兄妹関係に幻想もちすぎじゃないの?
pythonのコードと同じぐらいのどーでもいい文章量...この設定不要でしょ。
しかもさ、教師として教師の売春をネタにするって自虐が強すぎるし。

まぁ、文句はあるけれど1問目から考えていこうか。

大切なところは

imoto.boy_friend = Teacher()

だね。ここでimotoの属性が書き換えられちゃてる。
他のプログラミング言語だったら、アクセス修飾子をつけることで解決できるよね。
JAVAC++privateとか。恋人とかの話は簡単にするものじゃないから、privateにしておくべきだよね。
わたしに妹はいないけれど、もしいたら、"好き"とかそういう話はきっと2人っきりのときにしかしないよ。

そうそう、Descriptorの話。

class You:
    """ [Implement]
    """
    def __get__(self, obj, type=None):
        pass

    def __set__(self, obj, value):
        pass

細かいところは、Tutorial をみるとして、 get, setを定義しておけば、インスタンスの属性のアクセスの時にget関数やset関数を呼び出してくれる。

これがDescriptorの役割だね。

問題1だけならとっても簡単。 Descriptorを使ってteacherboy_friend 属性に代入するところの処理を無視するようにすればいいだけ。

class You:
    def __set__(self, obj, value):
        pass

最低限これだけ書けば、少なくとも、妹のboy_friend属性は守ることができるね。 本当は、

class You:
    def __set__(self, obj, value):
        if isinstance(value, Teacher):
            del value

ってしたいけどね! こうすれば、ふざけた教師は消せるからね。

はい。これで1問目は終わり。 次の問題がちょっと難しいね...

if isinstance(self.boy_friend, You):
    print("やっぱり、お兄ちゃんはまだ私がいないとだめだなぁ!")
elif isinstance(self.boy_friend, Teacher):
    print("お兄ちゃんなんてどうでもいい!")
    self.act()
elif isinstance(self.boy_friend, You):
    print("たどり着くのは難しいって分かっている。")
    print("1回壊して、もう1回関係を作り直さなきゃいけないから。")
    print("でも、ここが、お兄ちゃんとの理想の関係。")
    print("私たちの到達点だよ!")

このプログラムはひどいよね。 if/elifで同じ条件を使うって、馬鹿なの?
素直にこの部分を書き直せよっていいたい。すごく言いたいなー

それにさ、この問題の作成者のImotoという概念の使い方が低俗だと思うんだよね。
ちょっとした環境の変化で家族関係が変わってしまうのは考えにくいはずだよ。
兄弟の関係性にだけ焦点をあてて、ちょっと刺激的な文章を書けばいいと考えている
作者の"甘さ"が透けるんだよ。
人間どうしの関わりを観察するとき、定型的な関係を記号的にとらえるだけじゃ駄目だよね。

あ、問題の話だったねー
そうだね、君の言う通り早く宿題を終わらせるのが大事だよね。
この"歪んだ関係"を直すためには、歪んだプログラムが必要だね。
副作用、変数にアクセスする度に結果が変わる仕組みを使うしかないね。

class You:
    flag = False
    def __get__(self, obj, type=None):
        if not You.flag:
            You.flag = True
            return None
        return self

    def __set__(self, obj, value):
        pass

クラス変数を使ってこうすればできるけどさ、これはひどいよねー
こんなのdebug用にboy_friend属性にアクセスしただけでも破綻するプログラムだもんね。

歪んだ関数自体を直すという発想からは、関数自体を書き換えるという方針がありだね。
関数自体の中のパスを通すという課題からは少しずれてしまっているかもしれないけれど。
まあ、これもひどいプログラムだよ....

class You:
    def dummy_check(self, obj):
            print("たどり着くのは難しいって分かっている。")
            print("この穏やかな関係を1回壊して、もう1回関係を作り直さなきゃいけないから。")
            print("でも、ここが、お兄ちゃんとの理想の関係。")
            print("私たちの到達点だよ!")

    def __set__(self, obj, value):
        setattr(obj, "check", lambda: self.dummy_check(obj))

なんか疲れたなー
もう夜の2時か...なすごく不毛な宿題だったような気がする。
プログラムを読むより、日本語を読んでいた時間の方が長いような気がするねー
君とぼんやりでも話ながらやらないと、精神的にきつかったよ。
ごめんね、こんな時間まで付き合わせちゃって。 お互い早く寝たいよねー
ここまで付き合ってくれてありがと。
君の宿題も終わったよね。 また明日、学校でね。

今月の反省

  • RPの主人公は幼馴染の男の子とチャットしながら宿題をやっている女の子を目指しました。 ちょっと失敗感..
  • やりたいことが不明瞭になっていますね。 RPの中途半端さと、Python Descriptorの説明もどっちも中途半端なような気がします。 問題だけに焦点を当てるならもう少し短くまとめてもよかったし...
  • 文章の論旨が非常に不明確ですね。これは呪殺対応にてこずっていた偽でしょう。多分狂人。

プログラム練習ノート

こんにちは。StudentSです。
4月になりました。季節は春ですね。おひさまがぽかぽかしていて気持ちいいですね。
最近読んだ本の中にいいなぁ、と思ったフレーズがありました。
原文とは違うけれど、下記のような文章でした。

思い出って、おひさまの光に似ているね。心をあったかくしてくれる。
でも、抱きしめようとしても、重みを感じられないんだよね。

わたしの青春はいつくるんだろうなぁ? と思いながら、今日も何とか生きています...

1月に1度は何かブログをかけるようにしようと思っています。
Studentは、宿題やレポートの提出を迫られたら、例えネタがなかったとしても、
提出しなくちゃいけないんです。

最近、Pythonの練習にCheckIOというサイトで問題を解いています。
そして、上手い方々の答えを見て、勉強させてもらっています。
その中の1問に
Numbered triangles
という問題がありました。
問題の要旨としては、

  • 6枚の正三角形があり、それぞれの辺に数字がかいてある。
  • 6枚の正三角形をくっつけて正六角形を作る。ただし、くっつけることができる辺は同じ数字を書いてある辺だけ。
  • 作ることができる正六角形の辺の6つの数字(くっつけるのに使っていない数字)の和の最大値を求める。

という問題です。 この問題に恋に恋する人狼PLらしく、アタックしてみたいと思います。

パズルを解いてみる

それじゃあ、考えていこー よろしくね!
パズルも人狼も初手考えることは、全探索 だよね。
人狼なら自分以外を死亡させたら、基本的には自分の勝ちだし、
パズルだって、全てのケースを調べたら、問題は解ける。
吊り数や噛みの回数に余裕があったり、計算量的に可能なパズルだったら、まずそれだよね! このパズルを全探索するなら、1つの三角形の位置だけ固定させて、他の順番と向きを考えて... 考えなきゃいけないパターンは \( 5! \times (3!)^5 \times 3 \) になるかな?

factorial = lambda n: (lambda g,i:g(g, i))(lambda f, a: a if a <= 1 else f(f, a -1) * a, n)

簡単に階乗関数を使って計算してみると933120。これなら、全探索できそうだよね。
ちょっと効率の悪いスクリプトでも押し切れそうだね! ということで、愚直やってみよー!
クラスの女の子も、素直な男の子の方が扱いやすくて可愛いって言ってたしね!

def checkio(chips_input):
    pass

chips_inputにリストのリストで入力が入ってくるから、まずは6枚の三角形の並びを決めた時、スコアを計算する関数を書くね!

def calc_score(chips):
    """
    chip[i][0]: the length of hexagonal.
    chip[i][1]: counterclock's direction.
    chip[i][2]: anti-counterclock's direction.
    """
    #  chips[0][2] == chips[1][1] == ... chips[5][2] == chips[0][1]
    legal = all(chips[i][2] == chips[(i+1) % 6][1] for i in range(6))
    number_sum = sum(chip[0] for chip in chips)
    return legal * number_sum

とりあえず、まずはこんな感じかな? ルール違反の時は0にすればいいもんね。
ルールがはっきりすれば、後は全部のパターンを作るだけ。
「運命の人を見つけたら、後は愛するだけでいい」って言っていたのは誰だっけ?
授業で聞いた気がするんだけど、忘れちゃったなぁ。
好きな人をずっと好きでいるのが難しいのと同じで、やることが分かっていても実際に行うのは大変だよね。
ちょっと頑張ると、下みたいな感じでいいかな?

from itertools import permutations, product, chain
def checkio(chips_input):
    fixed = chips_input[0] # fixed triangle.
    chips = chips_input[1:] # triangles to be permutated.

    # For fixed triangle, only the length of hexagonal is fixed. (3, not3!.)
    fixed_patterns = [[fixed[li]] + [fixed[i] for i in range(len(fixed)) if i != li] for li in range(len(fixed))]
    concatenate_patterns = lambda chips: list(map(lambda fixed: [fixed] + chips, fixed_patterns))

    ps = map(lambda p:permutations(p), chips)  # Triangle Direction's patterns.  (3!)
    s = product(*ps)  # The group of triangles (whose direction's are not considered.)
    cand = map(permutations, s)  # Consider the order of triangles.
    # Generate the all patterns and check the maximum.
    result = 0
    for q in chain.from_iterable(cand):
        cands = concatenate_patterns(map(list, q))
        result = max(result, max(map(calc_score, cands)))
    return result

これで提出してみると、Task Solved!
でも、もう少し面白い感じにしたいよね...
このコードはエンターテイメントがないんだよね。
驚きやときめきあがないように感じる...
ボクなりにちょっと頑張るね!

from itertools import permutations, product, chain
checkio = lambda ci: max(max(map(lambda chs: all(chs[i][2] == chs[(i+1) % 6][1] for i in range(6)) * sum(ch[0] for ch in chs),
                            ([elem] + list(map(list, q)) for elem in ([ci[0][li]] + [ci[0][i] for i in range(3) if i != li] for li in range(3)))))
                         for q in chain.from_iterable(map(permutations, product(*map(permutations, ci[1:])))))

これが、ボクの答えだ! いつか運命の人に出会えることを祈った4-liner!

  • 2行目は答えを出す部分
  • 3行目は固定する三角形と他の三角形をくっつけるのを考える部分
  • 4行目は三角形の置き方を全て列挙する部分

これでどうかなぁ...ときめきがあるかな?
ちょっと細かいところで変なところもあるけれど、わくわくするようなそんなプログラムだといいなぁ。
少しずつ戸惑うかもしれないけれど、一緒にいて楽しい思い出をたくさん作れる...そんな素敵な人に出会いたいな。

速度改善について

これで正解はできたけれど...遅いんだよね...
短期村の人狼ゲームで人外の騙りとして大切なのは質の高い占い文を短時間で作り上げること
...速度が重要になるんだよね。  
遅い理由は、permutationsで、ルールに一致しない例を大量に作っているから。
permutationsで、例を作る時にルールに一致するかをチェックすれば、早くなるはず。
ということで、その関数を作ろうー!

from itertools import permutations, product, chain

predicator = lambda l, n: not l or l[2] == n[1]
def mperm(cands):
    def _inner(indices):
        if len(cands) == len(indices):
            yield tuple(cands[i] for i in indices)
        for i in range(len(cands)):
            if i not in indices and predicator(tuple(cands[indices[-1]]), tuple(cands[i])): 
                yield from _inner(indices + [i])
    for i in range(len(cands)):
        yield from _inner([i])


checkio = lambda ci: max(max(map(lambda chs: all(chs[i][2] == chs[(i+1) % 6][1] for i in range(6)) * sum(ch[0] for ch in chs),
                            ([elem] + list(map(list, q)) for elem in ([ci[0][li]] + [ci[0][i] for i in range(3) if i != li] for li in range(3)))))
                         for q in chain.from_iterable(map(mperm, product(*map(permutations, ci[1:])))))

ということで、itertools.permutations もどきを作って見たよ。
そして、要素を追加する時に、ルールをチェックするようにしたけれど、不格好だよね...
それに最悪計算量は変わらないし...
うーん、人狼も恋もプログラムも難しいね!

終わりに

ここまで読んでくれた人、ありがとー!
全探索の問題ってさ、人狼の詰み進行の時みたいに何も考えずにやっていくだけで頭をつかうことって少ないんだよね。
でも、人狼ゲームの結果が決まっているとしても、その中の過程がつまらないものになるか、面白いものになるかはプレイヤーたちの腕で決まると思っているよ!
このパズルの問題は全探索の問題だけど、その中の過程でなにかのときめきを作ることができていたら、ボクは嬉しいな!
また会うときがあったらよろしくね!

補足と反省

  • まずは問題作成者の方に感謝を。他の問題も含めてよい練習をさせていただいております。

  • 回答編にはすごい人達のコードがあるので、興味のある方は読まれることをおすすめします。先駆者の方々のコードをみたら、類似のアイデアはすでに上がっています。

  • 上記の回答は自分の中でかなり苦労して書いています...最初に正解にたどりついたコードは... (ときめきなんて、全然なかったよ!)

  • RPとネタのはさみ方が人狼と恋愛関係で半分ずつぐらいになっていて統一性がないなぁ。次はもう少し統一性を持たせないかな?

  • RPのキャラクターの幅をもう少し広くすることは今後の課題です。

What scenery did you see in villages, this Winter?

はじめに

視点整理, 情報整理人狼をする上では大切」と説く、人狼の熟練者の方がいました。
情報の整理、確かに重要な場面は多いと感じます。
詰み進行をみつけたり、他のPLの発言に応酬するために、情報整理は必須なのでしょう。

今回は、るる鯖の情報整理をやってみました。
間違いが含まれているかもしれませんが、ご了承ください。
指摘してくださるのはありがたいですが、だからといって処刑対象にするのは止めてください。

「情報の把握漏れ・把握ミスをするのは狂人の占い騙りだから? 違うよ! それはただ単に情報整理ができていないPLというだけ! PLとしての、実力の無さを露呈させているだけど、自分は真占いだよ!」

サマリー

「時間の無いゲーム中に、朝一の長文は読まれないから、簡潔に主張をまとめた方がいい」と人狼の熟練者が語っていました。
アドバイスに従って、簡潔に要点をまとめると下のようになります。

2017年 冬, るる鯖普通村12A猫の傾向について

  • るる鯖の村は半分近くが身内村
  • 普通村の中で12A猫の割合は12%程度
  • 期間中のGMさんの数は約50人。しかし、村の大半は少数のGMさんによって建てていただいている
  • 週末, 20時台 ~ 22時台の村が多い
  • 半分の12A猫の普通村は8分以内に始まる
  • ゲーム時間の平均は約35分

以上です。 呪殺が発生した時、偽占いかどうかを見極めるために占い文を精査する方は、下記の詳細をご覧ください。

調査対象

調査対象は以下の通り。

普通村と身内村の割合

はじめに、身内村と普通村の比率を調べます。 f:id:StudentS:20180316213039p:plain

普通村の方が多いと思っていたのですが、身内村の方が多かったです。
半分以上が身内村だったんですね...
以降、この4772村の普通村について見ていきます。

普通村配役の割合

普通村の中で一番遊ばれている配役を集計した結果が下になります。

f:id:StudentS:20180316212740p:plain

詳細テーブル

配役_0 村数_0 配役_1 村数_1 配役_2 村数_2 配役_3 村数_3 配役_4 村数_4
12B 854 19D猫 41 20Z 5 4A猫 2 7C猫 1
11A 741 13Z 38 17Z猫 5 8C猫 2 11D猫 1
12A猫 559 6B 37 10C猫 5 4C猫 1 30C猫 1
7C 425 5Z 31 4C 4 22Z猫 1 5A猫 1
8Z 249 5A 28 10Z猫 4 12C 1 7A 1
8C 190 17Z 28 22C猫 3 11D 1
17A 158 16A 26 5B 3 15A猫 1
4A 149 6C 22 9A 3 15C猫 1
18A猫 139 18Z猫 18 18D猫 3 6C猫 1
10Z 128 18Z 15 20Z猫 3 21D 1
6Z 110 19Z 12 22Z 3 15D猫 1
4Z 100 5C 10 17A猫 3 21C猫 1
12Z 81 9D 9 21Z 3 23D猫 1
7Z 78 14Z猫 9 12A 3 13C猫 1
11Z 65 6A 8 16B猫 2 18C猫 1
9Z 65 19Z猫 8 19C猫 2 8A 1
14Z 65 16Z猫 7 8B 2 11B 1
16Z 62 13Z猫 6 20C猫 2 15B猫 1
14D猫 57 12Z猫 6 23Z 2 8Z猫 1
15Z 44 11Z猫 5 21Z猫 2 23Z猫 1

コメント

  • 11Aよりも12Bの方が多いことは意外
  • 19D猫や18A猫は面白い配役だと思います! (呪殺対応が難しいけれど...)

以降、12A猫村の559村についてみていきます

GMさんと村数の関係性について

多くの村をGMさんが建ててくださっています。 最初に、GMさんと村数の関係をみます。

f:id:StudentS:20180317214241p:plain (仮GMさんの村はGMなしとして集計した)

統計値 村数
最大値 90
平均値 10.75
標準偏差 19.41
中央値 2.5
人数 52

コメント

グラフの横軸がGMさんが建ててくださった村の数。縦軸がGMさんの数です。
少数のGMさんが多くの村を建ててくださっていることが明確に分かります。
もう少し詳しくみると、村建て数が上位4人のGMさんだけで半分近くの村を建ててくださっているということが分かりました。
一番多いGMさんの村建て数は90村。1/6の村が1人のGMさんによって建られていたのですね....

GMさんの人数は52人。 私、StudentSも含め多くの方は一番左側の少ない村建て数のGMグループに属しますね。

村建てから開始までの時間

村が立ってからゲームが開始されるまでの時間を集計しました。 ログに「ゲーム開始時間」ということで、分単位で表示がされますが、ここでは秒単位で時間を取得したかったので、

  • [村建て開始時間] GMとして入村したメッセージが表示された時刻
  • [ゲーム開始時間] 「1日目の夜になりました。」というメッセージが表示された時刻

として、集計しました。

f:id:StudentS:20180318174742p:plain

統計値 秒数
最小値 30.0
最大値 6196.0
平均値 877.43
標準偏差 998.69
中央値 475.0

コメント

最も早く始まった村はなんと30秒!
* No.442024「12A猫番街」村
点呼すら不要の村だったようです。

一方、最も時間のかかった村は100分以上!
* No.447586「お気楽のんびり12A猫」村
時間帯によって、人の集まり方が違うというのをあらためて感じます。

中央値が475秒なので、半分の村は8分以内に始まるということですね。

ゲーム時間について

次に、ゲーム時間についてみます。 こちらも秒数単位で集計したかったので、時刻を使って集計しました。

f:id:StudentS:20180318175917p:plain

統計値 秒数
最小値 781.0
最大値 3261.0
平均値 2127.08
標準偏差 435.42
中央値 2155.0

コメント

正規分布に近い分布ですね。 最も時間が早い村は781秒(約13分)です。
* No.442020「12A猫番街」村
想像できると思いますが、1日目のグレランで引き分けになっています。 次に早い村は883秒(約15分)です。
* No.441663「12A猫が好きなGMの」村
これも、引き分け...だと予想していたのですが、村勝ちでした。
狼吊り → 猫噛み → 狼指定という、3Wが最短で死亡する進行でした。

逆に最も時間がかかった村は3261秒(約54分)
* No.439697「雨の日の12A猫」村
狩人の平和が2回あり、7日目まで続いていました。

そして、多くのゲームは35分 - 40分前後で終了するということが分かります。

村が建てられる時間帯

12A猫の普通村が建てられる時間帯を集計しました。 f:id:StudentS:20180318204334p:plain

詳細テーブル

時間帯 月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日曜日
0時台 4 7 5 8 6 8 6
1時台 1 1 0 2 1 2 2
2時台 0 0 1 0 0 0 0
3時台 0 0 0 0 0 0 0
4時台 0 0 0 0 0 0 0
5時台 0 0 0 0 0 0 0
6時台 0 0 0 0 0 0 0
7時台 0 0 0 0 0 0 0
8時台 0 0 0 0 0 0 0
9時台 0 0 0 0 0 0 0
10時台 0 0 0 0 0 1 2
11時台 0 1 0 0 1 5 2
12時台 0 1 0 0 1 4 6
13時台 0 0 0 0 0 2 2
14時台 0 0 0 0 0 2 4
15時台 0 0 0 0 0 3 7
16時台 0 0 0 1 0 4 7
17時台 0 0 0 0 0 3 9
18時台 0 0 0 0 1 4 9
19時台 0 0 0 3 4 6 13
20時台 6 4 8 14 11 14 12
21時台 11 12 11 12 17 19 14
22時台 17 21 18 16 21 20 15
23時台 18 15 19 13 16 13 10

コメント

  • 週末, 20時 ~ 22時台の村が多い
  • 朝、9時以前に村が建つことはほとんどない
  • 深夜、2時以降に村が建つことはほとんどない

使用CNセットについて

f:id:StudentS:20180318182708p:plain

詳細テーブル

CNセット_0 村数_0 CNセット_1 村数_1 CNセット_2 村数_2
全て 110 かくりよ 18 Q.E.D. 2
花畑 69 AAイラスト 15
Cathedral 53 シルエット 15
ジランドール 37 azuma 13
壱番街 36 あひる小屋 12
ランダムCNなし 33 メトロポリス 11
orange 29 ハロリンカ 10
わんだフル 29 トロイカ 10
あやかし 22 mtmt 8
shirone 21 KRNN 6

コメント

  • 全てが最も多い
  • 花畑とCathedralが2強
  • ランダムCNなしが意外と多い

おわりに

"ゲームの時間"を中心として集計してみました。
個人的に特に印象に残っていることは

  • GMさんが50人以上いること
  • 1ゲーム辺りの時間が30 ~ 40分程度であること
  • 10分以内に半分の村が始まること

ですね。特にGMさんの数が想像より多かったです。