HID ProMicro CH32V003 / Tools → USB: Keyboard+Mouse+WebHID / Sketch: MazeSolver.ino
※ Chrome / Edge のみ対応。接続後に「開始」ボタンが有効になります。
Feature Report 32バイト(Web → UIAPduino)/ Input Report 8バイト(UIAPduino → Web)
| 方向 | byte[0] | byte[1] | byte[2] | 説明 |
|---|---|---|---|---|
| Web→Arduino | 0x01 CMD_START | sx (x座標) | sy (y座標) | ゲーム開始・スタート座標通知 |
| Web→Arduino | 0x02 CMD_RESULT | result_code | — | 移動/センス要求への応答 |
| Web→Arduino | 0x03 CMD_MAZE_SIZE | width=15 | height=15 | 迷路サイズ通知 |
| Web→Arduino | 0x04 CMD_GOAL | gx (x座標) | gy (y座標) | ゴール座標通知 |
| Web→Arduino | 0x05 CMD_RESET | — | — | リセット |
| Arduino→Web | 0x10 CMD_MOVE | x (列) | y (行) | (x,y)へ移動要求・位置更新 |
| Arduino→Web | 0x11 CMD_SENSE | x (列) | y (行) | (x,y)を確認(移動しない) |
| Arduino→Web | 0x12 CMD_SOLVED | — | — | 解決完了通知 |
| Arduino→Web | 0x14 CMD_READY | — | — | 起動完了・接続確認 |
result_code: 0x00 OPEN 0x01 WALL 0x02 GOAL 0x03 START 0x04 OUT_OF_BOUNDS
X = 列(右方向)、Y = 行(下方向)、原点 (0,0) は左上。X=15(右端)または Y=15(下端)の出口セルです。loop() で受け取って
グローバル変数に保存されます。solveMaze() を呼ぶ時点では
以下の変数が利用可能です:startX, startY — スタート座標goalX, goalY — ゴール座標mazeW, mazeH — 迷路サイズ(どちらも 15)
maze.sense(x, y) は移動せずにそのセルの状態を確認します。maze.moveTo(x, y) はそのセルへの移動を試みます。MazeHID::RESULT_OPEN (0) — 通路(移動可)MazeHID::RESULT_WALL (1) — 壁(移動不可)MazeHID::RESULT_GOAL (2) — ゴール!MazeHID::RESULT_START (3) — スタート地点(移動可)MazeHID::RESULT_OUT (4) — 範囲外(x±1, y) または (x, y±1) です。斜め移動はありません。isPassable(r) は RESULT_OPEN / RESULT_START / RESULT_GOAL
のいずれかなら true を返す便利なヘルパーです。isValidTarget(nx, ny) は座標が迷路内またはゴール出口なら true を返します。
dir(0=右, 1=下, 2=左, 3=上)として管理します:(dir+1)&3 を試すdir を試す(dir+3)&3 を試す(dir+2)&3 へ戻るconst int8_t dx[] = { 1, 0, -1, 0 };
const int8_t dy[] = { 0, 1, 0, -1 };
uint8_t x = startX, y = startY;
uint8_t dir = 0; // 最初は右向き
while (true) {
const uint8_t order[] = {
(dir+1)&3, dir, (dir+3)&3, (dir+2)&3
};
for (uint8_t i = 0; i < 4; i++) {
uint8_t d = order[i];
int8_t nx = (int8_t)x + dx[d];
int8_t ny = (int8_t)y + dy[d];
if (!isValidTarget(nx, ny)) continue;
uint8_t r = maze.moveTo((uint8_t)nx, (uint8_t)ny);
if (r == MazeHID::RESULT_GOAL) return;
if (r == MazeHID::RESULT_OPEN || r == MazeHID::RESULT_START) {
x = (uint8_t)nx; y = (uint8_t)ny;
dir = d;
break;
}
}
}
sense() で壁か確認してから moveTo() で進み、
行き止まりなら1つ前の座標へ moveTo() で戻ります:
bool visited[16][16] = {};
uint8_t stackX[256], stackY[256];
uint8_t nextDir[256];
const int8_t dx[] = { 1, 0, -1, 0 };
const int8_t dy[] = { 0, 1, 0, -1 };
uint16_t sp = 0;
stackX[sp] = startX; stackY[sp] = startY;
nextDir[sp] = 0;
visited[startY][startX] = true;
while (true) {
uint8_t x = stackX[sp], y = stackY[sp];
bool advanced = false;
while (nextDir[sp] < 4) {
uint8_t d = nextDir[sp]++;
int16_t nx = (int16_t)x + dx[d];
int16_t ny = (int16_t)y + dy[d];
if (!isValidTarget(nx, ny)) continue;
if (visited[ny][nx]) continue;
if (!isPassable(maze.sense((uint8_t)nx, (uint8_t)ny))) continue;
uint8_t mr = maze.moveTo((uint8_t)nx, (uint8_t)ny);
if (mr == MazeHID::RESULT_GOAL) return;
if (mr == MazeHID::RESULT_OPEN || mr == MazeHID::RESULT_START) {
sp++;
stackX[sp] = (uint8_t)nx; stackY[sp] = (uint8_t)ny;
nextDir[sp] = 0;
visited[ny][nx] = true;
advanced = true; break;
}
}
if (advanced) continue;
// 行き止まり → バックトラック
if (sp == 0) return;
sp--;
maze.moveTo(stackX[sp], stackY[sp]);
}
ボード: HID ProMicro CH32V003 / Tools → USB: Keyboard+Mouse+WebHID