Skip to content

Instantly share code, notes, and snippets.

@takeru
Last active March 22, 2025 00:12
Show Gist options
  • Select an option

  • Save takeru/5fe4f5512dfde678403a9a688cbe7eee to your computer and use it in GitHub Desktop.

Select an option

Save takeru/5fe4f5512dfde678403a9a688cbe7eee to your computer and use it in GitHub Desktop.
M5 Atom Matrix LED Demo for OpenBlink
# M5 Atom Matrix Beautiful LED Demo
# 自然現象と数学的パターンを組み合わせた魅力的なデモ
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
#
# 使用できない機能:
# - Math モジュール(sin, cos, sqrt, atan2など)
# - rand - 乱数生成
# - LED.get - 現在のLED色の取得
# - respond_to? - メソッド存在チェック
# - reverse, reverse_each - 配列の逆順処理
# - 浮動小数点数の%演算
# - backtrace - エラーのスタックトレース
#
# 代替手段:
# - Math.sin → sin_approx関数で近似
# - Math.sqrt → 単純な距離計算(マンハッタン距離など)
# - Math.atan2 → 簡易的な角度計算
# - rand → 線形合同法で疑似乱数を自前で実装
# - reverse_each → downtoで代用
# - LED.get → 固定値で代用
# - 浮動小数点の剰余 → 整数での計算に変更
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256 # 0-255の範囲に正規化
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# オーロラのような波動パターン
def aurora_wave
phase = 0
intensity = 0
direction = 1
50.times do
5.times do |y|
5.times do |x|
wave = (sin_approx((x + phase) * 51) * 128) / 64 + 128
hue = (wave + y * 20 + phase * 5) % 256
brightness = intensity + ((sin_approx((x + y + phase) * 51) * 32) / 64 + 32)
rgb = hsv_to_rgb(hue, 255, brightness)
LED.set(rgb, y * 5 + x)
end
end
phase = (phase + 1) % 256
intensity += direction
if intensity >= 192 || intensity <= 64
direction = -direction
end
sleep 0.05
end
end
# 銀河渦巻きパターン
def galaxy_spiral
center_x = 2
center_y = 2
angle = 0
60.times do
25.times do |i|
x = i % 5
y = i / 5
dx = x - center_x
dy = y - center_y
# マンハッタン距離を使用
distance = dx.abs + dy.abs
# 簡易角度計算
spiral_angle = (((dx * 128) / (distance + 1)) + angle) % 256
hue = (spiral_angle + distance * 32) % 256
brightness = 255 - (distance * 48)
brightness = brightness < 0 ? 0 : brightness
rgb = hsv_to_rgb(hue, 192, brightness)
LED.set(rgb, i)
end
angle = (angle + 3) % 256
sleep 0.05
end
end
# 量子の波動関数的なパターン
def quantum_wave
time = 0
particles = []
5.times do |i|
particles << [(i * 51) % 256, i * 5] # [phase, position]
end
40.times do
# 古い粒子の跡をフェードアウト
25.times do |i|
LED.set([1, 1, 1], i)
end
# 粒子の更新と描画
particles.each_with_index do |p, idx|
phase, pos = p
# 波動関数的な動き
wave_effect = ((sin_approx(time * 25) * 2) / 64 + 2) % 5
new_pos = (pos + wave_effect) % 25
particles[idx][1] = new_pos
# 干渉パターンの生成
3.times do |i|
interference_pos = (new_pos + i) % 25
hue = (phase + i * 32 + time * 2) % 256
rgb = hsv_to_rgb(hue, 255, 255 - i * 64)
LED.set(rgb, interference_pos)
end
end
time += 1
sleep 0.05
end
end
# 生命の樹パターン
def tree_of_life
energy_points = [[2, 0], [1, 1], [3, 1], [2, 2], [0, 2], [4, 2], [1, 3], [3, 3], [2, 4]]
base_hue = 0
45.times do
# エネルギーの流れを表現
energy_points.each_with_index do |point, i|
x, y = point
pos = y * 5 + x
# エネルギーの脈動
pulse = (sin_approx(i + base_hue * 25) * 128) / 64 + 128
hue = (base_hue + pulse / 2) % 256
rgb = hsv_to_rgb(hue, 192 + pulse / 4, 192 + pulse / 4)
LED.set(rgb, pos)
# エネルギーの放射
[[-1,0], [1,0], [0,-1], [0,1]].each do |dx, dy|
nx = x + dx
ny = y + dy
if nx >= 0 && nx < 5 && ny >= 0 && ny < 5
npos = ny * 5 + nx
rgb2 = hsv_to_rgb(hue, 128, pulse / 2)
LED.set(rgb2, npos)
end
end
end
base_hue = (base_hue + 1) % 256
sleep 0.05
end
end
# メインループ
begin
while true do
aurora_wave # オーロラのような波動
sleep 0.2
galaxy_spiral # 銀河の渦巻き
sleep 0.2
quantum_wave # 量子の波動関数
sleep 0.2
tree_of_life # 生命の樹
sleep 0.2
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラーモードでLEDをゆっくりと赤く点滅
while true do
25.times { |i| LED.set([32, 0, 0], i) }
sleep 1
25.times { |i| LED.set([0, 0, 0], i) }
sleep 1
break if Blink.req_reload?
end
end
# M5 Atom Matrix LED Demo for OpenBlink
# Demo patterns ported from C++ to Ruby
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
#
# 使用できない機能:
# - LED.get - 現在のLED色の取得
# - rand - 乱数生成
# - respond_to? - メソッド存在チェック
# - reverse, reverse_each - 配列の逆順処理
# - 浮動小数点数の%演算
# - backtrace - エラーのスタックトレース
#
# 代替手段:
# - 乱数 → 線形合同法で疑似乱数を自前で実装
# - reverse_each → downtoで代用
# - LED.get → 固定値で代用
# - 浮動小数点の剰余 → 整数での計算に変更
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256 # 整数での剰余演算
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
# 256スケールで計算
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
# 最終的な値を4で割って明るさを1/4に
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
def demo1
# 渦巻きパターンの座標定義
spiral = [
12,17,22,23,24,
19,18,13,8,3,
4,9,14,13,12,
11,6,1,2,7,
8,13,18,17,16
]
hue = 0
# 渦巻き状に点灯
spiral.each_with_index do |pos, i|
h = (hue + (i * 256/25)) % 256
rgb = hsv_to_rgb(h, 255, 255)
LED.set(rgb, pos)
sleep 0.05
end
# 消灯エフェクト
(spiral.size - 1).downto(0) do |i|
LED.set([0, 0, 0], spiral[i])
sleep 0.05
end
end
def demo2
hue = 0
drops = [0, 0, 0, 0, 0] # 各列の落下位置
drop_speed = [1, 2, 1, 2, 1] # 各列の落下速度
wave = 0 # 波紋の中心位置
25.times do
# マトリックス風の落下エフェクト
5.times do |x|
# 落下する粒子の位置を更新
drops[x] = (drops[x] + drop_speed[x]) % 10
# 各列に光の粒子を描画
5.times do |y|
pos = y * 5 + x
if y == drops[x] / 2
rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 255)
LED.set(rgb, pos)
elsif y == (drops[x] / 2) - 1
rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 128)
LED.set(rgb, pos)
else
# フェードアウト効果を固定値で代用
LED.set([2, 2, 2], pos)
end
end
end
# 波紋エフェクト
center_x = wave % 5
center_y = wave / 5
5.times do |y|
5.times do |x|
pos = y * 5 + x
distance = (x - center_x).abs + (y - center_y).abs # マンハッタン距離
if distance < 3
rgb = hsv_to_rgb((hue + distance * 32) % 256, 192, 255 / (distance + 1))
LED.set(rgb, pos)
end
end
end
sleep 0.05
hue = (hue + 3) % 256
wave = (wave + 1) % 25
end
end
def demo3
hue = 0
pulse = 0
pattern = 0
pseudo_random = 0 # 疑似ランダム値
100.times do
# 全LEDをフェードアウト(固定値で代用)
25.times do |i|
LED.set([2, 2, 2], i)
end
case pattern % 4
when 0 # 爆発エフェクト
center_led = 12
25.times do |i|
distance = ((i % 5) - (center_led % 5)).abs + ((i / 5) - (center_led / 5)).abs
if distance == pulse / 8
rgb = hsv_to_rgb((hue + distance * 30) % 256, 255, 255)
LED.set(rgb, i)
end
end
when 1 # ジグザグパターン
5.times do |i|
pos = ((pulse + i) % 5) * 5 + (((pulse + i) / 5) % 2 == 1 ? 4 - i : i)
rgb = hsv_to_rgb((hue + i * 50) % 256, 255, 255)
LED.set(rgb, pos)
end
when 2 # ランダムフラッシュ(疑似ランダムに変更)
3.times do
pseudo_random = (pseudo_random * 17 + 23) % 25 # 簡単な線形合同法
rgb = hsv_to_rgb((hue + pseudo_random * 8) % 256, 255, 255)
LED.set(rgb, pseudo_random)
end
if pulse % 8 == 0
25.times do |i|
rgb = hsv_to_rgb(hue % 256, 128, 255)
LED.set(rgb, i)
end
end
when 3 # 螺旋状の高速回転
rotation_pos = pulse % 25
5.times do |i|
pos = (rotation_pos + i * 5) % 25
rgb = hsv_to_rgb((hue + i * 32) % 256, 255, 255)
LED.set(rgb, pos)
end
end
# 高速なストロボ効果(疑似ランダムに変更)
pseudo_random = (pseudo_random * 17 + 23) % 100 # 簡単な線形合同法
if pseudo_random < 20 # 20%の確率で発生
25.times do |i|
LED.set([64, 64, 64], i) # ストロボの明るさも調整
end
sleep 0.005
25.times do |i|
LED.set([0, 0, 0], i)
end
end
sleep 0.02
pulse += 1
hue = (hue + 3) % 256
# パターン切り替え
if pulse % 64 == 0
pattern += 1
# 切り替え時の特殊エフェクト
25.times do |i|
rgb = hsv_to_rgb((hue + i * 7) % 256, 255, 255)
LED.set(rgb, i)
end
sleep 0.05
end
end
end
# メインループ
begin
while true do
demo1
sleep 0.5
demo2
sleep 0.5
demo3
sleep 0.5
# OpenBlinkのリロードチェック
break if Blink.req_reload?
end
rescue => e
puts "Error occurred:"
puts " Class: #{e.class}"
puts " Message: #{e.message}"
# エラーモードでLEDを赤く点滅
n = 0
while true do
puts "Error mode: cycle #{n}"
25.times { |i| LED.set([64, 0, 0], i) } # 赤色で点灯
sleep 0.5
25.times { |i| LED.set([0, 0, 0], i) } # 消灯
sleep 0.5
break if Blink.req_reload?
n += 1
end
end
# M5 Atom Matrix LED Demo for OpenBlink
# Demo patterns ported from C++ to Ruby
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256 # 整数での剰余演算
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
# 256スケールで計算
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
# 最終的な値を4で割って明るさを1/4に
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
def demo1
# 渦巻きパターンの座標定義
spiral = [
12,17,22,23,24,
19,18,13,8,3,
4,9,14,13,12,
11,6,1,2,7,
8,13,18,17,16
]
hue = 0
# 渦巻き状に点灯
spiral.each_with_index do |pos, i|
h = (hue + (i * 256/25)) % 256
rgb = hsv_to_rgb(h, 255, 255)
LED.set(rgb, pos)
sleep 0.05
end
# 消灯エフェクト
(spiral.size - 1).downto(0) do |i|
LED.set([0, 0, 0], spiral[i])
sleep 0.05
end
end
def demo2
hue = 0
drops = [0, 0, 0, 0, 0] # 各列の落下位置
drop_speed = [1, 2, 1, 2, 1] # 各列の落下速度
wave = 0 # 波紋の中心位置
25.times do
# マトリックス風の落下エフェクト
5.times do |x|
# 落下する粒子の位置を更新
drops[x] = (drops[x] + drop_speed[x]) % 10
# 各列に光の粒子を描画
5.times do |y|
pos = y * 5 + x
if y == drops[x] / 2
rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 255)
LED.set(rgb, pos)
elsif y == (drops[x] / 2) - 1
rgb = hsv_to_rgb((hue + x * 32) % 256, 255, 128)
LED.set(rgb, pos)
else
# フェードアウト効果を固定値で代用
LED.set([2, 2, 2], pos)
end
end
end
# 波紋エフェクト
center_x = wave % 5
center_y = wave / 5
5.times do |y|
5.times do |x|
pos = y * 5 + x
distance = (x - center_x).abs + (y - center_y).abs # マンハッタン距離
if distance < 3
rgb = hsv_to_rgb((hue + distance * 32) % 256, 192, 255 / (distance + 1))
LED.set(rgb, pos)
end
end
end
sleep 0.05
hue = (hue + 3) % 256
wave = (wave + 1) % 25
end
end
def demo3
hue = 0
pulse = 0
pattern = 0
pseudo_random = 0 # 疑似ランダム値
100.times do
# 全LEDをフェードアウト(固定値で代用)
25.times do |i|
LED.set([2, 2, 2], i)
end
case pattern % 4
when 0 # 爆発エフェクト
center_led = 12
25.times do |i|
distance = ((i % 5) - (center_led % 5)).abs + ((i / 5) - (center_led / 5)).abs
if distance == pulse / 8
rgb = hsv_to_rgb((hue + distance * 30) % 256, 255, 255)
LED.set(rgb, i)
end
end
when 1 # ジグザグパターン
5.times do |i|
pos = ((pulse + i) % 5) * 5 + (((pulse + i) / 5) % 2 == 1 ? 4 - i : i)
rgb = hsv_to_rgb((hue + i * 50) % 256, 255, 255)
LED.set(rgb, pos)
end
when 2 # ランダムフラッシュ(疑似ランダムに変更)
3.times do
pseudo_random = (pseudo_random * 17 + 23) % 25 # 簡単な線形合同法
rgb = hsv_to_rgb((hue + pseudo_random * 8) % 256, 255, 255)
LED.set(rgb, pseudo_random)
end
if pulse % 8 == 0
25.times do |i|
rgb = hsv_to_rgb(hue % 256, 128, 255)
LED.set(rgb, i)
end
end
when 3 # 螺旋状の高速回転
rotation_pos = pulse % 25
5.times do |i|
pos = (rotation_pos + i * 5) % 25
rgb = hsv_to_rgb((hue + i * 32) % 256, 255, 255)
LED.set(rgb, pos)
end
end
# 高速なストロボ効果(疑似ランダムに変更)
pseudo_random = (pseudo_random * 17 + 23) % 100 # 簡単な線形合同法
if pseudo_random < 20 # 20%の確率で発生
25.times do |i|
LED.set([64, 64, 64], i) # ストロボの明るさも調整
end
sleep 0.005
25.times do |i|
LED.set([0, 0, 0], i)
end
end
sleep 0.02
pulse += 1
hue = (hue + 3) % 256
# パターン切り替え
if pulse % 64 == 0
pattern += 1
# 切り替え時の特殊エフェクト
25.times do |i|
rgb = hsv_to_rgb((hue + i * 7) % 256, 255, 255)
LED.set(rgb, i)
end
sleep 0.05
end
end
end
# メインループ
begin
while true do
demo1
sleep 0.5
demo2
sleep 0.5
demo3
sleep 0.5
# OpenBlinkのリロードチェック
break if Blink.req_reload?
end
rescue => e
puts "Error occurred:"
puts " Class: #{e.class}"
puts " Message: #{e.message}"
# エラーモードでLEDを赤く点滅
n = 0
while true do
puts "Error mode: cycle #{n}"
25.times { |i| LED.set([64, 0, 0], i) } # 赤色で点灯
sleep 0.5
25.times { |i| LED.set([0, 0, 0], i) } # 消灯
sleep 0.5
break if Blink.req_reload?
n += 1
end
end
# M5 Atom Matrix 日本の美しさを表現するLEDデモ
# 日本の伝統的な景色や文化を表現する5つのテーマによるアニメーション
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256 # 0-255の範囲に正規化
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# 疑似乱数生成(線形合同法)
def pseudo_random(seed)
(seed * 17 + 23) % 256
end
# 1. 桜 - 春の桜吹雪をイメージした淡いピンク色のエフェクト
def sakura_blossoms
petals = []
# 初期の花びらを生成
8.times do |i|
# [x, y, 速度, 揺れ位相]
petals << [i % 5, (i / 5 * 2) % 5, (i % 3) + 1, i * 32]
end
phase = 0
# 約10秒間のアニメーション
100.times do |time|
# 背景を暗く(春の夜空のような青みがかった黒)
25.times do |i|
LED.set([1, 1, 3], i)
end
# 桜の花びらを更新し描画
petals.each_with_index do |petal, i|
x, y, speed, wobble = petal
# 花びらが揺れながら下に落ちる動き
new_y = (y + speed) % 5
wobble_phase = (wobble + 7) % 256
wobble_x = (sin_approx(wobble_phase) * 2) / 64
new_x = (x + wobble_x) % 5
petals[i] = [new_x, new_y, speed, wobble_phase]
# 桜の色(淡いピンク)
petal_pos = new_y.to_i * 5 + new_x.to_i
# 明るさをsin波で変化させる
brightness = 128 + (sin_approx(time * 5 + i * 32) * 64) / 64
# 桜色のHSV: 約330-350°, 彩度はやや低め
hue = (342 + sin_approx(time + i * 32) * 10 / 64) % 256
saturation = 160 + (sin_approx(wobble_phase) * 32) / 64
rgb = hsv_to_rgb(hue, saturation, brightness)
LED.set(rgb, petal_pos)
# 花びらの軌跡(薄いピンク)
tail_y = (new_y - speed + 5) % 5
tail_pos = tail_y.to_i * 5 + new_x.to_i
tail_rgb = hsv_to_rgb(hue, saturation / 2, brightness / 3)
LED.set(tail_rgb, tail_pos)
end
# 時々、新しい花びらを生成
if time % 10 == 0
seed = phase + time
new_x = seed % 5
# 花びらは画面上部から降ってくる
petals[time % 8] = [new_x, 0, (seed % 3) + 1, seed * 32]
end
phase = (phase + 1) % 256
sleep 0.1 # 10秒で約100フレーム
end
end
# 2. 富士山 - 青と白のグラデーションで富士山と日の出を表現
def fuji_sunrise
# 富士山の形状(5x5のLEDマトリックス上)
fuji_shape = [
0, 0, 0, 0, 0,
0, 0, 1, 0, 0,
0, 1, 1, 1, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1
]
# 日の出のアニメーション
100.times do |time|
sun_pos = 5 - time / 20 # 日の位置(下から上へ)
5.times do |y|
5.times do |x|
pos = y * 5 + x
# 富士山の描画
if fuji_shape[pos] == 1
# 山頂の雪は白く、麓は青紫
snow_line = 3 - (x - 2).abs # 山の形に合わせた雪線
if y < snow_line
# 山頂(白)
brightness = 200 - (time % 20) * 3 # 日の出に合わせて少しずつ明るく
LED.set([brightness / 4, brightness / 4, brightness / 4], pos)
else
# 山肌(青紫)
mountain_blue = 180 + (sin_approx(time * 3) * 10) / 64
mountain_hue = (mountain_blue + (y - snow_line) * 5) % 256
rgb = hsv_to_rgb(mountain_hue, 200, 100 + time / 4)
LED.set(rgb, pos)
end
else
# 空の描画
if y >= sun_pos && x >= 2 - (y - sun_pos) && x <= 2 + (y - sun_pos)
# 朝日(赤〜黄色)
sun_distance = (x - 2).abs + (y - sun_pos).abs
sun_hue = (20 - sun_distance * 5 + time / 5) % 256
sun_brightness = 255 - sun_distance * 20
rgb = hsv_to_rgb(sun_hue, 255, sun_brightness)
LED.set(rgb, pos)
else
# 空(日の出の進行で徐々に青く)
sky_time = time / 2
if sky_time > 40
sky_time = 40
end
sky_blue = 150 + (sin_approx(time) * 10) / 64
sky_brightness = 50 + sky_time * 3
rgb = hsv_to_rgb(sky_blue, 200, sky_brightness)
LED.set(rgb, pos)
end
end
end
end
sleep 0.1 # 10秒で約100フレーム
end
end
# 3. 提灯 - 和風の灯りを表現
def chochin_light
time = 0
# 提灯の形状(5x5のLEDマトリックス上)
chochin_shape = [
0, 1, 1, 1, 0,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
1, 1, 1, 1, 1,
0, 1, 0, 1, 0
]
# 約10秒間のアニメーション
100.times do
pseudo_random_val = pseudo_random(time)
flicker = (sin_approx(time * 8) * 40) / 64 # 炎のようなゆらぎ効果
5.times do |y|
5.times do |x|
pos = y * 5 + x
if chochin_shape[pos] == 1
# 提灯の内側(暖かい光)
if pos % 7 == time % 7
# 時々明るく光る(キラキラ効果)
brightness = 200 + flicker
hue = (25 + sin_approx(time + pos) * 5 / 64) % 256 # 黄色〜橙色
saturation = 180
else
# 通常の提灯の明かり
brightness = 160 + flicker
hue = (30 + sin_approx(time * 2 + pos * 3) * 10 / 64) % 256 # 橙色〜赤色
saturation = 220
end
rgb = hsv_to_rgb(hue, saturation, brightness)
LED.set(rgb, pos)
else
# 提灯の外側(暗い背景)
dark_blue = 170 # 夜の青
rgb = hsv_to_rgb(dark_blue, 200, 40 + (flicker / 4))
LED.set(rgb, pos)
end
end
end
time += 1
# 時々大きなゆらぎを入れる(風が吹いた感じ)
if pseudo_random_val < 30 # 約12%の確率
sleep 0.15
else
sleep 0.1
end
end
end
# 4. 紅葉 - 秋の風景を赤と金色で表現
def momiji_autumn
leaves = []
10.times do |i|
# [x, y, 色相, 位相]
leaves << [i % 5, (i / 2) % 5, 0 + (i * 7), i * 25]
end
wind_phase = 0
# 約10秒間のアニメーション
100.times do |time|
# 森の背景を暗い緑で描画
25.times do |i|
rgb = hsv_to_rgb(100, 180, 40) # 深緑
LED.set(rgb, i)
end
# 紅葉の葉を描画
leaves.each_with_index do |leaf, i|
x, y, hue_base, phase = leaf
# 風で揺れる効果
wind = (sin_approx(wind_phase + phase) * 2) / 64
new_x = (x + wind + 5) % 5
# 葉が徐々に散る
if time % 20 == i % 20
new_y = (y + 1) % 5
else
new_y = y
end
# 紅葉の色相を変化(赤~黄色)
hue_shift = (sin_approx(time + i * 10) * 20) / 64
hue = (hue_base + hue_shift) % 256
# 季節に応じた色変化(赤→黄→茶色)
if i % 3 == 0
leaf_hue = (0 + hue) % 256 # 赤
saturation = 255
elsif i % 3 == 1
leaf_hue = (30 + hue) % 256 # 橙
saturation = 240
else
leaf_hue = (40 + hue) % 256 # 黄
saturation = 230
end
brightness = 180 + (sin_approx(phase + time * 5) * 50) / 64
rgb = hsv_to_rgb(leaf_hue, saturation, brightness)
# 葉を描画
pos = new_y.to_i * 5 + new_x.to_i
LED.set(rgb, pos)
# 葉の状態を更新
leaves[i] = [new_x, new_y, hue_base, phase]
end
wind_phase = (wind_phase + 3) % 256
sleep 0.1 # 10秒で約100フレーム
end
end
# 5. 禅庭 - 静けさと調和を表現するミニマルなパターン
def zen_garden
# 砂紋の位相
wave_phase = 0
stone_positions = [7, 17] # 石の位置
# 約10秒間のアニメーション
100.times do |time|
5.times do |y|
5.times do |x|
pos = y * 5 + x
# 石の描画
if stone_positions.include?(pos)
# 石は静的なグレー
stone_brightness = 100 + (sin_approx(time) * 20) / 64
LED.set([stone_brightness / 4, stone_brightness / 4, stone_brightness / 4], pos)
else
# 砂紋の描画
distance_from_stone1 = (x - (stone_positions[0] % 5)).abs + (y - (stone_positions[0] / 5)).abs
distance_from_stone2 = (x - (stone_positions[1] % 5)).abs + (y - (stone_positions[1] / 5)).abs
min_distance = distance_from_stone1 < distance_from_stone2 ? distance_from_stone1 : distance_from_stone2
# 波紋効果
wave_value = (sin_approx(min_distance * 32 + wave_phase) * 30) / 64
# 砂の色(明るいベージュ)
sand_brightness = 120 + wave_value
sand_hue = 45 # ベージュの色相
sand_saturation = 40 # 低い彩度
rgb = hsv_to_rgb(sand_hue, sand_saturation, sand_brightness)
LED.set(rgb, pos)
end
end
end
# 非常にゆっくりとした波紋の動き(禅の静けさを表現)
if time % 5 == 0
wave_phase = (wave_phase + 1) % 256
end
sleep 0.1 # 10秒で約100フレーム
end
end
# 6. 花火 - 夏祭りの夜空を彩る花火
def hanabi_festival
sky_phase = 0
fireworks = []
# 約10秒間のアニメーション
100.times do |time|
# 夜空の背景
25.times do |i|
LED.set([1, 1, 5], i) # 暗い青紫色の夜空
end
# 時々新しい花火を発射
if time % 15 == 0 || fireworks.empty?
x = (time * 17 + 23) % 5
y = 4 # 画面下部から発射
hue = (time * 71) % 256 # ランダムな色
fireworks << [x, y, 0, hue] # [x, y, stage, hue]
end
# 花火の更新と描画
new_fireworks = []
fireworks.each do |fw|
x, y, stage, hue = fw
if stage < 5 # 上昇中
# 花火が上昇する軌跡
new_y = y - 1
if new_y >= 0
pos = new_y * 5 + x
brightness = 100 + stage * 30
rgb = hsv_to_rgb(hue, 100, brightness)
LED.set(rgb, pos)
# 軌跡を残す
if new_y < 4
tail_pos = (new_y + 1) * 5 + x
tail_rgb = hsv_to_rgb(hue, 50, 50)
LED.set(tail_rgb, tail_pos)
end
new_fireworks << [x, new_y, stage + 1, hue]
else
# 画面上部に達したら爆発
new_fireworks << [x, 0, 5, hue]
end
elsif stage < 15 # 爆発中
center_x = x
center_y = (stage - 5) / 3 # 爆発の中心位置
# 爆発のパターンを描画
explosion_size = (stage - 4) / 2
5.times do |ey|
5.times do |ex|
distance = (ex - center_x).abs + (ey - center_y).abs
if distance <= explosion_size
pos = ey * 5 + ex
# 爆発の中心ほど明るく
brightness = 255 - distance * 40 - (stage - 5) * 10
if brightness > 0
explosion_hue = (hue + distance * 10) % 256
rgb = hsv_to_rgb(explosion_hue, 255, brightness)
LED.set(rgb, pos)
end
end
end
end
if stage < 14 # まだ爆発中なら続ける
new_fireworks << [x, y, stage + 1, hue]
end
end
end
fireworks = new_fireworks
sky_phase = (sky_phase + 1) % 256
sleep 0.1 # 10秒で約100フレーム
end
end
# 7. 水面 - 日本庭園の池の静かな水面を表現
def mizuumi_reflection
ripple_phase = 0
koi_pos = [12, 7] # 錦鯉の位置
koi_direction = [1, -1] # 錦鯉の移動方向
# 約10秒間のアニメーション
100.times do |time|
5.times do |y|
5.times do |x|
pos = y * 5 + x
# 水面の基本色(青緑)
water_hue = 190
# 水面の波紋効果
ripple = (sin_approx(x * 20 + y * 30 + ripple_phase) * 15) / 64
brightness = 100 + ripple
# 水の深さによる色の変化
depth_hue = (water_hue - y * 5) % 256
# 月光の反射
reflection_x = 2
reflection_y = 1
distance_from_reflection = (x - reflection_x).abs + (y - reflection_y).abs
if distance_from_reflection < 2
brightness = 150 - distance_from_reflection * 30 + ripple
saturation = 100
else
saturation = 200
end
rgb = hsv_to_rgb(depth_hue, saturation, brightness)
LED.set(rgb, pos)
# 錦鯉の描画
koi_x = koi_pos[0] % 5
koi_y = koi_pos[0] / 5
koi_x2 = koi_pos[1] % 5
koi_y2 = koi_pos[1] / 5
if (x == koi_x && y == koi_y) || (x == koi_x2 && y == koi_y2)
# 錦鯉の色(赤と白)
if pos % 2 == 0
koi_rgb = hsv_to_rgb(0, 255, 200) # 赤
else
koi_rgb = hsv_to_rgb(0, 0, 200) # 白
end
LED.set(koi_rgb, pos)
end
end
end
# 錦鯉の移動
if time % 3 == 0
koi_pos[0] = (koi_pos[0] + koi_direction[0] + 25) % 25
if time % 10 == 0
koi_direction[0] = -koi_direction[0] # 方向転換
end
end
if time % 4 == 0
koi_pos[1] = (koi_pos[1] + koi_direction[1] + 25) % 25
if time % 12 == 0
koi_direction[1] = -koi_direction[1] # 方向転換
end
end
ripple_phase = (ripple_phase + 1) % 256
sleep 0.1 # 10秒で約100フレーム
end
end
# メインループ
begin
while true do
puts "桜のデモを開始"
sakura_blossoms # 桜
sleep 0.5
puts "富士山と日の出のデモを開始"
fuji_sunrise # 富士山と日の出
sleep 0.5
puts "提灯のデモを開始"
chochin_light # 提灯の灯り
sleep 0.5
puts "紅葉のデモを開始"
momiji_autumn # 紅葉
sleep 0.5
puts "禅庭のデモを開始"
zen_garden # 禅庭
sleep 0.5
puts "花火のデモを開始"
hanabi_festival # 花火
sleep 0.5
puts "水面のデモを開始"
mizuumi_reflection # 水面
sleep 0.5
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラー表示(日の丸風)
error_count = 0
while true do
25.times do |i|
x = i % 5
y = i / 5
center_distance = (x - 2).abs + (y - 2).abs
if center_distance < 2
LED.set([64, 0, 0], i) # 赤(日の丸)
else
LED.set([32, 32, 32], i) # 白(背景)
end
end
sleep 0.5
25.times do |i|
LED.set([0, 0, 0], i) # 全消灯
end
sleep 0.5
error_count += 1
break if Blink.req_reload? || error_count > 5
end
end
# M5 Atom Matrix マインクラフトデモ
# ブロックの世界観を5x5マトリックスで表現
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
#
# 使用できない機能:
# - Math モジュール(sin, cos など)
# - rand - 乱数生成
# - LED.get - 現在のLED色の取得
# - respond_to? - メソッド存在チェック
# - reverse, reverse_each - 配列の逆順処理
# - 浮動小数点数の%演算
# - backtrace - エラーのスタックトレース
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256 # 0-255の範囲に正規化
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# 疑似乱数生成(線形合同法)
def pseudo_random(seed)
(seed * 17 + 23) % 256
end
# 1. マインクラフトの日夜サイクル
def day_night_cycle
# 一日のサイクル(朝→昼→夕方→夜→朝)
time = 0
# 約8秒間のアニメーション
count = 0
while count < 80
# 時間帯に応じて空の色を変更
sky_hue = 0
sky_sat = 0
sky_bright = 0
cycle_pos = time % 80 # 80フレームを一日とする
if cycle_pos < 20 # 朝
sky_hue = 200 - cycle_pos # 青から水色へ
sky_sat = 100 + cycle_pos * 2
sky_bright = 100 + cycle_pos * 3
elsif cycle_pos < 40 # 昼
sky_hue = 180
sky_sat = 140
sky_bright = 160
elsif cycle_pos < 60 # 夕方
sky_hue = 30 - (cycle_pos - 40) # オレンジから赤へ
sky_sat = 200
sky_bright = 160 - (cycle_pos - 40) * 3
else # 夜
sky_hue = 240
sky_sat = 180
sky_bright = 40 + (sin_approx(time * 8) * 10) / 64 # 星のきらめき
end
# 空を描画
2.times do |y|
5.times do |x|
pos = y * 5 + x
# 空の色
rgb = hsv_to_rgb(sky_hue, sky_sat, sky_bright)
LED.set(rgb, pos)
# 夜には星を表示
if cycle_pos >= 60 && pseudo_random(pos + time) < 20 && y == 0
LED.set([16, 16, 16], pos) # 星
end
end
end
# 地形を描画(3段目以降)
3.times do |y|
real_y = y + 2 # 3,4,5段目
5.times do |x|
pos = real_y * 5 + x
# 地形の種類を決定
terrain_type = (pseudo_random(x * 10 + real_y * 5) % 4)
case terrain_type
when 0 # 草ブロック
if real_y == 2
rgb = cycle_pos >= 60 ? [0, 3, 0] : [0, 16, 0] # 夜は暗く
LED.set(rgb, pos)
else
# 土
rgb = cycle_pos >= 60 ? [3, 2, 0] : [16, 10, 0] # 夜は暗く
LED.set(rgb, pos)
end
when 1 # 石
rgb = cycle_pos >= 60 ? [3, 3, 3] : [14, 14, 14] # 夜は暗く
LED.set(rgb, pos)
when 2 # 砂
rgb = cycle_pos >= 60 ? [5, 5, 0] : [24, 24, 0] # 夜は暗く
LED.set(rgb, pos)
when 3 # 水
water_bright = 80 + (sin_approx(time * 10 + x * 20) * 20) / 64
water_bright = water_bright / 3 if cycle_pos >= 60 # 夜は暗く
rgb = hsv_to_rgb(190, 200, water_bright)
LED.set(rgb, pos)
end
end
end
time += 1
sleep 0.1
count += 1
end
end
# 2. ブロック採掘アニメーション
def mining_blocks
# ブロックの配置
blocks = []
25.times do |i|
x = i % 5
y = i / 5
# [x, y, タイプ, 耐久度]
blocks[i] = [x, y, pseudo_random(i) % 5, 3]
end
time = 0
pickaxe_pos = 12 # 中央からスタート
# 約10秒間のアニメーション
count = 0
while count < 100
# すべてのブロックを描画
25.times do |i|
block = blocks[i]
block_type = block[2]
durability = block[3]
if durability > 0
# ブロックの種類ごとに色を設定
case block_type
when 0 # 石
rgb = [12, 12, 12]
when 1 # 石炭
rgb = [5, 5, 5]
when 2 # 鉄
rgb = [24, 18, 16]
when 3 # 金
rgb = [24, 24, 0]
when 4 # ダイヤモンド
rgb = [0, 24, 24]
end
# 採掘中は点滅
if i == pickaxe_pos && time % 6 < 3
LED.set([24, 24, 24], i) # 白く点滅
else
LED.set(rgb, i)
end
else
# 採掘済みは暗く
LED.set([1, 1, 1], i)
end
end
# ツルハシの移動と採掘
if time % 15 == 0
# ツルハシの移動
move_dir = pseudo_random(time) % 4
old_x = pickaxe_pos % 5
old_y = pickaxe_pos / 5
case move_dir
when 0 # 上
new_y = (old_y - 1 + 5) % 5
new_x = old_x
when 1 # 右
new_y = old_y
new_x = (old_x + 1) % 5
when 2 # 下
new_y = (old_y + 1) % 5
new_x = old_x
when 3 # 左
new_y = old_y
new_x = (old_x - 1 + 5) % 5
end
pickaxe_pos = new_y * 5 + new_x
end
# 採掘アクション
if time % 6 == 0
block = blocks[pickaxe_pos]
if block[3] > 0
block[3] -= 1
# 採掘完了時のエフェクト
if block[3] == 0
5.times do
LED.set([24, 24, 24], pickaxe_pos) # 白く光る
sleep 0.05
case block[2]
when 0 # 石
rgb = [12, 12, 12]
when 1 # 石炭
rgb = [5, 5, 5]
when 2 # 鉄
rgb = [24, 18, 16]
when 3 # 金
rgb = [24, 24, 0]
when 4 # ダイヤモンド
rgb = [0, 24, 24]
end
LED.set(rgb, pickaxe_pos)
sleep 0.05
end
end
end
end
time += 1
sleep 0.1
count += 1
end
end
# 3. クリーパーのアニメーション
def creeper_animation
# クリーパーの顔のパターン
face = [
0, 0, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 1, 0, 1, 0
]
time = 0
# 約8秒間のアニメーション
count = 0
while count < 80
# 背景(夜の空)
25.times do |i|
LED.set([0, 0, 4], i) # 暗い青
end
# クリーパーの表示
25.times do |i|
if face[i] == 1
# 通常状態
LED.set([0, 16, 0], i) # 緑色
end
end
# クリーパーがシューッと音を立てる
if time % 40 > 30
hiss_intensity = (time % 40) - 30 # 0-9の強度
# クリーパーが白く点滅(爆発の前兆)
if time % 2 == 0
25.times do |i|
if face[i] == 1
# 点滅強度を増加
bright = 16 + hiss_intensity * 4
bright = bright > 64 ? 64 : bright
LED.set([bright, bright, 0], i) # 黄色っぽく
end
end
else
25.times do |i|
if face[i] == 1
LED.set([0, 16, 0], i) # 通常の緑色
end
end
end
end
# 爆発アニメーション
if time % 40 == 39
# 爆発の3段階
3.times do |stage|
# 爆発の広がり
25.times do |i|
x = i % 5
y = i / 5
dx = (x - 2).abs
dy = (y - 2).abs
distance = dx + dy # マンハッタン距離
if distance <= stage
rgb = [64, 48, 0] # 爆発の色
else
rgb = [0, 0, 4] # 背景
end
LED.set(rgb, i)
end
sleep 0.1
end
# 爆発の後、すべて消える
25.times do |i|
LED.set([0, 0, 0], i)
end
sleep 0.5
# 再び暗い背景に戻る
25.times do |i|
LED.set([0, 0, 4], i)
end
end
time += 1
sleep 0.1
count += 1
end
end
# 4. ブロック建設アニメーション
def building_blocks
# 空のグリッド
grid = []
25.times do |i|
grid[i] = 0 # 0=空、1-6=ブロックの種類
end
time = 0
cursor_pos = 12 # 中央からスタート
current_block = 1 # 最初のブロックタイプ
# 約10秒間のアニメーション
count = 0
while count < 100
# 空の背景
25.times do |i|
if grid[i] == 0
# 空(水色)
LED.set([0, 8, 16], i)
else
# 設置されたブロック
case grid[i]
when 1 # 木材
rgb = [12, 6, 0]
when 2 # 石
rgb = [12, 12, 12]
when 3 # レンガ
rgb = [16, 8, 8]
when 4 # ガラス
rgb = [16, 16, 24]
when 5 # 赤石
rgb = [24, 0, 0]
when 6 # ラピスラズリ
rgb = [0, 0, 24]
end
LED.set(rgb, i)
end
end
# カーソルの描画
led_state = grid[cursor_pos]
# カーソル位置を点滅
if time % 6 < 3
case current_block
when 1 # 木材
rgb = [20, 10, 0]
when 2 # 石
rgb = [20, 20, 20]
when 3 # レンガ
rgb = [24, 12, 12]
when 4 # ガラス
rgb = [24, 24, 32]
when 5 # 赤石
rgb = [32, 0, 0]
when 6 # ラピスラズリ
rgb = [0, 0, 32]
end
else
if led_state == 0
# 空
rgb = [0, 8, 16]
else
# 既に設置されたブロック
case led_state
when 1 then rgb = [12, 6, 0]
when 2 then rgb = [12, 12, 12]
when 3 then rgb = [16, 8, 8]
when 4 then rgb = [16, 16, 24]
when 5 then rgb = [24, 0, 0]
when 6 then rgb = [0, 0, 24]
end
end
end
LED.set(rgb, cursor_pos)
# カーソル移動と操作
if time % 10 == 0
# カーソル移動
move_dir = pseudo_random(time) % 4
old_x = cursor_pos % 5
old_y = cursor_pos / 5
case move_dir
when 0 # 上
new_y = (old_y - 1 + 5) % 5
new_x = old_x
when 1 # 右
new_y = old_y
new_x = (old_x + 1) % 5
when 2 # 下
new_y = (old_y + 1) % 5
new_x = old_x
when 3 # 左
new_y = old_y
new_x = (old_x - 1 + 5) % 5
end
cursor_pos = new_y * 5 + new_x
end
# ブロック設置/破壊
if time % 20 == 0
if grid[cursor_pos] == 0
# ブロック設置
grid[cursor_pos] = current_block
# 設置エフェクト
3.times do
LED.set([32, 32, 32], cursor_pos)
sleep 0.05
case current_block
when 1 then rgb = [12, 6, 0]
when 2 then rgb = [12, 12, 12]
when 3 then rgb = [16, 8, 8]
when 4 then rgb = [16, 16, 24]
when 5 then rgb = [24, 0, 0]
when 6 then rgb = [0, 0, 24]
end
LED.set(rgb, cursor_pos)
sleep 0.05
end
# ブロックタイプを変更
current_block = current_block % 6 + 1
else
# ブロック破壊
3.times do
LED.set([32, 32, 32], cursor_pos)
sleep 0.05
case grid[cursor_pos]
when 1 then rgb = [12, 6, 0]
when 2 then rgb = [12, 12, 12]
when 3 then rgb = [16, 8, 8]
when 4 then rgb = [16, 16, 24]
when 5 then rgb = [24, 0, 0]
when 6 then rgb = [0, 0, 24]
end
LED.set(rgb, cursor_pos)
sleep 0.05
end
grid[cursor_pos] = 0 # ブロックを空に
end
end
time += 1
sleep 0.1
count += 1
end
end
# 5. ミニマップモード
def minimap_mode
# 地形のミニマップを表現
map = [
1, 1, 1, 2, 2, # 1=草原, 2=山
1, 1, 2, 2, 3, # 3=水
1, 2, 2, 3, 3,
1, 2, 3, 3, 4, # 4=砂漠
5, 5, 4, 4, 4 # 5=森
]
player_x = 0
player_y = 0
time = 0
# 約8秒間のアニメーション
count = 0
while count < 80
# 地図を描画
25.times do |i|
x = i % 5
y = i / 5
terrain = map[i]
# プレイヤー位置は点滅
if x == player_x && y == player_y
if time % 6 < 3
LED.set([32, 32, 32], i)
next
end
end
# 地形ごとに色を設定
case terrain
when 1 # 草原
rgb = [0, 16, 0]
when 2 # 山
rgb = [12, 12, 12]
when 3 # 水
bright = 80 + (sin_approx(time * 10 + i) * 20) / 64
rgb = hsv_to_rgb(190, 200, bright)
when 4 # 砂漠
rgb = [24, 24, 0]
when 5 # 森
rgb = [0, 8, 0]
end
LED.set(rgb, i)
end
# プレイヤーの移動
if time % 10 == 0
move_dir = pseudo_random(time) % 4
case move_dir
when 0 # 上
player_y = (player_y - 1 + 5) % 5
when 1 # 右
player_x = (player_x + 1) % 5
when 2 # 下
player_y = (player_y + 1) % 5
when 3 # 左
player_x = (player_x - 1 + 5) % 5
end
end
time += 1
sleep 0.1
count += 1
end
end
# メインループ
begin
while true do
puts "マインクラフト日夜サイクルデモを開始"
day_night_cycle
sleep 0.5
puts "ブロック採掘デモを開始"
mining_blocks
sleep 0.5
puts "クリーパーアニメーションデモを開始"
creeper_animation
sleep 0.5
puts "ブロック建設デモを開始"
building_blocks
sleep 0.5
puts "ミニマップモードデモを開始"
minimap_mode
sleep 0.5
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# マインクラフトのエラー画面(赤い背景)
while true do
25.times do |i|
LED.set([16, 0, 0], i)
end
sleep 0.5
# "JAVA"という文字をドット絵で表現
[1, 2, 3, 5, 6, 8, 11, 13, 16, 17, 18, 21, 23].each do |i|
LED.set([32, 32, 32], i)
end
sleep 0.5
break if Blink.req_reload?
end
end
# M5 Atom Matrix 海の生き物デモ
# 海洋生物と海中の光の表現
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - シンプルな配列操作(配列アクセス、push)
# - 条件分岐(if, case)
# - 単純なループ(times, while)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
#
# 使用できない機能:
# - 配列の分解代入 |(a, b), i| → 代わりにインデックスアクセスを使用
# - each, each_with_index などの高度なイテレータ
# - .abs などの標準ライブラリメソッド
# - LED.get - 現在のLED色の取得
# - rand - 乱数生成
# - Math モジュール(sin, cos など)
# - 浮動小数点数の%演算
# - 複雑なブロック引数(any?などのブロック内で複雑な条件を使用)
# - 三項演算子(条件 ? 真の場合 : 偽の場合)
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
if s > 255
s = 255
elsif s < 0
s = 0
end
if v > 255
v = 255
elsif v < 0
v = 0
end
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
# .absを使わずに絶対値計算
h_mod_diff = (h_mod * 2) - 255
if h_mod_diff < 0
h_mod_diff = -h_mod_diff
end
x = (c * (255 - h_mod_diff)) / 255
m = v - c
r = 0
g = 0
b = 0
case h_section
when 0
r = c
g = x
b = 0
when 1
r = x
g = c
b = 0
when 2
r = 0
g = c
b = x
when 3
r = 0
g = x
b = c
when 4
r = x
g = 0
b = c
when 5
r = c
g = 0
b = x
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256 # 0-255の範囲に正規化
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# 疑似乱数生成(線形合同法)
def pseudo_random(seed)
(seed * 17 + 23) % 256
end
# 絶対値を計算(.absを使わずに)
def abs_val(num)
if num < 0
-num
else
num
end
end
# 1. 熱帯魚の群れ - カラフルな魚が泳ぐ様子
def tropical_fish_school
fish = []
# 初期の魚を生成
i = 0
while i < 6
# [x, y, 色相, 方向, 速度]
fish[i] = [i % 5, (i / 5) % 5, i * 36, i % 4, (i % 2) + 1]
i += 1
end
time = 0
# 約10秒間のアニメーション
count = 0
while count < 100
# 海の背景(青緑色の濃淡)
i = 0
while i < 25
x = i % 5
y = i / 5
depth = y + 1 # 深さによる色の変化
water_hue = 185 - depth * 5 # 深いほど青みが強くなる
water_brightness = 100 - depth * 10 # 深いほど暗くなる
# 水中の光の屈折効果
light_effect = (sin_approx(time * 2 + x * 30 + y * 20) * 15) / 64
rgb = hsv_to_rgb(water_hue, 180, water_brightness + light_effect)
LED.set(rgb, i)
i += 1
end
# 魚を更新・描画
idx = 0
while idx < fish.size
f = fish[idx]
x = f[0]
y = f[1]
hue = f[2]
direction = f[3]
speed = f[4]
# 魚の動きを計算
new_x = x
new_y = y
case direction
when 0 # 右
new_x = (x + speed) % 5
when 1 # 下
new_y = (y + speed) % 5
when 2 # 左
new_x = (x - speed + 5) % 5
when 3 # 上
new_y = (y - speed + 5) % 5
end
# 時々方向転換
if time % 10 == idx % 10
direction = (direction + 1 + (time % 3)) % 4
end
# 魚の色を更新(わずかに変化)
new_hue = (hue + 1) % 256
# 魚の描画
pos = new_y * 5 + new_x
brightness = 180 + (sin_approx(time * 8 + idx * 32) * 50) / 64 # キラキラ効果
rgb = hsv_to_rgb(new_hue, 240, brightness)
LED.set(rgb, pos)
# 魚の尾(動きの方向に描画)
tail_x = new_x
tail_y = new_y
if direction == 0
tail_x = (new_x - 1 + 5) % 5
elsif direction == 2
tail_x = (new_x + 1) % 5
end
if direction == 1
tail_y = (new_y - 1 + 5) % 5
elsif direction == 3
tail_y = (new_y + 1) % 5
end
tail_pos = tail_y * 5 + tail_x
# 尾は本体より暗く
tail_rgb = hsv_to_rgb(new_hue, 200, brightness / 2)
LED.set(tail_rgb, tail_pos)
# 魚の状態を更新
fish[idx] = [new_x, new_y, new_hue, direction, speed]
idx += 1
end
time += 1
sleep 0.1
count += 1
end
end
# 2. クラゲの脈動 - ゆっくり浮遊するクラゲの表現
def jellyfish_pulsation
jellies = []
# 初期のクラゲを生成
i = 0
while i < 3
# [x, y, サイズ, 脈動位相]
jellies[i] = [1 + i, 4, 1, i * 64]
i += 1
end
time = 0
# 約10秒間のアニメーション
count = 0
while count < 100
# 深い海の背景(暗い青)
i = 0
while i < 25
LED.set([0, 2, 4], i)
i += 1
end
# クラゲを更新・描画
idx = 0
while idx < jellies.size
j = jellies[idx]
x = j[0]
y = j[1]
size = j[2]
pulse_phase = j[3]
# 脈動による上下運動
pulse = sin_approx(pulse_phase)
new_y = (y - (pulse / 128)) % 5
# 左右の揺れ
wobble = (sin_approx(pulse_phase / 2) * 1) / 64
new_x = (x + wobble + 5) % 5
# クラゲの大きさは脈動とともに変化
new_size = 1 + (pulse / 128)
# クラゲの体を描画
center_pos = new_y.to_i * 5 + new_x.to_i
jelly_hue = (180 + idx * 25 + sin_approx(time * 2) * 5 / 64) % 256 # 青紫〜ピンク
# 透明感のある色
transparency = 128 + (sin_approx(pulse_phase) * 64) / 64
# クラゲの体
rgb = hsv_to_rgb(jelly_hue, 150, transparency)
LED.set(rgb, center_pos)
# クラゲの触手
if new_y < 4
tentacle_pos = (new_y.to_i + 1) * 5 + new_x.to_i
tentacle_rgb = hsv_to_rgb(jelly_hue, 100, transparency / 2)
LED.set(tentacle_rgb, tentacle_pos)
end
# 大きいクラゲは周囲も光る
if new_size > 1
# 配列の分解代入を使わずに記述
offset_idx = 0
offsets = [[-1,0], [1,0], [0,-1]]
while offset_idx < offsets.size
offset = offsets[offset_idx]
dx = offset[0]
dy = offset[1]
nx = (new_x.to_i + dx + 5) % 5
ny = (new_y.to_i + dy + 5) % 5
if nx >= 0 && nx < 5 && ny >= 0 && ny < 5
edge_pos = ny * 5 + nx
edge_rgb = hsv_to_rgb(jelly_hue, 100, transparency / 3)
LED.set(edge_rgb, edge_pos)
end
offset_idx += 1
end
end
# クラゲのバイオルミネセンス効果(時々光る)
if pulse_phase % 100 < 10
glow_rgb = hsv_to_rgb(jelly_hue, 50, 255)
LED.set(glow_rgb, center_pos)
end
# クラゲの状態を更新
new_pulse_phase = (pulse_phase + 3) % 256
jellies[idx] = [new_x, new_y, new_size, new_pulse_phase]
idx += 1
end
time += 1
sleep 0.1
count += 1
end
end
# 3. タコの擬態 - 色を変えるタコの表現
def octopus_camouflage
# タコの位置と状態
octopus_x = 2
octopus_y = 2
octopus_state = 0 # 0=通常, 1=隠れる, 2=逃げる
color_phase = 0
# 障害物(岩や珊瑚)の位置
obstacles = []
obstacles[0] = [0, 0]
obstacles[1] = [4, 0]
obstacles[2] = [0, 4]
obstacles[3] = [4, 4]
time = 0
# 約10秒間のアニメーション
count = 0
while count < 100
# 海底の背景(砂地と岩)
i = 0
while i < 25
x = i % 5
y = i / 5
# 障害物(岩や珊瑚)- any?メソッドを使わずに検出
is_obstacle = false
obstacle_idx = 0
while obstacle_idx < 4 # 障害物の数は4つと固定
ox = obstacles[obstacle_idx][0]
oy = obstacles[obstacle_idx][1]
if ox == x && oy == y
is_obstacle = true
break
end
obstacle_idx += 1
end
if is_obstacle
rock_hue = (25 + sin_approx(time * 3 + i) * 5 / 64) % 256 # 茶色〜オレンジ
rgb = hsv_to_rgb(rock_hue, 140, 100)
LED.set(rgb, i)
else
# 砂地
sand_brightness = 70 + (sin_approx(time + x * 10 + y * 10) * 10) / 64
rgb = hsv_to_rgb(40, 40, sand_brightness) # 薄い黄色(砂)
LED.set(rgb, i)
end
i += 1
end
# 捕食者(影)が時々現れる
if time % 30 < 10
predator_x = time % 5
predator_y = 0
predator_pos = predator_y * 5 + predator_x
LED.set([0, 0, 8], predator_pos) # 暗い影
# タコが捕食者を感知して状態変化
# abs関数を使わずに距離を計算
dx = predator_x - octopus_x
if dx < 0
dx = -dx
end
dy = predator_y - octopus_y
if dy < 0
dy = -dy
end
if dx + dy < 3
# 三項演算子を使わずに条件分岐
if time % 20 < 10
octopus_state = 1 # 隠れる
else
octopus_state = 2 # 逃げる
end
end
else
octopus_state = 0 # 通常状態に戻る
end
# タコの動き
case octopus_state
when 0 # 通常:ゆっくり動く
if time % 10 == 0
direction = time % 4
case direction
when 0 then octopus_x = (octopus_x + 1) % 5
when 1 then octopus_y = (octopus_y + 1) % 5
when 2 then octopus_x = (octopus_x - 1 + 5) % 5
when 3 then octopus_y = (octopus_y - 1 + 5) % 5
end
end
when 1 # 隠れる:動かない
# 何もしない
when 2 # 逃げる:素早く移動
if time % 5 == 0
# 捕食者から離れる方向へ
octopus_x = (octopus_x + 1) % 5
octopus_y = (octopus_y + 1) % 5
end
end
# タコの描画
octopus_pos = octopus_y * 5 + octopus_x
# 状態に応じた色変化
octopus_hue = 0
saturation = 0
brightness = 0
case octopus_state
when 0 # 通常:赤褐色
octopus_hue = (0 + sin_approx(color_phase) * 10 / 64) % 256
saturation = 200
brightness = 150
when 1 # 隠れる:周囲の環境に合わせる(砂色)
octopus_hue = 40
saturation = 60
brightness = 80
when 2 # 逃げる:警戒色(明るい赤)
octopus_hue = 0
saturation = 255
brightness = 255
end
# タコの体
rgb = hsv_to_rgb(octopus_hue, saturation, brightness)
LED.set(rgb, octopus_pos)
# タコの触手(8本)- 各種イテレータを使わずにシンプルに記述
tentacle_dx = [-1, 1, 0, 0, -1, 1, -1, 1]
tentacle_dy = [0, 0, -1, 1, -1, -1, 1, 1]
i = 0
while i < 8 # 8本の触手
dx = tentacle_dx[i]
dy = tentacle_dy[i]
nx = (octopus_x + dx + 5) % 5
ny = (octopus_y + dy + 5) % 5
# 一部の触手だけを表示(すべてを表示すると密集しすぎる)
if i < 4 || time % 10 < 5
tentacle_pos = ny * 5 + nx
tentacle_hue = (octopus_hue + i * 5) % 256 # 触手ごとに少し色を変える
tentacle_rgb = hsv_to_rgb(tentacle_hue, saturation, brightness / 2)
LED.set(tentacle_rgb, tentacle_pos)
end
i += 1
end
color_phase = (color_phase + 3) % 256
time += 1
sleep 0.1
count += 1
end
end
# 4. 深海の発光生物 - 暗闇で光る深海魚
def deep_sea_bioluminescence
creatures = []
i = 0
while i < 4
# [x, y, 色相, 明るさ, 明滅周期]
creatures[i] = [i, i, 180 + i * 15, 150 + i * 20, 50 + i * 10]
i += 1
end
time = 0
# 約10秒間のアニメーション
count = 0
while count < 100
# 深海の暗い背景
i = 0
while i < 25
LED.set([0, 0, 2], i) # ほぼ真っ暗
i += 1
end
# 時々、水中を漂う微粒子が光を反射
if time % 3 == 0
particle_pos = (pseudo_random(time) % 25)
particle_brightness = 20 + (pseudo_random(time + 1) % 20)
rgb = hsv_to_rgb(200, 50, particle_brightness)
LED.set(rgb, particle_pos)
end
# 発光生物の更新と描画
idx = 0
while idx < creatures.size
c = creatures[idx]
x = c[0]
y = c[1]
hue = c[2]
brightness = c[3]
cycle = c[4]
# ゆっくりと移動
if time % 15 == idx
move_direction = pseudo_random(time + idx) % 4
case move_direction
when 0 then x = (x + 1) % 5
when 1 then y = (y + 1) % 5
when 2 then x = (x - 1 + 5) % 5
when 3 then y = (y - 1 + 5) % 5
end
end
# 明滅パターン
glow = (sin_approx(time * 256 / cycle) * 128) / 64 + 128
current_brightness = (brightness * glow) / 256
# 生物の描画
pos = y * 5 + x
rgb = hsv_to_rgb(hue, 255, current_brightness)
LED.set(rgb, pos)
# 特別な発光パターン(アンコウのような誘導発光器)
if idx == 0 && time % 20 < 10
lure_x = (x + 1) % 5
lure_y = (y - 1 + 5) % 5
lure_pos = lure_y * 5 + lure_x
lure_brightness = 200 + (sin_approx(time * 10) * 55) / 64
rgb = hsv_to_rgb((hue + 30) % 256, 255, lure_brightness)
LED.set(rgb, lure_pos)
end
# 生物の状態を更新
creatures[idx] = [x, y, hue, brightness, cycle]
idx += 1
end
time += 1
sleep 0.1
count += 1
end
end
# 5. サンゴ礁 - 色とりどりのサンゴと熱帯魚
def coral_reef
# サンゴの配置
coral_positions = [0, 1, 5, 6, 10, 15, 20, 21, 22]
fish = []
# 初期の魚を生成
i = 0
while i < 3
# [x, y, 色相, 方向]
fish[i] = [4 - i, i, i * 85, i % 4]
i += 1
end
time = 0
# 約10秒間のアニメーション
count = 0
while count < 100
# 浅い海の背景(明るい青)
i = 0
while i < 25
x = i % 5
y = i / 5
# サンゴをinclude?を使わないで判定
is_coral = false
coral_idx = 0
while coral_idx < coral_positions.size
if coral_positions[coral_idx] == i
is_coral = true
break
end
coral_idx += 1
end
if is_coral
# サンゴの描画
coral_type = i % 3
coral_hue = 0
coral_sat = 0
case coral_type
when 0 # ピンク/紫のサンゴ
coral_hue = (300 + sin_approx(time + i * 10) * 20 / 64) % 256
coral_sat = 200
when 1 # オレンジ/黄色のサンゴ
coral_hue = (30 + sin_approx(time + i * 10) * 15 / 64) % 256
coral_sat = 220
when 2 # 緑/青緑のサンゴ
coral_hue = (150 + sin_approx(time + i * 10) * 30 / 64) % 256
coral_sat = 180
end
brightness = 150 + (sin_approx(time * 3 + i * 20) * 50) / 64
rgb = hsv_to_rgb(coral_hue, coral_sat, brightness)
LED.set(rgb, i)
else
# 海水
water_brightness = 100 - (y * 10) # 深さによる明るさの変化
water_brightness += (sin_approx(time * 3 + x * 20 + y * 20) * 20) / 64 # 光の揺らぎ
rgb = hsv_to_rgb(190, 150, water_brightness)
LED.set(rgb, i)
end
i += 1
end
# 魚の更新と描画
idx = 0
while idx < fish.size
f = fish[idx]
x = f[0]
y = f[1]
hue = f[2]
direction = f[3]
# 魚の動き
if time % (idx + 3) == 0
case direction
when 0 then x = (x + 1) % 5
when 1 then y = (y + 1) % 5
when 2 then x = (x - 1 + 5) % 5
when 3 then y = (y - 1 + 5) % 5
end
# サンゴに当たったら方向転換 - include?を使わずに判定
fish_pos = y * 5 + x
hit_coral = false
coral_idx = 0
while coral_idx < coral_positions.size
if coral_positions[coral_idx] == fish_pos
hit_coral = true
break
end
coral_idx += 1
end
if hit_coral
direction = (direction + 2) % 4 # 反対方向へ
case direction
when 0 then x = (x + 1) % 5
when 1 then y = (y + 1) % 5
when 2 then x = (x - 1 + 5) % 5
when 3 then y = (y - 1 + 5) % 5
end
end
# ランダムな方向転換
if pseudo_random(time + idx) < 30 # 約12%の確率
direction = (direction + 1) % 4
end
end
# 魚の描画
pos = y * 5 + x
# 熱帯魚の鮮やかな色
fish_hue = (hue + time) % 256
fish_brightness = 200 + (sin_approx(time * 10 + idx * 50) * 55) / 64
rgb = hsv_to_rgb(fish_hue, 255, fish_brightness)
LED.set(rgb, pos)
# 魚の状態を更新
fish[idx] = [x, y, hue, direction]
idx += 1
end
time += 1
sleep 0.1
count += 1
end
end
# メインループ
begin
while true do
puts "熱帯魚の群れデモを開始"
tropical_fish_school
sleep 0.5
puts "クラゲの脈動デモを開始"
jellyfish_pulsation
sleep 0.5
puts "タコの擬態デモを開始"
octopus_camouflage
sleep 0.5
puts "深海の発光生物デモを開始"
deep_sea_bioluminescence
sleep 0.5
puts "サンゴ礁デモを開始"
coral_reef
sleep 0.5
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラー表示(赤い波紋)
error_count = 0
while true do
y = 0
while y < 5
x = 0
while x < 5
pos = y * 5 + x
# absを使わずに距離計算
dx = x - 2
if dx < 0
dx = -dx
end
dy = y - 2
if dy < 0
dy = -dy
end
distance = dx + dy
if distance < 3
intensity = 64 - distance * 20
LED.set([intensity, 0, 0], pos)
else
LED.set([0, 0, 0], pos)
end
x += 1
end
y += 1
end
sleep 0.5
i = 0
while i < 25
LED.set([0, 0, 0], i)
i += 1
end
sleep 0.5
error_count += 1
break if Blink.req_reload? || error_count > 5
end
end
# M5 Atom Matrix Gentle LED Demo
# 目に優しい、ゆっくりとした光のパターン
# mruby/c 3.3の制限事項に準拠
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# やさしい呼吸パターン
def gentle_breathing
hue = 160 # 緑がかった青色(リラックス効果)
phase = 0
60.times do
brightness = (sin_approx(phase) * 128) / 64 + 64 # 明るさを抑えめに
rgb = hsv_to_rgb(hue, 128, brightness) # 彩度を抑えめに
25.times do |i|
LED.set(rgb, i)
end
phase = (phase + 1) % 256
sleep 0.1 # ゆっくりとした変化
end
end
# 穏やかな波紋
def gentle_ripple
center_x = 2
center_y = 2
phase = 0
50.times do
5.times do |y|
5.times do |x|
distance = (x - center_x).abs + (y - center_y).abs
wave = (sin_approx((distance * 32 + phase) % 256) * 96) / 64 + 32
hue = 200 # 落ち着いた青色
rgb = hsv_to_rgb(hue, 128, wave)
LED.set(rgb, y * 5 + x)
end
end
phase = (phase + 2) % 256 # ゆっくりとした波紋
sleep 0.1
end
end
# 星空のきらめき
def starry_night
stars = []
phase = 0 # phaseを関数内で定義
3.times do # 星の数を制限
x = (phase * 17 + 23) % 5
y = (phase * 13 + 7) % 5
stars << [x, y, 0] # [x, y, 明るさ]
phase += 1 # 各星の位置を少しずつずらす
end
40.times do |time|
# 背景を暗い青に
25.times do |i|
LED.set([0, 0, 1], i)
end
# 星のきらめき
stars.each do |star|
x, y, brightness = star
pos = y * 5 + x
new_brightness = (sin_approx((brightness + time * 8) % 256) * 32) / 64 + 16
rgb = hsv_to_rgb(40, 32, new_brightness) # 温かみのある光
LED.set(rgb, pos)
end
sleep 0.15
end
end
# 夕暮れグラデーション
def sunset_gradient
base_hue = 20 # オレンジ色
phase = 0
40.times do
5.times do |y|
hue = (base_hue + y * 4 + phase / 2) % 256
rgb = hsv_to_rgb(hue, 192, 96) # 明るさを抑えめに
5.times do |x|
LED.set(rgb, y * 5 + x)
end
end
phase = (phase + 1) % 256
sleep 0.2 # ゆっくりとした変化
end
end
# メインループ
begin
while true do
gentle_breathing
sleep 0.5
gentle_ripple
sleep 0.5
starry_night
sleep 0.5
sunset_gradient
sleep 0.5
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラー時は非常に暗い赤色でゆっくり点滅
while true do
25.times { |i| LED.set([8, 0, 0], i) }
sleep 2
25.times { |i| LED.set([0, 0, 0], i) }
sleep 2
break if Blink.req_reload?
end
end
# M5 Atom Matrix Vibe Coding Demo
# 心震える感覚を与えるリズミカルなLEDパターン
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# ビースケープ - バイブスがうねるベースのようなパターン
def bass_vibe
base_hue = 280 # 紫外線のようなカラー
phase = 0
intensity = 0
direction = 1
40.times do
# ベースのリズムを生成
beat = sin_approx(phase * 12) * 192 / 64
5.times do |y|
5.times do |x|
# うねるようなベースパターン
wave = sin_approx((x * 32 + y * 24 + phase * 4) % 256) * 128 / 64
# リズミカルな輝度変化
brightness = 96 + (beat * wave) / 192
# バイブ感のある色変化
hue = (base_hue + wave / 2 + beat) % 256
saturation = 192 + beat / 4
rgb = hsv_to_rgb(hue, saturation, brightness)
LED.set(rgb, y * 5 + x)
end
end
phase = (phase + 1) % 256
intensity += direction
if intensity >= 255 || intensity <= 0
direction = -direction
end
# リズミカルな待機時間
if phase % 8 == 0
sleep 0.08 # ビートの強拍
else
sleep 0.04 # ビートの弱拍
end
end
end
# ネオンドリップ - 現代的でトレンディなネオンの雫
def neon_drip
drops = []
5.times do |i|
drops << [i, -1, (i * 73) % 256] # [x, y, hue]
end
pseudo_random = 123
50.times do |time|
# 背景を暗く
25.times do |i|
LED.set([1, 1, 2], i)
end
# 雫の更新と描画
drops.each_with_index do |drop, i|
x, y, hue = drop
# 雫の位置を更新
if time % 3 == 0 # 3フレームごとに下に移動
y += 1
end
# 画面外に出たら新しい雫を作成
if y >= 5
pseudo_random = (pseudo_random * 17 + 23) % 256
y = -1
hue = (hue + pseudo_random) % 256
end
drops[i] = [x, y, hue]
# 雫を描画
if y >= 0 && y < 5
pos = y * 5 + x
brightness = 128 + (sin_approx(time * 10) * 64) / 64
rgb = hsv_to_rgb(hue, 255, brightness)
LED.set(rgb, pos)
# 雫の尾を描画
tail_y = y - 1
if tail_y >= 0
tail_pos = tail_y * 5 + x
rgb = hsv_to_rgb(hue, 200, brightness / 2)
LED.set(rgb, tail_pos)
end
end
end
# リズミカルな待機
if time % 10 == 0
sleep 0.12 # 小休止
else
sleep 0.06
end
end
end
# ロー・ファイ・グリッチ - 意図的なグリッチ効果
def lofi_glitch
hue = 180 # シアンをベースに
phase = 0
pseudo_random = 42
60.times do |time|
# グリッチの強度を変化
glitch_intensity = sin_approx(time * 5) * 64 / 64
# ランダムグリッチを生成
pseudo_random = (pseudo_random * 17 + 23) % 256
# グリッチパターンを生成
is_glitch_frame = pseudo_random < 64 # 約25%の確率でグリッチフレーム
5.times do |y|
5.times do |x|
pos = y * 5 + x
# 基本的な波模様
wave = sin_approx((x * 24 + y * 32 + time * 3) % 256)
current_hue = (hue + wave / 2) % 256
if is_glitch_frame
# グリッチ効果
if ((x + y + pseudo_random) % 7) < 3
# ノイズの多いピクセル
pixel_random = (pseudo_random * (x+1) * (y+1)) % 256
current_hue = (current_hue + 128) % 256 # 補色
rgb = hsv_to_rgb(current_hue, 128, 192 + (pixel_random % 64))
else
# 暗いピクセル
rgb = hsv_to_rgb(current_hue, 192, 48)
end
else
# 通常のレトロ波紋
brightness = 128 + wave / 2
rgb = hsv_to_rgb(current_hue, 192, brightness)
end
LED.set(rgb, pos)
end
end
# グリッチフレームのときは短く表示
if is_glitch_frame
sleep 0.02
else
# 通常のリズミカルな待機
if time % 8 == 0
sleep 0.1
else
sleep 0.05
end
end
phase = (phase + 1) % 256
end
end
# ハイパーウェーブ - 未来的な波動効果
def hyper_wave
time = 0
energy = 0
direction = 1
45.times do
# エネルギー波形を生成
wave_power = sin_approx(time * 5) * 128 / 64 + 128
5.times do |y|
5.times do |x|
# 波の中心からの距離
dx = x - 2
dy = y - 2
distance = dx.abs + dy.abs # マンハッタン距離
# 波動効果
wave = sin_approx((distance * 64 + time * 8) % 256)
wave_hue = (210 + wave * 30 / 64 + time) % 256 # 青から紫へ
# エネルギーレベルによる輝度
brightness = 128 + (wave * energy) / 255
# エネルギー波動を表現
saturation = 128 + wave_power / 4
rgb = hsv_to_rgb(wave_hue, saturation, brightness)
LED.set(rgb, y * 5 + x)
end
end
# エネルギーの増減
energy += direction * 4
if energy >= 255 || energy <= 0
direction = -direction
end
# リズミカルな待機
beat_interval = wave_power / 128 # 0.5-1.5の範囲
sleep 0.03 + (beat_interval * 0.03)
time += 1
end
end
# メインループ
begin
while true do
bass_vibe # バイブスがうねるベース
sleep 0.3
neon_drip # 現代的でトレンディなネオンの雫
sleep 0.3
lofi_glitch # レトロフューチャーなグリッチ効果
sleep 0.3
hyper_wave # 未来的な波動効果
sleep 0.3
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラー時はサイバーパンク的な赤と青の点滅
phase = 0
while true do
5.times do |y|
5.times do |x|
pos = y * 5 + x
if (x + y + phase) % 2 == 0
LED.set([32, 0, 0], pos) # 赤
else
LED.set([0, 0, 32], pos) # 青
end
end
end
phase += 1
sleep 0.2
break if Blink.req_reload?
end
end
# M5 Atom Matrix 天気・気象アニメーション
# 直感的に理解できる天気現象を5x5 LEDマトリックスで表現
# mruby/c 3.3の制限事項と注意点
# 使用可能な機能:
# - 基本的な算術演算(+, -, *, /, %)※ただし整数のみ
# - 配列操作(each, each_with_index)
# - 条件分岐(if, case)
# - ループ(times, while, downto)
# - LED.set([r,g,b], pos) - LEDの色設定
# - sleep(秒数) - 待機
# - Blink.req_reload? - リロードチェック
#
# 使用できない機能:
# - Math モジュール(sin, cos など)
# - rand - 乱数生成
# - LED.get - 現在のLED色の取得
# - respond_to? - メソッド存在チェック
# - reverse, reverse_each - 配列の逆順処理
# - 浮動小数点数の%演算
# - backtrace - エラーのスタックトレース
# HSVからRGBへの変換(整数演算版)
def hsv_to_rgb(h, s, v)
h = h - (h / 256) * 256
s = s > 255 ? 255 : (s < 0 ? 0 : s)
v = v > 255 ? 255 : (v < 0 ? 0 : v)
c = (v * s) / 255
h_section = (h * 6) / 256
h_mod = ((h * 6 * 255) / 256) - (h_section * 255)
x = (c * (255 - ((h_mod * 2) - 255).abs)) / 255
m = v - c
r, g, b = case h_section
when 0 then [c, x, 0]
when 1 then [x, c, 0]
when 2 then [0, c, x]
when 3 then [0, x, c]
when 4 then [x, 0, c]
when 5 then [c, 0, x]
else [0, 0, 0]
end
[(r + m) / 4, (g + m) / 4, (b + m) / 4]
end
# 簡易サイン波生成(整数演算版)
def sin_approx(x)
x = x - (x / 256) * 256 # 0-255の範囲に正規化
if x < 64
x
elsif x < 128
128 - x
elsif x < 192
x - 192
else
256 - x
end
end
# 疑似乱数生成(線形合同法)
def pseudo_random(seed)
(seed * 17 + 23) % 256
end
# 1. 晴れ - 太陽と青空のアニメーション
def sunny_animation
time = 0
# 約8秒間のアニメーション
80.times do
# 空の青色
25.times do |i|
# 青空(上部ほど明るい青)
y = i / 5
sky_brightness = 180 - y * 15
rgb = hsv_to_rgb(210, 180, sky_brightness)
LED.set(rgb, i)
end
# 太陽の位置と大きさ
sun_x = 2
sun_y = 1
# 太陽の光線の強さ(サイン波で変動)
sun_power = 128 + (sin_approx(time * 5) * 64) / 64
# 太陽と光線を描画
5.times do |y|
5.times do |x|
pos = y * 5 + x
# 太陽からの距離
distance = (x - sun_x).abs + (y - sun_y).abs
if distance <= 1
# 太陽本体(黄色)
brightness = 200 + (sin_approx(time * 10) * 55) / 64
rgb = hsv_to_rgb(40, 230, brightness)
LED.set(rgb, pos)
elsif distance <= 3
# 光線(距離に応じて弱くなる)
ray_strength = sun_power - distance * 40
if ray_strength > 0
# 太陽光線(やや橙色)
rgb = hsv_to_rgb(35, 200, ray_strength)
LED.set(rgb, pos)
end
end
end
end
time += 1
sleep 0.1
end
end
# 2. 曇り - 動く雲のアニメーション
def cloudy_animation
cloud_positions = []
# 複数の雲を作成
3.times do |i|
# [x, 幅, 高さ]
cloud_positions << [i * 3 - 2, 3, 2]
end
time = 0
# 約8秒間のアニメーション
80.times do
# 背景(やや曇った空)
25.times do |i|
rgb = hsv_to_rgb(210, 40, 140) # 薄い青灰色
LED.set(rgb, i)
end
# 雲を動かして描画
cloud_positions.each_with_index do |cloud, i|
x_pos, width, height = cloud
# 雲を横に移動
x_pos = (x_pos + 0.2).to_f
if x_pos > 7
x_pos = -4 # 画面外から再登場
end
cloud_positions[i][0] = x_pos
# 雲を描画
cloud_y = i % 2 # 雲の高さをずらす
width.times do |w|
height.times do |h|
x = (x_pos + w).to_i
y = cloud_y + h
if x >= 0 && x < 5 && y >= 0 && y < 5
pos = y * 5 + x
# 雲の明るさを微妙に変化(立体感)
cloud_brightness = 200 + (sin_approx(time * 3 + i * 50) * 30) / 64
# 雲の色(白〜薄いグレー)
rgb = hsv_to_rgb(0, 0, cloud_brightness)
LED.set(rgb, pos)
end
end
end
end
time += 1
sleep 0.1
end
end
# 3. 雨 - 雨粒が落ちるアニメーション
def rainy_animation
drops = []
6.times do |i|
# [x, y, 速度, 強度]
drops << [pseudo_random(i * 10) % 5, pseudo_random(i * 20) % 5, 1 + pseudo_random(i) % 2, 140 + pseudo_random(i + 50) % 60]
end
time = 0
# 約8秒間のアニメーション
80.times do
# 背景(雨の空)
25.times do |i|
rgb = hsv_to_rgb(210, 60, 80) # 暗い青灰色
LED.set(rgb, i)
end
# 雨粒を更新して描画
drops.each_with_index do |drop, i|
x, y, speed, intensity = drop
# 古い位置の雨粒を消す(背景色で上書き)
if y >= 0 && y < 5
pos = y * 5 + x
rgb = hsv_to_rgb(210, 60, 80) # 背景色
LED.set(rgb, pos)
end
# 雨粒を下に移動
y = (y + speed) % (5 + 3) # 画面外も含めてループ
# 画面外から再登場するとき、x位置をランダムに変更
if y < 0
x = pseudo_random(time + i * 10) % 5
end
# 雨粒の状態を更新
drops[i] = [x, y, speed, intensity]
# 雨粒を描画(画面内の場合のみ)
if y >= 0 && y < 5
pos = y * 5 + x
# 雨粒の色(青)
rain_brightness = intensity - (pseudo_random(time + i) % 30)
rgb = hsv_to_rgb(200, 180, rain_brightness)
LED.set(rgb, pos)
end
end
time += 1
sleep 0.1
end
end
# 4. 雪 - 雪の結晶が舞い落ちるアニメーション
def snowy_animation
snowflakes = []
8.times do |i|
# [x, y, 落下速度, 揺れ幅, 揺れ位相]
snowflakes << [
pseudo_random(i * 11) % 5,
pseudo_random(i * 7) % 8 - 3, # 一部は画面外からスタート
pseudo_random(i * 13) % 4 + 1, # 1-4の速度(整数に変更)
pseudo_random(i * 19) % 3, # 0-2の揺れ幅
pseudo_random(i * 23) # 揺れの初期位相
]
end
time = 0
# 約8秒間のアニメーション
80.times do
# 背景(冬の空)
25.times do |i|
rgb = hsv_to_rgb(210, 20, 100) # 薄い青色(冬の空)
LED.set(rgb, i)
end
# 雪を更新して描画
snowflakes.each_with_index do |flake, i|
x, y, speed, sway_width, phase = flake
# 雪の動き(下に落ちながら左右に揺れる)
# 速度を整数値として扱う(2フレームに1回動くなど)
if time % 2 == 0
new_y = y + (speed / 2) # 速度を整数で調整
else
new_y = y
end
# 左右の揺れ(整数演算に変更)
phase_new = (phase + 7) % 256
sway = (sin_approx(phase_new) * sway_width) / 64 # 小数点を削除
new_x = (x + sway + 5) % 5 # 整数値になるので問題なし
# 画面外に出たら上から再登場
if new_y >= 5
new_y = -1
new_x = pseudo_random(time + i * 31) % 5
end
# 雪の状態を更新
snowflakes[i] = [new_x, new_y, speed, sway_width, phase_new]
# 雪を描画(画面内の場合のみ)
if new_y >= 0 && new_y < 5
pos = new_y * 5 + new_x
# 雪の色(純白)
snow_brightness = 220 + (sin_approx(time * 10 + i * 20) * 35) / 64 # キラキラ効果
rgb = hsv_to_rgb(180, 5, snow_brightness)
LED.set(rgb, pos)
end
end
time += 1
sleep 0.1
end
end
# 5. 雷雨 - 雨に雷が加わるアニメーション
def thunderstorm_animation
# 雨粒の初期化
raindrops = []
7.times do |i|
# [x, y, 速度]
raindrops << [pseudo_random(i * 10) % 5, pseudo_random(i * 20) % 5, 1 + pseudo_random(i) % 2]
end
# 雷の状態(0=なし、1-5=フラッシュ強度)
lightning = 0
time = 0
# 約8秒間のアニメーション
80.times do
# 通常の雨の背景(暗めの空)
background_brightness = 60
# 雷が光っているとき
if lightning > 0
# 背景を明るく(雷の光)
background_brightness = 60 + lightning * 30
lightning -= 1 # 雷の強度を徐々に弱める
end
# 背景を描画
25.times do |i|
rgb = hsv_to_rgb(210, 60, background_brightness)
LED.set(rgb, i)
end
# 雨粒を更新して描画
raindrops.each_with_index do |drop, i|
x, y, speed = drop
# 雨粒を下に移動
y = (y + speed) % (5 + 3) # 画面外も含めてループ
# 画面外から再登場するとき、x位置をランダムに変更
if y < 0
x = pseudo_random(time + i * 10) % 5
end
# 雨粒の状態を更新
raindrops[i] = [x, y, speed]
# 雨粒を描画(画面内の場合のみ)
if y >= 0 && y < 5
pos = y * 5 + x
# 雨粒の色(青)- 雷の光を反映
rain_brightness = 140 + lightning * 20
rgb = hsv_to_rgb(200, 180, rain_brightness)
LED.set(rgb, pos)
end
end
# 時々雷を発生
if lightning == 0 && pseudo_random(time * 13) < 20 # 約8%の確率
lightning = 5 # 最大強度の雷
# 雷のパターンを描画
lightning_path = []
# 雷の経路を生成(上から下へジグザグ)
x = 2 # 中央からスタート
5.times do |y|
pos = y * 5 + x
lightning_path << pos
# 次のxを決定(左右にジグザグ)
x_change = pseudo_random(time + y * 10) % 3 - 1 # -1, 0, 1
x = (x + x_change + 5) % 5
end
# 雷を描画
lightning_path.each do |pos|
rgb = hsv_to_rgb(60, 100, 240) # 明るい黄色
LED.set(rgb, pos)
end
# 雷の音/衝撃を表現するための短い休止
sleep 0.2
end
time += 1
sleep 0.1
end
end
# 6. 虹 - 雨上がりの虹のアニメーション
def rainbow_animation
time = 0
# 約8秒間のアニメーション
80.times do
# 背景(雨上がりの空)
25.times do |i|
x = i % 5
y = i / 5
if y < 2
# 上部は青空
sky_brightness = 180 - y * 15
rgb = hsv_to_rgb(210, 100, sky_brightness)
else
# 下部は地面(緑)
ground_brightness = 120 - (y - 2) * 20
rgb = hsv_to_rgb(120, 160, ground_brightness)
end
LED.set(rgb, i)
end
# 時間の経過とともに虹が現れる
if time > 10
# 虹の位置(中央のやや上)
rainbow_y = 2
# 虹の弧
rainbow_arc = [
[0, 4], # 左端
[1, 3], # 左から2番目
[2, 2], # 中央
[3, 3], # 右から2番目
[4, 4] # 右端
]
rainbow_arc.each_with_index do |coords, i|
x, y_offset = coords
y = rainbow_y - y_offset
if y >= 0 && y < 5
pos = y * 5 + x
# 虹の色(赤から紫へ)
# 色相を0(赤)から300(紫)の範囲で変化
hue = (i * 60) % 256
# 徐々に現れる効果
fade_in = time - 10
if fade_in > 20
fade_in = 20
end
brightness = 100 + fade_in * 7
# 虹をきらめかせる
shimmer = (sin_approx(time * 10 + i * 50) * 20) / 64
rgb = hsv_to_rgb(hue, 230, brightness + shimmer)
LED.set(rgb, pos)
end
end
end
time += 1
sleep 0.1
end
end
# メインループ
begin
while true do
puts "晴れのアニメーションを開始"
sunny_animation
sleep 0.5
puts "曇りのアニメーションを開始"
cloudy_animation
sleep 0.5
puts "雨のアニメーションを開始"
rainy_animation
sleep 0.5
puts "雪のアニメーションを開始"
snowy_animation
sleep 0.5
puts "雷雨のアニメーションを開始"
thunderstorm_animation
sleep 0.5
puts "虹のアニメーションを開始"
rainbow_animation
sleep 0.5
break if Blink.req_reload?
end
rescue => e
puts "エラーが発生しました:"
puts " クラス: #{e.class}"
puts " メッセージ: #{e.message}"
# エラー表示(天気マーク的な点滅)
error_count = 0
while true do
5.times do |y|
5.times do |x|
pos = y * 5 + x
if (x == 2 && y == 2) || (x - 2).abs + (y - 2).abs == 1
LED.set([64, 0, 0], pos) # 警告色(赤)
else
LED.set([0, 0, 0], pos) # 消灯
end
end
end
sleep 0.5
5.times do |y|
5.times do |x|
pos = y * 5 + x
LED.set([0, 0, 0], pos) # 全消灯
end
end
sleep 0.5
error_count += 1
break if Blink.req_reload? || error_count > 5
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment