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

...What are you learning now?

Mouseless 12A -Playing only with keyboard-

はじめに

こんにちは、StudentSです。 自分は人狼を遊ばせていただく時、PCを使っています。
最近、現実世界で、キーボードだけでPCを操作する必要がある 状況になっています。
ふと、るる鯖で遊ぶ時に、マウスを極力使わないためにはどうすればいいかを考えました。
自分のPC環境はWindows7です。

サマリー

  • VIMを使って、キーボードだけで文章を入力できるようにする
  • Google Chrome / Vimium を使って、ブラウザ操作をキーボードで出来るようにする
  • Windows ショートカットの利用

想定環境

文章の入力について

テキスト・エディター

兎に角、これがなければはじまりません。 自分は人狼をするときには、sakuraエディタを使わせていただいていました。

好きなソフトです。 しかし、キーボードだけで文章を作るには、ちょっと厳しい。 キーボードで操作するといったら、 やはり...vim だろうということで使うことにします。 Emacs等でも出来ると思います。

Vimを配布してくださっているサイト https://www.kaoriya.net/からダウンロードさせていただき、使える状態まで学習していきます。

ブラウザ操作

  • ブラウザから、更新ボタンをクリックする必要がある
  • ブラウザをスクロールする必要がある
  • COの時などにボタンを押す必要がある

要するに、ブラウザの基本操作を出来る限りキーボードで操作することが必要と。

https://qiita.com/satoshi03/items/9fdfcd0e46e095ec68c1

上記サイト等の設定を参考にさせていただき、インストールします。 ChromeのショートカットキーボードとVimniumを使って、 ブラウザ操作が出来る状態まで学習していきます。

Windowsショートカット

さて、最後にテキスト入力とブラウザ操作の切り替えを素早くするためのWindowsショートカットを習得すればいいですね。

必須のショートカット

  • ALT + TAB: 開いているTabを切り替える
    • これでVimChromeを行ったりきたり
  • Windowsロゴキー + 左(右)方向キー: 画面の左側(右側)のウィンドウを最大化する
    • これでVimChromeを並べることが簡単に

残りは自分のPC操作に役立ちそうなものを少しずつ試してみるという感じでしょうか。

終わりに

さぁ、これで準備は整いました。 マウスを使わずに、るる鯖で遊ぶことができるはず!

...実際に遊んでみました。 確かに、マウスを使う頻度は半減しました。 しかし、朝の15秒の間に貼る文章を張り替えるまで、熟れていないです...
(マウス有りでも自分は苦手なことですが...)

あと、焦るとマウスに手が伸びる自分がいます。

「まずい...狩人を●にしてしまった...」
(狂人の時)「まずい...これは潜伏LWに誤爆だ...」

などとなって焦るとマウスに手が伸びてしまう傾向があります。 もう少し練習していきたいと思います。

雑多な話

音声入力...

音声入力は1つの作戦だと思いました。 Skype等で人狼を行う方がいらっしゃるので 1つの良い戦略だと思います。しかし、自分には厳しいそうです。 音声から文章への変換精度 という技術的な問題もあるのですが、それ以上に、自分の能力的な問題の方が大きいです。 話しことばをそのまま文字にしても、非常に読みにくい文章なってしまいます。

また、そもそもの話としてです、 音声入力は曲がりなりにも、書く内容を話さなくてはいけないわけです。 例えば No.296936 「今宵も12A猫」 の猫又CO (ちょっと改変)

猫又CO 「あたしはねこにゃーん」って、この年になるとちょっと恥ずかしくて言えないのよね。 30が少し見え始めたこの年に猫かぶって、媚びるような女に私はなりたくないんだよ。 若さが全てだとは思わないけどさ、やっぱり男達は若い子に惹かれやすいのはわかってる。 ・・・悔しいね。でも、プライドと自信は・・・まだ・・・なくさないよ。 こんな私が猫又です。

... 少し読むのは気恥ずかしいなぁ (そもそも、こんなことを書くなよとは言わないで...)

Vimの日本語入力

今の自分ではまだちょっと難しい。 半角全角の切り替えになかなか慣れませんね...

特に厳しいのが、

あ、12A猫なのに、狼が4匹いることになっちまったZE!
H☆A☆T☆A☆N しちまったYo!

などのように、半角・全角を織り交ぜて文章を打つときです。
ちょっと煩わしさを感じます。 日本に存在しているVimの修羅の方々は、様々な工夫をしておられるようです。 自分も少しずつ自分に合うやり方を見つけていくことが必要ですねー

最近、色々と無理やり感がありますが、何とかやっております。 3月がいい1ヶ月になりますように!

Werewolves' Zen of Python

こんにちは、StudentSです。 先月は恋愛的Python Zenを考えました。

Zen of Python by a maiden - 12A猫で学んだこと-Memoir-

今月は人狼Python Zenを考えてみました。
あ、先月みたいに17歳の高校生みたいな口調はやめます。
かなり苦しいですが、掲載します。

Werewolves' Zen of Python

Beautiful is better than ugly.

発言は読みやすくしましょう。

Explicit is better than implicit.

人外候補への誘導ははっきりとしましょう。

Simple is better than complex. Complex is better than complicated.

狼の戦略は単純な方がいい。
複雑な戦略を採用する時は、1日で全てを実行するものではなく、
全日に渡って、単純な戦略を絡み合わせて遂行できるものにしよう。

Flat is better than nested.

大量のアンカーの応酬は見にくい。

Sparse is better than dense.

朝一で長文を投下するのではなくて、発言を散逸させよう。

Readability counts.

他のプレイヤーの役職を読もう。

