(我的本科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、没有裁判,没有犯规。