判断推理の家の位置関係の問題をPythonで解いてみた。

 知恵袋で見つけた判断推理の家の位置関係の問題Pythonで解いてみました。(^_^;

 次の図のような十字型の道路に面してA~Jの10軒の家が並んでいる。今、次のア~エのことが分かっているとき、Aの家の位置として有り得るのはどれか。ただし、各家の玄関は道路に面して1つであり、図では東西南北の方向は示されていない。

ア Aの家の道路をはさんだ正面の家の東隣にBの家がある。
イ Aの家の玄関は南を向いている。
ウ Cの家は、Eの家の北隣にあり、玄関は西を向いている。
エ Dの家は、Eの家の道路をはさんだ正面にあり、玄関は北を向いている。

1 あ 2 い 3 か 4 き 5 こ

        |  |
     +--+  +--+
     |あ|  |い|
  +--+--+  +--+--+
  |う|え|  |お|か|
--+--+--+  +--+--+--

--+--+--+  +--+--+--
  |き|く|  |け|こ|
  +--+--+  +--+--+
        |  |

 拙ブログのプログラムBoardPuz1.pyを雛形にしました。
 A~Eまでしか、条件に出てこないので、A~Eとx5個の同じものを含む順列(30240個)で回してみました。(^_^;
 今回、選択肢のチェックには、「有り得るのはどれか」ということなので論理和を用いました。

● House1.py

# coding: UTF-8
# House1.py

from time import time
import itertools

RM, CM = 6, 7   # 行・列の最大数
# マップの初期状態
MAP = '''
+-----+
| ア#イ |
|ウエ#オカ|
|#####|
|キク#ケコ|
+-----+
'''
##print(MAP)
MAP = MAP.replace('\n','')

# 通し番号n番目の行数を得る(0スタート)
def getRow(n):
    return n//CM

# 通し番号n番目の列数を得る(0スタート)
def getCol(n):
    return n%CM

# r行c列の通し番号を得る(0スタート)
def getN(r,c):
    if not(0<=r< RM): return -1  # ドメインエラーは-1を返す
    if not(0<=c< CM): return -1
    return r*CM+c

# 家の順列pを加えて新しいマップを作る
PLACE = 'アイウエオカキクケコ'
def mkNewMap(p):
    r = MAP
    for n in range(len(p)):
        if p[n]!='x':
            r = r.replace(PLACE[n],p[n])
    return r

# map上の通し番号n番目のとこの上下左右の文字のリストを得る
def getSurroundings(n, map):
    li = []
    if n< 0 or RM*CM-1< n: return []    # ドメインエラーなら[]を返す
    r,c = getRow(n),getCol(n)
    for i,j in [(-1,0),(1,0),(0,-1),(0,1)]:
        m = getN(r+i,c+j)
        if m!=-1: li.append(map[m])
    return li

# 北が上下左右(UDLR)方向uのとき、東西南北(EWSN)方向eが上下左右方向でどの方向になるか
UDLR = 'URDL'*2
EWSN = 'NESW'
def getDir(u,e):
    return UDLR[UDLR.index(u)+EWSN.index(e)]

# map上でA~Eの家hの上下左右(UDLR)方向dの隣のものを得る
def getNeighborOf(h,d, map):
    n = map.find(h)
    li = getSurroundings(n, map)
    return li['UDLR'.find(d)]

# map上でA~Eの家hの道路を挟んで真向かいの家のリストを得る
def getHousesAcrossTheRoadFrom(h, map):
    n = map.find(h)
    li = getSurroundings(n, map)
    if li.count('#')==0: return []
    r,c = getRow(n),getCol(n)
    lr = []
    for i in range(len(li)):
        if li[i]=='#':
            if   i==0: m = getN(r-2,c  )
            elif i==1: m = getN(r+2,c  )
            elif i==2: m = getN(r  ,c-2)
            elif i==3: m = getN(r  ,c+2)
            lr.append(map[m])
    return lr

# 結果を表示
def PrintMap(m):
    for i in range(RM):
        print(m[i*CM:(i+1)*CM])
##    print('')

# 確実にいえる選択肢を得る
def getAns(cho,lbl='12345'):
    return ','.join([lbl[i] for i in range(len(cho)) if cho[i]])

def main():
    tm = time()  # Timer Start
    HOUSE = 'ABCDExxxxx'
    choices = [False]*5
    cnt = 0
    for d in 'UDLR':    # 上下左右
        for p in set(itertools.permutations(HOUSE)):    # 同じものを含む順列
            map = mkNewMap(p)
            nb = getNeighborOf('B',getDir(d,'W'),map)   # Bの西隣
            if not nb in getHousesAcrossTheRoadFrom('A', map): continue     # 条件ア
            if not getNeighborOf('A',getDir(d,'S'),map)=='#': continue      # 条件イ
            if not getNeighborOf('E',getDir(d,'N'),map)=='C': continue      # 条件ウ
            if not 'D' in getHousesAcrossTheRoadFrom('E', map): continue    # 条件エ
            if not getNeighborOf('D',getDir(d,'N'),map)=='#': continue      # 条件エ
            pass # チェックを潜り抜けたものだけを表示
            cnt+=1
            print('[%d] %s %s'%(cnt,d,MAP[map.find('A')]))
            PrintMap(map)
            pass # 選択肢のチェック
            choices[0] |= (map.find('A')==MAP.find('ア'))
            choices[1] |= (map.find('A')==MAP.find('イ'))
            choices[2] |= (map.find('A')==MAP.find('カ'))
            choices[3] |= (map.find('A')==MAP.find('キ'))
            choices[4] |= (map.find('A')==MAP.find('コ'))

    print("∴%s"%getAns(choices))
    print("Runtime : %.3f [sec]"%(time()-tm))   # Timer Stop & Disp

if __name__ == '__main__':
    main()

●実行結果

[1] U ウ
+-----+
| ア#C |
|Aエ#Eカ|
|#####|
|キB#Dコ|
+-----+
[2] U エ
+-----+
| ア#C |
|ウA#EB|
|#####|
|キク#Dコ|
+-----+
[3] U オ
+-----+
| C#イ |
|ウE#Aカ|
|#####|
|キD#ケB|
+-----+
[4] L エ
+-----+
| ア#B |
|ウA#オカ|
|#####|
|CE#Dコ|
+-----+
[5] L ク
+-----+
| B#イ |
|CE#Dカ|
|#####|
|キA#ケコ|
+-----+
[6] R イ
+-----+
| ア#A |
|ウB#オカ|
|#####|
|キD#EC|
+-----+
∴2
Runtime : 6.109 [sec]

※参考URL
日能研のマインスイーパーに似たボードパズルの問題をPythonで解いてみた。 - rscのブログ
判断推理のロッカーの位置関係の問題をPythonで解いてみた。 - rscのブログ
判断推理の家の位置関係の問題をPythonで解いてみた。(2) - rscのブログ