Моделирование футбольной зональной опеки в Engee
Автор
In [ ]:
using Plots
plotlyjs()
# ─────────────────────────────────────────
# ПАРАМЕТРЫ МОДЕЛИ
# ─────────────────────────────────────────
# Защитник — левее нападающего, между ним и центром поля
xc = -2.5 # X защитника (левее бровки)
yc = 0.0 # Y защитника
Vc = 5.0 # скорость защитника, м/с
βc = 0.0 # движется на север (вдоль поля, +Y), азимут 0°
R = 3.0 # радиус зоны контроля, м
φ = 90.0 # угол сектора зоны, градусы
τ = 0.2 # время реакции, с
# Нападающий — у правой бровки, бежит вдоль неё вперёд
x1 = 0.0 # X нападающего (у бровки)
y1 = 0.0 # Y нападающего
V1 = 5.2 # скорость нападающего, м/с
β1 = 0.0 # тоже бежит на север (+Y), азимут 0°
# ─────────────────────────────────────────
# ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
# ─────────────────────────────────────────
deg2r(d) = d * π / 180
# Азимут → компоненты скорости (Vx = V*sin β, Vy = V*cos β)
vel(V, β_deg) = (V * sin(deg2r(β_deg)), V * cos(deg2r(β_deg)))
# ─────────────────────────────────────────
# РАССТОЯНИЕ и ВРЕМЯ
# ─────────────────────────────────────────
D1 = sqrt((xc - x1)^2 + (yc - y1)^2)
t1 = D1 / Vc + τ
println("D1 = $(round(D1, digits=2)) м")
println("t1 = $(round(t1, digits=2)) с")
# ─────────────────────────────────────────
# СКОРОСТИ (компоненты)
# ─────────────────────────────────────────
Vxc, Vyc = vel(Vc, βc)
Vx1, Vy1 = vel(V1, β1)
# ─────────────────────────────────────────
# ГРАНИЧНЫЕ ТОЧКИ СЕКТОРА (t = 0)
# Зона направлена вправо (+90° от направления движения)
# βM = βc + 90 - φ/2, βK = βc + 90 + φ/2
# ─────────────────────────────────────────
βM = βc + 90.0 - φ/2
βK = βc + 90.0 + φ/2
xM = xc + R * sin(deg2r(βM)); yM = yc + R * cos(deg2r(βM))
xK = xc + R * sin(deg2r(βK)); yK = yc + R * cos(deg2r(βK))
xL = xc; yL = yc
# ─────────────────────────────────────────
# ПОЗИЦИИ ЧЕРЕЗ t1
# ─────────────────────────────────────────
xA = x1; yA = y1
xA2 = xA + t1 * Vx1; yA2 = yA + t1 * Vy1 # нападающий A'
xL2 = xL + t1 * Vxc; yL2 = yL + t1 * Vyc # защитник L'
xM2 = xM + t1 * Vxc; yM2 = yM + t1 * Vyc # M'
xK2 = xK + t1 * Vxc; yK2 = yK + t1 * Vyc # K'
# ─────────────────────────────────────────
# ПРЯМЫЕ M'L' и K'L'
# ─────────────────────────────────────────
A_ML = yM2 - yL2; B_ML = xL2 - xM2
C_ML = -xL2 * A_ML - yL2 * B_ML
A_KL = yK2 - yL2; B_KL = xL2 - xK2
C_KL = -xL2 * A_KL - yL2 * B_KL
# ─────────────────────────────────────────
# ТРИ УСЛОВИЯ ПОПАДАНИЯ
# ─────────────────────────────────────────
dist_AL2 = sqrt((xA2 - xL2)^2 + (yA2 - yL2)^2)
cond1 = dist_AL2 <= R
f_A_ML = xA2*A_ML + yA2*B_ML + C_ML
f_K_ML = xK2*A_ML + yK2*B_ML + C_ML
cond2 = f_A_ML * f_K_ML >= 0
f_A_KL = xA2*A_KL + yA2*B_KL + C_KL
f_M_KL = xM2*A_KL + yM2*B_KL + C_KL
cond3 = f_A_KL * f_M_KL >= 0
in_zone = cond1 && cond2 && cond3
println("\n─────────────────────────────────────────")
println("Результат через t1 = $(round(t1, digits=2)) с:")
println(" Усл.1 расстояние ≤ R : $cond1 ($(round(dist_AL2,digits=2)) м ≤ $(R) м)")
println(" Усл.2 сторона M'L' : $cond2")
println(" Усл.3 сторона K'L' : $cond3")
println("─────────────────────────────────────────")
println(in_zone ? "✅ Нападающий в зоне — ситуация под контролем"
: "❌ Нападающий вышел из зоны — нужна подстраховка!")
println("─────────────────────────────────────────\n")
# ─────────────────────────────────────────
# ВРЕМЯ ВЫХОДА ИЗ ЗОНЫ (перебор)
# ─────────────────────────────────────────
function in_zone_at(t)
xA_ = xA + t*Vx1; yA_ = yA + t*Vy1
xL_ = xL + t*Vxc; yL_ = yL + t*Vyc
xM_ = xM + t*Vxc; yM_ = yM + t*Vyc
xK_ = xK + t*Vxc; yK_ = yK + t*Vyc
Aml = yM_-yL_; Bml = xL_-xM_; Cml = -xL_*Aml - yL_*Bml
Akl = yK_-yL_; Bkl = xL_-xK_; Ckl = -xL_*Akl - yL_*Bkl
d = sqrt((xA_-xL_)^2 + (yA_-yL_)^2)
c1 = d <= R
c2 = (xA_*Aml+yA_*Bml+Cml)*(xK_*Aml+yK_*Bml+Cml) >= 0
c3 = (xA_*Akl+yA_*Bkl+Ckl)*(xM_*Akl+yM_*Bkl+Ckl) >= 0
return c1 && c2 && c3
end
t_exit = NaN
for t in 0.0:0.01:10.0
if !in_zone_at(t)
global t_exit = t
break
end
end
if isnan(t_exit)
println("За 10 с нападающий не покинул зону контроля.")
else
println("⏱ Выход из зоны: t_exit = $(round(t_exit, digits=2)) с")
println(" Сигнал партнёру не позднее: $(round(max(0.0, t_exit - τ), digits=2)) с")
end
# ─────────────────────────────────────────
# ВИЗУАЛИЗАЦИЯ
# ─────────────────────────────────────────
function sector_pts(xL, yL, β_center_deg, R, φ_deg; n=80)
β0 = deg2r(β_center_deg - φ_deg/2)
β1 = deg2r(β_center_deg + φ_deg/2)
angs = range(β0, β1, length=n)
xs = [xL; [xL + R*sin(a) for a in angs]; xL]
ys = [yL; [yL + R*cos(a) for a in angs]; yL]
return xs, ys
end
# Направление центра зоны — вправо (+90° от направления движения)
zone_dir = βc + 90.0
sx0, sy0 = sector_pts(xL, yL, zone_dir, R, φ) # начало
sx1, sy1 = sector_pts(xL2, yL2, zone_dir, R, φ) # через t1
# ── Фон поля ────────────────────────────
p = plot(
xlims=(-7, 4), ylims=(-3, 12),
size=(660, 780),
background_color=:honeydew,
grid=true, gridalpha=0.3,
xlabel="← Центр поля | Бровка →",
ylabel="Направление атаки →",
titlefontsize=12,
aspect_ratio=:equal,
legend=:topleft,
legendfontsize=8
)
# Бровка
vline!(p, [0.5], color=:green, linewidth=2, linestyle=:dash, label="Бровка")
# Разметка поля (пунктир)
hline!(p, [0.0], color=:gray, linewidth=0.8, linestyle=:dot, label=false)
# ── Зоны контроля ───────────────────────
plot!(p, sx0, sy0,
seriestype=:shape,
fillcolor=:royalblue, fillalpha=0.20,
linecolor=:royalblue, linewidth=1.5,
label="Зона контроля (t=0)")
plot!(p, sx1, sy1,
seriestype=:shape,
fillcolor=:darkorange, fillalpha=0.20,
linecolor=:darkorange, linewidth=1.5,
label="Зона контроля (t=t₁)")
# ── Траектории ───────────────────────────
plot!(p, [xL, xL2], [yL, yL2],
linewidth=2, linestyle=:dash, color=:royalblue,
arrow=true, label="Путь защитника")
plot!(p, [xA, xA2], [yA, yA2],
linewidth=2, linestyle=:dash, color=:crimson,
arrow=true, label="Путь нападающего")
# ── Игроки (t = 0) ───────────────────────
# Защитник
scatter!(p, [xL], [yL],
markersize=14, markershape=:circle,
color=:royalblue, markerstrokewidth=0,
label="Защитник (t=0)")
annotate!(p, xL - 0.3, yL - 0.5,
text("Защ.", :royalblue, :right, 8))
# Нападающий
scatter!(p, [xA], [yA],
markersize=14, markershape=:circle,
color=:crimson, markerstrokewidth=0,
label="Нападающий (t=0)")
annotate!(p, xA + 0.2, yA - 0.5,
text("Напад.", :crimson, :left, 8))
# ── Игроки (t = t1) ──────────────────────
scatter!(p, [xL2], [yL2],
markersize=14, markershape=:circle,
color=:darkorange, markerstrokewidth=0,
label="Защитник (t=t₁)")
annotate!(p, xL2 - 0.3, yL2 + 0.4,
text("Защ.'", :darkorange, :right, 8))
scatter!(p, [xA2], [yA2],
markersize=14, markershape=:circle,
color=:darkred, markerstrokewidth=0,
label="Нападающий (t=t₁)")
annotate!(p, xA2 + 0.2, yA2 + 0.4,
text("Напад.'", :darkred, :left, 8))
# ── Вектор скорости нападающего ──────────
quiver!(p, [xA], [yA],
quiver=([Vx1*0.4], [Vy1*0.4]),
color=:crimson, linewidth=2)
# ── Радиус R (пунктир) ───────────────────
θs = range(0, 2π, length=120)
plot!(p, xL .+ R.*sin.(θs), yL .+ R.*cos.(θs),
color=:royalblue, linewidth=0.8, linestyle=:dot, label="R (t=0)")
plot!(p, xL2 .+ R.*sin.(θs), yL2 .+ R.*cos.(θs),
color=:darkorange, linewidth=0.8, linestyle=:dot, label="R (t=t₁)")
# ── Аннотации R и φ ──────────────────────
# R — стрелка от защитника до края зоны
mid_ang = deg2r(zone_dir)
xR_tip = xL + R * sin(mid_ang)
yR_tip = yL + R * cos(mid_ang)
plot!(p, [xL, xR_tip], [yL, yR_tip],
color=:gray30, linewidth=1, linestyle=:solid, label=false)
annotate!(p, (xL + xR_tip)/2 - 0.3, (yL + yR_tip)/2 + 0.1,
text("R=$(R)м", :gray30, 8))
# φ — дуга угла в зоне
arc_r = 1.2
arc_angs = range(deg2r(zone_dir - φ/2), deg2r(zone_dir + φ/2), length=40)
plot!(p, xL .+ arc_r.*sin.(arc_angs), yL .+ arc_r.*cos.(arc_angs),
color=:gray50, linewidth=1.2, label=false)
annotate!(p, xL + arc_r*sin(deg2r(zone_dir)) + 0.2, yL + arc_r*cos(deg2r(zone_dir)),
text("φ=$(Int(φ))°", :gray50, 8))
# ── Статус ───────────────────────────────
status_str = in_zone ? "✅ В зоне" : "❌ Вне зоны\nНужна подстраховка!"
status_col = in_zone ? :darkgreen : :darkred
annotate!(p, xA2 + 0.3, yA2 + 1.0,
text(status_str, status_col, :left, 9, :bold))
# Линия от нападающего до статуса
plot!(p, [xA2, xA2 + 0.25], [yA2, yA2 + 0.85],
color=status_col, linewidth=0.8, label=false)
Out[0]: