Skip to Navigation

ZRacer code

  1. /*
  2.  * Remigiusz Jan Andrzej Modrzejewski, http://lrem.net/ <lrem at go2.pl>
  3.  * Distributed under the GPL, for more details see:  
  4.  * http://lrem.net/zracer.xhtml
  5.  *  
  6.  * ZRacer - a simple arcade game in ncurses
  7.  *
  8.  * ZRacer is a racing game where 1 - 2 players race on a randomly
  9.  * generated racecourse with split-screen and using the same keyboard.
  10.  *  
  11.  * Conventions taken:
  12.  *      - coordinates order is (y, x)
  13.  *      - (0, 0) is upper left corner of everything
  14.  *      - as a result, finish line is at line 0
  15.  *      - car doesn't take up the whole rectangle (for collision checking)
  16.  *      - when a car crashes, it's window is frozen and no input is taken
  17.  *      - the track is stored as its ascii-art representation
  18.  */
  19.  
  20. #include <curses.h>
  21. #include <cstdarg>
  22. #include <cstdio>
  23. #include <string>
  24. #include <vector>
  25. #include <ctime>
  26. #include <cassert>
  27. #include <cstdlib>
  28.  
  29. using namespace std;
  30.  
  31. // This one defines a random function with results in range 0..1 (double).
  32. #define drand()((double)rand()/RAND_MAX)
  33.  
  34. // Various constants
  35. #define MAX_CAR_SIZE 20
  36. #define MAX_PLAYERS 2
  37. #define INF 123456789
  38. #define KEY_ESC 27 // Missing in ncurses...
  39. #define RESULTS_COLORS 11
  40. #define MESSAGE_LENGTH 100
  41.  
  42. // Action values
  43. #define ACCELERATE -1
  44. #define BRAKE 1
  45. #define LEFT -1
  46. #define RIGHT 1
  47.  
  48. // Make passage, but don't exceed available space.
  49. #define MINIMAL_WIDTH\
  50. (min(settings.players*settings.car_size*2.5, (double)settings.race_width-2))
  51.  
  52.    // Main menu item defines, for convenience.
  53. #define MENU_QUIT 0
  54. #define MENU_START 1
  55. #define MENU_OPTIONS 2
  56.  
  57.    /*
  58.     * In-game settings. This needn't really by a struct, but it looks more
  59.     * readable to access settings by "settings.delay" than "delay".
  60.     */
  61.  
  62.    struct _settings
  63. {
  64.    // Basic game delay, is not equal to move time, but is a factor.
  65.    timespec delay;
  66.    // The axis of splitscreen.
  67.    bool vertical_split;
  68.    // Whether both players race on the same-looking racecourse.
  69.    bool similar_track;
  70.    // Or maybe literally the same one? (Collisions possible)
  71.    bool shared_track;
  72.    // The sizes of the course, better make it bigger than the car ;)
  73.    int race_length, race_width;
  74.    // The minimal width of the road the players can drive.
  75.    int minimal_width;
  76.    // The number of participants.
  77.    int players;
  78.    // Character with which the cars are drawn.
  79.    char character;
  80.    // The size of the car.
  81.    int car_size;
  82.    // The distance interval at which the speed of the car changes.
  83.    int speed_base;
  84.    // The chance of generating a rock on a given line
  85.    double rock_chance;
  86.    // The chance of generating a turning on a given line
  87.    double turn_chance;
  88.    // Keys players use to interact with the game.
  89.    int controls[MAX_PLAYERS][4];
  90.  
  91.    void reset(void)
  92.    {
  93.       delay.tv_sec = 0;
  94.       // Hundredth of a second * const.
  95.       delay.tv_nsec = 1000000*25;
  96.       similar_track = vertical_split = true;
  97.       shared_track = true;
  98.       race_length = 500;
  99.       // Zero makes these 2 variables adjusted to screen size.                                                 race_width = 0;                                                                                   minimal_width = 0;
  100.       players = 1;
  101.       character = '^';
  102.       car_size = 10;
  103.       speed_base = 5;
  104.       rock_chance = 0.025;
  105.       turn_chance = 0.125;
  106.  
  107.       // Arrow keys for first player
  108.       controls[0][0]=KEY_UP;
  109.       controls[0][1]=KEY_DOWN;
  110.       controls[0][2]=KEY_LEFT;
  111.       controls[0][3]=KEY_RIGHT;
  112.       // WSAD for second
  113.       controls[1][0]='w';
  114.       controls[1][1]='s';
  115.       controls[1][2]='a';
  116.       controls[1][3]='d';
  117.    }
  118.  
  119.    void editor(void);
  120.    void _edit_players(void);
  121.    void _edit_length(void);
  122.    void _edit_width(void);
  123.    void _edit_rocks(void);
  124.    void _edit_turns(void);
  125.    void _edit_delay(void);
  126.    void _edit_sharing(void);
  127. } settings;
  128.  
  129. class car_image
  130. {
  131.    /*
  132.     * This is where we store the image of the car.
  133.     * For performance purposes, it is created only once when scaled.
  134.     */
  135.    bool storage [MAX_CAR_SIZE+1][MAX_CAR_SIZE+1];
  136.    // And as list of used pixels coords.
  137.    vector<pair<int, int> > dots;
  138.    char character;
  139.    int color, size;
  140.  
  141.    /*
  142.     * Internal functions. First one is _clear() - it just sets all
  143.     * the storage area to false. Second one is line() - takes coords
  144.     * of 2 points and draws a line between them (or more literally
  145.     * just marks certain values within storage as true).
  146.     */
  147.    void _clear(void);
  148.    void _line(int, int, int, int);
  149.  
  150.    public:
  151.    /*
  152.     * Constructor, takes input from the settings.
  153.     */
  154.    car_image(void);
  155.    /*
  156.     * This one simply draws the car on the given window. It assumes  
  157.     * that this can clearly be done, so all the checks need to be
  158.     * done earlier. The parameters it takes are the window and position
  159.     * of upper left corner of the car.
  160.     */
  161.    void display(WINDOW*, int, int);
  162.    /*
  163.     * Draws the explosion of the car. Parameters are windows, position
  164.     * of upper left corner of the car. Position is relative to the window,  
  165.     * _not_ the track.
  166.     */
  167.    void explode(WINDOW*, int, int);
  168.    /*
  169.     * Collision happens only when an obstacle is on a pace taken by the
  170.     * car. It is possible to have the obstacle between the car's "ribs".
  171.     * This checks whether given pace relative to *car's position* is
  172.     * taken by the car.
  173.     */
  174.    bool collision_check(int, int);
  175.    /*
  176.     * These are simple mutators.
  177.     */
  178.    void set_character(char);
  179.    void set_color(int);
  180.  
  181.    // And a simple accessor.
  182.    vector<pair<int, int> > get_dots(void);
  183. };
  184.  
  185. class track
  186. {
  187.    /*
  188.     * These are the most important data for the game. The track should
  189.     * be generated only once each game, preferably shared between players.
  190.     */
  191.    vector<vector<char> > circuit;
  192.  
  193.    public:
  194.    /*
  195.     * Two constructors. One takes input from the settings, second just  
  196.     * copies another track.
  197.     */
  198.    track(void);
  199.    track(track*);
  200.    /*
  201.     * Takes the number of the top line to display and the window.
  202.     * Window's height and width are grabbed by getmaxyx().
  203.     * There's an assertion that the window is wide enough.
  204.     */
  205.    void display(WINDOW*, int);
  206.    /*
  207.     * Tells whether there's an obstacle at a given pace.
  208.     */
  209.    bool taken(int, int);
  210.  
  211.    void mark(int, int, car_image*);
  212.    void unmark(int, int, car_image*);
  213. };
  214.  
  215. class player_handler
  216. {
  217.    WINDOW* screen;
  218.    track* course;
  219.    car_image* car;
  220.    // last_move is the time value of the previous player's action
  221.    // (y, x) is the position of the upper left corner of the car
  222.    // top_line is the top displayed line of the course
  223.    // commands store what player clicked
  224.    int last_move, y, x, top_line, command_x, command_y, screen_height;
  225.    int controls[4];
  226.  
  227.    public:
  228.    /*
  229.     * This constructor prepares the part of the screen for the player.
  230.     * It decides which part of screen to take, and the color of the car,
  231.     * taking the settings and the player number. The track is generated
  232.     * outside it.
  233.     */
  234.    player_handler(int, track*);
  235.    /*
  236.     * Player's main loop. Returns true if the player continues to play,
  237.     * and false if the game ends for him. Also does redraw the window.
  238.     */
  239.    bool tick(int);
  240.    /*
  241.     * Knowing the player's key controls, this one does what it's named for.
  242.     * Only sets the action to perform during next move.
  243.     */
  244.    void parse_input(int);
  245.    /*
  246.     * These are used only in shared track races in order to catch player-player
  247.     * collisions.
  248.     */
  249.    void mark_position(void);
  250.    void unmark_position(void);
  251.    /*
  252.     * Apart from deallocating standard structures, this one has also to
  253.     * destroy the ncurses window it created, so it needs a separate destructor.
  254.     */
  255.    //~player_handler(void);
  256. };
  257.  
  258. class game
  259. {
  260.    int time;
  261.    player_handler* players[MAX_PLAYERS];
  262.    bool alive[MAX_PLAYERS];
  263.  
  264.    public:
  265.    /*
  266.     * Constructor does all the fancy things like initializing ncurses,
  267.     * while destructor brings back normal tty behaviour. This is great,
  268.     * as it doesn't force me to remember about deinitialization any
  269.     * time I want to break the game.
  270.     */
  271.    game(void);
  272.    ~game(void);
  273.    /*
  274.     * This is the game's main loop action...
  275.     * It returns true as long as game continues.
  276.     */
  277.    bool tick(void);
  278. };
  279.  
  280. int main_menu (void);
  281. // This is a wrapper around printw, also accepts arbitrary number of arguments
  282. void message (char*, ...);
  283.  
  284. int main (void)
  285. {
  286.    bool keep_asking = true;
  287.  
  288.    settings.reset();
  289.  
  290.    while(keep_asking)
  291.    {
  292.       switch(main_menu())
  293.       {
  294.          case MENU_START:
  295.             {
  296.                game race;
  297.                while(race.tick())
  298.                   nanosleep(&settings.delay, NULL);
  299.                break;
  300.             }
  301.          case MENU_OPTIONS:
  302.             settings.editor();
  303.             break;
  304.          case MENU_QUIT:
  305.             keep_asking = false;
  306.       }
  307.    }
  308.  
  309.    // In C a "return 0;" would come here, but this is not C...
  310. }
  311.  
  312. /*
  313.  * This function opens a window with a message disregarding anything else that was
  314.  * running. Good for displaying error messages, final results and so. Waits for an
  315.  * ESC pressed before quiting.
  316.  */
  317. void message (char* format_string, ...)
  318. {
  319.    va_list args;
  320.    // Allocate a buffer for the message.
  321.    char* final_string = new char[MESSAGE_LENGTH];
  322.  
  323.    // Fetch the "..." arguments.
  324.    va_start(args, format_string);
  325.    // Transform all the arguments into a single string.
  326.    vsprintf(final_string, format_string, args);
  327.    // Finalize the work on the "..." arguments.
  328.    va_end(args);
  329.  
  330.    // Get screen resolution.
  331.    int screen_height, screen_width;
  332.    getmaxyx(stdscr, screen_height, screen_width);
  333.  
  334.    // Create a window for the message, of it's size  
  335.    WINDOW* message_win = newwin(1, strlen(final_string),
  336.          // and at the centre of the screen.  
  337.          screen_height/2, screen_width/2 - strlen(final_string)/2);
  338.  
  339.    // Print the message.
  340.    wattron(message_win, COLOR_PAIR(RESULTS_COLORS));
  341.    wattron(message_win, A_BOLD);
  342.    waddstr(message_win, final_string);
  343.    wattroff(message_win, COLOR_PAIR(RESULTS_COLORS));
  344.    wattroff(message_win, A_BOLD);
  345.    wrefresh(message_win);
  346.  
  347.    // Wait for an ESC.
  348.    while(getch()!=KEY_ESC)
  349.       nanosleep(&settings.delay, NULL);
  350.  
  351.    // Clean up after myself.
  352.    delwin(message_win);
  353. }
  354.  
  355. /*
  356.  * This class is created solely for the cool trick used in game constructor/destructor.
  357.  * It's struct just because it doesn't have anything private.
  358.  * Watch it's ingenuity and simplicity!
  359.  */
  360. struct simple_curses
  361. {
  362.    simple_curses(void)
  363.    {
  364.       // Initialize ncurses. This is done separately from initializing for
  365.       // the game in order to have different options.  
  366.       initscr();
  367.       cbreak();
  368.       clear();
  369.    }
  370.  
  371.    ~simple_curses(void)
  372.    {
  373.       // Fall back to normal options
  374.       nocbreak();
  375.       endwin();
  376.    }
  377. };
  378.  
  379. int main_menu (void)
  380. {
  381.    // Whole trick is: constructor get's run here, destructor whenever leaving
  382.    // the function.
  383.    simple_curses enviroment;
  384.  
  385.    // Tell them what to do...
  386.    printw("\t\tWelcome to ZRacer by lRem!\n");
  387.    printw("If you like this game look for more at http://lrem.net/\n\n");
  388.    printw("\t\t\tMAIN MENU:\n\n");
  389.    printw("q) Quit the game.\n");
  390.    printw("s) Start a new game.\n");
  391.    printw("o) Options.\n\n");
  392.  
  393.    // And wait for them do it.
  394.    char pressed = 0;
  395.    for(;;)
  396.    {
  397.       printw("Choose any option: ");
  398.       refresh();
  399.       pressed = getch();
  400.       switch(pressed)
  401.       {
  402.          case 'q':
  403.             return MENU_QUIT;
  404.             // No breaks - return already quits the switch.
  405.          case 's':
  406.             return MENU_START;
  407.          case 'o':
  408.             return MENU_OPTIONS;                                                                                                                                 }
  409.             // In case user missed the key, print the next request in next line.
  410.             addch('\n');  
  411.    }
  412. }      
  413.  
  414. void _settings::editor(void)
  415. {
  416.    simple_curses enviroment;
  417.  
  418.    printw("\t\tSETTINGS EDITOR\n");                                                                                                                                                                                                  printw("q) Quit the editor\n");
  419.    printw("p) Set the number of players\n");
  420.    printw("l) Set the length of the racecourse\n");
  421.    printw("r) Set the chance to generate a rock\n");
  422.    printw("t) Set the chance to generate a turning\n");
  423.    printw("s) Set master delay\n");
  424.    printw("h) Set track sharing\n");
  425.    // This doesn't give nice results...
  426.    //printw("w) Set the width of the racecourse\n");
  427.    char pressed = 0;
  428.    for(;;)
  429.    {                                                                                                                                                                                                                printw("Choose any option: ");
  430.       refresh();
  431.       pressed = getch();
  432.       switch(pressed)
  433.       {
  434.          case 'q':
  435.             return;
  436.          case 'p':
  437.             _edit_players();
  438.             break;
  439.          case 'l':                                                                                                                                _edit_length();
  440.                                                                                                                                                   break;
  441.          case 'r':
  442.                                                                                                                                                   _edit_rocks();
  443.                                                                                                                                                   break;
  444.          case 't':
  445.                                                                                                                                                   _edit_turns();
  446.                                                                                                                                                   break;
  447.          case 's':
  448.                                                                                                                                                   _edit_delay();
  449.                                                                                                                                                   break;                                                                                                                                                       case 'h':
  450.                                                                                                                                                      _edit_sharing();
  451.                                                                                                                                                   //case 'w':
  452.                                                                                                                                                   //      _edit_width();
  453.                                                                                                                                                   //      break;
  454.       }
  455.    }
  456.  
  457. }
  458.  
  459. void _settings::_edit_players(void)                                                                                              {
  460.    printw("\n\tSelect the number of players (1 - %d, currently %d):", MAX_PLAYERS, players);                                                                        players = 0;
  461.    // While players outside the possible range.
  462.    for(; players<1 || MAX_PLAYERS<players;)
  463.    {
  464.       players = getch() - '0';
  465.       addch(' ');
  466.    }
  467.    addch('\n');
  468. }
  469.  
  470. void _settings::_edit_length(void)
  471. {
  472.    printw("\n\tSet the length of the track (arbitrary, currently %d):", race_length);
  473.    race_length = -1;
  474.    // While players outside the possible range.
  475.    for(; race_length<0;)
  476.    {
  477.       scanw("%d", &race_length);
  478.    }
  479. }
  480.  
  481. void _settings::_edit_width(void)
  482. {
  483.    printw("\n\tSet the width of the track (narrower than terminal, 0 means max, currently %d):", race_width);
  484.    race_width = -1;
  485.    // While players outside the possible range.
  486.    for(; race_width<0;)
  487.    {
  488.       scanw("%d", &race_width);
  489.    }
  490. }
  491.  
  492. void _settings::_edit_rocks(void)
  493. {
  494.    printw("\n\tSet the chance of generating a rock (0-1, currently %lf):", rock_chance);
  495.    rock_chance = -1;
  496.    for(; rock_chance<0 || 1<rock_chance;)
  497.    {
  498.       scanw("%lf", &rock_chance);
  499.    }
  500. }
  501.  
  502. void _settings::_edit_turns(void)
  503. {
  504.    printw("\n\tSet the chance of generating a turn (0-1, currently %lf):", turn_chance);
  505.    turn_chance = -1;
  506.    for(; turn_chance<0 || 1<turn_chance;)
  507.    {
  508.       scanw("%lf", &turn_chance);
  509.    }
  510. }
  511.  
  512. void _settings::_edit_delay(void)
  513. {
  514.    printw("\n\tSet the master delay (positive, nanosceonds, currently %d):", delay.tv_nsec);
  515.    delay.tv_nsec = -1;
  516.    for(; delay.tv_nsec<0;)
  517.    {
  518.       scanw("%d", &delay.tv_nsec);
  519.    }
  520. }
  521.  
  522. void _settings::_edit_sharing(void)
  523. {
  524.    printw("\n\tShould the track be Similar, sHared or Different for different palyers? ");
  525.    char response;
  526.    for(; response!='s' && response!='h' && response!='d';)
  527.    {      
  528.       scanw("%c", &response);
  529.       switch(tolower(response))
  530.       {
  531.          case 's':
  532.             similar_track = true;
  533.             shared_track = false;
  534.             break;
  535.          case 'h':
  536.             similar_track = true;
  537.             shared_track = true;
  538.             break;
  539.          case 'd':
  540.             similar_track = false;
  541.             shared_track = false;
  542.             break;
  543.       }
  544.    }
  545. }      
  546.  
  547. bool game::tick(void)
  548. {      
  549.    bool game_continues = false;
  550.    time++;
  551.  
  552.    // For every key waiting in buffer...
  553.    int pressed_key;
  554.    while((pressed_key = getch()) != ERR)
  555.    {
  556.       if(pressed_key == KEY_ESC) // End the game.
  557.          for(int i = 0; i<settings.players; i++)
  558.             alive[i]=false; // By killing all players.
  559.  
  560.       // Pass the input to each player.
  561.       for(int i = 0; i<settings.players; i++)
  562.          players[i]->parse_input(pressed_key);
  563.    }
  564.  
  565.    // Checking for player-player collisions is realized by marking each player's position
  566.    // as an obstacle on the track.
  567.    if(settings.shared_track)
  568.       for(int i=0; i<settings.players; i++)
  569.          if(alive[i])
  570.             players[i]->mark_position();
  571.  
  572.    for(int i=0; i<settings.players; i++)
  573.       if(alive[i])
  574.          if(settings.shared_track)
  575.          {      
  576.             players[i]->unmark_position();
  577.             game_continues = (alive[i] = players[i]->tick(time)) || game_continues;
  578.             players[i]->mark_position();
  579.          }
  580.          else
  581.             // If the player dies it sets his alive status to false.
  582.             // If he lives, then there is a reason to continue the game.
  583.             game_continues = (alive[i] = players[i]->tick(time)) || game_continues;
  584.  
  585.    if(settings.shared_track)
  586.       for(int i=0; i<settings.players; i++)
  587.          players[i]->unmark_position();
  588.  
  589.  
  590.    // Results.
  591.    if(!game_continues)
  592.       message("Game finished after %d turns.", time);
  593.  
  594.    return game_continues;
  595. }      
  596.  
  597. game::game (void)
  598. {
  599.    // Initialize the RNG
  600.    // We have already a variable called "time", and we need a function of the same name.
  601.    // In order to circumvent this problem, we use the namespaces.
  602.    srand(std::time(NULL));
  603.  
  604.    // Initialize ncurses.
  605.    initscr();
  606.    // Initialize colors (refuse to start without them).
  607.    assert(has_colors());
  608.    start_color();
  609.    keypad(stdscr, TRUE);
  610.    cbreak();
  611.    noecho();
  612.    nonl();
  613.    nodelay(stdscr, true);
  614.  
  615.    // Color palette.
  616.    init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
  617.    init_pair(COLOR_RED, COLOR_RED, COLOR_BLACK);
  618.    init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);                                                                                                                                                        init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
  619.    init_pair(COLOR_BLUE, COLOR_BLUE, COLOR_BLACK);
  620.    init_pair(COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
  621.    init_pair(COLOR_CYAN, COLOR_CYAN, COLOR_BLACK);
  622.    init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
  623.    init_pair(RESULTS_COLORS, COLOR_YELLOW, COLOR_BLUE);
  624.  
  625.    // Adjust settings, if needed.
  626.    if(settings.race_width == 0)
  627.    {      
  628.       int y, x;
  629.       getmaxyx(stdscr, y, x);
  630.       settings.race_width = settings.vertical_split ? x/settings.players : x;
  631.    }              
  632.    if(settings.minimal_width == 0)
  633.       settings.minimal_width = (int)(MINIMAL_WIDTH);
  634.  
  635.    // Prepare players  
  636.    if(settings.shared_track)
  637.    {              
  638.       track* course = new track();
  639.       for(int i = 0; i<settings.players; i++)
  640.          players[i]=new player_handler(i, course);
  641.    }      
  642.    else
  643.       if(settings.similar_track)
  644.       {              
  645.          track* course = new track();
  646.          for(int i = 0; i<settings.players; i++)
  647.             players[i]=new player_handler(i, course);
  648.       }      
  649.       else
  650.       {
  651.          for(int i = 0; i<settings.players; i++)
  652.             players[i]=new player_handler(i, new track());
  653.       }
  654.    for(int i = 0; i<settings.players; i++)
  655.       alive[i]=true;
  656.  
  657.    // Let the moves begin.
  658.    time = 0;                                                                                                                                                    }
  659.  
  660. game::~game (void)
  661. {                                                                                                                                                                                                                echo();
  662.    nl();          
  663.    nocbreak();
  664.    nodelay(stdscr, false);  
  665.    endwin();      
  666. }              
  667.  
  668. track::track (void)
  669. {                              
  670.    // Allocate the structures.
  671.    circuit.resize(settings.race_length);                                                                                                                                                                    for(vector<vector<char> >::iterator it = circuit.begin(); it!=circuit.end(); it++)
  672.       it->resize(settings.race_width);
  673.  
  674.    // And create the course!
  675.    int borders[2];
  676.    int borders_directions[2]={0,0};
  677.  
  678.    // Initially make the road halfway between minimal and maximal possible.
  679.    assert(settings.minimal_width <= settings.race_width);
  680.    borders[0] = (settings.race_width - settings.minimal_width)/4;
  681.    borders[1] = (settings.race_width*3 + settings.minimal_width)/4;
  682.  
  683.    // And generate the lines.
  684.    for(int i=settings.race_length-1; 0<=i; i--)
  685.    {
  686.       // Put some background.
  687.       for(int j=0; j<(int)circuit[i].size(); j++)  
  688.          circuit[i][j]=' ';
  689.       // Distance meter.  
  690.       circuit[i][0]='0'+i%10;
  691.       // Occasional rock on the track :>
  692.       if(drand()<settings.rock_chance)
  693.          circuit[i][rand()%settings.race_width]='*';
  694.       // Move the kerbs.
  695.       borders[0]+=borders_directions[0];
  696.       borders[1]+=borders_directions[1];
  697.       // Draw the kerbs.
  698.       switch(borders_directions[0])
  699.       {// Different chars, depending on the kerb direction.
  700.          case 0:
  701.             circuit[i][borders[0]]='|';
  702.             break;
  703.          case 1:
  704.             circuit[i][borders[0]]='/';
  705.             break;
  706.          case -1:
  707.             circuit[i][borders[0]]='\\';
  708.       }
  709.       switch(borders_directions[1])
  710.       {
  711.          case 0:
  712.             circuit[i][borders[1]]='|';
  713.             break;
  714.          case 1:
  715.             circuit[i][borders[1]]='/';
  716.             break;
  717.          case -1:
  718.             circuit[i][borders[1]]='\\';
  719.       }
  720.  
  721.  
  722.       // Turn the kerbs...
  723.       int tries = 0; // This is in case it gets to narrow and no space at once (hangs).
  724.       while(
  725.             tries++<5 &&
  726.             (drand()<settings.turn_chance || // If RNG wants so,
  727.              borders[0]+borders_directions[0] == 0 // or no space.
  728.             ))
  729.          borders_directions[0] = rand()%3-1;
  730.       if(borders[1]-borders[0] < settings.minimal_width) // If to narrow,
  731.          borders_directions[0] = -1; // Make it wider
  732.       // A sanity check.
  733.       if(borders[0]+borders_directions[0] == 0)
  734.          borders_directions[0] = 0;
  735.       // And the second one.
  736.       tries = 0;
  737.       while(
  738.             tries++<5 &&
  739.             (drand()<settings.turn_chance || // If RNG wants so,
  740.              borders[1]+borders_directions[1] == settings.race_width // or no space.    
  741.             ))
  742.          borders_directions[1]=rand()%3-1;
  743.       if(borders[1]-borders[0] < settings.minimal_width)
  744.          borders_directions[1] = 1;
  745.       if(borders[1]+borders_directions[1] == settings.race_width)
  746.          borders_directions[1] = 0;
  747.    }
  748. }
  749.  
  750. void track::display(WINDOW* screen, int top_line)
  751. {
  752.    // Get the geometry.
  753.    int screen_width, screen_height;
  754.    getmaxyx(screen, screen_height, screen_width);
  755.  
  756.    // For every visible line...
  757.    for(int i = top_line; i<top_line+screen_height; i++)
  758.    {                                                                                                                                                                                                                // Move the cursor at it's beginning...
  759.       // Position at screen centre.
  760.       wmove(screen, i-top_line, (screen_width-settings.race_width)/2);
  761.       // And print all the characters.
  762.       for(int j=0; j<settings.race_width; j++)
  763.          waddch(screen, circuit[i][j]);
  764.    }
  765. }      
  766.  
  767. bool track::taken(int y, int x)
  768. {      
  769.    return circuit[y][x]!=' ';
  770. }      
  771.  
  772. void track::mark(int y, int x, car_image *car)
  773. {
  774.    vector<pair<int, int> > dots = car->get_dots();
  775.  
  776.    for(unsigned int i=0; i<dots.size(); i++)
  777.       if(circuit[y+dots[i].first][x+dots[i].second] == ' ')
  778.          circuit[y+dots[i].first][x+dots[i].second] = settings.character;
  779. }
  780.  
  781. void track::unmark(int y, int x, car_image *car)
  782. {
  783.    vector<pair<int, int> > dots = car->get_dots();
  784.  
  785.    for(unsigned int i=0; i<dots.size(); i++)
  786.       if(circuit[y+dots[i].first][x+dots[i].second] == settings.character)
  787.          circuit[y+dots[i].first][x+dots[i].second] = ' ';
  788.  
  789. }      
  790.  
  791. player_handler::player_handler(int position, track* racecourse)
  792. {
  793.    // Set the sizes for the windows.
  794.    // Height gets reused and thus is declared within class.
  795.    int screen_width;
  796.    getmaxyx(stdscr, screen_height, screen_width);
  797.    int width = settings.vertical_split? screen_width/settings.players : screen_width;
  798.    int height = settings.vertical_split? screen_height : screen_height/settings.players;
  799.  
  800.    // We can't set the track to be wider than the display.
  801.    assert(settings.race_width<=width);
  802.  
  803.    // Prepare screen part.
  804.    if(settings.vertical_split)
  805.    {
  806.       screen = newwin(
  807.             height, width,  
  808.             // Take the corresponding vertical stripe.
  809.             0, width*(settings.players - position - 1)
  810.             );
  811.    }      
  812.    else
  813.    {      
  814.       screen = newwin(
  815.             height, width,
  816.             // Take the corresponding horizontal stripe.
  817.             height*position, 0
  818.             );
  819.    }      
  820.  
  821.    // Just copy this pointer.
  822.    course = racecourse;
  823.  
  824.    // And create an image for yourself
  825.    car = new car_image();
  826.  
  827.    // Place the car at a reasonable place.
  828.    y = settings.race_length - settings.car_size;
  829.    if(settings.shared_track)
  830.       x = (settings.race_width - (settings.car_size+1)*(settings.players-2*position))/ 2;                          
  831.    else
  832.       x = settings.race_width/2;
  833.  
  834.    // We want the car at the very bottom of the screen.
  835.    top_line = settings.race_length - height;
  836.  
  837.    // If not set, the player actually could freeze for a while.
  838.    last_move = -INF;
  839.  
  840.    // Copy the controls.
  841.    memcpy(controls, settings.controls[position], 4*sizeof(int));
  842.    // And make sure player doesn't take off.
  843.    command_y = command_x = 0;
  844. }
  845.  
  846. /*player_handler::~player_handler(void)
  847.   {
  848. // It's so simple...
  849. delwin(screen);
  850. }*/
  851.  
  852. // Just a simple switched assignment.  
  853. void player_handler::parse_input(int pressed_key)                                                                                {
  854.    if(pressed_key == controls[0])                                                                                                           command_y=ACCELERATE;                                                                                                                                                                            if(pressed_key == controls[1])
  855.       command_y=BRAKE;                                                                                                                                                                                 if(pressed_key == controls[2])                                                                                                           command_x=LEFT;
  856.    if(pressed_key == controls[3])                                                                                                           command_x=RIGHT;                                                                                                                                                                         }
  857.  
  858. void player_handler::mark_position(void)
  859. {
  860.    course->mark(y, x, car);
  861. }      
  862.  
  863. void player_handler::unmark_position(void)
  864. {                      
  865.    course->unmark(y, x, car);
  866. }
  867.  
  868. bool player_handler::tick(int time)
  869. {      
  870.    bool survive = true;
  871.  
  872.    // The higher the car on the screen, the faster it moves.
  873.    if(last_move + (y-top_line)/settings.speed_base < time)
  874.    {
  875.       last_move=time;
  876.       y--;  
  877.  
  878.       // Watch to not segfault here.
  879.       top_line=max(0, top_line-1);
  880.  
  881.       // Handle commands
  882.       y+=command_y;
  883.       x+=command_x;
  884.       command_y = command_x = 0;
  885.  
  886.       // Make sure he doesn't escape from the screen.
  887.       y = max(top_line, min(top_line + screen_height - settings.car_size, y));
  888.  
  889.       if(y <= 0) // Plain win
  890.          return false;
  891.  
  892.       course->display(screen, top_line);
  893.       // Check for collisions.
  894.       for(int i=y; i<y+settings.car_size; i++)  
  895.          for(int j=x; j<x+settings.car_size; j++)
  896.             if(course->taken(i, j) && car->collision_check(i-y, j-x))
  897.                survive=false;
  898.  
  899.       if(survive)
  900.          car->display(screen, y-top_line, x);
  901.       else
  902.          car->explode(screen, y-top_line, x);
  903.       wrefresh(screen);
  904.    }
  905.  
  906.    return survive;
  907. }
  908.  
  909. car_image::car_image(void)
  910. {
  911.    character = settings.character;
  912.    color = COLOR_YELLOW;
  913.    size = settings.car_size;
  914.  
  915.    // The coolest part - drawing the damned thing  
  916.    _clear();
  917.    // Original key points were (0,0), (1,4), (2,0), (3,4) and (4,0).
  918.    // Now we just need to scale them and draw lines between.
  919.    _line(0, 0, 1*(size-1)/4, 4*(size-1)/4);                                                                          _line(1*(size-1)/4, 4*(size-1)/4, 2*(size-1)/4, 0);
  920.    _line(2*(size-1)/4, 0, 3*(size-1)/4, 4*(size-1)/4);
  921.    _line(3*(size-1)/4, 4*(size-1)/4, 4*(size-1)/4, 0);  
  922. }              
  923.  
  924. void car_image::_clear(void)
  925. {              
  926.    // Simply clear the image
  927.    for(int i=0; i<size; i++)
  928.       for(int j=0; j<size; j++)
  929.          storage[i][j]=false;                                                                                                                         }
  930.  
  931. void car_image::display(WINDOW* screen, int y, int x)
  932. {              
  933.    // We don't need to store this anywhere, so it can be set ad-hoc.
  934.  
  935.    wattron(screen, COLOR_PAIR(color));
  936.    wattron(screen, A_BOLD);
  937.  
  938.    for(int i=0; i<size; i++)
  939.    {
  940.       for(int j=0; j<size; j++)
  941.          if(storage[i][j])
  942.             mvwaddch(screen, y+i, x+j, character);
  943.  
  944.    }
  945.  
  946.    // Restore the normal color for everything else...
  947.    wattroff(screen, COLOR_PAIR(color));
  948.    wattroff(screen, A_BOLD);
  949. }
  950.  
  951. void car_image::explode(WINDOW* screen, int y, int x)
  952. {// Even in ASCII we can do cool explosions :>
  953.    for(int i=0; i<size; i++)
  954.    {
  955.       for(int j=0; j<size; j++)
  956.          if(rand()%2)
  957.          {
  958.             int color = rand()%7 + 1;
  959.             wattron(screen, COLOR_PAIR(color));
  960.             mvwaddch(screen, y+i, x+j, '*');
  961.             wattroff(screen, COLOR_PAIR(color));
  962.          }
  963.  
  964.    }
  965.  
  966. }
  967.  
  968. bool car_image::collision_check(int y, int x)
  969. {
  970.    return storage[y][x];
  971. }
  972.  
  973. vector<pair<int, int> > car_image::get_dots(void)
  974. {
  975.    return dots;
  976. }
  977.  
  978. /*
  979.  * This function is the heart of the original task for which this program
  980.  * was created. It is based on the simple formula, that for a line between
  981.  * (y1, x1) and (y2, x2) and a given coordinate x, the y coordinate for a
  982.  * point on a line is y1 + (y2-y1) / ( (x2-x1)/(x-x1) ).
  983.  */
  984. void car_image::_line(int y1, int x1, int y2, int x2)
  985. {
  986.    // It can be given the other way round...
  987.    if(x2 < x1)
  988.    {
  989.       // So just swap the points.
  990.       int tmp = x1;
  991.       x1 = x2;
  992.       x2 = tmp;
  993.       // And swap the other coordinate.
  994.       tmp = y1;
  995.       y1 = y2;
  996.       y2 = tmp;
  997.    }
  998.    for(int i=x1; i<=x2; i++)
  999.    {
  1000.       int y = y1 + (int)((float) (y2-y1)*(i-x1)/(x2-x1)+0.5);
  1001.       // +0.5 is in order to do real rounding, not just truncation.
  1002.       storage[y][i]=true;
  1003.       dots.push_back(make_pair(y, i));
  1004.    }
  1005. }