The team Debrecen Great Forest FC++ is further developed from the team Debreceni Hivatásos FC++ that was developed in the book [PARP]. The interesting thing about Debrecen Great Forest FC++ is that it has already used the newly introduced pos command.
If we compare the lobogofcpp.h
source files of Debrecen Great Forest FC++
and Debreceni Hivatásos FC++ we will see
the that the former one is shorter than the latter one. Accordingly,
the lexer has also changed as it is shown in the next code snippet
from the point of view of the ball:
{BALL}{WS}{FLOAT}{WS}{FLOAT} {
std::sscanf(yytext, "((b) %f %f", &dist_buffer, &ang_buffer);
ball.setDistAng(time, dist_buffer, ang_buffer); }The lexer had to be extended with further player modes, for example the following referee's messages are received by the clients:
enum RefereePlayMode { before_kick_off,
play_on, half_time, drop_ball, kick_off_r, kick_off_l, corner_kick_r,
corner_kick_l, free_kick_l, free_kick_r, kick_in_l, kick_in_r, goal_l,
goal_r, free_kick_fault_l, free_kick_fault_r, offside_l, offside_r
};Our lecture notes focus on the corner kick and the throw-in play
modes. The kick off and the ball in play modes have already been
processed in the previous versions of the teams, see the book
[PROP]. The kick off is handled by the
beforeKickOff, the ball in play and the newly
handled modes are processed by the function
playOn:
switch
(thisclient->bl.get_play_mode()) { case half_time: case goal_l:
case goal_r: case before_kick_off: beforeKickOff(thisclient);
//beforeKickOff_light_testing(thisclient); break; case play_on: case
kick_off_l: case kick_off_r: thisclient->bl.nof_kickin_quantums =
0; playOn(thisclient); //playOn_light_testing(thisclient); break; case
kick_in_l: if (thisclient->bl.get_lr() == 'l') kickIn(thisclient);
else playOn(thisclient); break; case kick_in_r: if
(thisclient->bl.get_lr() == 'r') kickIn(thisclient); else
playOn(thisclient); break; case corner_kick_r: if
(thisclient->bl.get_lr() == 'r') cornerKickAtt(thisclient); else
cornerKickDef(thisclient); break; case corner_kick_l: if
(thisclient->bl.get_lr() == 'l') cornerKickAtt(thisclient); else
cornerKickDef(thisclient); break; case free_kick_l: case free_kick_r:
case free_kick_fault_l: case free_kick_fault_r: case offside_l: case
offside_r: case drop_ball: playOn(thisclient); break;
}In this snippet, the commented out
beforeKickOff_light_testing was used only to test
the simplification. At taking a throw-in (in the case of
kick_in_l and kick_in_r) we
distinguish the team which throws in the ball. And finally the
attacker and defender teams are treated separately at corner kicks.
We follow the solution shown in the book
[MIRC], where we distiguished the moving into the
position to make the throw-in and the throw-in itself. The latter
activity includes the looking out for teammates to whom the ball can
be passed. These activities are not divided into various functions
but we use the following kickIn function:
static void kickIn(Client *
thisclient) { // http://youtu.be/HiaPWqSzYxk char buf[1024]; int
time = thisclient->bl.get_time(); int squad_number =
thisclient->bl.get_squad_number() -1; int lr =
thisclient->bl.get_lr(); // ha látja a lasztit if
(thisclient->bl.get_see_ball()) { // kinek kell majd dobni int
to; thisclient->coach.set_formation(Formations::F_4_4_2_F); int
tx =
thisclient->coach.get_formation().get_formation_x(squad_number,
lr); int ty =
thisclient->coach.get_formation().get_formation_y(squad_number);
if (thisclient->bl.get_ball_dist() < .8) { if ((to =
thisclient->bl.pass_to_farthes(3)) != -1) { std::snprintf(buf,
64, "(kick %d %f)\0",
thisclient->bl.tavhozEro(thisclient->bl.get_own_player(to).getDist()),
thisclient->bl.get_own_player(to).getAng());
thisclient->sndCmd(buf); std::cout << time << " "
<< thisclient->bl.get_squad_number()<< " passz
(bedobas) " << to+1 << std::endl; } else { // ha nem
lát társat if (++thisclient->bl.nof_kickin_quantums > 10) {
// ha többen fogognak éppen nem látva egymást, akkor ne a
játékvezetői beavatkozásig tegyék std::snprintf(buf, 64, "(kick %d
%f)\0", 20, thisclient->bl.get_ball().getAng());
thisclient->sndCmd(buf); std::cout <<
thisclient->bl.get_squad_number()<< " vissza pocc a
palyara " << std::endl; } else {
thisclient->sndCmd("(turn 25)\0"); std::cout <<
thisclient->bl.get_squad_number()<< " kinek kene dobni?
..." << thisclient->bl.nof_kickin_quantums <<
std::endl; } } // megy a labdára innen } else if
(std::abs(thisclient->bl.get_ball_ang()) > 20.0) {
std::snprintf(buf, 64, "(turn %f)\0",
thisclient->bl.get_ball_ang()); thisclient->sndCmd(buf);
std::cout << thisclient->bl.get_squad_number()<<
"turn" << thisclient->bl.get_ball_ang() <<
std::endl; } else if (thisclient->bl.get_ball_dist() < 30.0
&& thisclient->bl.pass_to_farthes(2)==-1) { // 30-ról
is sprintel bedobni, ha nincs közelebbi társ
thisclient->sndCmd("(dash 100)"); std::cout <<
thisclient->bl.get_squad_number()<< " megyek dobni 100"
<< std::endl; } else if (thisclient->bl.get_ball_dist()
< 40.0 && ((to=thisclient->bl.nof_seen_teammates())
< 3)) { // 40-ról is jön bedobni, ha esetleg lát is társakat
(mert az előző ág nem teljesült) std::snprintf(buf, 64, "(dash
%f)\0", 100.0/(to + 1.0)); thisclient->sndCmd(buf); std::cout
<< thisclient->bl.get_squad_number()<< " megyUNK
dobni " << std::endl; } else { std::snprintf(buf, 64, "(pos
%d %d 30)\0", tx, ty); thisclient->sndCmd(buf); } } else { //
ha nem látja a lasztit thisclient->sndCmd("(turn 40)\0");
std::cout << thisclient->bl.get_squad_number()<<
"meccs van? ... (dobas)" << std::endl; } }
| If an agent can see the ball we will investigate its distance from the ball |
| then the following situations will be investigated: |
| Is the ball close enough to kick it? |
| If yes, then, using the
|
| If there are no suitable teammates, the thrower will kick the ball or turn 25 degrees to look for suitable teammates depending on whether this situation has already happened more than 10 times. |
| Returning to the investigation of the distance of the ball, if the agent can see the ball under an angle smaller than 20 degrees, it will turn in order to try to correct the angle to 0. |
| In the same indentation level if the ball's distance is less than 30 meters and the player is nearest to the ball, the player will sprint to take the throw-in. |
| If none of the above conditions are met, the agent will move towards its tactical position (tx, ty). |
| And finally, if the agent cannot see the ball, it will search the ball, to be more precise, it will make a 40 degrees turn. |
We have made a video on testing that can be seen at http://youtu.be/HiaPWqSzYxk,
.
Using screenshots we emphasize some moments in order to better understand how the previous code snippet works. (These pictures are made independently from the video.)
The next code snippet moves the player towards the ball .
} else if
(thisclient->bl.get_ball_dist() < 40.0 &&
((to=thisclient->bl.nof_seen_teammates()) < 3)) { // 40-ról
is jön bedobni, ha esetleg lát is társakat (mert az előző ág nem
teljesült) std::snprintf(buf, 64, "(dash %f)\0", 100.0/(to +
1.0)); thisclient->sndCmd(buf); std::cout <<
thisclient->bl.get_squad_number()<< " megyUNK dobni "
<< std::endl;After some simulation cycles the player 4 will be closer to the ball than 30 meters.
} else if
(thisclient->bl.get_ball_dist() < 30.0 &&
thisclient->bl.pass_to_farthes(2)==-1) { // 30-ról is sprintel
bedobni, ha nincs közelebbi társ thisclient->sndCmd("(dash
100)"); std::cout <<
thisclient->bl.get_squad_number()<< " megyek dobni 100"
<< std::endl;When the player agent reaches the ball, it will turn in order to see teammates who are available for receiving the ball.
The player 4 has found player 3 who is available for receiving the ball.
if
(thisclient->bl.get_ball_dist() < .8) { if ((to =
thisclient->bl.pass_to_farthes(3)) != -1) { std::snprintf(buf,
64, "(kick %d %f)\0",
thisclient->bl.tavhozEro(thisclient->bl.get_own_player(to).getDist()),
thisclient->bl.get_own_player(to).getAng());
thisclient->sndCmd(buf); std::cout << time << " "
<< thisclient->bl.get_squad_number()<< " passz
(bedobas) " << to+1 << std::endl;The Debrecen Great Forest FC++'s
main new feature is the introduction of the tactical lineups into
the agents. The lineups are embedded in the
Coach class.
class Coach { public: Coach(int formi
= 0):formi(formi) { if (formi >= Formations::NOF || formi <0)
formi = 0; formations = new Formation[Formations::NOF] { // (a +--os
megadásokkal már tisztára FerSML feeling) // 4_3_3 h {-51, 0, // att
-4, 10, -1, 0, -3, -11, // mid -18, -16, -17, 2, -18, 17, // def
-35, -19, -33, -10, -34, 12, -35, 24},For illustrative purposes we use two lineups, these are the 4-4-2 and 4-3-3 soccer formations.
class Formations { public: static int
const F_4_3_3_H = 0; static int const F_4_3_3_F = 1; static int
const F_4_4_2_H = 2; static int const F_4_4_2_F = 3; static int
const F_CORNER_ATT = 4; static int const F_CORNER_DEF = 5; // number
of all formations static int const NOF = F_CORNER_DEF+1;
};The other two formations control the positioning of the players at corner kicks.
Example 5.1. Placing a player beside a goalpost
Modify the defensive formation
F_CORNER_DEF so that a player will stand
beside a goalpost.
Taking and defending a corner have been implemented in two different ways. The organization of the attackers' source code is similar to what was used in the implementation of the throw-in. But in the case of the corner kicking there is a distinguished player who will be taking the corner kick. This player will be player 4 in default:
static void cornerKickAtt(Client *
thisclient) { // http://youtu.be/93F43ppEQ2g char buf[1024]; int
time = thisclient->bl.get_time(); int squad_number =
thisclient->bl.get_squad_number() -1; int lr =
thisclient->bl.get_lr();
thisclient->coach.set_formation(Formations::F_CORNER_ATT); int
tx =
thisclient->coach.get_formation().get_formation_x(squad_number,
lr); int ty =
thisclient->coach.get_formation().get_formation_y(squad_number);
SeenFlag & sf_opp_gt =
(lr=='l')?thisclient->bl.get_flag(Flag::GRT):thisclient->bl.get_flag(Flag::GLT);
SeenFlag & sf_opp_gb =
(lr=='l')?thisclient->bl.get_flag(Flag::GRB):thisclient->bl.get_flag(Flag::GLB);
// ha látja a lasztit if (thisclient->bl.get_see_ball()) { if
(thisclient->bl.get_ball_dist() < .8) { // ha már ott van a
lasztinál, ha látja a kaput, elvégzi a szögletet: if (time -
sf_opp_gt.get_time_stamp() < 2) { std::snprintf(buf, 64, "(kick
%d %f)\0", 100, sf_opp_gt.getAng()); thisclient->sndCmd(buf);
std::cout << time << " " <<
thisclient->bl.get_squad_number()<< " berug (szoglet) "
<< std::endl; } else if (time - sf_opp_gb.get_time_stamp()
< 2) { std::snprintf(buf, 64, "(kick %d %f)\0", 100,
sf_opp_gb.getAng()); thisclient->sndCmd(buf); std::cout
<< time << " " <<
thisclient->bl.get_squad_number()<< " berug (szoglet) "
<< std::endl; } else { // ha nem látja a kaput
thisclient->sndCmd("(turn 25)\0"); std::cout <<
thisclient->bl.get_squad_number()<< " hol a kapu? ..."
<< std::endl; } // megy a labdára innen } else if
(std::abs(thisclient->bl.get_ball_ang()) > 20.0) {
std::snprintf(buf, 64, "(turn %f)\0",
thisclient->bl.get_ball_ang()); thisclient->sndCmd(buf);
std::cout << thisclient->bl.get_squad_number()<<
"turn" << thisclient->bl.get_ball_ang() <<
std::endl; } else if (thisclient->bl.get_ball_dist() < 30.0
&& thisclient->bl.pass_to_farthes(2)==-1) { // 30-ról
is sprintel bedobni, ha nincs közelebbi társ
thisclient->sndCmd("(dash 100)"); std::cout <<
thisclient->bl.get_squad_number()<< " megyek dobni 100"
<< std::endl; } else if (squad_number == 3) { // a kijelölt
játékos (ez tk. a 4-es player) megy elvégezni
thisclient->sndCmd("(dash 100)"); std::cout <<
thisclient->bl.get_squad_number()<< " kijeloltkent megyek
dobni 100" << std::endl; } else { // többiek (akik nem
mennek a labdára elvégezni) helyezkednek a szöglethez adott
felállás szerint std::snprintf(buf, 64, "(pos %d %d 90)\0", tx,
ty); thisclient->sndCmd(buf); } } else { // ha nem látja a
lasztit thisclient->sndCmd("(turn 40)\0"); std::cout <<
thisclient->bl.get_squad_number()<< "meccs van? ...
(szoglet)" << std::endl; } }The defending is organized much more simply:
static void cornerKickDef(Client *
thisclient) { // http://youtu.be/93F43ppEQ2g char buf[1024]; int
time = thisclient->bl.get_time(); int squad_number =
thisclient->bl.get_squad_number() -1; int lr =
thisclient->bl.get_lr();
thisclient->coach.set_formation(Formations::F_CORNER_DEF); int
tx =
thisclient->coach.get_formation().get_formation_x(squad_number,
lr); int ty =
thisclient->coach.get_formation().get_formation_y(squad_number);
// ha látja a lasztit if (thisclient->bl.get_see_ball()) {
std::snprintf(buf, 64, "(pos %d %d 85)\0", tx, ty);
thisclient->sndCmd(buf); } else { // ha nem látja a lasztit
thisclient->sndCmd("(turn 40)\0"); std::cout <<
thisclient->bl.get_squad_number()<< "meccs van? ...
(szoglet)" << std::endl; } }We have made a video on testing that can be seen at http://youtu.be/93F43ppEQ2g,
.
We cannot say that this team is able to play the soccer game itself. However the Debrecen Great Forest FC++ has already handled most of the play modes. The role of this team is merely to be used as an important step in developing the next team called Debrecen Deep Forest FC++ that will win agains Debrecen Great Forest FC++.
Example 5.2. Free kicks
Following the implementations of the throw-in or the corner kick, develop your own implementation for the missing play modes like the free kick.