Python で論理クイズ Solve Logic Quiz with Python

はじめに

Python は手軽に書けるスクリプト言語です. 論理クイズは与えられた説明と矛盾しない解答を論理的に導くクイズです. 今回は Python を使って論理クイズを解きます.

Python

文法が分かりやすく,シンプルに書くことができ,標準ライブラリも充実していて,とても使いやすいと感じています. 今回は itertools モジュールを使って,組み合わせを総当りすることによって,論理クイズを解きます.

論理クイズ

ここで扱う論理クイズでは,初期設定と容疑者達の証言をもとに事件の真相を論理的に導きます.

初期設定とは容疑者の集合および嘘つきの人数であり,最初に与えられます.

証言とは事件に関する主張を1つ以上並べたもので,容疑者は最大で1つの証言を行います.

嘘つきとは容疑者のうち,証言をし,それが真相と矛盾するような人物のことです.

正直者とは容疑者のうち,嘘つきではない人物のことです.

真相とは1人の真犯人と0人以上の嘘つきの組み合わせのうち,初期設定,嘘つきの証言の否定および正直者の証言のどれとも矛盾しない唯一のものです.

ソースコード

次のソースコードは論理クイズを解く Python3 のプログラムです. suspectsは容疑者とその証言を表します. 容疑者名をキーとし,lambda 式を値とする辞書で実装します. ここで,lambda 式は嘘つきの名前のタプルおよび犯人の名前を受け取り,証言の真偽を返します. liarsCountは嘘つきの人数です.

このプログラムは容疑者の中から犯人を1人選ぶ組み合わせおよび嘘つきを人数分選ぶ組み合わせの直積の中から, 初期設定,嘘つきの証言の否定および正直者の証言のどれとも矛盾しないものを標準出力に出力します. 出力されるものが1つならそれが真相です.

# -*- coding: utf-8 -*-
import itertools as it

def implies(p, q):
    return (not p) or q

def give_testimony(testimony, is_liar):
    return not testimony if is_liar else testimony

# 容疑者名と証言の辞書
suspects = {}
# 嘘つきの人数
liarsCount = 0

for truth in it.product(
        it.combinations(suspects.keys(), liarsCount),
        suspects.keys()):
    liars, criminal = truth
    isConsistent = all([give_testimony(t(liars, criminal), s in liars)
                        for s, t in suspects.items()])
    if isConsistent:
        print("嘘つきは{},犯人は{}.".format("と".join(liars), criminal))

実行

今回は説法系推理アドベンチャシリーズの3つのゲーム,

で出題される論理クイズを解きます. これらのゲームは私が作成し,ウディフェスまたはウディコンにエントリしたゲームです. 以下ではこれらのゲームのネタバレがあります. 未プレイの方は以下の文章を読む前に,プレイするかプレイ動画を見てください.

愛と血の修羅場(サスペンス)

嘘つきは1人,容疑者と証言をまとめると以下のようになります.

  • 恵伊 : 史衣と出井は嘘をつかず,史衣と出井は犯人ではない.
  • 美衣 : 恵伊と良威のどちらかは正直者.
  • 史衣 : 犯人は出井か恵伊.
  • 出井 : 出井と美衣はどちらも犯人ではない.
  • 良威 : 犯人は美衣.

本ゲームで正解となる真相は,「嘘つきは良威,犯人は恵伊.」です.

上のプログラムのsuspectsおよびliarsCount

#容疑者名と証言の辞書
suspects = {
    "恵伊": lambda ls, c: ("史衣" not in ls) and ("出井" not in ls) and ("史衣" != c) and ("出井" != c),
    "美衣": lambda ls, c: ("恵伊" not in ls) or ("良威" not in ls),
    "史衣": lambda ls, c: ("恵伊" == c) or ("出井" == c),
    "出井": lambda ls, c: ("美衣" != c) and ("史衣" != c),
    "良威": lambda ls, c: ("美衣" == c)
}
#嘘つきの人数
liarsCount = 1

と初期化し,実行すると,嘘つきは良威,犯人は恵伊.と出力されます. よって,「嘘つきは良威,犯人は恵伊.」が唯一の真相であることが確認できます.

恋と友情の常識(ファイト)

