PONG

par davidnurdin · Arcade
pas encore noté
▶ Jouer Sources Éditer GitHub
games/pong / src / Ai / CpuPlayer.php · commit 8d554d3
<?phpnamespace App\Ai;use App\Entity\Ball;use App\Entity\Paddle;/** * The computer: predicts where the ball will cross its side (bounces included) and goes there, with a * reaction time, a speed limit and an aiming error that depend on the level. */final class CpuPlayer{    public const LEVELS = [        'easy' => ['reaction' => 0.30, 'speed' => 0.55, 'error' => 18.0],        'normal' => ['reaction' => 0.16, 'speed' => 0.80, 'error' => 9.0],        'hard' => ['reaction' => 0.06, 'speed' => 1.00, 'error' => 3.0],    ];    private array $level;    private float $think = 0.0;    private float $target;    private float $error = 0.0;    public function __construct(private Paddle $paddle, private int $side, private float $top, private float $bottom, string $level = 'normal')    {        $this->level = self::LEVELS[$level] ?? self::LEVELS['normal'];        $this->target = ($top + $bottom) / 2;    }    public function update(Ball $ball, float $dt): void    {        $this->think -= $dt;        if ($this->think <= 0) {            $this->think = $this->level['reaction'];            [$vx] = $ball->getVelocity();            $coming = $this->side < 0 ? $vx < 0 : $vx > 0;            if ($coming) {                $this->target = $this->predict($ball) + $this->error;            } else {                $this->target = ($this->top + $this->bottom) / 2;   // back to the middle                $this->error = (mt_rand() / mt_getrandmax() * 2 - 1) * $this->level['error'];            }        }        $this->paddle->moveTowards($this->target, $dt, $this->level['speed']);    }    /** y of the ball centre when it reaches the paddle, walls bounces unfolded. */    private function predict(Ball $ball): float    {        [$vx, $vy] = $ball->getVelocity();        if ($vx == 0.0) {            return $ball->centerY();        }        $time = ($this->paddle->centerX() - $ball->centerX()) / $vx;        $height = $this->bottom - $this->top - Ball::SIZE;        $y = $ball->getFloatY() - $this->top + $vy * $time;        // reflect into [0, height]        $period = 2 * $height;        $y = fmod(fmod($y, $period) + $period, $period);        if ($y > $height) {            $y = $period - $y;        }        return $this->top + $y + Ball::SIZE / 2;    }}