用Mathematica對足球賽進行簡單模擬
(我的本科Mathematica課程論文)
足球賽是一項非常複雜的運動,現今FIFA實況的AI對足球賽的模擬還挺不錯的。大學裡我把足球變成了自己的第二專業,這裡我試著用一些儘量簡單的規則來對一場足球賽進行還大致像回事的模擬。
一、基本模型
攻方:
1、攻方球員向對方球門移動。
2、攻方球員之間拉開合適的距離以便傳球。
3、攻方持球球員有向前傳球的線路時就傳球,沒有時自己帶球向前並和防守方球員保持一定距離。
4、攻方持球隊員距離球門足夠近並有射門角度時就射門。
5、隊員傳球要考慮隊友的跑動,隊員接球也要考慮球的移動。
防守方:
1、拖後的防守隊員補位和封堵射門角度。
2、靠近球的隊員上搶。
3、其餘隊員盯好附近的人。
4、正常情況下防守隊員間也需保持一定距離。
規則:
1、不設門將,邊界球,角球和門球。
2、沒有裁判,沒有犯規。
球:
1、設定球場是2D的,球都在地面上運轉,所以球的滾動要考慮地面摩擦力。
2、射門精準度具有一定的隨機性。
3、在一定範圍內,離球最近的隊員取得球的控制權。
4、達到邊界時,會減速彈回場內。
二、具體實現
規則:
1、無論進攻還是防守,己方隊友對於自己的影響力大小為一個已雙方相對距離為引數的分段函式,這個函式的作用是使自己能和最近的隊員保持合適的距離。
2、進攻時,對方球門對於自己有恆定的吸引力,來促使自己向前進攻。
3、進攻時,靠近的對方球員對於自己有排斥力,來使自己躲避對方球員的防守。
4、進攻時,如果隊友傳球給自己,那麼球對於自己會有巨大的吸引力,自己將根據球未來的位置來主動去接球。
5、進攻時,如果自己持球,那麼自己靠近球門時立即射門,有隊友的站位比自己好或者自己已經帶球過多時傳球給隊友,否則自己帶球向更好的位置移動。
6、防守時,球對於自己有較大吸引力,所以最靠近球的幾個人會上前逼搶。
7、防守時,附近的對手的隊員對自己有一定吸引力,所以部分隊員會盯死對手附近的接應隊員。
8、防守時,己方球門對於自己有一定吸引力,所以拖後隊員會注意封堵射門角度。
三、截圖
四、結果
使用場的觀點來構建各種因素對球員的影響力,能使得程式較短,效果較好。我們不是用大量的條件判斷語句來寫的,而是通過構建場函式,來實現對球員和球的控制,這樣比較方便簡潔。
還有一個構想:表示出球員的心理狀態,沒有實現。
具體構想是:
心理狀態由心理優勢度和情緒興奮度兩個引數構成,心理優勢的決定球員的射門、傳球、帶球精確度和情緒興奮度的變化,情緒興奮度決定球員的跑動、傳球、帶球、射門速度和心理優勢度的變化。
心理優勢度的變化由全隊的行為和這個隊員的行為的成敗結果、以及球員的情緒興奮度決定,球員情緒興奮度的變化由心理優勢度的變化和球員本身的屬性決定。
五、程式程式碼:
(*設定比賽時間,減少其可使預處理時間減少*)
matchTime = 4000;
(*設定賽場邊界*)
check[p_] := {Min[105, Max[0, p[[1]]]],
Min[68, Max[0, p[[2]]]]} /;(p[[2]] > 30.1) && (p[[2]] < 37.9)
check[p_] := {Min[2*105 - p[[1]], Max[Abs[p[[1]]], p[[1]]]],
Min[2*68 - p[[2]],
Max[Abs[p[[2]]], p[[2]]]]} /;(p[[2]] <= 30.1) || (p[[2]] >= 37.9)
(*畫球場*)
field = Graphics[{Thickness[0.003], White,
Line[{{0.13, 30.1}, {0.13,37.9}}],
Line[{{0, 14}, {16.5, 14}, {16.5,54}, {0, 54}}],
Line[{{0, 24.5}, {5.5, 24.5},{5.5, 43.5}, {0, 43.5}}],
Circle[{11, 34}, 9.15,{-ArcCos[5.5/9.15], ArcCos[5.5/9.15]}],
Line[{{52.5, 0}, {52.5,68}}], Circle[{52.5, 34}, 9.15],
Line[{{104.68, 30.1},{104.68, 37.9}}],
Line[{{105, 14}, {88.5, 14},{88.5, 54}, {105, 54}}],
Line[{{105, 24.5}, {95.5,24.5}, {95.5, 43.5}, {105, 43.5}}],
Circle[{94, 34},
9.15, {Pi -ArcCos[5.5/9.15], Pi + ArcCos[5.5/9.15]}]},
Background -> Green,PlotRange -> {{0, 105}, {0, 68}},
ImageSize -> 700];
(*畫球*)
ball[t_] := Graphics[{Black, Disk[vb[[t]], 0.4]}];
(*畫球員*)
player[t_, team_, i_] :=
Graphics[{Blue,
Rectangle[vp[[t, team, i]] -{1, 1},
vp[[t, team, i]] + {1, 1}]}]/; team == 1
player[t_, team_, i_] :=
Graphics[{Red,
Rectangle[vp[[t, team, i]] -{1, 1},
vp[[t, team, i]] + {1, 1}]}]/; team == 2
(*球的初始位置*)
vb = {{50, 30}, {50, 30}};
(*球的初始速度*)
vba = {0, 0};
(*球員的初始位置*)
vp = {{{{50, 30}, {50, 38}, {40, 12}, {40, 56}, {32, 28}, {32,
40}, {21, 17}, {21, 51}, {18, 28}, {18, 40},{5, 34}},
{{55, 34}, {60, 22}, {60,46}, {68, 34}, {75, 26}, {78, 38}, {82,
15}, {82, 53}, {88, 29},{88, 41}, {100, 34}}}, {{{50, 30}, {50,
38}, {40, 12}, {40, 56},{32, 28}, {32, 40}, {23, 15}, {23,
53}, {18, 28}, {18, 40},{5, 34}},
{{55, 34}, {60, 22}, {60,46}, {68, 34}, {75, 26}, {78, 38}, {82,
15}, {82, 53}, {88, 29},{88, 41}, {100, 34}}}};
(*球門的位置*)
vd = {{3, 34}, {102, 34}};
(*初始比分*)
goal = {{"0:0"}, {"0:0"}};
playerSpeed = 1;(*球員無球速度*)
ballSpeed = 3;(*距離為給定距離passDis時的傳球球速*)
dribblingSpeed = 0.9;(*球員帶球速度*)
passDis = 10;(*一般傳球距離*)
friendDis = 35; (*無球隊友間距引數*)
fbDis = 15; (*有球隊友間距引數*)
rivalDis = 10; (*己方持球進攻時對手距離引數*)
defendDis = 2;(*己方無球防守時對手距離引數*)
aFriend = 5;(*隊友權值*)
aRival = 3;(*己方持球進攻時對手權值*)
aDefend = 10;(*己方無球防守時對手權值*)
aDoor = 5;(*己方持球進攻時對方球門權值*)
bDoor = 50;(*己方無球防守時己方球門權值*)
aBall = 100;(*己方無球防守時球權值*)
bBall = 200;(*己方持球進攻時球權值*)
t = 2;(*當前時間*)
lastCbt = -1;(*控球方*)
aGoal = 0;(*己方進球數*)
bGoal = 0;(*對方進球數*)
wait = 0;(*進球后等待時間*)
win = 0;(*進球方*)
tCbt = 0;(*上回合持球方*)
dribblingTime = 0;(*己方隊員帶球時間*)
(*主程式*)
While[t <= matchTime,
cbt = -1;
cbp = -1;
minS2 = 10000;
AppendTo[goal,
FromCharacterCode[48 + aGoal]<> ":" <>
FromCharacterCode[48 +bGoal]];(*更新比分*)
(*wait>0表示之前有某一方進球,比賽暫停*)
If[wait > 0,
vba = {0, 0};(*球速*)
If[wait == 20,
If[win == 2, AppendTo[vb,vp[[1, 1, 1]]],
AppendTo[vb, vp[[1, 2,1]]]],
AppendTo[vb, vb[[t]]]](*更新球的位置*)
];
(*確定控球權屬於哪一方*)
Do[
If[wait > 0,
If[j == 1, If[i == 1,AppendTo[vp, {}]];
AppendTo[vp[[t + 1]], {}]];
If[wait == 20, AppendTo[vp[[t+ 1, i]], vp[[1, i, j]]],
AppendTo[vp[[t + 1, i]],vp[[t, i, j]]]]];(*更新隊員的位置*)
s2 = (vp[[t, i, j]] -vb[[t]]).(vp[[t, i, j]] - vb[[t]]) + 0.0001;
If[s2 < minS2, minS2 = s2;cbt = i]
, {i, 1, 2}, {j, 1,Length[vp[[1, 1]]]}];
If[wait > 0, t++; wait--;Continue[]];
If[minS2 < 3, lastCbt = cbt,cbt = -1];
(*確定控球隊員行為,傳球或者帶球或者射門*)
Do[
minS2 = 10000;
bestJ = -1;
Do[
s2 = (vp[[t, i, j]] -vb[[t]]).(vp[[t, i, j]] - vb[[t]]) + 0.0001;
If[s2 < minS2,
minS2 = s2; bestJ = j],
{j, 1, Length[vp[[1, 1]]]}];
If [cbt == i,
vba = {0, 0};
cbp = bestJ;
bestMin = 0;
bestK = 1;
Do[
If[(dribblingTime > 3)&& (k == bestJ), Continue[]];
findRival = False;
ts1 =
Norm[(vp[[t, i, k]] - vp[[t- 1, i, k]])*1.5 + vp[[t, i, k]] -
vp[[t, i, bestJ]]] +0.1 // N;
minRival = 10000;
Do[
ts2 = Norm[vp[[t, 3 - i,l]] - vp[[t, i, bestJ]]] // N;
ts3 = Norm[vp[[t, i, k]] -vp[[t, 3 - i, l]]] // N;
If [ts3 -passDis/ballSpeed*playerSpeed - rivalDis/4 < minRival,
minRival = ts3 -passDis/ballSpeed*playerSpeed - rivalDis/4];
If[((ts2 + ts3)/ts1 <1.2) || minRival < 0,
findRival = True;Break[];];
,
{l, 1, Length[vp[[1, 1]]]}
];
If[findRival, Continue[]];
minRival *= 105 -Norm[vp[[t, i, k]] - vd[[3 - i]]] // N;
If[minRival > bestMin,bestMin = minRival; bestK = k];
, {k, 1, Length[vp[[1, 1]]]}
];
If[Norm[vp[[t, i, bestJ]] -vd[[3 - i]]] < 15,
vba =
5*Normalize[
vd[[3 - i]] + {0,Random[]*2 - 1} - vp[[t, i, bestJ]]];
vba = {vba[[1]] // N,vba[[2]] // N};
cbt = -1; cbp = -1,
If [((bestMin > 0) && (bestK !=bestJ)) || (dribblingTime > 12),
tballSpeed =
ballSpeed*(Norm[vp[[t, i,bestK]] - vp[[t, i, bestJ]]]/
passDis)^0.5 // N;
vba =
tballSpeed*
Normalize[
check[(vp[[t, i,bestK]] - vp[[t - 1, i, bestK]])*1.5 +
vp[[t, i, bestK]]] -vp[[t, i, bestJ]]];
vba = {vba[[1]] // N,vba[[2]] // N};
bestJ = bestK;
cbt = -1; cbp = -1];
];
];
If[cbt == -1, dribblingTime =0, If[cbt == tCbt, dribblingTime++];
tCbt = cbt];
(*確定每個球員的新位置*)
Do[
If[j == 1, If[i == 1,AppendTo[vp, {}]];
AppendTo[vp[[t + 1]], {}]];
vpa = {0, 0};(*球員速度*)
Do[
If[k == j, Continue[]];
s2 = (vp[[t, i, k]] - vp[[t,i, j]]).(vp[[t, i, k]] -
vp[[t, i, j]]) +0.0001;
If[(cbt == i) &&(cbp == k), ta = bBall; tb = fbDis,
ta = aFriend; tb =friendDis];
If[s2 >= friendDis^2,
vpa +=ta*(1/s2)*tb^2*Normalize[vp[[t, i, k]] - vp[[t, i, j]]],
vpa +=
ta*(2/tb^2 - 1/s2)*tb^2*
Normalize[vp[[t, i, k]] -vp[[t, i, j]]]],
{k, 1, Length[vp[[1, 1]]]}];
Do[
s = (Norm[vp[[t, 3 - i, k]]- vp[[t, i, j]]])^2;
If[lastCbt == i, s =aRival*rivalDis^2/Max[9, s],
s = -aDefend*defendDis^2/Max[9,s]];
vpa += s*Normalize[vp[[t, i,j]] - vp[[t, 3 - i, k]]],
{k, 1, Length[vp[[1, 1]]]}];
If[lastCbt == i,
vpa += aDoor*Normalize[vd[[3- i]] - vp[[t, i, j]]],
vpa +=bDoor*Normalize[vd[[i]] - vp[[t, i, j]]]];
If[(cbt == -1) && ((j== bestJ) || (lastCbt == 3 - i)),
vpa +=
aBall*Normalize[
vb[[t]] + Norm[vp[[t, i,j]] - vb[[t]]]/(Norm[vba] + 1)*vba -
vp[[t, i, j]]],
If[cbt == 3 - i, vpa +=aBall*Normalize[vb[[t]] - vp[[t, i, j]]]];
];
vpa = {vpa[[1]] // N,vpa[[2]] // N};
If[(cbt == i) && (cbp== j), ta = dribblingSpeed,
ta = playerSpeed];
AppendTo[vp[[t + 1, i]],
check[vp[[t, i, j]] +ta*Normalize[vpa]]], (*更新球員位置*)
{j, 1, Length[vp[[1, 1]]]}];
, {i, 1, 2}];
If[cbt != -1,
vba = (vp[[t + 1, cbt, cbp]] -vp[[t, cbt, cbp]])/
playerSpeed*(dribblingSpeed*2);];
AppendTo[vb, check[vb[[t]] +vba]];(*更新球的位置*)
If[vb[[t]] + vba != vb[[t +1]], vba = -vba*0.5];
If[cbt == -1, vba = vba*0.9,vba = vba*0.6];
(*判斷是否進球*)
If[(vb[[t + 1, 1]] == 0)&& (Abs[vb[[t + 1, 2]] - 34] < 3.9), bGoal++;
wait = 40; win = 2];
If[(vb[[t + 1, 1]] == 105)&& (Abs[vb[[t + 1, 2]] - 34] < 3.9),
aGoal++; wait = 40; win = 1];
If[wait > 0, bDoor += 5;];
t++;];
(*畫圖*)
Animate[Show[{field,
Table[{player[t, i, j]}, {i,1, 2}, {j, 1, Length[vp[[1, 1]]]}],
ball[t]}, PlotLabel ->goal[[t]]], {t, 1, matchTime, 1},
AnimationRate -> 7]
規則:
1、不設邊界球,角球和門球。
2、沒有裁判,沒有犯規。