Special cases aren't special enough to break the rules. Although practicality beats purity.

奇策は貴方が思うほど珍しくはない。
そして、作戦は美しさや一貫性よりも、実用性で評価されるべき。

Errors should never pass silently. Unless explicitly silenced.

他のプレイヤーのミスや把握漏れは、明確に指摘しよう。
そのプレイヤーが確定村陣営でない限りはね。

In the face of ambiguity, refuse the temptation to guess.

確定情報を手にいれることができない時、 推測を確定情報と同一視することは避けたほうがよい。

There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch.

占い師はたった1人。でもGMじゃないと、誰が占い師か最初はわからない。

Now is better than never. Although never is often better than right now.

村認定出来ないPLは早く処刑した方がいい。しかし、思考停止で即時処刑するよりも
狐や狼の数などを考えて、絶対に処刑しないほうが良いことも多い。

If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea.

勝率を高めるためには、人狼の戦略は単純な方がいい。
仲間の狼に説明しにくい戦略は高勝率を達成できない 。
仲間の狼に説明しやすい戦略は、いい戦略の必要条件を満たしている。

Namespaces are one honking great idea -- let's do more of those!

呪殺対応の時に、名前の情報を使うのはいいアイデアだね。

苦しいですねー ... 寒い日が続きますが、お体に気をつけて!

Zen of Python by a maiden

はじめに

お久しぶりです。StudentSです。寒い時期になってきましたねー

自分は12Aの村に参加させていただく時、 全く違った分野の話題を人狼の言葉に置き換えることがあります。 ランダムCNで与えられた名前と人狼の話題と結びつけるのは面白いです。

さて、今回はそれと似たことを他の分野でやってみるーという軽いノリのエントリです。

"Zen of Python" というものがあります。

PEP 20 -- The Zen of Python | Python.org

Pythonというプログラミング言語を使う時に大切な考え方をまとめた項目です。
Pythonの実行環境がある方は、

import this 

と入力することで、読むことができます。

Zen of Python by Love Maiden

この手の話をするときはちょっと堅さは捨てたほうがよさそうだねー だから、ちょっと口調をかえるね。 拙いけれど、私が訳してみたZen of Python. ちょっとでもくすりと笑ってくれるとうれしいな!

Beautiful is better than ugly.

綺麗な女の子の方が好きだよね?

Explicit is better than implicit.

好きっていうのは、はっきり口にしないと伝わらないよ。

Simple is better than complex. Complex is better than complicated.

恋は直球勝負。 「人の心は複雑だから分かりあえない」なんて言わないで。
色々あると思うけれど、君の事が知りたいんだよ。

Flat is better than nested.

君と同じところに立ちたいんだ。

Sparse is better than dense.

たまーに会えるこの時間が大切なんだよ。

Readability counts.

不機嫌になると、すぐ顔にでる所も可愛いと思うよ。

Special cases aren't special enough to break the rules. Although practicality beats purity.

君と一緒に世界を広げていくことは、特別な事じゃなくなった。
私の心は綺麗なだけじゃないけれど、これからも一緒に楽しい思い出を作ろうね。

Errors should never pass silently. Unless explicitly silenced.

言ってよ。何か言いたいことがあるならちゃんと。
でも、静かに横にいて欲しいときもあるんだけどね。

In the face of ambiguity, refuse the temptation to guess.

私のことを勝手に分かったつもりにならないで。

There should be one-- and preferably only one --obvious way to do it. Although that way may not be obvious at first unless you're Dutch.

たった1つの大切なこと。はっきりしてるよ。
最初は全然こんな風になるなんて思わなかったけどね。

Now is better than never. Although never is often better than right now.

目先のことに目を奪われちゃいけないけれど、
私にとっては"今"が大切なの。

If the implementation is hard to explain, it's a bad idea. If the implementation is easy to explain, it may be a good idea.

恋って単純な方がいいんだよね。きっと。

Namespaces are one honking great idea -- let's do more of those!

君ともっと名前で呼び合いたいな。ねぇ、もっと私の名前を呼んでよ。

ひどいのもいくつか混じっているね...でも、まぁ許して。

Ask forgiveness not permission.

好きになってもいいなんて聞かないで。好きになってから考えてよ。

って、誰かも言ってたしー 正直さ、幼馴染のあいつのこともPythonもまだ良く分かっていないんだけど、 少しずつでいいから、知りたいな。

終わりに

こんな感じでいいのでしょうか。良くない気がしますね...
RPの雑さや無理や原文をかなり無視した意訳。
これが今の自分の力ですか...うーん、厳しい。

まぁ、何であってもやってみないと駄目なんでしょうね。

  • たとえ吊られても話さないと人狼ゲームをやっている感じがしない。
  • プログラミング言語は何か書いてみないと始まらない。
  • 恋愛関係が全てとは思わないけど、他人との関係を意識しないと、生きる世界が狭くなってしまう

人から見れば、簡単で単純なことかもしれないけれど、自分ができるようになりたいと願い、達成できたという経験...
そういうものを大切にしながら、来年も過ごせたらなぁと思っています。

とりあえず、少し風邪気味のこの時期、自分はゆっくり休もうと思います。

寒い日が続くのでどうか皆様ご自愛ください... 忙しない時期ですが、どうかお体に気をつけて。

Werewolves’ Puzzle Auto-Generator 人狼パズル自動生成[一応完成版]

はじめに

Happy Halloween!
こんにちは、StudentSです。 先月から作ってみた人狼パズル自動生成の仕組みが一応出来ました。 大体、やってみたいことはできたかなぁと思っています。 ソースコードGithub の方にアップロードしています。

このエントリのまとめ

使い方

