寄り道してRaspberryPi Picoで反射神経ゲームを作る

龍野プログラミングクラブはJR本竜野駅2階多目的室(上の写真)で定期開催しています。教材にそって進めるスタイルで運営しています。が、この日はすこし寄り道して、RaspberryPi Picoを使ってゲームを作ります。

タッチタイピングの練習とアルゴロジック

わたしが運営するクラブでは、かならずタッチタイピングを練習します。キーを打つことを、息をするようにできるまで練習します。

タッチタイピングで使うツールは、無料タイピングと寿司打と右手専用タイピングです。

無料タイピング https://manabi.benesse.ne.jp/gakushu/typing

寿司打 https://sushida.net/

右手専用タイピング https://typing.twi1.me/game/15206

この日は、メンバーが自己ベストを大幅に更新しました! 無料タイピングでも寿司打でも両方自己ベストを更新したので、一回限りのまぐれではありません。特異点を越えたかもしれません。

しばらく停滞が続いていたので、喜びもひとしおです。メンバーが嬉しいことは、わたしも嬉しいのです!

ただし、本番は別に用意しているので、タイピングとアルゴロジックを20分くらいで切り上げます。

Raspberrypi Picoで電子工作+プログラミング

前回メンバーに電子工作のゲームを見せたら、やりたい!と興味を持ったようなので、今回は寄り道してMicroPythonでプログラミングします。

プログラミングの前に、ブレットボードの上に電子回路を組み立てます。散々苦労して小学生でもかんたんにブレットボード上に電子回路を作れる資料を完成させました。ツールがどこかに公開されていればよかったのですが、公開されているツールではなかなかうまくいきませんでした。「無ければ作る」これが、わたしたちの合言葉です。

さて、回路制作はあっという間に終わります。

反射神経ゲームのコーディングはハードモードで

反射神経ゲームのコードをすべて公開しておきます。

import machine
import time
import random
from machine import Pin

# ピン設定
btn = Pin(9, Pin.IN, Pin.PULL_UP)
leds = [Pin(12, Pin.OUT), Pin(13, Pin.OUT), Pin(14, Pin.OUT), Pin(15, Pin.OUT)]  # 青、赤、黄、緑

# LEDの制御関数
def set_leds(b, r, y, g):
    values = [b, r, y, g]
    for i, led in enumerate(leds):
        led.value(values[i])

def all_leds_off(): set_leds(0, 0, 0, 0)
def all_leds_on(): set_leds(1, 1, 1, 1)

# ボタン関連
last_press_time = 0
debounce_ms = 50

def wait_for_button_press():
    global last_press_time
    
    while btn.value() == 1:  # ボタンが押されるまで待機
        time.sleep(0.01)
    
    current_time = time.ticks_ms()
    if time.ticks_diff(current_time, last_press_time) > debounce_ms:
        last_press_time = current_time
        while btn.value() == 0:  # ボタンが離されるまで待機
            time.sleep(0.01)
        return True
    return False
def check_button_press():
    global last_press_time
    
    if btn.value() == 0:  # ボタンが押されている
        current_time = time.ticks_ms()
        if time.ticks_diff(current_time, last_press_time) > debounce_ms:
            last_press_time = current_time
            while btn.value() == 0:  # ボタンが離されるまで待機
                time.sleep(0.01)
            return True
    return False

# メインゲームループ
def main():
    while True:
        try:
            all_leds_on()
            wait_for_button_press()  # 1回目
            all_leds_off()
            
            wait_time = random.uniform(1, 10)
            start_time = time.time()
            
            # フライング検出
            early_press = False
            while time.time() - start_time < wait_time:
                if check_button_press():
                    early_press = True
                    break
                time.sleep(0.01)
            
            if early_press:
                set_leds(0, 1, 1, 1)  # フライング: 青以外点灯
                wait_for_button_press()
                all_leds_off()
                time.sleep(1)
                continue
            
            leds[0].value(1)  # 青色LED点灯
            
            reaction_start = time.ticks_ms()
            wait_for_button_press()  # 3回目
            reaction_time = (time.ticks_ms() - reaction_start) / 1000
            
            # 反応時間に応じたLED点灯
            if reaction_time < 0.2:
                set_leds(1, 0, 0, 1)  # 青と緑
            elif reaction_time < 0.4:
                set_leds(1, 0, 1, 0)  # 青と黄
            else:
                set_leds(1, 1, 0, 0)  # 青と赤
            print(reaction_time)
            
            wait_for_button_press()  # 4回目
            all_leds_off()
            time.sleep(1)
            
        except KeyboardInterrupt:
            all_leds_off()
            print("プログラムを終了します")
            break
        except Exception as e:
            all_leds_off()
            print(f"エラー: {e}")
            time.sleep(3)

if __name__ == "__main__":
    main()

ここまでの取り組みで、このメンバーならこのコードをすべて時間内に入力して動かせるだろうと考えて、全部手入力してもらいました。

1時間位で見事完成。無事に動きました。

コードの意味をすべて理解するのは難しいものの、いつものパソコン画面で動くゲームとの違いを感じ取ってくれたようです。

今はまだ感じ取るだけで十分です。オブジェクト指向とか、例外処理とか、リスト内包表記なんてまだやっていませんから。進んでいくうちに、振り返ったらわかるようになります。

そして、電子工作のプログラムは、画面の中だけで完結するプログラムでは出てこない要素があります。それが「debounce_ms」が出てくるあたりです。これは、スイッチのチャタリング防止のコードで使うものです。

くわしくは書きませんが、電子工作は現実にある「モノ」を制御するため、理想通りの動きをしないことがあります。その一つがチャタリングです。

画面のコードだけを見ていたのではわからない、それが電子工作のプログラミングの面白さであって、難しさなのです。

ぜひそういう感性を養ってほしいと思います。

さて次回は?

もとに戻ってゲームプログラミングをしましょうか、もっとラズPicoをすすめましょうか。思案中です。