بخش ۸ - شبیه‌سازی حرکت توپ در میز

در این بخش برنامه‌ای نوشته می‌شود که در آن حرکت یک توپ در یک میز لبه‌دار شبیه‌سازی می‌شود. فرض می‌کنیم توپ در ابتدا داخل میز قرار دارد و با سرعتی به اندازه‌ی ثابت حرکت می‌کند. همچنین فرض می‌کنیم حرکت بدون اصطکاک است و برخورد توپ با لبه‌های میز کاملاً الاستیک است (هنگام برخورد انرژی تلف نمی‌شود). توپ را به شکل یک ذره‌ی مادی در نظر می‌گیریم (شعاع توپ صفر است).

فهرست مثال‌ها

نسخه‌ی یک - توپ در میز مستطیل شکل

در این نسخه از برنامه، میز به صورت مستطیل شکل فرض می‌شود. به همین خاطر ابعاد خود را از طریق متدهای getter به بیرون عرضه می‌کند.

01_billiards.cpp
#include <iostream>
#include <cstdlib>
using namespace std;

class Table {
public:
    Table(int w, int h);
    int get_width() { return width; }
    int get_height() { return height; }
private:
    int width;
    int height;
};

Table::Table(int w, int h) 
{
    if (w <= 0 || h <= 0)
        abort();
    width = w;
    height = h;
}

class Ball {
public:
    Ball(int _x, int _y, int _vx, int _vy, Table* t);
    void move(int dt);
    int get_x() { return x; }
    int get_y() { return y; }
    int get_vx() { return vx; }
    int get_vy() { return vy; }
private:
    int x;
    int y;
    int vx;
    int vy;
    Table* table;
};

Ball::Ball(int _x, int _y, int _vx, int _vy, Table* t)
{
    table = t;
    if (x < 0 || x >= table->get_width() || y < 0 || y >= table->get_height())
        abort();
    x = _x;
    y = _y;
    vx = _vx;
    vy = _vy;
}

void Ball::move(int dt)
{
    x += vx * dt;
    y += vy * dt;
    
    while (x < 0 || x >= table->get_width() || y < 0 || y >= table->get_height()) {
        if (x < 0) {
            x = -x;
            vx = -vx;
        }
        if (x >= table->get_width()) {
            x = 2 * table->get_width() - x;
            vx = -vx;
        }
        if (y < 0) {
            y = -y;
            vy = -vy;
        }
        if (y >= table->get_height()) {
            y = 2 * table->get_height() - y;
            vy = -vy;
        }
    }
}

int main()
{
    Table t(100, 50);
    Ball b(10, 20, 25, 5, &t);
    b.move(10); 
}
				
نسخه‌ی دو - رفع وابستگی توپ به مستطیل بودن میز

در این نسخه از برنامه، توپ دیگر به مستطیل‌بودن میز متکی نیست و تشخیص این که آیا توپ به لبه‌ی میز برخورد کرده یا نه به میز واگذار شده است. همچنین، در صورت برخورد، میز موقعیت توپ را اصلاح می‌کند.

02_billiards.cpp
#include <iostream>
#include <cstdlib>
using namespace std;

class Table {
public:
    Table(int w, int h);
    bool contains_point(int x, int y);
    void reflect(int& x, int& y, int& vx, int& vy);
private:
    int width;
    int height;
};

Table::Table(int w, int h) 
{
    if (w <= 0 || h <= 0)
        abort();
    width = w;
    height = h;
}

bool Table::contains_point(int x, int y) {
    return x >= 0 && x < width && y >= 0 && y < height;
}

void Table::reflect(int& x, int& y, int& vx, int& vy) {
    if (x < 0) {
        x = -x;
        vx = -vx;
    }
    if (x >= width) {
        x = 2 * width - x;
        vx = -vx;
    }
    if (y < 0) {
        y = -y;
        vy = -vy;
    }
    if (y >= height) {
        y = 2 * height - y;
        vy = -vy;
    }
}

class Ball {
public:
    Ball(int _x, int _y, int _vx, int _vy, Table* t);
    void move(int dt);
    int get_x() { return x; }
    int get_y() { return y; }
    int get_vx() { return vx; }
    int get_vy() { return vy; }
private:
    int x;
    int y;
    int vx;
    int vy;
    Table* table;
};

Ball::Ball(int _x, int _y, int _vx, int _vy, Table* t)
{
    table = t;
    if (!table->contains_point(_x, _y))
        abort();
    x = _x;
    y = _y;
    vx = _vx;
    vy = _vy;
}

void Ball::move(int dt)
{
    x += vx * dt;
    y += vy * dt;
    
    while (!table->contains_point(x, y))
        table->reflect(x, y, vx, vy);
}

int main()
{
    Table t(100, 50);
    Ball b(10, 20, 25, 5, &t);
    b.move(10); 
}
				
نسخه‌ی سه - رد کردن توپ به میز برای تصحیح موقعیت

در این نسخه از برنامه، به جای رد کردن تمام اجزای توپ به عنوان پارامتر، توپ آدرس خودش را به تابع reflect رد می‌کند.

03_billiards.cpp
#include <iostream>
#include <cstdlib>
using namespace std;

class Ball;

class Table {
public:
    Table(int w, int h);
    bool contains_point(int x, int y);
    void reflect(Ball* b);
private:
    int width;
    int height;
};

class Ball {
public:
    Ball(int _x, int _y, int _vx, int _vy, Table* t);
    void move(int dt);
    int get_x() { return x; }
    int get_y() { return y; }
    int get_vx() { return vx; }
    int get_vy() { return vy; }
    void set_location(int _x, int _y);
    void set_speed(int _vx, int _vy);
private:
    int x;
    int y;
    int vx;
    int vy;
    Table* table;
};

Table::Table(int w, int h) 
{
    if (w <= 0 || h <= 0)
        abort();
    width = w;
    height = h;
}

bool Table::contains_point(int x, int y) {
    return x >= 0 && x < width && y >= 0 && y < height;
}

void Table::reflect(Ball* b) {
    int x = b->get_x();
    int y = b->get_y();
    int vx = b->get_vx();
    int vy = b->get_vy();
    
    while (!contains_point(x, y)) {
        if (x < 0) {
            x = -x;
            vx = -vx;
        }
        if (x >= width) {
            x = 2 * width - x;
            vx = -vx;
        }
        if (y < 0) {
            y = -y;
            vy = -vy;
        }
        if (y >= height) {
            y = 2 * height - y;
            vy = -vy;
        }
    }
    b->set_location(x, y);
    b->set_speed(vx, vy);
}


Ball::Ball(int _x, int _y, int _vx, int _vy, Table* t)
{
    table = t;
    set_location(_x, _y);
    set_speed(_vx, _vy);
}

void Ball::set_location(int _x, int _y) {
    if (!table->contains_point(_x, _y))
        abort();
    x = _x;
    y = _y;
}

void Ball::set_speed(int _vx, int _vy) {
    vx = _vx;
    vy = _vy;
}

void Ball::move(int dt)
{
    x += vx * dt;
    y += vy * dt;
    
    if (!table->contains_point(x, y))
        table->reflect(this);
}

int main()
{
    Table t(100, 50);
    Ball b(10, 20, 25, 5, &t);
    b.move(10); 
}