Githubに書いている通りですが、

python puzzle_generator.py -lang jp

で問題が生成されます。

プレイヤーの数を指定したいときは、-v, -w, -lのオプションで指定できます。

日本語で出力したいときは、 -lang jpを付記します。

反復アルゴリズムで問題を生成しています。 一定回数の反復で問題が生成できなかったときは、 例外が発生するのでご容赦ください。(RuntimeException)

詳しいことは、

python puzzle_generator.py -h 

でみることが出来ます。

主な改善点

  • 狂人の役職を追加
  • 出力される問題の傾向の変更が可能になった

ルール

  • 人狼のプレイヤーを探し出すことが目的
  • プレイヤーは他のプレイヤーについて、狼かどうかを主張する。
  • 村陣営は決して嘘をつかない。
  • 狼と狂人は、嘘をつくかもしれない。

生成される問題と答えの例

いくつか問題を掲載します。

問題1 (初級編)

  • 内訳:村陣営/狼/狂=3/1/1, PL:A-E

各PLの主張

  • Aの主張:B○,D○,E○
  • Bの主張:E●
  • Cの主張:E●
  • Dの主張:E○

解答

  • 狼:C

問題2 (中級編)

  • 内訳:村陣営/狼/狂=4/1/2, PL:A-G

    各PLの主張

  • Aの主張:B○,C●,D○,E○,G○
  • Bの主張:C○,E○,F○
  • Cの主張:A○,F○,G○
  • Dの主張:B●,E○,F○
  • Eの主張:B○,C●,D○
  • Fの主張:C○,E○
  • Gの主張:D○,F○

    解答

  • 狼:B

問題3 (上級編)

  • 内訳:村陣営/狼/狂=5/1/3, PL:A-I

    各PLの主張

  • Aの主張:C○,E○,F●,G○,H○
  • Bの主張:A○,E○,G○,H○
  • Cの主張:B○,D○,G●,H○
  • Dの主張:A○,F●,G○,H○
  • Eの主張:A○,B○,F○
  • Fの主張:B○,C●
  • Gの主張:C○,I○
  • Hの主張:E○,I○
  • Iの主張:G●

    解答

  • 狼:F

感想

  • 実際の村では大切な結果を出す時系列の情報が抜け落ちている。
  • 自分で解いてみると、想像より問題が難しかった。
  • 自分自身の「作れた」という経験は大切にしたい...

Werewolves’ Puzzle Auto-Generator 人狼パズル自動生成[プロトタイプ]

このエントリのまとめ

  • 人狼を題材にしたにパズルを考えます。
  • パズルの問題と解答の自動生成をやってみました。

問題

  • 村陣営は決して嘘をつかない。
  • 狼は嘘をつくかもしれない。 さて、狼のPLは誰?

内訳:村陣営/狼=4/2, Players: A~F

各PLの主張

  • Aの主張:C●,F●
  • Bの主張:D○,E●
  • Cの主張:E●

凡例

  • Aは、CとFを狼だと主張している。
  • Bは、Dを狼ではないと主張し、Eを狼だと主張している。
  • Cは、Eを狼だと主張している。

解答

Wolves=A,E (狼はAとE)

解説

  • Aが狼陣営の証明

Aが村陣営だと仮定すると、 狼はC, Fになる。自動的にB・Eが村陣営となるけれど、BがEが狼と主張しているから矛盾。 だから、Aは嘘をついている。狼。

  • Eが狼陣営の証明

Eが村陣営だと仮定すると、B, Cが狼になってしまうけれど、 それだと狼数が3になってしまうので、あり得ない。 従って、Eは狼。

こんなパズルを自動で作る仕組みができないかなぁ...と思って、 まずは試しにプロトタイプを作っています。 「解説」を作ることは難しいけれど、それ以外の部分は以外と単純な仕組みで出来ないかなぁ... と思ってやってみました。

自動生成した数問の問題は以下のようになります。

問題1

内訳:村陣営/狼=4/2, PL:A~F

各PLの主張

  • Aの主張:B●,D○
  • Bの主張:E○,F○
  • Cの主張:A●,F○
  • Dの主張:F●

解答

Wolves=A,D

解説

  • Aが狼の証明 Aが村だと仮定すると、B・Cが狼となる。 しかし、DがFが狼と主張しているから矛盾。

  • Dが狼の証明 Dが村だと仮定すると、A・Fが狼となる。 しかし、Bが嘘をついていることになるので矛盾。

問題2

内訳:村陣営/狼=4/2, PL:A~F

各PLの主張

  • Aの主張:C○,F●
  • Bの主張:C●
  • Cの主張:B●
  • Dの主張:A○
  • Eの主張:B●

解答

  • Wolves=B,F

問題3

内訳:村陣営/狼=5/3, PL:A~H

各PLの主張

  • Aの主張:B●,F●
  • Bの主張:D●,G●
  • Cの主張:E○
  • Dの主張:A○
  • Eの主張:H●

解答

  • Wolves=B,F,H

解説

  • Bが村だと仮定すると、A・D・Gが狼となる。しかし、Eの主張より矛盾。
  • Fが村だと仮定すると、B・A・Dが狼となる。しかし、Eの主張より矛盾。
  • Hが村だと仮定すると、B・F・Eが狼となる。しかし、Cの結果より矛盾。

こんな感じです。

自分で解いていないですが... 次のような問題も作ってくれました。 (合っているか自信がない....)

問題4

内訳:村陣営/狼=10/5, PL:A~O

