乙Py先生のプログラミング教室

乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

今回は仮想通貨の自動売買ロジックの
組み立て方のチュートリアルコードを
動かしてみました。


解説動画はこちら



はじめに


今回は書籍
「日給300万円のSS級トレーダーが明かすbotterのリアル」
のチュートリアルコードを動かす方法などについてです。


書籍画像
No description has been provided for this image


チュートリアルのコードはこちら
githubのコード

こちらのコードは
そのままでは動かない可能性もあるので
それ含めて動かせる様にしてみました。


チュートリアル環境の構築


1.Dockerを用いる方
Readme通りにやるだけです。
# コードのクローン
git clone https://github.com/richmanbtc/mlbot_tutorial.git

# Dockerの起動
cd mlbot_tutorial
docker-compose up -d

# Jupyterの起動
# http://localhost:8888 をブラウザで開く

2.自分のJupyter環境 or Colabでやる方
必須ライブラリをインストールしておく必要があります。
# Jupyterの人は全部
ccxt : 仮想通貨取引用のライブラリ
TA-lib : 指標の計算ライブラリ

# Colabの人は不要
numba : Pythonの高速化ライブラリ
scikit-learn : 機械学習用ライブラリ
lightGBM : 機械学習のモデル構築ライブラリ

なおインストール方法は割愛します。
インストールが自力で進める方だけ
進んでください。


チュートリアルの進め方


ここからはチュートリアルのコードを
そのまま進める形になります。

チュートリアルのコードを
そのまま実行してみて下さい。

チュートリアルはこのような
内容になっていました。

1.ライブラリのインポート
2.データ取得
3.データ読み込み
4.前処理
5.目的変数の計算
6.モデルの学習
7.バックテスト


このうち
2.データ取得
3.データ読み込み
に関しては環境によっては
うまく行えない可能性がありました。

ライブラリの差異などにより
うまくデータが生成されませんでした。

なので代わりのコードを
置いておきます。

データ取得のコード
import urllib.request
import os
import time
from datetime import datetime, timedelta
# SSLの問題が有ったら追加
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

def get_date_range(start_date, end_date):
    start = datetime.strptime(start_date, '%Y-%m-%d')
    end = datetime.strptime(end_date, '%Y-%m-%d')
    date_range, current_date= [],start
    while current_date <= end:
        date_range.append((current_date.year, current_date.month, current_date.day))
        current_date += timedelta(days=1)
    return date_range

def get_data(market,start_date,end_date,data_dir = "data"):
    data_dir = f"{data_dir}/{market}"
    url_base = 'https://api.coin.z.com/data/trades/{0}/{1}/{2:02}/{1}{2:02}{3:02}_{0}.csv.gz'
    dates = get_date_range(start_date, end_date)
    for d in dates:
        year,month,day = d
        url = url_base.format(
            market,
            year,
            month,
            day
        )
        file_name = os.path.basename(url)
        if not os.path.exists(f"{data_dir}/{year}"):
            os.makedirs(f"{data_dir}/{year}")
        save_path = os.path.join(f"{data_dir}/{year}", file_name)
        urllib.request.urlretrieve(url, save_path)
        time.sleep(1.37)
# マーケットと開始日、終了日を指定
market = "BTC_JPY"
start_date = "2024-01-01"
end_date = "2024-02-22"
get_data(market,start_date,end_date)

実行するとこのコードが置いてある
ディレクトリ配下にdataが配置されます。

データの読み込み
import os
import glob

