Dodgems: A longer exercise
We'll now try to simulate fairground dodgems using what we know so far. Our dodgems will be circular, and our arena square. To make things more interesting we'll have 3 teams of dodgems with different sizes, speeds and colours.
Direction will be in degrees (going clockwise from 'north'). The coordinate system will have (0,0) bottom left.
We'll start by setting some constants and creating some enumerated types.
const float PI = 3.14159; const int arena_size =400; // side of arena in pixels const int turn_amount=20; // how much direction can change by each time enum Team {Green, Red, Blue}; enum Quadrant {NWQuadrant, NEQuadrant, SEQuadrant, SWQuadrant}; enum Direction {NWDirection, NEDirection, SEDirection, SWDirection}; enum Turn {Left, Straight, Right};
Now to write some little routines. None of them will be more than 10 lines or so. You may like to test them as you go along.
Arithmetic Routines
Write the following routines. You can assume that you have a
random()
routine that returns an integer in the range
0 to 2^31-1
.
//Convert radians to degrees int rtod(float radians); //Convert degrees to radians float dtor(int degrees); //Return a number in the range 1 to 'maximum' inclusive int random_integer(int maximum);
Geometric Routines
//Return the distance between x1,y1 and x2,y2 int distance (int x1, int x2, int y1, int y2); // Given the direction of the dodgem, determine the quadrant direction Direction get_quadrant_direction(int direction ) // Given coordinates, determine which quadrant the dodgem is in Quadrant which_quadrant(int x, int y); // Given coordinates, use which_quadrant and atan to calculate // the angle of the radius out to the dodgem int radial_direction(int x, int y); // calculate the change in x and y for the proposed movement void get_movement(int speed, int direction, int &x_offset, int &y_offset);
Graphics Routines
C++ doesn't have any graphics routines. CUED people can use the Vogle Library to create the following routines (about 40 lines of code). If you don't have vogle, you'll need to write the following routines using whatever graphics available.
void initialise_graphics(); // create a window, and arena outline // and suitable scale factors void draw_circle(int x,int y, int radius, Team t); //use t to select color void undraw_circle(int x,int y, int radius); void wait_for_key(); // wait for a key to be pressed before removing window
Decision Routine
// Decide which way - if any - to turn. Don't bother turning if // the dodgem is less than 'turn_amount' from the ideal direction Turn which_way(int ideal_direction, int current_direction);
Creating the Dodgem class
We have all the subsidary routines. Here's a workable class definition
class dodgem { private: int x; int y; int radius; int speed; int max_speed; string name; // name of driver Team team; void check_wall_collision(); int best_direction(); void draw(); public: // Constructor dodgem(Team t, string driver); int current_direction; int score; int getx() { return x;} int gety() { return y;} int getradius() { return radius;} Team getteam() { return team;} string getname() {return name;} void print(); void move(); void undraw(); };
Most of the class functions provide public access to private values.
draw()
and undraw()
call draw_circle
and undraw_circle
. print()
displays diagnostic information. You can write these
inline now. The other class functions might best be placed outside the main
class definition - e.g. write dodgem::move() { ... }
etc.
The constructor function fills in the team
and name
fields.
Use switch(t)
to set radius
and max_speed
fields -
values in the range 5 to 15 should be ok. Set score
to 0 and
speed
to max_speed
.
x
, y
and current_direction
can be initialised using
the random_integer
integer routine. Don't worry for the moment about
dodgems being created on top of others, but do create them so that they
don't project over the arena's edge.
Finish the constructor by calling draw()
.
check_wall_collision()
uses the current coordinates to determine
whether the dodgem has crashed against a wall. If there's a collision,
set x
or y
so that the dodgem is just touching the wall,
and set the current_direction
perpendicular to the wall, towards
the center. Write it now.
best_direction()
calculates the way the dodgem would like to go
if it could turn that far. For the
moment just try to circle clockwise, but if the dodgem is partly outside the
inscribed circle of the arena, try to head towards the centre.
move()
calls best_direction()
then which_way()
to decide how to change the
current_direction
. The dodgem is then removed from the screen and
new coordinates calculated. check_wall_collision()
is called,
then the dodgem redrawn.
Putting it all together
The main routine is going to initialise the randomiser (using
srandom(time(0))
and graphics,
create some dodgems, then for 100 time intervals move each dodgem in
turn, checking each time to see if the dodgem has crashed into another.
If the dodgem crashes into another team's dodgem, the latter is
eliminated and the first dodgem gains a point. If a dodgem crashes into
a fellow team member the former loses a point and is shoved 90 degrees clockwise.
At the end the scores of the remaining dodgems are printed out and
wait_for_key()
called.
If you store the dodgems in a list<dodgem>
then its easy to
add, remove and loop through them.
And that's it! you can develop this simulation if you want, changing the direction and speed to make the dodgems more predatory, and making the dodgems bounce off walls.