Department of Engineering

IT Services

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.