スライドパズル・15パズルの解法:完全ランダムと手順指定法

# 概要
スライドパズル・15パズルの解法を試してみたいと思っています。
使用言語は`python`です。
[ここ](https://www.afsgames.com/15puzzle.htm)で遊べます（リンクあり）。
![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/873fd044-0298-e91e-d4cf-b387107844e9.png)
空いているマスに、数字マスを移動させる事で1,2,3,・・・と順に並べることを目的とします。


# 完全ランダム
$20$回移動して、一致していなかったらさらに移動を繰り返します。
$100$セットごとに現状を`print`します。

### 座標系
![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/7cb56695-4a78-4544-0a34-97ca5e668b0a.png)


### クラスの関数

#### `move`

```python
slide.move(direction)
```
操作マスを`direction`の方向に動かす

```
direction = 
0:上
1:右
2:下
3:左
```
とします。

#### `shuffle`

```python

slide.shuffle(num)
```
`num`回、ランダムで移動する


### 結果
$10$分ほど回しましたが一致する事はありませんでした。

```
10105300times
[2, 7, 4, 1]
[3, 10, 8, 12]
[6, 5, 14, 15]
[0, 11, 9, 13]
10105400times
[0, 9, 2, 3]
[14, 1, 8, 7]
[13, 15, 11, 12]
[5, 10, 6, 4]
10105500times
[9, 10, 5, 6]
[15, 2, 13, 8]
[12, 7, 0, 1]
[4, 3, 11, 14]
```

<details><summary>実行プログラムは次のようになります。トグルなのでクリックして開いてみてください。</summary>

```python
import random
import numpy as np		#	Numpyライブラリ

class Slide():
    """
    0　が　操作マス
    左上を原点とする
    横に　ｘ
    縦に　ｙ  
    トスル
    
    """
    
    def __init__(self):
        self.puzzle = [[1,2,3,4],
                  [5,6,7,8],
                  [9,10,11,12],
                  [13,14,15,0]]
        self.x = 3
        self.y = 3
    
    def shuffle(self,num):
        for i in range(num):
            j=0
            while True:
                j = random.randint(0,3)
                break
            self.move(j)
            
    def move(self,direction):
        """
           0
        3     1
           2
        """
        if direction == 0:
            if self.y != 0:
                if self.fixed[self.y-1][self.x] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y-1][self.x]
                    self.y = self.y - 1
                    self.puzzle[self.y][self.x] = 0
        if direction == 1:
            if self.x != 3:
                if self.fixed[self.y][self.x+1] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y][self.x+1]
                    self.x = self.x + 1
                    self.puzzle[self.y][self.x] = 0
        if direction == 2:
            if self.y != 3:
                if self.fixed[self.y+1][self.x] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y+1][self.x]
                    self.y = self.y + 1
                    self.puzzle[self.y][self.x] = 0
        if direction == 3:
            if self.x != 0:
                if self.fixed[self.y][self.x-1] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y][self.x-1]
                    self.x = self.x - 1
                    self.puzzle[self.y][self.x] = 0
                
    def check(self):
        for i in range(4):
            for j in range(4):
                if self.puzzle[i][j] != j+1+i*4:
                    return -1
                        
        return 0
    
    
if __name__ == '__main__' :
    hoge = Slide()
    hoge.shuffle(500)
    print(hoge.puzzle)
    
    n=0
    while True:
        hoge.shuffle(20)
        flag = hoge.check()
        if flag == 0:
            break
        n=n+1
        
        if n%100 == 0:
            print(str(n)+"times")
            print(hoge.puzzle[0])
            print(hoge.puzzle[1])
            print(hoge.puzzle[2])
            print(hoge.puzzle[3])
            
    print("find")
    print(str(n) + "times")
    print(hoge.puzzle)
```
</details>

# 一致している部分を固定しながら・ランダム
完全ランダムは流石に無理がありました。
次は<font color=Red>正しい位置に移動できたマスから固定する</font>方法を使います。
この方法には

**固定されたコマに挟まれたコマは移動できなくなる**

という問題があります。

そこで、

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/da29a8de-0366-f87f-536d-ce97eb983009.png)