各PLの主張

  • Aの主張:F●,L●
  • Bの主張:G○,O●
  • Cの主張:G○
  • Dの主張:O○
  • Eの主張:F●
  • Fの主張:I○
  • Gの主張:K●
  • Hの主張:J●
  • Iの主張:J○
  • Jの主張:L●
  • Kの主張:I●
  • Lの主張:N○

解答

Wolves=B,F,H,K,L

やりたいことや希望....

  • リファクタ(ソースコードの整理)はしたいなぁ...

  • 狂人が入っている内訳は作りたいなぁ...

  • 120%自己満足で作っていますが、何かコメントを頂けると嬉しいなぁ...

おまけ

恥ずかしさを感じつつ、ソースコードを貼付けておきたいと思います。 全然整理ができていないので、コメント等々おかしい箇所が多いですね... 今年の秋 ~ 冬にかけて、自分の力量と相談しながら、満足のいく所までやってみたいなー と思っています。

""" Create Werewolve's Puzzle.  

"""

wolf_number = 5
village_number = 10

import os, glob
from itertools import combinations
import random
import numpy as np
from collections import namedtuple

class FResult(object):
    def __init__(self):
        pass
    @classmethod
    def get_id(cls):
        raise SyntaxError("Please Implement.")

class White_Result(FResult):
    def __init__(self):
        pass
    @classmethod
    def get_id(cls):
        return "white"

class Black_Result(FResult):
    def __init__(self):
        pass
    @classmethod
    def get_id(cls):
        return "black"



class Player(object):
    """ Abstract class for players.
    """
    def __init__(self, index):
        self.result = dict()
        self.index = index
        self.result[index] = White_Result.get_id()
    
    @classmethod
    def get_name(cls):
        raise SyntaxError("Please implement this function.")
    
    def add_result(self, index, result_name):
        self.result[index] = result_name

    def delete_result(self, target_index):  
        assert target_index in self.result
        assert target_index != self.index 

        del self.result[target_index]

    def refer_result(self):
        return self.result

    def has_result(self):
        return len(self.result) > 1

    def display_index_result(self, lang="en"):
        def _index_to_alphabet(number):
            return chr(ord("A") + number)
        indices = sorted(self.result.keys())
        indices = [index for index in indices if index != self.index]
        result_list = list()
        for index in indices:
            alphabet = _index_to_alphabet(index)
            result_id = self.result[index]
            if result_id == White_Result.get_id():
                result_list.append("{0}{1}".format(alphabet, "○"))
            elif result_id == Black_Result.get_id():
                result_list.append("{0}{1}".format(alphabet, "●"))
            else:
                raise ValueError(result_id)
        my_alphabet = _index_to_alphabet(self.index)

        if lang == "en":
            claim = "{0}'s claim".format(my_alphabet)
        elif lang == "jp":
            claim = "{0}の主張".format(my_alphabet)

        return "{0}:".format(claim) + ",".join(result_list)

    def replace_result_ids(self, replace_map):
        """ Replace the result and own ids.

        :param replace_map: prev_id: next_id.
        """
        self.index = replace_map[self.index]
        revised_result = dict()
        for prev_id, result_id in self.result.items():
            next_id = replace_map[prev_id]
            revised_result[next_id] = result_id
        self.result = revised_result
        self.result[self.index] = White_Result.get_id()
        

class Wolf(Player):
    """ Wolf.
    """
    def __init__(self, index):
        super(Wolf, self)
        self.result = dict()
        self.result[index] = White_Result.get_id()
        self.index = index

    def get_name(self):
        return "wolf"
    pass

class Forseener(Player):
    """ Forseener.
    """
    def __init__(self, index):
        super(Forseener, self)
        self.result = dict()
        self.result[index] = White_Result.get_id()
        self.index = index

    @classmethod
    def get_name(cls):
        return "forseener"

def generate_answer(f, w):
    """ Generate the answer.

    :param f: the number of forseener. 
    :param w: the number of wolf. 
    :return: the list of Players.  
    """
    answer_list = list()
    answer_list += [Forseener(index) for index in range(f)] 
    answer_list += [Wolf(len(answer_list) + index ) for index in range(w)] 
    return answer_list


def is_result_coherent(index_to_person, forseener_indices, wolf_indices):
    def _coherent_check(p_result_dict): 
        for index_key, name_value in p_result_dict.items():
            if index_key in wolf_indices:
                if name_value != Black_Result.get_id():
                    return False
            elif index_key in forseener_indices:
                if name_value != White_Result.get_id():
                    return False
            else:
                raise ValueError("Invalid", index_key)
        return True


    assert len(forseener_indices) == village_number
    assert len(wolf_indices) == wolf_number

    for f_index in forseener_indices:
        f_result = index_to_person[f_index].result
        if _coherent_check(f_result) is False:
            return False
    return True

def get_coherent_cases(index_to_person):
    """ Return the cases where the results are coherent.

    :param index_person: :obj:`dict`. 
    :return :obj:`list`: whose elem is :obj:`dict`.
                - refer to the **wolf_indices** and *forseener_indices*.
    """

    coherent_ret_list = list()
    indices = list(range(wolf_number + village_number))
    for candidate in combinations(indices, wolf_number):
        wolf_indices = [index for index in indices if index in candidate]
        forseener_indices = [index for index in indices if index not in candidate]

        if is_result_coherent(index_to_person, forseener_indices, wolf_indices):
            row = dict()
            row["wolf_indices"] = wolf_indices
            row["forseener_indices"] = forseener_indices
            coherent_ret_list.append(row)
    return coherent_ret_list