def make_df(file_path,interval_sec):
    df = pd.read_csv(file_path)
    df = df.rename(columns={'symbol': 'market',})
    df['price'] = df['price'].astype('float64')
    df['size'] = df['size'].astype('float64')
    df['timestamp'] = pd.to_datetime(df['timestamp'], utc=True)
    df['side'] = np.where(df['side'] == 'BUY', 1, -1).astype('int8')
    df['timestamp'] = df['timestamp'].dt.floor('{}S'.format(interval_sec))
    df.set_index("timestamp", inplace=True)
    volume = df.groupby('timestamp')['size'].sum().rename('volume')
    ohlc = df['price'].resample('1T').ohlc()
    df_merged = ohlc.merge(volume, left_index=True, right_index=True,how="left")
    df_merged = df_merged.fillna(method="ffill")
    df_merged = df_merged.rename(columns={"open":"op","high":"hi","low":"lo","close":"cl"})
    return df_merged
# データの保存場所を指定
data_dir = "data/BTC_JPY/2024/"
interval_sec = 60
df = pd.DataFrame()

for file_path in sorted(glob.glob(data_dir + "*.gz")):
    tmp_df = make_df(file_path,60)
    df = pd.concat([df,tmp_df],axis=0)

こちらのコードを実行すると
変数 df が生成されます。

チュートリアルで用いられている
データを読み込んだ変数になっているので
そのまま使用する事ができると思います。

データが読み込めたかどうかは
次のコードで確認してみて下さい。
df[["cl"]].plot(figsize=(12,4))
plt.show()
download


前処理部分のコードの追加

コードの前処理部分の最初で
手数料を記入するコードがあります。

チュートリアルが作成されてからだいぶ経っており
手数料などが更新されていないため
適宜追加してみて下さい。

maker_fee_history = [
    {
        # https://coin.z.com/jp/news/2020/08/6482/
        # 変更時刻が記載されていないが、定期メンテナンス後と仮定
        'changed_at': '2020/08/05 06:00:00Z',
        'maker_fee': -0.00035
    },
    {
        # https://coin.z.com/jp/news/2020/08/6541/
        'changed_at': '2020/09/09 06:00:00Z',
        'maker_fee': -0.00025
    },
    {
        # https://coin.z.com/jp/news/2020/10/6686/
        'changed_at': '2020/11/04 06:00:00Z',
        'maker_fee': 0.0
    },

    ### 追加
    {
        # 現在値
        'changed_at': '2023/08/05 06:00:00Z',
        'maker_fee': -0.0003
    },
]

あとはチュートリアルを
上から実行していけば
動いていくだろうと思います。

累積リターンのところで
次のような画像が出ればほぼほぼ成功してると思います。

download-1



特徴量の重要度の算出


一般的な機械学習では
予測にどの変数が寄与したのか
重要度を算出することが多いです。

こちらがチュートリアルの
モデルの重要度を算出するコードになります。
feature_importance = model.feature_importances_

# 特徴量名と重要度を紐づける
feature_importance_df = pd.DataFrame({'Feature': features, 'Importance': feature_importance})

# 重要度の降順でソート
feature_importance_df = feature_importance_df.sort_values(by='Importance', ascending=False).reset_index(drop=True)

# 結果の表示(トップ10)
feature_importance_df.head(10)
Feature Importance
0 HT_DCPERIOD 145
1 ADXR 137
2 BBANDS_upperband 125
3 KAMA 124
4 BBANDS_lowerband 116
5 LINEARREG 114
6 MFI 109
7 MACD_macdsignal 107
8 HT_PHASOR_quadrature 106
9 HT_DCPHASE 102


この意味合いとしては
chatGPTによると次の様な意味になっています。



HT_DCPERIOD: Hilbert Transform - Dominant Cycle Period
(ヒルベルト変換 - 優勢サイクル期間)と呼ばれる指標
時系列データの優勢周期を表します。この特徴量は
トレンドのサイクル性を捉えるために使用されます。


ADXR: Average Directional Movement Index Rating
(平均方向運動指数レーティング)は
トレンドの強さを測定する指標

特に、ADX(Average Directional Index)に基づいて計算され
トレンドの強さを示します。


KAMA: Kaufman's Adaptive Moving Average
(カウフマンの適応的移動平均)は移動平均線の一種
価格変動の速さに応じて重みを調整し
より滑らかな移動平均線を提供します。