の順に完成させます。（ランダムで回し、成立したら固定し、次に行く）
この段階で、残りは左下4マスのみなので、ランダムでも解けます。

### 結果
かなり良くなりました。

```
shuffled puzzle
[11, 5, 2, 7]
[1, 6, 12, 14]
[0, 13, 9, 3]
[8, 15, 4, 10]
start analyze
3652times
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 10, 11, 12]
[13, 14, 15, 0]
```
3652セット（3652*20回移動）で完成できました。完全ランダムだと、10105500セットを超えても完成しないので、かなりいいと思います。
何回かやりましたが、1秒もかかりませんが、セット数はまばらです。

(追記)コメントでより良いプログラムにしてもらったので変更しました。ありがとうございます。
<details><summary>クリックして開いてみてください。</summary>

```python
import random


class Slide:

    def __init__(self):
        self.puzzle = [
            [ 1,  2,  3,  4],
            [ 5,  6,  7,  8],
            [ 9, 10, 11, 12],
            [13, 14, 15,  0],
        ]
        self.x = 3
        self.y = 3
        self.fixed = [False] * 16

    def __iter__(self):
        return iter(self.puzzle)

    def shuffle(self, num):
        DIRS = (0, -1), (1, 0), (0, 1), (-1, 0)
        for _ in range(num):
            dx, dy = random.choice(DIRS)
            x = self.x + dx
            y = self.y + dy
            if 0 <= x <= 3 and 0 <= y <= 3 and not self.fixed[y * 4 + x]:
                self.puzzle[self.y][self.x] = self.puzzle[y][x]
                self.x = x
                self.y = y
        self.puzzle[self.y][self.x] = 0

    def fix(self, numbers):
        if all(self.fixed[number - 1] for number in numbers):
            return True
        if any(self.puzzle[(number - 1) // 4][(number - 1) % 4] != number
               for number in numbers):
            return False
        for number in numbers:
            self.fixed[number - 1] = True
        return True


def solve(puzzle):
    CORNER = 1, 4, 13
    UPPERSIDE = 2, 3
    LEFTSIDE = 5, 9
    ROW2 = 6, 7, 8
    COL2 = 6, 10, 14
    RIGHTLOWER = 11, 12, 15

    n = 0
    while not puzzle.fix(CORNER):
        puzzle.shuffle(20)
        n += 1
    while not puzzle.fix(UPPERSIDE) or not puzzle.fix(LEFTSIDE):
        puzzle.shuffle(20)
        n += 1
    while not puzzle.fix(ROW2) or not puzzle.fix(COL2):
        puzzle.shuffle(20)
        n += 1
    while not puzzle.fix(RIGHTLOWER):
        puzzle.shuffle(20)
        n += 1
    return n


def main():
    puzzle = Slide()
    puzzle.shuffle(500)
    print("shuffled puzzle")
    print(*puzzle, sep='\n')
    print("start analyze")
    n = solve(puzzle)
    print(n, "times")
    print(*puzzle, sep='\n')

if __name__ == '__main__':
    main()
```
</details>


# ランダムでなく、計算により目標位置に移動させる
任意の場所`A`にある"数字"を任意の場所`B`に移動させる手順の作成法を考えます。
`A`を左に移動させるには、操作マス（0マス）を`A`の左に移動させ、操作マスを右に移動させればよいです。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/451cde58-c9e0-46d0-7ce6-a654dcbd5b20.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/ea01f4bb-5f51-55fc-e4a6-dc0a189d02c8.png)

## 操作マス移動ルート探索について
`A`や固定しているマスを動かさないように、操作マスを移動させるルートは`A*アルゴリズム`によって解きます。
最後に、作成した`A*アルゴリズム`を貼っておきます。

```python
Astar(goal_x,goal_y,start_x,start_y,obstacle)
```

を与えると、最短ルートを返します。
このルートに基づき、操作マスを動かしたいマスの隣に付け、上下左右へ動かします。

### 確定順序
次に、どのマスから確定させていくか決めます。
![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/3d713da8-e680-3529-1ebc-4e49a398e6f4.png)