def random_add_result(index_to_person):
    """ Strategy to restrict the possibility. 
    """
    from_target_indices = [index for index, person in index_to_person.items()
                          if len(person.result) < wolf_number + village_number]
    from_index = random.choice(from_target_indices)
    from_person = index_to_person[from_index]
    current_indices = from_person.result.keys()
    to_target = [index for index in index_to_person.keys() if from_index != index
            and index not in current_indices]
    to_index = random.choice(to_target)
    if 0 <= random.random() <= 1/2:
        index_to_person[from_index].add_result(to_index, White_Result.get_id())
    else:
        index_to_person[from_index].add_result(to_index, Black_Result.get_id())
    return index_to_person

def random_delete_result(index_to_person):

    """ Strategy to loose the restriction.  
    """
    # The target should have result except himself.  
    target_indices = [index for index, person in index_to_person.items() if len(person.result) > 1]

    from_index = random.choice(target_indices)
    result_indices = list(index_to_person[from_index].result.keys())
    to_target = [index for index in result_indices if from_index != index]
    to_index = random.choice(to_target)

    index_to_person[from_index].delete_result(to_index)

    return index_to_person


def assort_person_id(index_to_person):
    """ Sort the person's id, by the order of the number of claims. 

    :return: the changed index_to_person.
    
    """
    indices = index_to_person.keys()
    indices = sorted(indices,
                    key=lambda index: len(index_to_person[index].result.keys()),
                    reverse=True)

    replace_map = {original_id: index for index, original_id 
                   in enumerate(indices)}  

    revised_index_to_person = dict()
    for prev_index, person in index_to_person.items():
        person.replace_result_ids(replace_map)

        next_index = replace_map[prev_index]
        revised_index_to_person[next_index] = person
    
    return revised_index_to_person

def _index_to_alphabet(index):
    return chr(ord("A") + index)

def display_problems(village_number, wolf_number, index_to_person, lang="en"):
    text_lines = list()
    persons = len(index_to_person)
    if lang == "en":
        first_line = "## Problem"
        second_line = "Roles:Villagers/Wolves={v}/{w}, PL:{first_pl}~{last_pl}"\
                     .format(v=village_number, 
                             w=wolf_number, 
                             first_pl='A',
                             last_pl=_index_to_alphabet(persons-1))
        third_line = "### Player's claims"
    elif lang == "jp":
        first_line = "## 問題"
        second_line = "内訳:村陣営/狼={v}/{w}, PL:{first_pl}~{last_pl}"\
                     .format(v=village_number, 
                             w=wolf_number,
                             first_pl='A',
                             last_pl=_index_to_alphabet(persons-1))

        third_line = "### 各PLの主張"
    else:
        raise ValueError("Specification of lang is invalid.", lang) 
    text_lines += [first_line, second_line, third_line]

    indices = sorted(index_to_person.keys(),
                    key=lambda index: len(index_to_person[index].result),
                    reverse=True)
    for index  in indices:
        person = index_to_person[index]
        if person.has_result():
            text_lines.append(person.display_index_result(lang))
    return "\n".join(text_lines)

def display_answers(answer_row, lang="en"):
    def _index_to_alphabet(index):
        return chr(ord("A") + index)

    text_lines = list()
    villagers = [_index_to_alphabet(elem) for elem in sorted(answer_row["forseener_indices"])]
    wolves = [_index_to_alphabet(elem) for elem in sorted(answer_row["wolf_indices"])]
    if lang == "en":
        first_line = "## Answer  "
        wolf_line = "Wolves={0}".format(",".join(wolves))
    elif lang == "jp":
        first_line = "## 解答  "
        wolf_line = "Wolves={0}".format(",".join(wolves))
    else:
        raise ValueError("Invalid lang", lang)
    
    text_lines += [first_line, wolf_line]
    return "\n".join(text_lines)


if __name__ ==  "__main__":
    lang = "jp"
    result = generate_answer(wolf_number, village_number)
    index_to_person = {index:elem for index, elem in enumerate(result)}

    max_iteration = 100
    for iter_number in range(max_iteration):

        ret_list = get_coherent_cases(index_to_person)

        for row in ret_list:
            wolf_indices = row["wolf_indices"]
            forseener_indices = row["forseener_indices"]

        #print("length", len(ret_list))

        if len(ret_list) == 0:
            random_delete_result(index_to_person)
        elif len(ret_list) > 1:
            random_add_result(index_to_person)
        else:
            break


    index_to_person = assort_person_id(index_to_person)
    ret_list = get_coherent_cases(index_to_person)
    #assert len(ret_list) == 1
    answer_row = ret_list[0]
    #print(answer_row) 
    texts = display_problems(village_number, wolf_number, index_to_person, lang)
    print(texts);
    texts = display_answers(answer_row, lang)
    print(texts);exit(0)
    str_list = list()

5人2W残り、猫又COと狩人CO。吊りの優先順番は?

はじめに

こんにちは、StudentSです。
季節の変わり目、体調を崩しやすいので気をつけていきたいですね。
今回は下記の問題を考えてみます。

問題設定

  • 昼の地点で、5人2W残り(3人は村陣営)、霊能1, 猫又CO2, 狩人CO1の状況
  • 霊能は真確定・猫又に1人真がいることも確定。
  • 狩人の真は確定していない。

この時、村陣営の期待勝率を最大化する吊り方以下のうちどれか?

  1. Case1: 狩人候補吊り → 猫又吊り
  2. Case2: 村人候補吊り → 猫又吊り
  3. Case3: 猫又吊り → (猫又吊りに成功すれば護衛指示村勝利)

予想に自信がある方から吊った方が良い というのが定性的な回答。
狩人だったら護衛指示等で、猫又だったら猫又ルーレットで、 ゲームのルールが予想の精度の悪さを補填してくれます。 今回は、上記の設定に置いて、定量的に解を導出します。

