//中リール
#include <M5Unified.h>
#include <esp_now.h>
#include <WiFi.h>
// =========================================================================
// 1. 画像データ(幅135px × 高さ1680px、16bit RGB565)
// =========================================================================
const uint16_t reel_image_data[226800] PROGMEM = {
//----------------------カラーコードの塊のため省略----------------------
};
// =========================================================================
// 2. ソフトウェア制御用の設定
// =========================================================================
// --- 図柄の定義 ---
enum SymbolType {
GRAPE = 0, // ブドウ
SEVEN = 1, // 赤7
BAR = 2, // BAR
BELL = 3, // ベル
RHINO = 4, // ツノッチ(サイ)
CLOWN = 5, // ピエロ
CHERRY= 6 // チェリー
};
const int TOTAL_SYMBOLS = 21; // 総図柄数
const int MY_REEL_ID = 1;
// --- リールの配列 ---
const SymbolType reel_array[TOTAL_SYMBOLS] = {
RHINO, SEVEN, GRAPE, CHERRY, RHINO, BELL, GRAPE, CHERRY, RHINO, BAR, GRAPE,
CHERRY, RHINO, BELL, GRAPE, CHERRY, RHINO, BAR, GRAPE, CHERRY, CLOWN
};
// --- 内部フラグ(成立役)の定義 ---
enum InternalFlag {
FLAG_BLANK = 0, // ハズレ
FLAG_REPLAY = 1, // リプレイ
FLAG_GRAPE = 2, // ブドウ
FLAG_CHERRY = 3, // チェリー
FLAG_BELL = 4, // ベル
FLAG_CLOWN = 5, // ピエロ
FLAG_BIG = 6, // BIG
FLAG_REG = 7 // REG
};
InternalFlag current_flag = FLAG_BLANK; // 現在の内部状態
// --- 左リール 第一停止用の制御表(下段基準) ---
// [注]引数0~20→図柄番号21~1
const int slip_tables[8][21] = {
// [0] FLAG_BLANK (ハズレ)
{2, 0, 1, 2, 2, 4, 3, 1, 2, 3, 4, 0, 1, 2, 2, 3, 3, 4, 3, 0, 1},
// [1] FLAG_REPLAY (リプレイ)
{2, 2, 0, 1, 0, 1, 0, 2, 4, 4, 0, 1, 1, 3, 4, 4, 2, 2, 4, 0, 1},
// [2] FLAG_GRAPE (ブドウ)
{2, 1, 1, 2, 0, 1, 0, 2, 4, 4, 0, 1, 2, 0, 0, 0, 2, 4, 4, 0, 1},
// [3] FLAG_CHERRY (チェリー)
{3, 4, 0, 4, 0, 4, 0, 0, 1, 2, 1, 4, 3, 4, 0, 0, 0, 1, 2, 1, 4},
// [4] FLAG_BELL (ベル)
{0, 1, 2, 2, 4, 4, 4, 2, 4, 4, 0, 1, 2, 1, 0, 0, 2, 4, 4, 0, 1},
// [5] FLAG_CLOWN (ピエロ)
{2, 1, 0, 4, 0, 4, 0, 2, 4, 4, 0, 1, 0, 1, 2, 3, 3, 3, 4, 0, 1},
// [6] FLAG_BIG (BIGボーナス)
{2, 0, 1, 2, 2, 4, 4, 4, 2, 3, 4, 0, 1, 2, 2, 3, 3, 4, 3, 0, 1},
// [7] FLAG_REG (REGボーナス)
{2, 0, 1, 2, 2, 4, 4, 4, 2, 3, 4, 0, 1, 2, 2, 3, 3, 4, 3, 0, 1}
};
// =========================================================================
// 通信設定
// =========================================================================
// メイン機から受け取る「命令とテーブル」
typedef struct struct_main_to_reel {
int command;
int slip_table[21]; // メイン機が計算した21コマ分の滑り表
} struct_main_to_reel;
struct_main_to_reel receiveData;
// メイン機へ報告する「停止位置」
typedef struct struct_reel_to_main {
int reel_id = 1;
int stopped_index;
} struct_reel_to_main;
struct_reel_to_main reportData;
// 一斉送信(ブロードキャストアドレス)
uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
esp_now_peer_info_t peerInfo;
enum ReelState { STOPPED, SPINNING, SLIPPING };
ReelState reel_state = STOPPED;
// --- メイン機からデータを受け取った時の処理 ---
void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
// データの重さがメイン機からの「命令+テーブル」と一致した場合のみ処理
if (len == sizeof(struct_main_to_reel)) {
struct_main_to_reel temp;
memcpy(&temp, incomingData, sizeof(temp));
// commandが 99(全員一斉回転) か、自分のID(0〜2)宛ての専用テーブルの時のみ
if (temp.command == 99 || temp.command == MY_REEL_ID) {
memcpy(&receiveData, &temp, sizeof(receiveData));
if (temp.command == 99 && reel_state == STOPPED) {
reel_state = SPINNING;
Serial.println(">> 全リール一斉回転スタート!");
} else if (temp.command == MY_REEL_ID) {
Serial.println(">> AIから最新の滑りテーブルを受信・更新しました");
}
}
}
}
// =========================================================================
// ハードウェア設定
// =========================================================================
const int SYMBOL_W = 135; const int SYMBOL_H = 80;
const int REEL_H = 1680; const int WINDOW_H = 240;
M5Canvas canvas(&M5.Display);
int current_y = 0; int target_y = 0; int bita_y = 0; int scroll_speed = 48;
void setup() {
auto cfg = M5.config(); M5.begin(cfg); Serial.begin(115200);
M5.Display.setRotation(0); M5.Display.fillScreen(TFT_BLACK);
canvas.createSprite(SYMBOL_W, WINDOW_H); canvas.setSwapBytes(true);
WiFi.mode(WIFI_STA);
if (esp_now_init() != ESP_OK) return;
// 受信設定
esp_now_register_recv_cb(OnDataRecv);
// 送信設定
memcpy(peerInfo.peer_addr, broadcastAddress, 6);
peerInfo.channel = 0; peerInfo.encrypt = false;
esp_now_add_peer(&peerInfo);
}
void loop() {
M5.update();
// --- Aボタン(ストップボタン) ---
if (M5.BtnA.wasPressed()) {
if (reel_state == SPINNING) {
reel_state = SLIPPING;
bita_y = (current_y / SYMBOL_H) * SYMBOL_H;
int bita_top_index = bita_y / SYMBOL_H;
int bita_btm_index = (bita_top_index + 2) % TOTAL_SYMBOLS;
int slip_frames = receiveData.slip_table[bita_btm_index];
target_y = bita_y - (slip_frames * SYMBOL_H);
if (target_y < 0) target_y += REEL_H;
}
}
// --- 回転・滑り処理 ---
if (reel_state == SPINNING) {
current_y -= scroll_speed;
if (current_y < 0) current_y += REEL_H;
}
else if (reel_state == SLIPPING) {
int distance = current_y - target_y;
if (distance < 0) distance += REEL_H;
if (distance <= scroll_speed) {
current_y = target_y;
reel_state = STOPPED;
int top_index = current_y / SYMBOL_H;
int btm_index = (top_index + 2) % TOTAL_SYMBOLS;
reportData.reel_id = 1;
reportData.stopped_index = btm_index;
esp_now_send(broadcastAddress, (uint8_t *) &reportData, sizeof(reportData));
Serial.printf("【完了】下段インデックス %d で停止。メイン機へ報告しました。\n", btm_index);
} else {
current_y -= scroll_speed;
if (current_y < 0) current_y += REEL_H;
}
}
// --- 描画処理 ---
if (current_y + WINDOW_H <= REEL_H) {
canvas.pushImage(0, 0, SYMBOL_W, WINDOW_H, (uint16_t*)reel_image_data + (current_y * SYMBOL_W));
} else {
int first_part_h = REEL_H - current_y;
int second_part_h = WINDOW_H - first_part_h;
canvas.pushImage(0, 0, SYMBOL_W, first_part_h, (uint16_t*)reel_image_data + (current_y * SYMBOL_W));
canvas.pushImage(0, first_part_h, SYMBOL_W, second_part_h, (uint16_t*)reel_image_data);
}
canvas.pushSprite(0, 0); delay(5);
}