rock_paper_scissors / tests /test_sad_animation.py
trtd56's picture
Initial commit
24836e5
"""悲しい負けモーションのテスト"""
import pytest
import numpy as np
from rock_paper_scissors.config.settings import settings
from rock_paper_scissors.robot.antenna_poses import AntennaPoses
from rock_paper_scissors.robot.head_poses import HeadPoses
from rock_paper_scissors.robot.animations import AnimationController
from rock_paper_scissors.game.states import GameState, GameResult
class TestSadHeadPose:
"""悲しい頭ポーズのテスト"""
def test_sad_pitch_is_deep(self):
"""しょんぼりは深くうつむく(絶対値25度以上)"""
# Reachy Miniではpitchがプラスで下向き
assert abs(settings.head.sad_pitch) >= 25.0
def test_get_sad_slow_shake_varies_over_time(self):
"""悲しいゆっくり首振りは時間で変化する"""
pose1 = HeadPoses.get_sad_slow_shake(0.0)
pose2 = HeadPoses.get_sad_slow_shake(1.0)
# 異なる時間で異なるポーズを返す(完全一致しない)
assert not np.array_equal(pose1, pose2)
def test_sad_slow_shake_frequency_is_very_slow(self):
"""悲しい首振りの周波数は非常に遅い(0.2Hz以下)- 公式サンプル準拠"""
assert settings.head.sad_shake_frequency <= 0.2
def test_sad_slow_shake_amplitude_is_small(self):
"""悲しい首振りの振幅は小さい(3度以下)- 控えめな動き"""
assert settings.head.sad_shake_amplitude <= 3.0
class TestSadAntennaPose:
"""悲しいアンテナポーズのテスト"""
def test_get_sad_droopy_is_low_position(self):
"""悲しいアンテナは下がった位置"""
pose = AntennaPoses.get_sad_droopy()
# 両方のアンテナが内側に少し傾いている(小さい値)
assert abs(pose[0]) <= 0.3
assert abs(pose[1]) <= 0.3
def test_sad_droopy_is_symmetrical(self):
"""悲しいアンテナは左右対称"""
pose = AntennaPoses.get_sad_droopy()
assert pose[0] == -pose[1] # 対称
class TestLoseAnimationSequence:
"""負けアニメーションシーケンスのテスト"""
def test_lose_animation_stays_sad_for_first_1_5_seconds(self):
"""負けアニメーションは最初の1.5秒間は悲しいまま"""
controller = AnimationController()
controller.set_state(GameState.REACT, game_result=GameResult.LOSE)
# 0.5秒後も悲しいポーズのはず(アンテナは下がった位置)
head, antennas = controller._get_react_target(0.5)
sad_droopy = AntennaPoses.get_sad_droopy()
assert np.array_equal(antennas, sad_droopy)
# 1.0秒後もまだ悲しいポーズ
head2, antennas2 = controller._get_react_target(1.0)
assert np.array_equal(antennas2, sad_droopy)
def test_lose_animation_recovers_after_1_5_seconds(self):
"""負けアニメーションは1.5秒後から回復を始める"""
controller = AnimationController()
controller.set_state(GameState.REACT, game_result=GameResult.LOSE)
# 2.0秒後は回復ポーズ
head, antennas = controller._get_react_target(2.0)
# アンテナが開いている(回復)ことを確認
open_wide = AntennaPoses.get_open_wide()
assert np.array_equal(antennas, open_wide)
def test_lose_animation_head_stays_still_during_sad_phase(self):
"""悲しいフェーズ中、頭は静かにうつむいたまま(動きなし)"""
controller = AnimationController()
controller.set_state(GameState.REACT, game_result=GameResult.LOSE)
# 異なる時間でも頭のポーズは同じ(静止)
head1, _ = controller._get_react_target(0.0)
head2, _ = controller._get_react_target(0.5)
# ポーズが同じ(静かにうつむいている)
assert np.array_equal(head1, head2)
class TestSadAnimationSettings:
"""悲しいアニメーション設定のテスト"""
def test_sad_duration_exists(self):
"""悲しい状態の持続時間設定が存在する"""
assert hasattr(settings.head, 'sad_duration')
assert settings.head.sad_duration >= 1.0
def test_sad_shake_settings_exist(self):
"""悲しい首振りの設定が存在する"""
assert hasattr(settings.head, 'sad_shake_frequency')
assert hasattr(settings.head, 'sad_shake_amplitude')