```
1
2
4と3 　:3を固定すると4が入らなくなるため
5
9と13
6　　　（ここから先は、ゲーム木探索で解ける、後で実装したい）
7と8
10と14
11と12と15  
```
上から順に確定させていきます。

### 最後の4マスについて
次の3パターンしかありません。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/ddb405c1-ed36-2285-7892-f7c78ff6fa13.png)
よって、各パターンで場合分けしてやれば完成します。

### 2か所同時に埋める手順
$2$か所同時に埋める必要がある場所（3,4など）のアルゴリズムを示します。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/f087f8bf-13fb-37e7-952b-2db0d5f7e9df.png)

例外として、$4$を$2$のとなりに置いた時点で$3$が右上隅に移動していた場合
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/0338887f-3522-3ba4-8509-2909620a59f5.png)

入れ替え手順を考えると、次のようになります。
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405484/aef335b0-55ba-fded-289c-210cbea73166.png)
$3$を遠くに移動させてから、$4-3$の縦の列を作るという手順です。

### 結果
以上で完全に解けるようになりました。
手数としては$200 \sim 50$手ぐらいです。
固定しながらのランダムで$8$万手ぐらい使っていたので、かなりの進歩だと思います。

以上をプログラムにすると

<details><summary>トグルなのでクリックして開いてみてください。</summary>

