Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PyconJP2018 09_18 小栗潤一 Pythonで「お絵描きパズル」を解いてみた

J-Ogu
September 18, 2018

PyconJP2018 09_18 小栗潤一 Pythonで「お絵描きパズル」を解いてみた

発表で使用した資料をアップします。

J-Ogu

September 18, 2018
Tweet

More Decks by J-Ogu

Other Decks in Programming

Transcript

  1. Pythonで
    「お絵描きパズル」

    解いてみた
    PyCon JP 2018
    09_18 小栗 潤一

    View full-size slide

  2. 自己紹介

    View full-size slide

  3. 小栗 潤一

    広島在住

    大学時代は福岡で
    デザインの勉強

    広島のチラシ制作会社勤務
    3年間デザイナーとして働き
    9年間社内業務改善のプログラマー
    自己紹介

    View full-size slide

  4. 自己紹介
    私とPythonについて

    勉強を初めて約2年

    広島のもくもく会
    「すごい広島」
    「すごい広島 with Python」

    View full-size slide

  5. 今日の議題

    View full-size slide

  6. Python初心者の私が約3ヶ月かけて
    ロジックを考えPythonのコードにした話^^
    そのコードを使って
    某懸賞雑誌一冊分
    120問に挑戦しました
    どこまで解けたのかお楽しみに!!

    View full-size slide

  7. 今日の議題
    ・お絵描きパズルのルール
    ・パズルを解くのにPythonを使用した利点
    ・ロジックを考え、
    コードにする話
    ・Jupyter Notebookを使用したデモ
    ・120問中どこまで解けたのか?
    ・まとめ

    View full-size slide

  8. お絵描きパズル
    とは

    View full-size slide

  9. お絵描きパズルとは
    数字をヒントに
    マスを塗っていき
    現れたイラストを
    答えるゲーム
    1990年代に
    流行

    View full-size slide

  10. お絵描きパズルとは
    お絵かきロジック
    (世界文化社)
    ピクロス
    (任天堂)
    ののぐらむ
    イラストロジック
    名称が違うだけで
    同じルール

    View full-size slide

  11. お絵描きパズルとは
    用語の説明
    白のマス

    処理が未確定の空白のマス

    □□□で表現

    配列内では[0, 0, 0]
    黒のマス

    塗りが確定した塗りつぶしたマス

    ■■■で表現

    配列内では[1, 1, 1]
    塗り指示

    問題の左・上辺にあるヒントの数字
    ×のマス

    塗られないことが確定したマス

    □□□で表現

    配列内では[-1, -1, -1]
    ×××

    View full-size slide

  12. 「お絵描きパズルを解いてみた」
    初心者が考えた
    Pythonの利点

    View full-size slide

  13. 「お絵描きパズルを解いてみた」
    Pythonの利点
    Pandas、
    Numpyなどの便利なライブラリ
    [0,0,0,0,0,0,0,0,0,0]
    [10]
    [0,0,0,0,0,0,0,0,0,0]
    [3,2,2]
    DataFrame
    Numpyの配列計算
    Series

    View full-size slide

  14. それを踏まえて
    もう一度ご覧ください

    View full-size slide

  15. 「お絵描きパズルを解いてみた」
    Pythonの利点
    Pandas、
    Numpyなどの便利なライブラリ
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    [10]
    [1, 1, 1,-1, 1, 1,-1,-1, 1, 1]
    [3,2,2]
    DataFrame
    Numpyの配列計算
    Series

    View full-size slide

  16. 「お絵描きパズルを解いてみた」
    Pythonの利点
    Jupyter Notebookを使った開発

    デモ環境
    Jupyter Notebook
    ipywidgets
    RISE (拡張機能)

    View full-size slide

  17. Jupyter Notebookを使って
    問題を解かせてみましょう
    or
    デバッグ環境の紹介

    View full-size slide

  18. ・Pandas,Numpyを使って
    簡単な配列計算で解けそう

    動きのあるデバック環境が
    簡単に作れる
    私の考えたPythonを使う利点

    View full-size slide

  19. お絵描きパズルのルール
    ロジックの話

    View full-size slide

  20. お絵描きロジックの基本的なルール
    ・数字と同じマスを塗りつぶす
    ・数字が複数ある場合は必ず1マス以上空ける
    3
    2,3
    2,2
    3
    3

    View full-size slide

  21. アルゴリズム
    (ロジック)
    を考えてみましょう

    View full-size slide

  22. ロジックを考える
    まずは手動で解いてみる
    普段何気なく問題を解く際に
    頭の中で行なっている
    アルゴリズムを書き出してみる

    View full-size slide

  23. まずは問題を解いてみよう
    処理1 確定マスの塗りつぶし
    処理5 端から確定マスを見つける
    処理2 端が確定した際の処理
    処理6 指示の最大数と同じ連続した塗り
    処理3 届かないマスの確定処理
    処理7 端から指示 +1のマスが塗られていたら
    処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    処理4 「×」
    のマスで分割して処理1〜3を実行
    処理8 端から
    「1」
    の連続した塗り指示の確定処理
    処理10 連続された塗りから対象の指示数字を特定する
    ロジックを書き出してみる

    View full-size slide

  24. ロジックの構造と全体の流れ
    # 変数の指定
    n_list = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    n_vals = [3,3]
    # 関数の指定
    def nuri_syori(n_list, n_vals):
    ck_n_list = copy.copy(n_list)
    ck_n_list[0] = 2
    while not np.allclose(n_list, ck_n_list) and 0 in n_list:
    ck_n_list = copy.copy(n_list)
    for i in range(2):
    # リストの反転
    if i == 1:
    n_list = n_list[::-1]
    n_vals = n_vals[::-1]
    # 処理1〜10
    logic_shori_1 ()

    logic_shori_10 ()
    # リストの反転(戻す処理)
    if i == 1:
    n_list = n_list[::-1]
    n_vals = n_vals[::-1]

    View full-size slide

  25. ロジックで
    某懸賞雑誌
    120問に挑戦します
    どこまで解けたのでしょうか^^
    ロジックの解説から始めましょう

    View full-size slide

  26. 6,2
    処理1 確定マスの塗りつぶし
    解き方

    View full-size slide

  27. 処理1 確定マスの塗りつぶし
    解き方
    6,2
    6,2
    6,2
    6,2
    左から詰めて6の配置
    右から詰めて6の配置
    左詰め時の一番右のマスから
    右詰め時の一番左マスまでは確定
    6,2
    6,2
    6,2
    左から詰めて2の配置
    右から詰めて2の配置
    左詰め時の一番右のマスから
    右詰め時の一番左マスまでは確定

    View full-size slide

  28. 解き方
    n_list = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    n_vals = [6,2]
    # 隙間の数を埋める処理リファクト後の処理
    for n,val in enumerate(n_vals):
    af = sum(n_vals[n:]) + len(n_vals[n:]) - 1
    bf = sum(n_vals[:n+1]) + len(n_vals[:n+1]) - 1
    if not list(n_list[-af:bf]):continue
    n_list[-af:bf] = 1
    ## n_list = [0,1,1,1,1,1,0,0,1,0]
    左から詰めて6の配置
    6,2
    右から詰めて6の配置
    6,2
    左詰め時の一番右のマスから
    右詰め時の一番左マスまでは確定
    6,2

    View full-size slide

  29. 処理2 端が確定した際の処理
    解き方
    3,2
    3,2
    ×
    3,2
    最初は確定できるマスはありません


    列を進める際に確定マスができた
    塗りマスと直後の
    「×」
    確定ができる

    View full-size slide

  30. 解き方
    n_list = np.array([1, 0, 0, 0, 0,
    0, 0, 0, 0, 0])
    n_vals = [3,2]
    # 前方から処理
    if n_list[0] == 1.0:
    n_list[0:n_vals[0]] = 1
    n_list[n_vals[0]:n_vals[0]+1] = -1
    # 後方から処理
    if n_list[-1] == 1.0:
    n_list[-n_vals[-1]:len(n_list)] = 1
    n_list[-n_vals[-1] -1:-n_vals[-1]] = -1
    ## n_list = [1,1,1,-1,0,0,0,0,0,0]
    3,2
    ×
    3,2
    行・列を進める際に確定マスができた
    塗りマスと直後の
    「×」
    確定ができる

    View full-size slide

  31. 処理3 届かないマスの確定処理
    解き方
    3
    3
    × × × × ×
    3
    3
    左右に3マス届く可能性のマス
    届かないマスは塗られる可能性がないことが確定

    View full-size slide

  32. 解き方
    n_list = np.array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0])
    n_vals = [3]
    # 指示が一つ以上あるものは処理中断
    if not len(n_vals) == 1:
    return n_list
    leftNumm = int(np.where(n_list == 1)[0][0])+int(n_vals[0])
    if leftNumm < len(n_list):
    n_list[leftNumm:len(n_list)] = -1
    # 反転してもう一度
    n_list = n_list[::-1]
    leftNumm = int(np.where(n_list == 1)[0][0])+int(n_vals[0])
    if leftNumm < len(n_list):
    n_list[leftNumm:len(n_list)] = -1
    # 反転してもとに戻す
    n_list = n_list[::-1]
    ## n_list = [-1,0,0,1,0,0,-1,-1,-1,-1]
    3
    × × × × ×
    3
    3
    左右に3マス届く可能性のマス
    届かないマスは塗られる
    可能性がないことが確定

    View full-size slide

  33. 処理1・2・3は最初に
    実装したロジック
    難易度★★☆☆☆
    28問中約8割の問題が
    解けるようになった^^v

    View full-size slide

  34. ロジックの言語化
    Try & Err の繰り返し
    処理1 確定マスの塗りつぶし
    処理5 端から確定マスを見つける
    処理2 端が確定した際の処理
    処理6 指示の最大数と同じ連続した塗り
    処理3 届かないマスの確定処理
    処理7 端から指示 +1のマスが塗られていたら
    処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    処理4 「×」
    のマスで分割して処理1〜3を実行
    処理8 端から
    「1」
    の連続した塗り指示の確定処理
    処理10 連続された塗りから対象の指示数字を特定する

    View full-size slide

  35. ロジックの言語化
    Try & Err の繰り返し
    処理1 確定マスの塗りつぶし
    処理5 端から確定マスを見つける
    処理2 端が確定した際の処理
    処理6 指示の最大数と同じ連続した塗り
    処理3 届かないマスの確定処理
    処理7 端から指示 +1のマスが塗られていたら
    処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    処理4 「×」
    のマスで分割して処理1〜3を実行
    処理8 端から
    「1」
    の連続した塗り指示の確定処理
    処理10 連続された塗りから対象の指示数字を特定する
    一部のロジックは
    Wikipediaに載ってる為、
    本日は解説しません

    View full-size slide

  36. まずは問題を解いてみよう
    全てのロジックを
    まとめた資料
    興味のある方は
    こちらから
    http://bit.ly/2PEDues

    View full-size slide

  37. ロジックの言語化
    Try & Err の繰り返し
    処理1 確定マスの塗りつぶし
    処理5 端から確定マスを見つける
    処理2 端が確定した際の処理
    処理6 指示の最大数と同じ連続した塗り
    処理3 届かないマスの確定処理
    処理7 端から指示 +1のマスが塗られていたら
    処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    処理4 「×」
    のマスで分割して処理1〜3を実行
    処理8 端から
    「1」
    の連続した塗り指示の確定処理
    処理10 連続された塗りから対象の指示数字を特定する

    View full-size slide

  38. wikiに載ってない
    オリジナルロジック
    の解説

    View full-size slide

  39. 処理7 端から指示 +1のマスが塗られていたら
    5,1
    ×
    5,1
    5,1
    ×
    5,1
    左端から
    「5」
    を塗ったと仮定すると
    「5」
    の塗りの可能性は
    左端のマスは塗られないことが確定

    View full-size slide

  40. 処理8 端から
    「1」
    の連続した塗り指示の確定処理
    1,1,1,2
    × × × ×
    1,1,1,2
    × × ×
    1,1,1,2
    仮に
    「1」
    が左から詰めて塗られていたら
    この範囲にある塗りは必ず
    「1」
    になる
    ここまではWikipediaに載ってるロジック

    View full-size slide

  41. 処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    1,2,1,1
    ×
    1,2,1,1
    ×
    1,2,1,1
    × × × ×
    1,2,1,1
    処理8は
    「1の連続した」
    という条件があった
    「1と2の連続した」
    という条件に変える
    この範囲にある[■,□,■]の[□]は×が確定

    View full-size slide

  42. ロジックの言語化
    難易度★★★★☆まで解ける(一部を除いて)
    処理1 確定マスの塗りつぶし
    処理5 端から確定マスを見つける
    処理2 端が確定した際の処理
    処理6 指示の最大数と同じ連続した塗り
    処理3 届かないマスの確定処理
    処理7 端から指示 +1のマスが塗られていたら
    処理9 端から
    「1」

    「2」
    の連続した塗り指示の確定処理
    処理4 「×」
    のマスで分割して処理1〜3を実行
    処理8 端から
    「1」
    の連続した塗り指示の確定処理
    処理10 連続された塗りから対象の指示数字を特定する
    どれ一つとして難しい
    処理は行なっていません
    PandasのDataFrameを
    Seriesで取得して
    Numpyのndarrayに変換して
    配列計算しているだけ

    View full-size slide

  43. それでも解けない問題

    View full-size slide

  44. ・確定するマスがなくなる
    ・仮説処理で進めるしかない
    それでも解けない問題がでてくる

    View full-size slide

  45. それでも解けない問題がでてくる
    総当たり処理
    ・確定するマスがなくなる
    ・仮説処理で進めるしかない

    View full-size slide

  46. どれくらいの計算量になるのか?
    それでも解けない問題がでてくる
    × × × × × ×
    30
    2,2,1,3,2,1,1
    2,2,1,3,2,1,1
    30
    13
    13**8= 815,730,721
    ??????

    View full-size slide

  47. それでも解けない問題がでてくる
    総当たり処理
    ・確定するマスがなくなる
    ・仮説処理で進めるしかない
    計算量の少ない仮説

    View full-size slide

  48. ではどう進める?

    View full-size slide

  49. 上下左右のうち
    計算量の少ない1辺の
    仮説を立てるだけ

    View full-size slide

  50. 仮説処理
    1辺だけの総当たり処理
    itertoolsを使って
    4辺のパターン数を計算
    1行目
    :4パターン
    15行目
    :11パターン
    1列目
    :19パターン
    20列目
    :120パターン

    View full-size slide

  51. 仮説処理
    1行目:4パターン
    そのまま最後まで解ける
    確定マスがなくなる
    (さらなる仮説処理へ)
    矛盾が発生
    一番計算処理が少ない
    辺から進める

    View full-size slide

  52. 仮説処理
    0個
    1個
    複数
    ありえない
    確定パターン
    パターンを
    記憶して
    別の辺を進める
    確定マスがなくなる
    パターン数が重要

    View full-size slide

  53. 仮説処理
    2つ目の辺も
    複数残った場合は
    1つ目と2つ目の
    パターンを
    総当たりチェック
    必ず確定パターンが見つかる
    1つ目の辺の残った
    パターンを記憶して
    2つ目の辺を仮説処理

    View full-size slide

  54. 仮説処理を
    追加することで
    懸賞雑誌に載っている
    問題がほぼ全て解けた
    難易度★★★★★

    View full-size slide

  55. 懸賞雑誌一冊分
    120問中カラーロジックを除く
    116問全て解けた ^^

    View full-size slide

  56. 本日デモに使用した
    問題もPythonで自作
    イラストから問題作成
    作成した問題を解く

    View full-size slide

  57. まとめ
    ・Pythonには便利なライブラリが豊富にあり
    「お絵描きパズル」
    のような配列計算で答えが
    出せるゲームと相性が良い

    ロジックを考えるのは難しいが
    コード自体はDataFrameをSeriesで取得して
    Numpyのndarrayで計算している簡単なコード
    「お絵描きパズル」
    「数独」
    とも相性がいい
    ぜひ自分でトライしてみてください ^ ^ v

    View full-size slide