Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/orxonox/objects/controllers/PongAI.cc @ 2860

Last change on this file since 2860 was 2860, checked in by landauf, 15 years ago

Improved PongAI - it tries to predict the position of the ball (but does some random mistakes, depending on the AI's strength) and moves to this position (with a reaction delay, also depending on the strength). Additionally fixed some inaccuracies caused by the hysteresis avoidance system.

  • Property svn:eol-style set to native
File size: 8.1 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      ...
26 *
27 */
28
29#include "OrxonoxStableHeaders.h"
30#include "PongAI.h"
31
32#include "core/CoreIncludes.h"
33#include "core/ConfigValueIncludes.h"
34#include "objects/worldentities/ControllableEntity.h"
35#include "objects/worldentities/PongBall.h"
36#include "tools/Timer.h"
37
38namespace orxonox
39{
40    CreateUnloadableFactory(PongAI);
41
42    const static float MAX_REACTION_TIME = 0.4;
43
44    PongAI::PongAI(BaseObject* creator) : Controller(creator)
45    {
46        RegisterObject(PongAI);
47
48        this->ball_ = 0;
49        this->ballDirection_ = Vector2::ZERO;
50        this->ballEndPosition_ = 0;
51        this->randomOffset_ = 0;
52        this->relHysteresisOffset_ = 0.02;
53        this->strength_ = 0.5;
54        this->movement_ = 0;
55
56        this->setConfigValues();
57
58//        this->randomOffsetTimer_.setTimer(MAX_REACTION_TIME * (1 - this->strength_), false, this, createExecutor(createFunctor(&PongAI::calculateRandomOffset)));
59//        this->ballEndPositionTimer_.setTimer(MAX_REACTION_TIME * (1 - this->strength_), false, this, createExecutor(createFunctor(&PongAI::calculateBallEndPosition)));
60//        this->randomOffsetTimer_.stopTimer();
61//        this->ballEndPositionTimer_.stopTimer();
62    }
63
64    PongAI::~PongAI()
65    {
66        for (std::list<std::pair<Timer<PongAI>*, char> >::iterator it = this->reactionTimers_.begin(); it != this->reactionTimers_.end(); ++it)
67            delete (*it).first;
68    }
69
70    void PongAI::setConfigValues()
71    {
72        SetConfigValue(strength_, 0.5).description("A value from 0 to 1 where 0 is weak and 1 is strong.");
73    }
74
75    void PongAI::tick(float dt)
76    {
77        if (!this->ball_ || !this->getControllableEntity())
78            return;
79
80        Vector3 mypos = this->getControllableEntity()->getPosition();
81        Vector3 ballpos = this->ball_->getPosition();
82        Vector3 ballvel = this->ball_->getVelocity();
83        float hysteresisOffset = this->relHysteresisOffset_ * this->ball_->getFieldDimension().y;
84
85        char move = 0;
86
87        // Check in which direction the ball is flying
88        if ((mypos.x > 0 && ballvel.x < 0) || (mypos.x < 0 && ballvel.x > 0))
89        {
90            // Ball is flying away
91            this->ballDirection_.x = -1;
92            this->ballDirection_.y = 0;
93
94            if (mypos.z > hysteresisOffset)
95                move = 1;
96            else if (mypos.z < -hysteresisOffset)
97                move = -1;
98        }
99        else if (ballvel.x == 0)
100        {
101            // Ball is standing still
102            this->ballDirection_.x = 0;
103            this->ballDirection_.y = 0;
104        }
105        else
106        {
107            // Ball is approaching
108            if (this->ballDirection_.x != 1)
109            {
110                this->ballDirection_.x = 1;
111                this->ballDirection_.y = sgn(ballvel.z);
112                this->ballEndPosition_ = 0;
113                this->randomOffset_ = 0;
114
115                this->calculateRandomOffset();
116                this->calculateBallEndPosition();
117                //this->randomOffsetTimer_.setInterval(MAX_REACTION_TIME * (1 - this->strength_));
118                //this->ballEndPositionTimer_.setInterval(MAX_REACTION_TIME * (1 - this->strength_));
119                //this->randomOffsetTimer_.startTimer();
120                //this->ballEndPositionTimer_.startTimer();
121            }
122
123            if (this->ballDirection_.y != sgn(ballvel.z))
124            {
125                this->ballDirection_.y = sgn(ballvel.z);
126
127                this->calculateBallEndPosition();
128                //this->ballEndPositionTimer_.startTimer();
129            }
130
131            float desiredZValue = /*((1 - this->strength_) * ballpos.z) + */(/*this->strength_ * */this->ballEndPosition_) + this->randomOffset_;
132
133            if (mypos.z > desiredZValue + hysteresisOffset * (this->randomOffset_ < 0))
134                move = 1;
135            else if (mypos.z < desiredZValue - hysteresisOffset * (this->randomOffset_ > 0))
136                move = -1;
137        }
138
139        this->move(move);
140        this->getControllableEntity()->moveFrontBack(this->movement_);
141    }
142
143    void PongAI::calculateRandomOffset()
144    {
145        // Calculate the exponent for the position-formula
146        float exp = pow(10, 1 - 2*this->strength_); // strength: 0   -> exp = 10
147                                                    // strength: 0.5 -> exp = 1
148                                                    // strength: 1   -> exp = 0.1
149
150        // Calculate the relative position where to hit the ball with the bat
151        float position = pow(rnd(), exp); // exp > 1 -> position is more likely a small number
152                                          // exp < 1 -> position is more likely a large number
153
154        // The position shouln't be larger than 0.5 (50% of the bat-length from the middle is the end)
155        position *= 0.48;
156
157        // Both sides are equally probable
158        position *= sgn(rnd(-1,1));
159
160        // Calculate the offset in world units
161        this->randomOffset_ = position * this->ball_->getBatLength() * this->ball_->getFieldDimension().y;
162    }
163
164    void PongAI::calculateBallEndPosition()
165    {
166        Vector3 position = this->ball_->getPosition();
167        Vector3 velocity = this->ball_->getVelocity();
168        Vector2 dimension = this->ball_->getFieldDimension();
169
170        // calculate end-height: current height + slope * distance
171        this->ballEndPosition_ = position.z + velocity.z / velocity.x * (-position.x + dimension.x / 2 * sgn(velocity.x));
172
173        // Calculate bounces
174        for (float limit = 0.35; limit < this->strength_ || this->strength_ > 0.99; limit += 0.4)
175        {
176            if (this->ballEndPosition_ > dimension.y / 2)
177            {
178                this->ballEndPosition_ = dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
179                continue;
180            }
181            if (this->ballEndPosition_ < -dimension.y / 2)
182            {
183                this->ballEndPosition_ = -dimension.y - this->ballEndPosition_ + (rnd(-1, 1) * dimension.y * (1 - this->strength_));
184                continue;
185            }
186            break;
187        }
188    }
189
190    void PongAI::move(char direction)
191    {
192        // The current direction is either what we're doing right now (movement_) or what is last in the queue
193        char currentDirection = this->movement_;
194        if (this->reactionTimers_.size() > 0)
195            currentDirection = this->reactionTimers_.back().second;
196
197        // Only add changes of direction
198        if (direction == currentDirection)
199            return;
200
201        // Calculate delay, but only to change direction or start moving (stop works without delay)
202        if (direction != 0)
203        {
204            float delay = MAX_REACTION_TIME * (1 - this->strength_);
205
206            // Add a new Timer
207            Timer<PongAI>* timer = new Timer<PongAI>(delay, false, this, createExecutor(createFunctor(&PongAI::delayedMove)));
208            this->reactionTimers_.push_back(std::pair<Timer<PongAI>*, char>(timer, direction));
209        }
210        else
211        {
212            this->movement_ = 0;
213        }
214    }
215
216    void PongAI::delayedMove()
217    {
218        this->movement_ = this->reactionTimers_.front().second;
219
220        Timer<PongAI>* timer = this->reactionTimers_.front().first;
221        delete timer;
222
223        this->reactionTimers_.pop_front();
224    }
225}
Note: See TracBrowser for help on using the repository browser.