BBANDS_upperbandおよびBBANDS_lowerband: Bollinger Bands
(ボリンジャーバンド)は、移動平均線を中心に上下に
標準偏差を加えたバンドをプロットする指標

上側バンドと下側バンドは、価格の上下の範囲を示し
価格の過熱やサポート/レジスタンスのレベルを示すのに使用されます。


LINEARREG: Linear Regression(線形回帰)は
価格や指標の値の線形トレンドを推定するための手法です。

この特徴量は、過去のデータからの線形回帰に基づいて
将来のトレンドを予測するために使用されます。


HT_PHASOR_quadratureおよびHT_PHASOR_inphase:
Hilbert Transform - Phasor Components
(ヒルベルト変換 - フェーザーコンポーネント)は
サイン波の位相成分と直交成分を表す指標です。

これらの特徴量はサイクル性や位相関係を捉えるために使用されます。

MFI: Money Flow Index(マネーフローインデックス)は
取引量と価格の関係を用いて買い圧力と売り圧力を計算する指標
主に過買いや過売りの状態を示すのに使用されます。


BETA: ベータは、株式のリスクを示す指標で
市場全体(代表的な指数など)との相関関係を指します。

ベータが1より大きい場合、株価の変動が市場の変動よりも
大きいことを示し、逆にベータが1より小さい場合はその逆を意味します。


ULTOSC: Ultimate Oscillator(アルティメットオシレータ)は
短期、中期、長期の3つの期間を用いてトレンドの強さを計算する指標
主に過買いや過売りの状態を示すのに使用されます。


HT_DCPHASE: Hilbert Transform - Dominant Cycle Phase
(ヒルベルト変換 - 優勢サイクル位相)は
優勢サイクルの位相を表す指標です。この特徴量は
トレンドの位相情報を捉えるために使用されます。


STOCHF_fastk: Stochastic Fast %K
(ストキャスティクスファスト%K)は
価格の変動範囲内での位置を示す指標
主に過買いや過売りの状態を示すのに使用されます。


おまけ

自分もチュートリアルを参考に
別のモデルを構築してみました。

説明変数は同一
目的変数を5分後の価格
に設定し

学習期間は
予測行の前期間step分のみ(step=30)
というモデルにし

1分ずつスライドして
学習しては予測を行うようにしました。

これが予測結果です。

実測と予測値の差分のプロット
download-3

実測と予測値の差分のヒストグラム
download-2

実測と予測のプロット
download-4


学習と予測を近いところで行っているので
ズレすぎることは少ないですね。

最後に予測が現在価格を上まっている条件で
5分後価格と現在価格の差分の累計をプロット
download-5

見事に右肩上がり
予測はかなり機能している様に見えました。


最後に

あくまでシミュレーション上ですが
予測が機能している様に見えるので
売買のロジックに組み込むことも出来るかもしれません。

簡易なロジックとしては
5分後価格予測が現在価格を上まっていたら買い
5分後に売る

5分後価格予測が現在価格を下まっていたら売り
5分後に買う

というような感じですね。

仮の予測モデルは出来ているので
あとは売買ロジック周りを構築すれば
仮想通貨の自動売買botが出来上がります。

その辺りも、もう少し進めていけたらと
思っています。

今回は仮想通貨の自動売買bot本の
チュートリアルを動かしてみたでした。

それでは。

今回はPandas_Datareaderを使って
日本の株価を取得して分析する方法についてです。

解説動画はこちら


 
始めに


最近、日本の株価がバブル期並みの
高音を付けている様です。

この際
Pythonで株価のデータ分析が行える様に
なってみるのも面白いかと思います。

日本の株価を取得するには
pandas_datareaderというものを使います。

Google Colabであれば
最初から使えるので、こちらを使っていきましょう。


pandas_datareader


