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. お絵描きパズルとは 用語の説明 白のマス ・ 処理が未確定の空白のマス ・ □□□で表現 ・ 配列内では[0, 0,

    0] 黒のマス ・ 塗りが確定した塗りつぶしたマス ・ ▪▪▪で表現 ・ 配列内では[1, 1, 1] 塗り指示 ・ 問題の左・上辺にあるヒントの数字 ×のマス ・ 塗られないことが確定したマス ・ □□□で表現 ・ 配列内では[-1, -1, -1] ×××
  2. 「お絵描きパズルを解いてみた」 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
  3. まずは問題を解いてみよう 処理1 確定マスの塗りつぶし 処理5 端から確定マスを見つける 処理2 端が確定した際の処理 処理6 指示の最大数と同じ連続した塗り 処理3

    届かないマスの確定処理 処理7 端から指示 +1のマスが塗られていたら 処理9 端から 「1」 と 「2」 の連続した塗り指示の確定処理 処理4 「×」 のマスで分割して処理1〜3を実行 処理8 端から 「1」 の連続した塗り指示の確定処理 処理10 連続された塗りから対象の指示数字を特定する ロジックを書き出してみる
  4. ロジックの構造と全体の流れ # 変数の指定 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]
  5. 処理1 確定マスの塗りつぶし 解き方 6,2 6,2 6,2 6,2 左から詰めて6の配置 右から詰めて6の配置 左詰め時の一番右のマスから

    右詰め時の一番左マスまでは確定 6,2 6,2 6,2 左から詰めて2の配置 右から詰めて2の配置 左詰め時の一番右のマスから 右詰め時の一番左マスまでは確定
  6. 解き方 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
  7. 処理2 端が確定した際の処理 解き方 3,2 3,2 × 3,2 最初は確定できるマスはありません 行 ・

    列を進める際に確定マスができた 塗りマスと直後の 「×」 確定ができる
  8. 解き方 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 行・列を進める際に確定マスができた 塗りマスと直後の 「×」 確定ができる
  9. 処理3 届かないマスの確定処理 解き方 3 3 × × × × ×

    3 3 左右に3マス届く可能性のマス 届かないマスは塗られる可能性がないことが確定
  10. 解き方 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マス届く可能性のマス 届かないマスは塗られる 可能性がないことが確定
  11. ロジックの言語化 Try & Err の繰り返し 処理1 確定マスの塗りつぶし 処理5 端から確定マスを見つける 処理2

    端が確定した際の処理 処理6 指示の最大数と同じ連続した塗り 処理3 届かないマスの確定処理 処理7 端から指示 +1のマスが塗られていたら 処理9 端から 「1」 と 「2」 の連続した塗り指示の確定処理 処理4 「×」 のマスで分割して処理1〜3を実行 処理8 端から 「1」 の連続した塗り指示の確定処理 処理10 連続された塗りから対象の指示数字を特定する
  12. ロジックの言語化 Try & Err の繰り返し 処理1 確定マスの塗りつぶし 処理5 端から確定マスを見つける 処理2

    端が確定した際の処理 処理6 指示の最大数と同じ連続した塗り 処理3 届かないマスの確定処理 処理7 端から指示 +1のマスが塗られていたら 処理9 端から 「1」 と 「2」 の連続した塗り指示の確定処理 処理4 「×」 のマスで分割して処理1〜3を実行 処理8 端から 「1」 の連続した塗り指示の確定処理 処理10 連続された塗りから対象の指示数字を特定する 一部のロジックは Wikipediaに載ってる為、 本日は解説しません
  13. ロジックの言語化 Try & Err の繰り返し 処理1 確定マスの塗りつぶし 処理5 端から確定マスを見つける 処理2

    端が確定した際の処理 処理6 指示の最大数と同じ連続した塗り 処理3 届かないマスの確定処理 処理7 端から指示 +1のマスが塗られていたら 処理9 端から 「1」 と 「2」 の連続した塗り指示の確定処理 処理4 「×」 のマスで分割して処理1〜3を実行 処理8 端から 「1」 の連続した塗り指示の確定処理 処理10 連続された塗りから対象の指示数字を特定する
  14. 処理7 端から指示 +1のマスが塗られていたら 5,1 × 5,1 5,1 × 5,1 左端から

    「5」 を塗ったと仮定すると 「5」 の塗りの可能性は 左端のマスは塗られないことが確定
  15. 処理8 端から 「1」 の連続した塗り指示の確定処理 1,1,1,2 × × × × 1,1,1,2

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

    1,2,1,1 × × × × 1,2,1,1 処理8は 「1の連続した」 という条件があった 「1と2の連続した」 という条件に変える この範囲にある[▪,□,▪]の[□]は×が確定
  17. ロジックの言語化 難易度★★★★☆まで解ける(一部を除いて) 処理1 確定マスの塗りつぶし 処理5 端から確定マスを見つける 処理2 端が確定した際の処理 処理6 指示の最大数と同じ連続した塗り

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