ぷよぷよsteam版のpuyo認識

![スクリーンショット 2024-10-09 22.05.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/8ce41d48-9b49-f992-e0a7-e85bd2cf3976.png)




steam版のぷよぷよeスポーツは有料です。

https://store.steampowered.com/app/971620/_e/?l=japanese



画像の取り扱いについては以下のサイトを参照してください。

https://puyo.sega.jp/PuyoPuyo_eSports/agreement/index.html

©SEGA

## library
keyboard, pyautogui, pygetwindow,numpy　の4つをpipインストールする。


```python
import keyboard as kb
import pyautogui as ag
import pygetwindow as ag
import numpy as np
from PIL import Image,ImageDraw
import cv2
```


```python
im = Image.open("all1600x900.png")
im
```
![output_3_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/c6bde39c-1082-8cca-bef4-9d3fa81def18.png)




    




```python
w,h = im.size
print(w,h)
```

    1600 900



```python
np_image = np.array(im)
im1 = Image.fromarray(np_image[0:50,0:100])
im1
```


![output_5_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/cfc1d60c-a76d-afc8-1268-852112a1a3a2.png)


    

    



縦方向の位置、横方向の位置、RGB  
この順であることが分かりました。


```python
# 0:black 1:red 2:green 3:yellow 4:blue 5: purple 6:skyblue 7:white
def convert_rgb_8(rgb):
    res = 0
    if rgb[0] > 100:
        res += 1
    if rgb[1] > 100:
        res += 2
    if rgb[2] > 100:
        res += 4        
    return res

x1 = w//8
y1 = h//12
lx = x1
rx = x1*3
uy = y1
ly = y1*11
for i in range(x1,x1*3):
    uy1 = y1*11
    ly1 = y1 
    l_max = 0
    l = 0
    for j in range(y1,y1*11):
        rgb = np_image[j,i]
        if convert_rgb_8(rgb) == 0: # black
            l+=1
            if l > l_max:
                uy1 = j-l
                ly1 = j
                l_max = l
        else: #white
            l = 0
    if l_max > y1 * 5:
        uy = uy1
        ly = ly1
        lx = i
        break

for i in range(x1*3,x1,-1):
    uy1 = y1*11
    ly1 = y1 
    l_max = 0
    l = 0
    for j in range(y1,y1*11):
        rgb = np_image[j,i]
        if convert_rgb_8(rgb) == 0: # black
            l+=1
            if l > l_max:
                uy1 = j-l
                ly1 = j
                l_max = l
        else: #white
            l = 0
    if l_max > y1 * 5:
        rx = i
        break
puyo_w = (rx-lx) // 6
puyo_h = (ly-uy) // 12
print(lx,uy,rx,ly,puyo_w,puyo_h)
```

    227 132 559 735 55 50



```python
np_stage = np_image[uy:ly,lx:rx]
im_stage = Image.fromarray(np_stage)
im_stage

```


![output_8_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/0de7626c-3aa7-a26b-13dc-ea5fae922df7.png)


    



ある色だけを抜き出すのが、cv2.inRangeです。
そのために、RGBからHSVに変換します。
認識した部位が255で、違うのが0になります。


```python
def cv2_mask(np_im,color_num):
# 0:0-360:white 1:0-5:red  2:50-80:green 3:100-120:blue 4:15-40:yellow 5:130-150:purple
    color_cfg =[[0,0,200,359,49,255],[0,0,50,5,255,255],[50,0,50,80,255,255],
                [100,50,50,120,255,255],[15,50,50,40,255,255],[130,0,50,150,255,255]]

    cv2_im = cv2.cvtColor(np_im, cv2.COLOR_RGB2HSV)
    cc0 = color_cfg[0]
    msk0 =  cv2.inRange(cv2_im,(cc0[0],cc0[1],cc0[2]),(cc0[3],cc0[4],cc0[5]))
    cc = color_cfg[color_num]
    mask =  cv2.inRange(cv2_im,(cc[0],cc[1],cc[2]),(cc[3],cc[4],cc[5]))
    return mask | msk0

```