様々な金融商品のデータを取得する事ができます
その中でも、日本の株価は「stooq」というもので
ポーランドかどこかの会社のデータの様です。

こちらを取得して分析を始めていきましょう。


基本的な使い方はこのようになっています。
import pandas_datareader.data as web

web.DataReader(銘柄コード, data_source='stooq', start=開始日,end=終了日)
株価は銘柄ごとのデータになっているので
銘柄コードを指定して取得します。

データを取得するコードはこちら
# ライブラリのインポート
import os
import datetime as dt
import pandas_datareader.data as web
import plotly.graph_objects as go
import pandas as pd

# 銘柄コード入力(7203はトヨタ自動車)
code = "7203"
symbol = code + ".JP"

# 2023-01-01以降の株価取得
start , end = '2023-01-01', dt.date.today()

#データ取得
df = web.DataReader(symbol, data_source='stooq', start=start,end=end)
df.insert(0, "code", code, allow_duplicates=False)
df = df.sort_index()
df.tail(3)
スクリーンショット 2024-02-17 17.28.58

銘柄を変えたい場合は
codeの数字を変えて下さい。



株価から指標を計算してみる


ここからは取得したデータを使って
株価の分析に必要な指標を計算していきます。


移動平均
過去X日間の価格の平均値です
通常は終値を使います。


ボリンジャーバンド

移動平均を表す線と
その上下に値動きの幅を示す線を加えた指標で
「価格の大半がこの帯(バンド)の中に収まる」という
統計学を応用したテクニカル指標のひとつです。

+-1-3@までが一般的に良く用いられます。
@は標準偏差の様な意味合いです。

移動平均などの指標の計算
# 移動平均を計算する期間
period = 20

# 移動平均を計算
df[f'MA{period}'] = df['Close'].rolling(window=period).mean()

# ボリンジャーバンドを計算
std = df['Close'].rolling(window=period).std()
df['Upper Band1'] = df[f'MA{period}'] + 1 * std
df['Upper Band2'] = df[f'MA{period}'] + 2 * std
df['Upper Band3'] = df[f'MA{period}'] + 3 * std
df['Lower Band1'] = df[f'MA{period}'] - 1 * std
df['Lower Band2'] = df[f'MA{period}'] - 2 * std
df['Lower Band3'] = df[f'MA{period}'] - 3 * std

指標が計算できたら描画してみましょう。
描画には「Plotly」を用います。

これはライブで操作が出来る描画ライブラリで
静止画ではないので、色々な操作が行え捗ります。

# プロット用のデータを作成
data = [
    go.Candlestick(
        x=df.index,
        open=df['Open'],
        high=df['High'],
        low=df['Low'],
        close=df['Close'],
        name='株価',
    ),
    go.Scatter(x=df.index,y=df['Upper Band2'],mode='lines',name='+2@',),
    go.Scatter(x=df.index,y=df['Upper Band1'],mode='lines',name='+1@',),
    go.Scatter(x=df.index,y=df[f'MA{period}'],mode='lines',name='移動平均',),
    go.Scatter(x=df.index,y=df['Lower Band2'],mode='lines',name='-2@',),
    go.Scatter(x=df.index,y=df['Lower Band1'],mode='lines',name='-1@',),
]

# レイアウト設定
layout = go.Layout(
    title='株価の移動平均とボリンジャーバンド',
    xaxis=dict(
        title='日付',
        tickformat='%Y-%m-%d'
    ),
    yaxis=dict(title='株価'),
)

# グラフを描画
fig = go.Figure(data=data, layout=layout)
fig.show()
スクリーンショット 2024-02-17 17.29.13



ここからはテクニカル指標を
計算して描画していきましょう。
# RSIを計算する関数
def calculate_rsi(data, window=14):
    delta = data.diff()
    up = delta.copy()
    down = delta.copy()
    up[up < 0] = 0
    down[down > 0] = 0
    avg_gain = up.rolling(window).mean()
    avg_loss = abs(down.rolling(window).mean())
    rs = avg_gain / avg_loss
    rsi = 100 - (100 / (1 + rs))
    return rsi