記号と前提

  • 狩人が偽と予想する確率 $\alpha$.
  • 猫又のうち偽目と予想する方が実際に偽の確率 $\beta (\geq ½)$,
    この予想の上で、期待勝率を最大にする吊り方を調べる。

結果

期待勝率を計算すると以下の通り。

  1. Case1: $\alpha\left(½\beta + ½\right)$
  2. Case2: $(1-\alpha)\left(½\beta + ½\right)$
  3. Case3: $\beta$

以上より、

  1. $\alpha \geq \frac{2\beta}{\beta+1}$ の時、Case1.
  2. $\alpha \leq 1 - \frac{2\beta}{\beta+1}$ の時、Case2.
  3. それ以外の時、 Case3.

の戦略を採用するのが良いと言える。

f:id:StudentS:20170910205921p:plain

あとがき

  • 上記の議論は、村人候補の代わりに狩人CO2 としても同様の結果。
  • 実は原稿を一回消してしまった…反省…
  • 人狼に参加させていただいたら、情報処理能力が落ちていた。反省…
  • 色々反省が多いなぁ…最近。

動画化村を振り返ってみたよ!

はじめに

「都合の良いことだけを並べて、他の人を説得する」
人狼ゲームでは、これが必要になりますよね。
情けなくても、みっともない暴論を振りかざし、
「やめて! 私を吊らないで! 吊るべき敵はあいつだよ!」
と主張を作るのが、人狼ゲームの面白さ。

だけど、情報処理能力の関係上、村の最中だけでは、深い議論が出来ないときがあります。
さらに、ゲーム中は、PLとしての立場が視野を狭くする傾向にあります。

参加した村を振りかえり、改善点を探すことで、
もっと、面白い主張を作ることができるかもしれない。
今回はそれをやってみたいと思います。
題材は、

No.343651「【動画化村】12A猫」村

禅乃蓮(ぜんのはす) さんが動画にして作成してくださった村

こんな人狼ゲームのお話【10】-配役12A猫 by 禅乃蓮(ぜんのはす) ゲーム/動画 - ニコニコ動画

です。
参加した『男の子』に振り返って、語ってもらおうと思います。 なお、ネタバレを含むのでご了承ください。

ボクの動画化村参加記録

おはよー! 動画化村に参加させてもらった男の子だよ!
この夏も暑いなって思いながら村を歩くと、
短いスカートや半袖の女の子が多くて、ちょっとどきどきしちゃう。
慌てて目をそらすんだけど、ボクが不自然に見られちゃうし。ちょっと見えちゃうし…
でも、仕方ないよね….だ、だって男の子だもん。夏って不思議な季節だね!

参加した村のことを思い出して、考えたことを書くつもりだよ。
学校の読書感想文とかが苦手だから、いい文章が書けるか不安だけど、 頑張るね!

大体の内容のまとめはこんな感じだよ!

  • 狼騙り初手○による囲い。猫噛み破綻は考慮する?

    • 4-1 展開なら意味が無い。
    • 3-1 展開なら薄く意味がある。
  • まろんさんからの○結果を使った村アピール

    • 真占い生存、2W死亡の可能性がある状況下で狂人は○を重ねない。
  • [おまけ] 「男の子が狼」という主張の例。

詳細を読みたい所だけ斜め読みしてくれたら嬉しいな。

狼騙り初手○による囲い。猫噛み破綻は考慮する?

(発言:3:4, 5:9) <注: 村中の発言日:発言番号を表しているよ。例えば、発言3:4は、3日目の発言4だね。>

…でもね、ボクが狼だとしたら、噛み役はどうすると考えるのかな?
○で狼を囲ったら、(霊能が噛めて●を隠せたとしても)
その騙り役の○が噛めなくなるよね!だから、狼の○では囲っていないと
考えるのが自然じゃないかな?

(発言3:4 一部抜粋)

いい文章になっていないけど、

「狼が2騙りをした場合、潜伏狼を初手で囲わない。理由は、その騙り役の○を潜伏狼が噛んで猫だと破綻するから。」

というのが、発言3:4でボクが言おうとしていたことだね。

これは今回の村のように

  • 4-1展開
  • 占いローラーが完遂される

状況だと、成り立たないことだね。

だって、

  • 占いローラー中に騙りが猫噛みをする場合は、破綻前提の噛み
  • 占いローラー中に騙りが猫噛みできない場合、猫噛み = LWの死亡

だから。

上の指摘点が問題になるのは、

  • 3-1(真狼狼)でローラーが途中で止まる

ケースなんだ。

「2W残りで潜伏役と騙り狼が生存している。」
この時、囲われた潜伏役の○が、騙り狼の別の○を噛んだ時、噛み先が猫又だと破綻してしまうよね。
この意味で噛み先が制限されてしまう。というのが、
発言3:4でボクがいいたかったこと。
狼が2騙りを行うときは、
「4-1を狙うよりも、3-1で真狼狼を真狼狂と誤認させたい」
という気持ちが強いだろうからね。
3-1だった場合想定ケースの主張は通ると思うな。

だけど、上の主張はあくまでも1つの要素を取り出しているだけ。

  • 4-1になった場合は、関係ない

という「意味がない」という主張だけじゃなくて

囲わないことで、

  • (狼狼-真)の2-1展開になった場合、グレーランダムで潜伏狼が吊れてしまう可能性がある
  • (真狼狼-真)の3-1展開で占いローラーが止まっても、囲わないとグレー詰めで指定される可能性がある

とか、そういうデメリットの要素も上げることが出来るから。