maskデータで、ビット単位のAND処理をします。すると、認識している場所の色だけが現れます。
ぷよの中は白いので、白もぷよとして認識しています。


```python
msk = cv2_mask(np_stage,5)
result = cv2.bitwise_and(np_stage, np_stage, mask=msk)
im_stage = Image.fromarray(result)
im_stage
```


![output_12_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/352c9efa-fdc7-e2d4-a11d-b52765b4137e.png)



    

    



全部の色をまとめて処理することにしました。


```python
def cv2_mask2(np_im):
# 0:0-360:white 1:0-5:red  2:50-80:green 3:100-120:blue 4:15-40:yellow 5:130-150:purple
    color_cfg =[[0,0,200,359,49,255],[0,0,50,5,255,255],[50,0,50,80,255,255],
                [100,50,50,120,255,255],[15,50,50,40,255,255],[130,0,50,150,255,255]]
    masks = []
    cv2_im = cv2.cvtColor(np_im, cv2.COLOR_RGB2HSV)
    cc0 = color_cfg[0]
    msk0 =  cv2.inRange(cv2_im,(cc0[0],cc0[1],cc0[2]),(cc0[3],cc0[4],cc0[5]))
    masks.append(msk0)
    for i in range(1,6):
        cc = color_cfg[i]
        mask =  cv2.inRange(cv2_im,(cc[0],cc[1],cc[2]),(cc[3],cc[4],cc[5]))
        masks.append(mask|msk0)
    return masks

msks = cv2_mask2(np_stage)
result = cv2.bitwise_and(np_stage, np_stage, mask=(msks[5]))
im_stage = Image.fromarray(result)
im_stage
```

![output_14_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/b0d63208-a3b0-ca62-f52c-1be46432afe0.png)



    

    



maskデータを場所ごと集計（平均化）して、半分以上があれば、その色と認識しています。


```python
def conv_masks_stage(masks):
    result = np.zeros((12,6))
    mask_h= masks[0].shape[0]
    mask_w= masks[0].shape[1]
    puyo_w = mask_w // 6
    puyo_h = mask_h // 12
    for i in range(6):
        for j in range(12):
            for k in range(1,6):
                c_ratio = np.mean(masks[k][puyo_h*j:puyo_h*(j+1),puyo_w*i:puyo_w*(i+1)])
                if c_ratio > 255 *0.5:
                    result[j,i] = k
    return result

conv_masks_stage(cv2_mask2(np_stage))

```




    array([[0., 0., 1., 0., 0., 0.],
           [0., 0., 0., 0., 0., 5.],
           [0., 0., 0., 0., 0., 2.],
           [0., 0., 0., 0., 0., 2.],
           [0., 0., 0., 0., 0., 1.],
           [0., 0., 0., 0., 0., 1.],
           [0., 0., 0., 0., 0., 4.],
           [0., 0., 0., 0., 0., 5.],
           [0., 0., 0., 0., 4., 5.],
           [0., 0., 0., 0., 4., 5.],
           [2., 5., 4., 0., 1., 2.],
           [1., 4., 4., 1., 4., 4.]])



認識できているようです。

***

![puyopuyo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3900427/1fdf8221-96c5-fcfa-3a52-383422a6fdc3.gif)
　ぷよぷよプログラミングAI学習システムは、中高生の自己調整学習に向けて開発されました。
　どうやって、Chromebookで、１０分で、素敵な人工知能を作れるのでしょうか？
　秘密は、３つ。
　１　jupyter liteで、webクライアントがpythonコードを実行
　２　超軽量な機械学習フレームワークdezeroを採用
　３　ぷよぷよのstageとactionがコンパクト

### Github&X

https://github.com/Lunanaedu/jupyterlite-puyopuyo

https://github.com/Lunanaedu/puyopuyo-kai

https://x.com/educationai_ai

### ぷよぷよプログラミングAI学習用まとめ

https://qiita.com/educational-ai/items/50c0fa689c01794d145e