# ストキャスティクスを計算する関数
def calculate_stochastic_oscillator(data, window=14):
    lowest_low = data.rolling(window).min()
    highest_high = data.rolling(window).max()
    stochastic_oscillator = (data - lowest_low) / (highest_high - lowest_low) * 100
    return stochastic_oscillator

# MACDを計算する関数
def calculate_macd(data, short_window=12, long_window=26, signal_window=9):
    short_ema = data.ewm(span=short_window, adjust=False).mean()
    long_ema = data.ewm(span=long_window, adjust=False).mean()
    macd = short_ema - long_ema
    signal_line = macd.ewm(span=signal_window, adjust=False).mean()
    histogram = macd - signal_line
    return macd, signal_line, histogram

# RSIを計算
df['RSI'] = calculate_rsi(df['Close'])

# ストキャスティクスを計算
df['%K'] = calculate_stochastic_oscillator(df['Close'])
df['%D'] = df['%K'].rolling(3).mean()

# MACDを計算
macd, signal_line, histogram = calculate_macd(df['Close'])
df['MACD'] = macd
df['Signal'] = signal_line
df['Histogram'] = histogram



MACD(移動平均収束拡散手法)
短期の移動平均線と中長期の移動平均線を使用することで
買いと売りを判断する手法です。

MACDは基本となる線(MACD線)
MACDの移動平均線である
シグナル線の推移でマーケットを判断

MACD線がシグナル線を上抜けると
ゴールデンクロス(買いタイミング)

MACD線がシグナル線を下抜けると
デッドクロス(売りタイミング)


# MACD
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['MACD'], line=dict(color='red', width=1), name="macd", yaxis='y2'),
                go.Scatter(x=df.index, y=df['Signal'], line=dict(color='blue', width=1), name="signal", yaxis='y2')
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[min(df.Signal.min(), df.MACD.min()),
                              max(df.MACD.max(), df.Signal.max())]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.28


RSI
「Relative Strength Index」の略で
日本語に訳すと「相対力指数」
買われすぎか、売られすぎかを判断するための指標です。

過去一定期間の上げ幅(前日比)の合計を
同じ期間の上げ幅の合計と下げ幅の合計を足した数字で割って
100を掛けたもの

一般的に70~80%以上で買われすぎ
20~30%以下で売られすぎと判断します。

# RSI
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['RSI'], line=dict(color='blue', width=1), name="RSI", yaxis='y2'),
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[df.RSI.min(), df.RSI.max()]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.38

ストキャスティクス

チャートは%K(Fast)と%D(Slow)の2本の線で表され
数値は0%から100%の範囲で推移します。

%Kラインは相場に対して敏感に動き
Dラインは%Kラインよりも遅く動きますが
より重要とされているのは%Dラインです。

一般的には20~30%以下で売られすぎ
70~80%以上で買われすぎと判断しますが
相場の勢いが強い場合には
20%や80%を突破することもあります。


# ストキャスティクス
fig = go.Figure(
        data=[
                go.Candlestick(
                    x=df.index,
                    open=df['Open'],
                    high=df['High'],
                    low=df['Low'],
                    close=df['Close'],
                    name='株価',
                    yaxis='y1'
                ),
                go.Scatter(x=df.index, y=df['%K'], line=dict(color='blue', width=1), name="%K", yaxis='y2'),
                go.Scatter(x=df.index, y=df['%D'], line=dict(color='red', width=1), name="%D", yaxis='y2')
            ],
        layout = go.Layout(
                xaxis=dict(
                    title='日付',
                    tickformat='%Y-%m-%d'
                ),
                yaxis = dict(title='株価', side='left', showgrid=False, range=[df.Low.min(), df.High.max()]),
                yaxis2 = dict(title='指標', side='right', overlaying='y',
                              range=[min(df['%K'].min(), df['%D'].min()),
                              max(df['%K'].max(), df['%D'].max())]
                              )
        )
)
fig.show()
スクリーンショット 2024-02-17 17.29.49