```python

# -*- coding: utf-8 -*-
"""
Created on Mon Jun 29 13:35:47 2020

@author: kisim
"""

"""
スライドパズルの解法を探す

最終的には、パズドラに応用したい。そして10combo を目指す
"""
"""
Astar アルゴリズムにより、fixed をよけながら移動する
場合分けはエラーの原因で面倒のため


"""
import random
import numpy as np		#	Numpyライブラリ
import copy

import Astar as As

class Slide():
    """
    0　が　操作マス
    左上を原点とする
    横に　ｘ
    縦に　ｙ  
    トスル
    
    """
    
    def __init__(self):
        self.puzzle = [[1,2,3,4],
                  [5,6,7,8],
                  [9,10,11,12],
                  [13,14,15,0]]
        self.x = 3   #操作マス　0 の場所
        self.y = 3
        
        self.fixed = np.zeros((4,4))
        self.route = [[self.x,self.y]]
        
    def route_clear(self):
        self.route = [[self.x,self.y]]
        
    def shuffle(self,num):
        for i in range(num):
            j=0
            while True:
                j = random.randint(0,3)
                break
            self.move(j)
            
    def move(self,direction):
        """
           0
        3     1
           2
           
        動くのに失敗したら　-1
        動いたら　　　　　　　0
        """
        if direction == 0:
            if self.y != 0:
                if self.fixed[self.y-1][self.x] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y-1][self.x]
                    self.y = self.y - 1
                    self.puzzle[self.y][self.x] = 0
                    self.route.append([self.x,self.y])
                    return 0
                else:
                    return -1
        if direction == 1:
            if self.x != 3:
                if self.fixed[self.y][self.x+1] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y][self.x+1]
                    self.x = self.x + 1
                    self.puzzle[self.y][self.x] = 0
                    self.route.append([self.x,self.y])
                    return 0
                else:
                    return -1
        if direction == 2:
            if self.y != 3:
                if self.fixed[self.y+1][self.x] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y+1][self.x]
                    self.y = self.y + 1
                    self.puzzle[self.y][self.x] = 0
                    self.route.append([self.x,self.y])
                    return 0
                else:
                    return -1
        if direction == 3:
            if self.x != 0:
                if self.fixed[self.y][self.x-1] == 0:
                    self.puzzle[self.y][self.x] = self.puzzle[self.y][self.x-1]
                    self.x = self.x - 1
                    self.puzzle[self.y][self.x] = 0
                    self.route.append([self.x,self.y])
                    return 0
                else:
                    return -1
        return -1
  
    
    def move_0(self,x,y):
        """
       操作マス("0"マス) を　x,y に動かす       
       
       先にルートを考え、fixに引っかからないとわかってから、移動を行います。
       これはある種の迷路です。
       別の関数で　迷路を解くための　A*　アルゴリズムを実装し、それを利用して解きましょう
           
        """
    
        hoge = As.Astar(x,y,self.x,self.y,self.fixed)
        result = hoge.explore()
        route = []
        if result == 0:
           route = hoge.route
        else:
            return -1
        
       
        for i in range(len(route)-1):
            if route[i][0] <  route[i+1][0]:
                self.move(1)
            elif route[i+1][0] <  route[i][0]:
                self.move(3)
            elif route[i+1][1] <  route[i][1]:
                self.move(0)
            elif route[i+1][1] >  route[i][1]:
                self.move(2)
                
        if self.x !=x or self.y != y:
            return -1
        else:
            return 0

    def move_any(self,position,direction):
        x=position[0]
        y=position[1]
        """
        任意の　"数"(x,y) を　direction の方向に動かす   
           0
        3     1
           2
           
        動くのに失敗したら　-1
        動いたら　　　　　　　0
        """
        if direction == 0:    #上に移動させる  操作マスを上に付ける
            self.fixed[y][x] = 1
            hoge = self.move_0(x,y-1)
            self.fixed[y][x] = 0
            if hoge == -1:
                return -1
            else:
                self.move_0(x,y)           
                return 0
                            
        elif direction == 2:  # 下に移動させる
            self.fixed[y][x] = 1
            hoge = self.move_0(x,y+1)
            self.fixed[y][x] = 0
            if hoge == -1:
                return -1
            else:
                self.move_0(x,y)           
                return 0
                            
        elif direction == 1:  # 右に移動させる
            self.fixed[y][x] = 1
            hoge = self.move_0(x+1,y)
            self.fixed[y][x] = 0
            if hoge == -1:
                return -1
            else:
                self.move_0(x,y)           
                return 0
                            
        elif direction == 3:  # 左に移動させる
            self.fixed[y][x] = 1
            hoge = self.move_0(x-1,y)
            self.fixed[y][x] = 0
            if hoge == -1:
                return -1
            else:
                self.move_0(x,y)           
                return 0    
                                
    def find_position(self,num):
        for i in range(4):
            for j in range(4):
                if self.puzzle[i][j] == num:
                    return (j,i)
                
    def move_x(self,number,position):
        target_x = position[0]
        target_y = position[1]
        """
        def move_any(self,position,direction):
        任意の　"数"(x,y) を　direction の方向に動かす   
           0
        3     1
           2
           
        動くのに失敗したら　-1
        動いたら　　　　　　　0
        """
        position2 = self.find_position(number)
        now_x = position2[0]
        now_y = position2[1]
        """
        Astar アルゴリズムで　number　の　ルート　見つけ
        ルートに従い、move_anyで動かし
        但し、道幅が広くないと成立しないので、move_anyが失敗する可能性がある
        －＞Astar , fixする順序で対処
        """        
        hoge = As.Astar(target_x,target_y,now_x,now_y,self.fixed)
        result = hoge.explore()
        route = []
        if result == 0:
           route = hoge.route
        else:
            return -1
        
        for i in range(len(route)-1):
            position2 = self.find_position(number)
            now_x = position2[0]
            now_y = position2[1]
            if route[i][0] <  route[i+1][0]:
                result = self.move_any((now_x,now_y),1)
                if result == -1:
                    return -1
                
            elif route[i+1][0] <  route[i][0]:
                result = self.move_any((now_x,now_y),3)
                if result == -1:
                    return -1
                
            elif route[i+1][1] <  route[i][1]:
                result = self.move_any((now_x,now_y),0)
                if result == -1:
                    return -1
                
            elif route[i+1][1] >  route[i][1]:
                result = self.move_any((now_x,now_y),2)
                if result == -1:
                    return -1
              
        position2 = self.find_position(number)
        now_x = position2[0]
        now_y = position2[1]
        if target_x != now_x or target_y != now_y:
            return -1
        else:
            return 0
    def exchange_row(self):
        """
        4 3
        x 0
        y z
        で入れ替える
        """
        self.move(0)
        self.move(3)
        """
        0 4
        x 3
        y z       
        """
        self.move(2)
        self.move(2)
        self.move(1)
        """
        x 4
        y 3
        z 0        
        """
        self.move(0)
        """
        x 4
        y 0
        z 3       
        """
        self.move(3)
        self.move(0)
        self.move(1)
        """
        4 0
        x y
        z 3       
        """
        
    def exchange_line(self):
        """
        13 0 y
        9  x z
        """
        self.move(3)
        self.move(2)
        """
        9 13 y
        0  x z
        """
        self.move(1)
        self.move(1)
        self.move(0)
        """
        9 13 0
        x y z
        """
        self.move(3)
        """
        9 0 13
        x y z
        """
        self.move(2)
        self.move(3)
        self.move(0)
        """
        0 y 13
        9 x z
        """
    
def route_test(slide,route):
    if route == []:
        return -1
    else:
        for i in range(len(route)-1):
            if route[i][0] <  route[i+1][0]:
                slide.move(1)
            elif route[i+1][0] <  route[i][0]:
                slide.move(3)
            elif route[i+1][1] <  route[i][1]:
                slide.move(0)
            elif route[i+1][1] >  route[i][1]:
                slide.move(2)
    
    return slide
        
                        
            
        
if __name__ == '__main__' :
    hoge = Slide()
    hoge.shuffle(600)
    hoge.route_clear()
    print(hoge.puzzle[0])
    print(hoge.puzzle[1])
    print(hoge.puzzle[2])
    print(hoge.puzzle[3])
    
    #test用
    hoge2 = Slide()
    hoge2.puzzle = copy.deepcopy(hoge.puzzle)
    hoge2.x = hoge.x
    hoge2.y = hoge.y
    
    
    #1
    hoge.move_x(1,(0,0))
    hoge.fixed[0][0] =1
    #2
    hoge.move_x(2,(1,0))
    hoge.fixed[0][1] =1
    #3,4
    hoge.move_x(4,(2,0))
    hoge.fixed[0][2] =1
    
    if hoge.x == 3 and hoge.y == 0 and hoge.puzzle[1][3] == 3:
        hoge.move(2)
    if hoge.puzzle[0][3] == 3:
        hoge.fixed[0][2] = 0
        hoge.move_0(3,1)
        hoge.exchange_row()
        print("errored 3-4")
    
    hoge.move_x(3,(2,1))
    hoge.fixed[1][2] = 1
    hoge.move_0(3,0)
    hoge.fixed[1][2] = 0
    hoge.fixed[0][2] = 0
    hoge.move(3)
    hoge.move(2)
    
    hoge.fixed[0][2] = 1
    hoge.fixed[0][3] = 1
    
        
    #5
    hoge.move_x(5,(0,1))
    hoge.fixed[1][0] =1
    #9,13
    hoge.move_x(9,(0,3))
    hoge.fixed[3][0] =1

    if hoge.x == 0 and hoge.y == 2 and hoge.puzzle[2][1] == 13:
        hoge.move(1)
    if hoge.puzzle[2][0] == 13:
        hoge.fixed[3][0] = 0
        hoge.move_0(1,2)
        hoge.exchange_line()
        print("error 9-13")
        hoge.fixed[3][0] = 1

    hoge.move_x(13,(1,3))
    hoge.fixed[3][1] = 1
    hoge.move_0(0,2)
    hoge.fixed[3][1] = 0
    hoge.fixed[3][0] = 0
    hoge.move(2)
    hoge.move(1)
    
    hoge.fixed[2][0] = 1
    hoge.fixed[3][0] = 1
    
    #6
    hoge.move_x(6,(1,1))
    hoge.fixed[1][1] =1
    
    #7,8
    hoge.move_x(8,(2,1))
    hoge.fixed[1][2] =1
    if hoge.x == 3 and hoge.y == 1 and hoge.puzzle[2][3] == 7:
        hoge.move(2)
    if hoge.puzzle[1][3] == 7:
        hoge.fixed[1][2] = 0
        hoge.move_0(3,2)
        hoge.exchange_row()
        print("error 7-8")
    
    hoge.move_x(7,(2,2))
    hoge.fixed[2][2] = 1
    hoge.move_0(3,1)
    hoge.fixed[2][2] = 0
    hoge.fixed[1][2] = 0
    hoge.move(3)
    hoge.move(2)
    
    hoge.fixed[1][3] = 1
    hoge.fixed[1][2] = 1
    
    #6マスなので　もう　ゲーム木探索でも解けるのでは？
    #10,14
    result = hoge.move_x(10,(1,3))
    print(str(result)+"result")

    hoge.fixed[3][1] =1
    if hoge.x == 1 and hoge.y == 2 and hoge.puzzle[2][2] == 14:
        hoge.move(1)
    if hoge.puzzle[2][1] == 14:
        hoge.fixed[3][1] = 0
        hoge.move_0(2,2)
        hoge.exchange_line()
        print("error10-14")
        hoge.fixed[3][1] = 1
        
    
    hoge.move_x(14,(2,3))
    hoge.fixed[3][2] = 1
    hoge.move_0(1,2)
    hoge.fixed[3][2] = 0
    hoge.fixed[3][1] = 0
    hoge.move(2)
    hoge.move(1)
    
    hoge.fixed[2][1] = 1
    hoge.fixed[3][1] = 1

    
    # これで行けるかと思ったが　、ちょっと違った
    hoge.move_0(3,3)
    
    print("a")
    print(hoge.puzzle[0])
    print(hoge.puzzle[1])
    print(hoge.puzzle[2])
    print(hoge.puzzle[3])
    print(hoge.fixed[0])
    print(hoge.fixed[1])
    print(hoge.fixed[2])
    print(hoge.fixed[3])
    
    if hoge.puzzle[3][2] == 11:
        #反時計回り一周
        hoge.move(0)
        hoge.move(3)
        hoge.move(2)
        hoge.move(1)
    elif hoge.puzzle[3][2] == 12:
        #時計回り一周
        hoge.move(3)
        hoge.move(0)
        hoge.move(1)
        hoge.move(2)
        
    print(hoge.puzzle[0])
    print(hoge.puzzle[1])
    print(hoge.puzzle[2])
    print(hoge.puzzle[3])

    
    print(len(hoge.route))
    
    hoge2 = route_test(hoge2,hoge.route)
    if hoge2 == -1:
        print("error")
    else:
        print(hoge2.puzzle[0])
        print(hoge2.puzzle[1])
        print(hoge2.puzzle[2])
        print(hoge2.puzzle[3])
    
    
    

    
```