「総合的にどっちがいいの?」という問いに対してのシンプルな答えはないけれど、
ボクは狼騙りの○もらいだったから。自分にとって都合のいい要素を取り出して主張したんだ。

まろんさんからの○結果を使った村アピール

村の最中にボクが言えなかった主張。
まろんさんと新人さんのどっちが狂だと思う?」という質問に対して、
ボクは、発言5:23で、

新人刑事が狂人だとボクは思うよ。対抗の○に意識がいくのは狂人特有の動きだからね。

と答えている。これは、感情論、フィーリングに訴えかける主張だね。
少しロジカルな主張を付け加えることができたなぁ。と思ったよ。
それは、「まろんさんが狂だとしたら、自分○は狂人にとって、戦略の幅を狭める結果だよ!」 という主張。

5日目の霊界発言で、有像無像さんが話していたね。

まろん狂人で見た場合この展開で真の〇に重ねて確定〇は極力作る展開は避けたいはず
霊能結果を見る前出していましたし下手に〇打つのはリスクが高かったのではないかと

これくらいのすんごい薄い根拠の(男の子)村置きではありましたが

ボクなりの解釈と主張で言葉にするね!

まろんさんが狂人だと仮定するね。
まろんさんが狂人だとすると、誰が真かは2日目の段階で分からないよね。
そして、朝の段階でユウリさん狼が露呈。
2日目の吊りは新人刑事さんだった。
まろんさんの視点では2通りのケースがあるね。

  • A 新人さんが○、真の可能性
  • B 新人さんが●、狼の可能性

Aのケースだけを考えるのなら、狂人視点、グレーに対しての色はフラット。自分○の結果はあり得るね。

でも、Bのケースを考えたらどうかな?
猫噛みがあって新人さんが●だったら、
このケースでは、狂人視点、○を打つべきは「グレーの狼っぽい所」だよ。
村の進行として、占い師候補のグレー詰め/占い師吊りという両方の進行の可能性があるけれど、
狼が囲えた場合、

  • グレー詰めの進行になった時、狼を保護できる。
  • 占いローラーが続いた時に、真狂占いの○結果は狼にとって有利に働く。 (例えば、狼を囲えた後で真が狼を見つけても、狂人が確実に村の場所に●を打っているように見せられるよね。)

以上2点によって、狼有利な状況となるから。

さらに、シンプルに考えるなら、 AとBのケースのどちらを中心に考えるべきかは、以下の観点からBのケースだと言えるね。

  • 新人さんが○ or ●の確率は大体 1/3 or 2/3 だと考えられる。Bのケースの方が発生しやすい。
  • Bのケースは、狼陣営にとって苦しい展開となっている。(狂人のサポートが必要な状況)

発生しやすくて、悪い状況を想定しておくのが自然だよね。
だから、Bの可能性をみないのは不合理だと思うんだ。
まろんさんが狂だとしたら、相当に不自然な選択をとったことになる。
これが、まろんさん真の根拠だよ!

というのが、村の最中にできなかった、少しだけロジカルな主張。

もちろん、この主張に反論をつくることは簡単にできるよね。

  • ボク(男の子)が○結果を無視してでも、狂人視点狼として分かるようなグレーだった。
  • ボク(男の子)が狼だった場合、上記の議論で村アピールができる。その状況を作るために、ボク狼を読んでギャンブルをした。

こんな感じかな。

でも、有象無象さんがまとめてくれたように、発言を無視してシンプルに考えると、3日目の状況で

  • 真の可能性がある○先に○を重ねるのは、狂人として不利に働く可能性が高い戦術である。

というのは、ロジック上、ある程度の説得力を持っていると思うんだ。

だから、こういう主張ができたら良かったなーと振り返って思ったよ!

実際は、まろんさんは狂人。自分●をほぼ決め打って○を出しているみたいだった。
多分、まろんさんは勝負を賭けたんだよね! そして、自分が狼だったら、上の要素を使って村アピールができるよね。

その他いろいろ

最終日の反省点。

ボクの発言5:4は、鳳さんの発言4:5を踏まえた上で行うべきものだったよね。 読めていなかったよ…

発言2:29 「女の子に『豚』っていったら駄目だね…」

可愛い豚の鳴き真似をした、豚鼻幸子 (豚系女子) さんに 「豚」って言っていたね。 「豚」って言われて傷つく女の子もいるみたい。 あんまり言わない方がいいみたいだなぁ。

仲良くなるには時間が必要…

発言2:12の

  • 「おとこなら誰でもいい」
  • 「誰でもいいけど付き合って」

っていうのは、無責任じゃないかなぁ。
仲良くなっていく為には時間が必要だと思ったね。
ボクは、誰でもいいような人と付き合いたくないよ。

ボクはまだ子供で恋愛とかよく分からないけれど、
困った時や大変な時に助け合える人と関係を築いていく。
っていうのが恋愛なんじゃないかな、って想像しているよ。

大人になったら、そんな素敵な女の子に出会えるといいなぁ。
そして、ボクもその人にとっていい男の子になりたいな。

おまけ

井上美羽さんっていう人狼プレイヤーが、自分狼の主張を作ってくれた。おまけとしてのせておくね。

Summary

端的にまとめますね。

  • 男の子は狼。狼騙り初手○先を村陣営アピールに利用している狼。

以下の2つが証拠。

  • 「狼騙りが初手○囲いをしない」主張が実際とは違う3-1の場合前提。実際に4-1となる前(初日夜)に主張を作っていた証拠。
  • 思考の前提に「霊能者噛み・色隠し」の意識がある。噛みの戦略を考えていた狼の証拠。
Detail

少し長くなるけれど、以下が詳細です。

[3-1の場合でしか成立しない主張]