トレードシミュレーション

ここからは株価と指標を用いて
売買のシミュレーションを行ってみましょう。

MACDでエントリーポイントを決めて売買する
簡単なシミュレーションを行ってみます。

# トレードのシミュレーションを行う関数
def simulate_trades(df):
    positions = []  # トレードのポジション(買い or 売り)
    entry_prices = []  # エントリー価格
    exit_prices = []  # エグジット価格
    pnl = []  # 損益

    for i in range(len(df)):
        if i == 0:
            positions.append(None)
            entry_prices.append(None)
            exit_prices.append(None)
            pnl.append(None)
        else:
            if df['MACD'][i] > df['Signal'][i] and df['MACD'][i-1] <= df['Signal'][i-1]:
                # MACDがシグナルを上回った時に買いポジション
                positions.append('Buy')
                entry_prices.append(df['Close'][i])
                exit_prices.append(None)
                pnl.append(None)
            elif df['MACD'][i] < df['Signal'][i] and df['MACD'][i-1] >= df['Signal'][i-1]:
                # MACDがシグナルを下回った時に売りポジション
                positions.append('Sell')
                entry_prices.append(df['Close'][i])
                exit_prices.append(None)
                pnl.append(None)
            elif positions[-1] == 'Buy':
                # 買いポジションを保持している場合
                positions.append('Buy')
                entry_prices.append(entry_prices[-1])
                exit_prices.append(df['Close'][i])
                pnl.append(df['Close'][i] - entry_prices[-1])
            elif positions[-1] == 'Sell':
                # 売りポジションを保持している場合
                positions.append('Sell')
                entry_prices.append(entry_prices[-1])
                exit_prices.append(df['Close'][i])
                pnl.append(entry_prices[-1] - df['Close'][i])
            else:
                positions.append(None)
                entry_prices.append(None)
                exit_prices.append(None)
                pnl.append(None)

    df['Position'] = positions
    df['Entry Price'] = entry_prices
    df['Exit Price'] = exit_prices
    df['P&L'] = pnl
    return df

# トレードのシミュレーションを実行
df = simulate_trades(df)

結果を描画してみると

# プロット用のデータを作成
buy_indices = df[df['Position'] == 'Buy'].index
sell_indices = df[df['Position'] == 'Sell'].index

fig = go.Figure()

# Closeをプロット
fig.add_trace(go.Scatter(x=df.index, y=df['Close'], name='Close', mode='lines'))

# Entry Priceをプロット
fig.add_trace(go.Scatter(x=df.loc[buy_indices].index, y=df.loc[buy_indices]['Entry Price'],
                         name='Entry Price (BUY)', mode='markers', marker=dict(color='blue', size=8)))
fig.add_trace(go.Scatter(x=df.loc[sell_indices].index, y=df.loc[sell_indices]['Entry Price'],
                         name='Entry Price (SELL)', mode='markers', marker=dict(color='red', size=8)))

# Exit Priceをプロット
fig.add_trace(go.Scatter(x=df.loc[buy_indices].index, y=df.loc[buy_indices]['Exit Price'],
                         name='Exit Price (BUY)', mode='markers', marker=dict(color='blue', size=8, symbol='triangle-up')))
fig.add_trace(go.Scatter(x=df.loc[sell_indices].index, y=df.loc[sell_indices]['Exit Price'],
                         name='Exit Price (SELL)', mode='markers', marker=dict(color='red', size=8, symbol='triangle-down')))

# P&Lをプロット(二番目の軸)
fig.add_trace(go.Scatter(x=df.index, y=df['P&L'], name='P&L', mode='lines', yaxis='y2'))