</details>

# A*アルゴリズム
あまりうまくないプログラムですが・・・

<details><summary>トグルなのでクリックして開いてみてください。</summary>

```python:Aster.py
import numpy as np		#	Numpyライブラリ
import random
import copy

class Node():
    def __init__(self,x,y,cost,parent,num):
        self.x = x
        self.y = y
        self.state = 1   # 0:none 1:open 2:closed
        self.score = 0
        self.cost = cost
        self.parent = parent
        self.expect_cost =  0
        self.num = num
        self.calculated = 0

    
    def close(self):
        self.state = 2
        
        
    

class Astar():
    def __init__(self,g_x,g_y,s_x,s_y,obstacle):
        self.width = obstacle.shape[1]
        self.height = obstacle.shape[0]
        self.g_x = g_x
        self.g_y = g_y
        self.s_x = s_x
        self.s_y = s_y
        self.x = s_x
        self.y = s_y
        self.obstacle_list = copy.deepcopy(obstacle)
        self.maked_list = []
        self.num = 0
        
        start = Node(s_x,s_y,0,-1,self.num)
        self.Node_list = [start]
        self.num = self.num + 1
        self.now = start           #現在のノード
        self.route = []
        self.goal = -1            #gaal の　ノード
        self.finished = 0         #goal したかどうか
        
        if g_x == s_x and g_y == s_y:
            self.finished == 1
            self.goal = start
            self.route = [[s_x,s_y]]
            
        
    def open(self):
        self.now.close()
        #周りをopen
        """
        壁・障害　が有るときはopen できない　－＞obstacle_list
        既に作っていないか？－＞maked_list 
        """
        cost = self.now.cost
        parent = self.now.num
        if self.x!=0:
            if self.maked_list.count([self.x-1,self.y]) == 0 and  self.obstacle_list[self.y][self.x-1] == 0 :
                 self.Node_list.append(Node(self.x-1,self.y,cost+1,parent,self.num))
                 self.num = self.num + 1
                 self.maked_list.append([self.x-1,self.y])
        if self.x!=self.width-1:
            if self.maked_list.count([self.x+1,self.y]) == 0 and  self.obstacle_list[self.y][self.x+1] == 0 :
                 self.Node_list.append(Node(self.x+1,self.y,cost+1,parent,self.num))
                 self.num = self.num + 1
                 self.maked_list.append([self.x+1,self.y])
        if self.y!=0:
            if self.maked_list.count([self.x,self.y-1]) == 0 and  self.obstacle_list[self.y-1][self.x] == 0 :
                 self.Node_list.append(Node(self.x,self.y-1,cost+1,parent,self.num))
                 self.num = self.num + 1
                 self.maked_list.append([self.x,self.y-1])
        if self.y!=self.height-1:
            if self.maked_list.count([self.x,self.y+1]) == 0 and  self.obstacle_list[self.y+1][self.x] == 0 :
                 self.Node_list.append(Node(self.x,self.y+1,cost+1,parent,self.num))
                 self.num = self.num + 1
                 self.maked_list.append([self.x,self.y+1])
        
        """
        #デバッグ
        print("test")
        for i in self.Node_list:
            print(i.state)
        """ 
            
        #open しているものを計算
        for i in self.Node_list:
            if i.state == 1 and i.calculated == 0:
                i.calculated = 1
                i.expect_cost = abs(i.x - self.g_x)+abs(i.y-self.g_y)
                i.score = i.cost + i.expect_cost
            
        #open しているもののうち、スコアの小さいものをリストにまとめる
        min_cost  = 100
        min_cost_list = []
        for i in self.Node_list:
            if i.state == 1:
                if i.cost < min_cost:
                    min_cost = i.cost
                    min_cost_list = [i]
                elif i.cost == min_cost:
                    min_cost_list.append(i)
        
        if min_cost_list != []:
            self.now = min_cost_list[random.randint(0,len(min_cost_list)-1)]
            self.x = self.now.x
            self.y = self.now.y
        else:
            print("none min")
            return -1
                
        if self.now.x == self.g_x and self.now.y == self.g_y:
            return 1
        else:
            return 0
        
        
    
    def explore(self):
        """
        0 :goal
        -1:goal できない
        """
        if self.finished == 1:
            return 0
        else:
            while True:
                hoge = self.open()
                if hoge == 1:
                    #print("goal!")
                    self.goal = self.now
                    self.finished = 1
                    self.Route()
                    return 0
                elif hoge == -1:
                    return -1
                
    
    def Route(self):
        if self.finished == 1:
            while True:
                self.route.append((self.now.x,self.now.y))
                if self.now.parent == -1:
                    break
                else:
                    self.now = self.Node_list[self.now.parent]
            
            self.route.reverse()
            #print(self.route)
            
    def Express(self):
        if self.finished == 1:
            if self.route ==[]:
                print("not goaled")
            else:
                graph    =  self.obstacle_list
                for i in self.route:
                    graph[i[1]][i[0]] = 2
                    
                print(graph)
                
            
        
        
if __name__ == '__main__' :
    width = 5
    height = 5
    obstacle    =np.zeros((height,width))
    """
    obstacle[2][1] = 1
    obstacle[2][2] = 1
    obstacle[1][2] = 1
    obstacle[3][2] = 1
    """
    obstacle[1][0] = 1
    print(obstacle)
    g_x = 0
    g_y = 2
    s_x = 0
    s_y = 0
    
    hoge = Astar(g_x,g_y,s_x,s_y,obstacle)  
    result = hoge.explore()
    if result == 0:
        hoge.Express()
    
```

</details>

# 感想
ほんとはこんな泥臭いやり方ではなく、Q学習とかより最適化した手数の少ない方法を考えたいのですが私には今のところ難しいです。
群論とか学習したらいいのかなぁ～？？？