男の子は発言3:4で

  • 「囲いの○猫噛みによる破綻露呈」を防ぐために、2騙り中の狼騙りが潜伏狼に初手○を打たない

と主張をしているわね。

これは、今回の村のように 4-1の展開になった地点で、占い騙りの狼が猫噛みを狙うので、成り立たない主張。
3-1の展開の場合であったら、「囲いの○猫噛みによる破綻露呈を防ぐため、狼騙りが初手○を潜伏狼に出さない」 という主張は、弱いけれども成立する。
でも、男の子から、「3-1の場合だったら…」という発言はない。
実際は4-1展開だったのに、当然のように「たられば」の3-1のケースでの主張しているわ。
前提条件への言及がないのは、彼が、2日目昼4-1展開になる前に主張を作ったからですね。
2日目前に主張を作ることが出来る理由は、
「男の子が狼で初日の夜に仲間から○を出されることが分かっていた」から。
それ以外の理由は存在しないですよね。

狼が2騙りであったら、4-1よりも3-1の方を望むでしょうし、
初日夜に主張を作るとき、希望的観測が前提条件として彼の思考に入ったのでしょうね。

[霊能者噛み・色隠しへの意識]

さらに、発言3:4で「霊能者噛みの色隠しが発生した場合に…」といっているわね。これはおかしい。
占い狼2騙りで、吊られた狼占いが破綻したとしても、何も問題はないわ。他に潜伏狼がいないんだから。
それにもかかわらず、「霊能者噛み・色隠し」という単語が出てくるのは、

  • 初めから狼騙りの初手○を村要素として使おうとした
  • 意識が噛みの戦略に向けられていた

という2つの要因しかないですわ。

前者の要因は、[3-1でしか成立しない主張]と同じで初日夜に主張を作っていたからですわね。
後者の要因は、毎晩の噛み先決定のときの思考が、村アピール考察を作るときの思考に混ざったのでしょうね。
おそらく、初日の夜に決めた主張の大枠に、噛み先決定の時の「思いつき」を付け加えたのだわ。
該当箇所の発言が括弧"()“で括られていることが、付記的な内容だったことを示唆しています。

以上2つが、男の子が狼、とくに

  • 初日の夜に考えた「2騙りで狼騙りが初手○で潜伏狼を囲わない」 という理論を押し通す潜伏狼

であることを明確に示す証拠ですわ。

補足・まろんさん真視 + まろん吊り容認は狼の村アピールだった。

さらに、 発言3:39, 3:42から以下のことも分かるわ。

  • 男の子はまろんさん真の印象を主張しつつ、まろんさん吊りを止めない。

これは、狼でしかあり得ない行動。

  • 偶数進行で狂人のまろんさんを吊らせる必要があった。
  • まろんさんからの○を村アピールに使う必要があった。

この2つを同時に行う必要から、中途半端なまろん擁護になったのよ。
村陣営なら、まろんさんが真と思うのであれば、豚系女子(豚鼻幸子)さん吊りを主張すべき場面だったわ。
男の子は『占い師候補の真贋判定に決定的な違いはない』と言っているけれど、
例えば、3日目の結果からは、猫に○を出した豚系女子(豚鼻幸子)さんの狂目が低いというのがシンプルな考察だよね。
単純な考察の提出もせずに「まろん真目でみて、まろん吊りを推す」というのは、 狂人を吊らせる必要がある狼だったから。

あとがき

迷った所

まろんさんの擁護の中途半端さの扱いに悩んだよね。まろんさん擁護の中途半端さを積極的に狼誘導につかうと、
まろん真の時男の子は村人だよね』となって、誘導が逆に働いてしまうかもしれない。
だから、『男の子が狼』と思ってくれる人への確度を高めるためには使える主張かもしれないけど、
『どっちだろ?』って迷っている人に対して使うかどうかは、実際の村では少し微妙かなー って思った。 ま、でも、兎に角、攻撃しやすい発言だねー

男の子君へ

こんな感じで作ってみたけどどうかな? 落ち着いて書いた後付けの文章だから、いつもの村の文章よりはある程度質は高いと信じたいなー
実際の村ではまとまっている文章を書くのは難しいけれど、たまには、時間をかけて練られた文章で誘導合戦できたら楽しいかもねー
またよろしくねー

おわりに

ここまで読んでくれた人、ありがと!
動画村参加させてもらって楽しかったよ! ボク、今でも時々思い出すよ!
参加して思ったのは、ボクの力はまだまだ足りないなぁ…ってことだね。
だけど、人狼で一番大事なのは楽しむことだよね! また、楽しい村に参加することができたらいいなぁ。
また、一緒に遊ばせてもらう時があったらよろしくね! 一緒に楽しい村が作れるといいね!
暑い夏はまだまだ続くけど、体に気をつけようね! ボクも体に気をつけて残りの夏休み、楽しく過ごしたいな。

StudentS’s Afterwords.

感想

久しぶりにブログ用の文章を書いたーという感じでした。 書こうと思った理由は

こんな人狼ゲームのお話【13】-配役14D猫 by 禅乃蓮(ぜんのはす) ゲーム/動画 - ニコニコ動画

の編集技術に圧倒されたからですね… 自分も何かできるかなぁ? と思って書いてみた…という感じですねー

ファイルの管理にご注意を

現実世界の知り合いの方との共有フォルダに誤ってこの原稿の下書きを入れてしまって1週間立っていました。赤面ものです。どうか、見られていませんように。 …現実世界で「…んで、『男の子』はどう思うの?」とか言われたら、私はどうすればいいんでしょうか? 皆様も、重要な情報の管理にはご注意ください。