嘘つきは2人,容疑者と証言をまとめると以下のようになります.

  • 恵伊 : 良威は正直者.
  • 美衣 : 恵伊は嘘つきではない,または史衣は嘘つきではない.
  • 史衣 : 出井は嘘つき.
  • 出井 : 美衣と恵夫はどちらも犯人ではない.
  • 良威 : 犯人は恵夫.
  • 恵夫 : 良威は嘘つき.

本ゲームで正解となる真相は「嘘つきは出井と恵夫,犯人は恵夫.」です.

上のプログラムのsuspectsおよびliarsCount

#容疑者名と証言の辞書
suspects = {
    "恵伊": lambda ls, c: "良威" not in ls,
    "美衣": lambda ls, c: ("恵伊" not in ls) or ("史衣" not in ls),
    "史衣": lambda ls, c: "出井" in ls,
    "出井": lambda ls, c: ("美衣" != c) and ("恵夫" != c),
    "良威": lambda ls, c: "恵夫" == c,
    "恵夫": lambda ls, c: "良威" in ls,
}
#嘘つきの人数
liarsCount = 2

と初期化し,実行すると,嘘つきは出井と恵夫,犯人は恵夫.と出力されます. よって,「嘘つきは出井と恵夫,犯人は恵夫.」が唯一の真相であることが確認できます.

罪と幸せの四苦八苦(ノアズアーク)

嘘つきは2人,容疑者と証言をまとめると以下のようになります.

  • 美衣 : 犯人は嘘つきの中の誰かで,出井は犯人ではない.
  • 史衣 : 犯人は恵夫.
  • 出井 : 永一と史衣はどちらも嘘つき.
  • 恵夫 : (証言無し)
  • 永一 : 出井が正直者ならば出井は犯人ではなく,美衣は正直者.

本ゲームで正解となる真相は「嘘つきは史衣と出井,犯人は史衣」です.

上のプログラムのsuspectsおよびliarsCount

#容疑者名と証言の辞書
suspects = {
    "美衣": lambda ls, c: (c in ls) and ("出井" != c),
    "史衣": lambda ls, c: "恵夫" == c,
    "出井": lambda ls, c: ("永一" in ls) and ("史衣" in ls),
    "恵夫": lambda ls, c: True,
    "永一": lambda ls, c: implies("出井" not in ls, "出井" != c) and ("美衣" not in ls),
}
#嘘つきの人数
liarsCount = 2

と初期化し,実行すると,嘘つきは史衣と出井,犯人は史衣.と出力されます. よって,「嘘つきは史衣と出井,犯人は史衣」が唯一の真相であることが確認できます.

まとめ

辞書やリストをシンプルに記述でき,その取り扱いも標準ライブラリが充実しているため,とても楽でした. PyCharmで書けば,静的チェックもしてくれたので,助かりました.

罪と幸せの四苦八苦(ノアズアーク)

f:id:Jumpaku:20170724043656p:plain

仲間達との船旅で起こる悲劇.
船上で発見された仲間の死体.
容疑者達は己の信じる愛を貫く.
すれちがう証言と譲れない主張.
私は論理の果てに真実をつかむ.

ゲーム情報

本ゲームは登場人物の説法を聞くことと,論理クイズを組み合わせた説法系推理アドベンチャです.

  • タイトル : 罪と幸せの四苦八苦(ノアズアーク)
  • 読み : つみとしあわせののあずあーく
  • 作者 : Jumpaku
  • ジャンル : 説法系推理アドベンチャ
  • プレイ時間 : 30分程度
  • プラットフォーム : Windows
  • リリース : 2017年7月24日
  • 言語 : 日本語
  • 開発環境 : WOLF RPGエディター

遊び方 How to play

上のリンクからダウンロードしてください.

ゲームを始めるには"Game.exe"を実行して下さい. 詳しい操作方法は"README.pdf"を読んで下さい.

ウディコンにエントリ

このゲームをウディコン2017に提出しました.

コンテストのページはここです. WOLF RPGエディターコンテスト -ウディコン- / 力作フリーゲーム!

ヴァージョン Version

  • v1.3 : 誤字の訂正
  • v1.2 : 誤字脱字の訂正
  • v1.1 : README.pdfの間違いを訂正
  • v1.0 : 最初の完成品

±ZERO

f:id:Jumpaku:20170722214902j:plain

離れていると死んでしまう!!

紹介