# レイアウト設定
fig.update_layout(title='Trading Simulation',
                  xaxis_title='Date',
                  yaxis=dict(title='Price', side='left'),
                  yaxis2=dict(title='P&L', side='right', overlaying='y', showgrid=False),
                  legend=dict(x=0, y=1),
                  hovermode='x unified')

# グラフの表示
fig.show()
スクリーンショット 2024-02-17 17.28.45

こんな感じで、売買ポイントの可視化が出来ました。

今回は簡易なデータ取得と
分析手法になっているので
気軽に始められるかと思います。

色々試してみて下さいね
それでは。


今回は迷路の自販機を
再現するコードの実装です。

解説動画はこちら




迷路の自販機

先日、テレビで迷路の自販機なるものを
特集していました。


こんな感じのものですね

お金を入れるとその分の長さの
迷路が生成されて
プリントされるというものです。

高いほど長くなる仕組みの様です。

迷路を自動生成する事ができれば
これを再現する事ができるんじゃ
ないかなと思います。

早速コードを見てみましょう。


迷路作成コード

迷路を作成する部分と
画像を生成する部分です。
import random
import sys
from PIL import Image
import matplotlib.image as mpimg
import matplotlib.pyplot as plt
sys.setrecursionlimit(10**6)

# 迷路を作成する
def make_maze(size):

    def make(ny, nx,tmp_maze):
        ar = list(range(4)) 
        random.shuffle(ar)
        for i in ar:
            if ny+dy[i][1]<1 or ny+dy[i][1]>=size[0]:
                continue
            if nx+dx[i][1]<1 or nx+dx[i][1]>=size[1]:
                continue
            if tmp_maze[ny+dy[i][1]][nx+dx[i][1]]==0:
                continue
            for j in range(2):
                tmp_maze[ny+dy[i][j]][nx+dx[i][j]]=0
            make(ny+dy[i][1], nx+dx[i][1],tmp_maze)
    
    tmp_maze = [[1]*size[1] for _ in range(size[0])]
    dx,dy = [(1,2), (-1,-2), (0,0), (0,0)],[(0,0), (0,0), (1,2), (-1,-2)]
    make(1, 1,tmp_maze)
    tmp_maze[1][1],tmp_maze[size[0]-2][size[1]-2] = 2,3
    return tmp_maze

# 画像を作成する
def create_image_from_maze(maze, file_path,pixel_size=20):
    # 色のマッピング
    colors = {
        0: (255, 255, 255),  # 白
        1: (0, 0, 0),        # 黒
        2: (0, 0, 255),      # 青
        3: (0, 255, 0)       # 緑
    }
    width = len(maze[0]) * pixel_size
    height = len(maze) * pixel_size
    image = Image.new("RGB", (width, height), "white")
    pixels = image.load()
    for y in range(len(maze)):
        for x in range(len(maze[0])):
            color = colors.get(maze[y][x], (255, 255, 255))
            for dy in range(pixel_size):
                for dx in range(pixel_size):
                    pixels[x * pixel_size + dx, y * pixel_size + dy] = color
    image.save(file_path)
    return image

実行する際はコレ
# 迷路を作成する
size = (31, 101) # height , width
maze = make_maze(size)

# 画像を生成
image = create_image_from_maze(maze,"image.png")
plt.figure(figsize=(16,10))
plt.imshow(image, cmap='gray')
plt.axis('off')
plt.show()
download

こんな感じで迷路が出来上がります。

黒が壁で、白が通路
青がスタート、緑がゴールです。

いい感じで横長の迷路が
出来ているんじゃないでしょうか

縦は31ピクセル固定として
横は100円分で
31ピクセルとかにすれば
いい感じに再現出来るんじゃないかと。

もっと大きな迷路も
作成は出来ると思うので
サイズ変更して試してみて下さい。

Google Colabなどで
実行できると思います。


今回は迷路の自販機を再現する
迷路作成のコードについてでした。

それでは。

このページのトップヘ