私はProject ZEROという友人のゲーム開発に参加しました. そこで"±0" というゲームを作って,ウディコン2017に参加したので宣伝します.

このゲームは離れていると死んでしまう2人のキャラクタを切り替えながら, ダンジョンを攻略し,ストーリーを進めていくRPGです. ダンジョンには様々なギミックがあり,その攻略にはパズルを解く面白さがあります.

ウディコン2017のページからダウンロードして遊べます. http://www.silversecond.com/WolfRPGEditor/Contest/entry.shtml

ゲーム情報

リンク

Gradle でライブラリを追加 Add libraries with Cradle

Gradle とは

Gradle とは Ant や Maven にかわるビルドツールです. Maven などに比べて, ビルドスクリプトがシンプルになり, 見やすい気がします. また, Mavenリポジトリのライブラリを利用することができます.

Gradle の準備

NetBeans

NetBeansでGradleを利用するためにプラグインをインストールします. “ツール” –> “プラグイン” –> “使用可能なプラグイン” –> “Gradle Support” を選択し, “インストール” を押します.

Intellij

Intellij では最初から Gradle を利用することができます.

Gradle プロジェクトの作成

新しく自分の Gradle プロジェクトを作成する方法を説明します.

NetBeans

“ファイル” –> “新しいプロジェクト” –> “Gradle” –> “Single Gradle Project” –> “次” と進み, “Project Name” などを埋め, “終了” を押すと Gradle プロジェクトが作成されます.

Intellij

“New” –> “Project” –> “Gradle” –> “Java” と進み,
“GroupId”, “ArtifactId” および “Version” を埋め, “Next” –> “Next” と進んで,
“Project name” および “Project Location” を埋め, “Finish” を押すと Gradle プロジェクトが作成されます.

このとき, 自分のプロジェクトの “GroupId”, “ArtifactId” および “Version” が必要となる場合はそれらを自分で決めます.

依存性の追加

依存性とはプロジェクトで使用する外部のライブラリです. 自分のプロジェクトで使用したい外部のライブラリを自分のプロジェクトへ追加する方法を説明します.

まず, Google などを利用して, 使用する外部のライブラリのグループID, アーティファクトIDおよびバージョンを調べます.

プロジェクト内のbuild.gradleファイルのdependencies { }の中に, 調べた情報を次のように書き加えます.

dependencies {
    //使いたいライブラリの情報を調べておいて,
    //ここに書き加える.
    compile group: 'グループID', name: 'アーティファクトID', version: 'バージョン'
}

Gradle プロジェクトのオープン

Gradle プロジェクトを IDE で開くときに, 開きたいプロジェクトのbuild.gradleファイルを指定します.

Siv3D こだわりのボタン

この記事は Siv3D Advent Calendar 2016 の記事 (12/13) です.

概要

Siv3D でのゲーム開発などで使う為にこだわりのボタンを作りました.

サンプル

実行結果


Siv3D Button

ソースコード

こだわりポイント

このボタンのこだわりポイントは次の通りです.

見た目と機能の分離

ボタンの見た目は別のクラスで実装するようになっています. 従って, 見た目を容易に変更したり, 画像に合わせた当たり判定を設定したりする事ができます.

イベント駆動

ボタン生成時に, ボタンクリック時に呼び出されるイベントハンドラを設定します. これにより, 1フレーム毎に状態チェックをしなくても, ボタンクリック時に自動的に処理が実行されます.

押し心地

マウスの状態に応じてボタンの状態が変化します. ボタンの状態に応じた描画処理が実行されるので, マウスカーソルを合わせた時にボタンを大きくしたり, ボタンを押下した時にボタンが沈んだり, ボタンを離上した時に元に戻ったりする事ができます. その結果, ボタンの押し心地が良くなります.

ボタンの状態遷移もこだわりました. ボタンのクリックが検出されるには

  1. MOUSE_OVER 状態
  2. PRESSED 状態
  3. RELEASED 状態

の3つの状態を経る必要があります. これにより, 間違って押してしまった場合でも指を離さずにカーソルをボタン領域外まで移動させる事でキャンセルする事ができます.

経緯

以下のゲームを作成する際に, ボタンは私が担当する事になりました.

jumpaku.hatenablog.com

そこで作成したボタンをもっと使い易くこだわったものが今回のボタンです. 詳しいソースコードGitHub で見る事ができます. github.com