|
25 Years of Programming
An open source source for C, C++, OWL, BASIC, MDB, XLS, DOT, and more... |
Home Projects Up Sitemap Search Blog Forum+Chat About Us Privacy Terms of Use Feedback FAQ Images Services Ads Donate Humor |
|
|
Traject.cpp - gravity simulationThis program was derived from adapt.cpp, but has a completely different purpose. It creates one agent (cell) and shoots it from the left side of the screen at a random angle trajectory. Its horizontal velocity stays constant, but its vertical velocity changes similarly to the way a real object does when pulled down by gravity. If it is travelling upward, its vertical velocity decreases, and as it travels downward, it accelerates. The result is a parabolic path which the object traverses at a constantly changing velocity. It is hardly a remarkable demonstration, except that it was so easily derived from adapt.cpp by ripping out most of the code and giving the agent a different motion behavior. On the other hand, if I had set out to write this program from scratch using this type of program structure used by adapt.cpp, that would really have been overkill for what is needed here! Borland C++ 4.0 for MSDOS. |
|
/* traject.cpp 6-26-97
Copyright (C)1995-1997 Steven Whitney.
Initially published by http://25yearsofprogramming.com.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License (GPL)
Version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Plots the trajectory of an object (dot) launched from the ground with a given
velocity and angle. Simulates effect of gravity. Based on adapt.cpp.
------
to do:
try launching a dot from a circle (planet) at screen center & plot orbits.
("Does God Play Dice?" has sections on gravity & pendulums, which may be
related. Says this calculation is very difficult.)
*/
#include <math.h>
#include <graphics.h>
#include <classlib\arrays.h>
#pragma hdrstop
#include "c:\bcs\my.h"
#include "c:\bcs\mylib.cpp"
/////////////////////////////////////////////////////////////////////////
// global variables
BOOL beepon = TRUE; // whether to use sound effects, test is in play()
BOOL delayon = FALSE; // whether to impose delay for better viewing
BOOL trailon = TRUE; // whether to show trail as a point moves
BOOL lookon = FALSE; // whether to show direction of sight
BOOL autoclear = TRUE; // whether to auto-clear screen every ## loops
Trigtable t; // so they only have to be calculated once
/////////////////////////////////////////////////////////////////////////
// entity, shown as a screen dot, that can move around, interact with others, etc.
class agent
{
public:
agent(); // constructor
~agent(); // destructor
BOOL operator == (const agent& other) const;
BOOL operator < (const agent& other) const;
double calcdistance(class agent* other); // distance from this to another agent
class agent* identifynearestneighbor();
class agent* move();
int run();
BOOL sense(); // update all sensors with current surroundings
int mapneighbors();
double x; // this cell's screen location
double y;
double heading; // direction of motion (and sight): 0=North=Up, 90=East
double velocity; // dies when 0
int color; // dot leaves a TRAIL in this color (the dot itself is always white)
};
/////////////////////////////////////////////////////////////////////////
// global array of agents. Must define here because some agent:: functions use it.
TIArrayAsVector<agent> ag(5,0,0);
//-----------------------------------------------------------------------
// constructor
agent::agent()
{
x = 0.; // starts at lower left corner
y = 479.;
heading = (double)(random(90)); // straight up to straight out
velocity = (double)(random(200)); // random starting velocity
color = LIGHTGREEN; // trail color
putpixel((int)x,(int)y,WHITE); // display on screen
}
//-----------------------------------------------------------------------
// destructor, removes from screen. OK if a dead cell's velocity is zero,
// but if residual velocity is ever allowed, it might be better to leave lying
// around for another cell to eat (scavenger).
agent::~agent()
{
putpixel((int)x,(int)y,BLACK);
}
//-----------------------------------------------------------------------
// == currently isn't used for anything, but there should be a way to make it useful.
BOOL agent::operator == (const agent& other) const
{
return(&other == this);
}
//-----------------------------------------------------------------------
// < Sorts agents by their Manhattan distance from (0,0). Not currently used.
BOOL agent::operator < (const agent& other) const
{
if((x+y) < (other.x + other.y))
return(TRUE);
return(FALSE);
}
//-----------------------------------------------------------------------
// computes distance between two cells
double agent::calcdistance(class agent* other)
{
double xdif = fabs(x - other->x);
double ydif = fabs(y - other->y);
return(sqrt((xdif * xdif) + (ydif * ydif)));
}
//-----------------------------------------------------------------------
// identify which agent is occupying a pixel & return pointer
// returns pointer, or ZERO if match not found.
// generally, you should know that the pixel IS occupied before calling, because this is slow.
// This is NOT a member function, because it's needed outside the class.
class agent* identify(int findx, int findy)
{
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
if(((int)(ag[i]->x) == findx) && ((int)(ag[i]->y) == findy))
return(ag[i]);
return(0);
}
//-----------------------------------------------------------------------
// move a cell along its heading until it hits the ground.
// Can only move 1 pixel at a time so it knows whether next step will hit something.
// returns pointer to the cell that blocked this one's path, if any,
// or ZERO if path was clear.
class agent* agent::move()
{
double xvelocity = velocity * cosine(heading);
double yvelocity = velocity * sine(heading);
int xi = (int)x;
int yi = (int)y;
// 1 loop = .1 second elapsed
do
{
delay(25);
putpixel((int)x,(int)y,trailon ? color : BLACK); // show or erase previous position
x += xvelocity * .1; // x velocity doesn't change
y -= yvelocity * .1; // y axis upside down
yvelocity -= 3.2; // change due to gravity, in ft/sec
if(y < -1000)
break;
while(x > 639.) // screen wraps around
x -= 639.;
if(y > 479.) // below bottom (landed)
y = 479.;
xi = (int)x;
yi = (int)y;
putpixel(0,yi,LIGHTRED); // show x & y positions on axes
putpixel(xi,479,LIGHTRED);
putpixel(xi,yi,WHITE); // show new position
}
while(y < 479.); // until it hits the ground
return(0);
/*
double targetx = x;
double targety = y;
// must get away from current pixel, or it'll be shown as "occupied" (by itself!)
while(((int)targetx == (int)x) && ((int)targety == (int)y))
{
targetx += t.sines[(int)heading];
targety -= t.cosines[(int)heading]; // screen's y axis is upside down
}
// Screen wraps around.
if((int)targetx >= 640) targetx -= 640.;
if((int)targetx <= -1) targetx += 640.;
if((int)targety >= 480) targety -= 480.;
if((int)targety <= -1) targety += 480.;
// if new location is occupied, can't move there
// This also causes the dots to stick when they hit, just like in DLA!
if(getpixel((int)targetx,(int)targety) == WHITE)
return(identify((int)targetx,(int)targety));
*/
} // end move
//-----------------------------------------------------------------------
// create a map of neighboring pixels
// each pixel encoded in 1 bit (excluding pixel occupied by itself).
// use if(mapneighbors()) to determine if *any* neighboring pixel is occupied.
// use if(mapneighbors() & 1) to determine if below right pixel is occupied.
// For use in DLA and in 2nd version of Life.
int agent::mapneighbors()
{
int xi = (int)x;
int yi = (int)y;
int map = 0;
// getpixel(offscreen) returns 0
if(getpixel(xi-1,yi-1) == WHITE) map |= 128; // above left
if(getpixel(xi ,yi-1) == WHITE) map |= 64; // above
if(getpixel(xi+1,yi-1) == WHITE) map |= 32; // above right
if(getpixel(xi-1,yi ) == WHITE) map |= 16; // to left
if(getpixel(xi+1,yi ) == WHITE) map |= 8; // to right
if(getpixel(xi-1,yi+1) == WHITE) map |= 4; // below left
if(getpixel(xi ,yi+1) == WHITE) map |= 2; // below
if(getpixel(xi+1,yi+1) == WHITE) map |= 1; // below right
return(map);
}
//-----------------------------------------------------------------------
// update all sensors with current surroundings
// determine if there is another cell in the current line of sight along our heading
// similar to move() except it just looks, doesn't go anywhere, and vision doesn't wrap
// beyond edge of screen.
// returns TRUE if another cell is in view, FALSE if not.
BOOL agent::sense()
{
return(TRUE);
// mapneighbors(); // construct a map of the neighborhood
/*
double targetx = x;
double targety = y;
while(1) // exits loop if an object sighted or vision ran off screen
{ // because it returns, this loop must be last thing sense() does.
double oldx = targetx;
double oldy = targety;
// must get away from current pixel, or it'll be shown as "occupied" (by itself!)
while(((int)targetx == (int)oldx) && ((int)targety == (int)oldy))
{
targetx += t.sines[(int)heading];
targety -= t.cosines[(int)heading]; // screen's y axis is upside down
}
// our "vision" ran off screen, so we're through looking.
if(((int)targetx >= 640) || ((int)targetx <= -1) ||
((int)targety >= 480) || ((int)targety <= -1))
return(FALSE);
// if new location is occupied, it is the object we're looking at.
if(getpixel((int)targetx,(int)targety) == WHITE)
{
inview = identify((int)targetx,(int)targety);
if(inview)
return(TRUE);
printf("ERROR: Object was sighted that isn't in array.\n");
return(FALSE);
}
if(lookon) // didn't sight anything or run off screen: show where we're looking
putpixel((int)targetx,(int)targety,BLUE);
} // end while(1)
*/
} // end sense
//-----------------------------------------------------------------------
// find mass center of all points on screen. uses average, could use least squares
// This could be an agent:: member function.
void calccenterofmass(double* x, double* y) // variables to fill
{
double sumx, sumy;
sumx = sumy = 0.;
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
{
sumx += ag[i]->x;
sumy += ag[i]->y;
}
*x = sumx / (double)count;
*y = sumy / (double)count;
} // end calccenterofmass
//-----------------------------------------------------------------------
// returns pointer to nearest cell neighbor, or 0 if none
class agent* agent::identifynearestneighbor()
{
double distance;
double mindistance = 1e6;
int nearest = -1;
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
if(ag[i] != this) // don't find itself
{
distance = calcdistance(ag[i]);
if(distance < mindistance)
{
mindistance = distance;
nearest = i;
}
}
return((nearest == -1) ? 0 : ag[nearest]);
}
//-----------------------------------------------------------------------
int agent::run()
{
return(TRUE); // survived alive
} // end run
//-----------------------------------------------------------------------
// end class agent
/////////////////////////////////////////////////////////////////////////
// global functions
//-----------------------------------------------------------------------
// display a short program description
void intro()
{
}
//-----------------------------------------------------------------------
// show the help screen
void drawhelp()
{
cout << "\nThese commands are available while program is running:\n\n";
cout << "A AUTO-CLEAR screen between launches (toggle, now "
<< (autoclear ? "ON" : "OFF") << ")\n";
cout << "B BEEP (sound effects) (toggle)\n";
cout << "C CLEAR screen once\n";
cout << "D DELAY for better viewing (toggle)\n";
cout << "H HELP (show this screen)\n";
cout << "L LINE-of-sight display (toggle)\n";
cout << "P PAUSE (any key to resume)\n";
cout << "T TRAIL display (toggle)\n";
cout << "<ESC> Quit\n\n";
presskey();
}
//-----------------------------------------------------------------------
// redraw all the agents on the screen
void drawagents()
{
clearviewport();
int count = ag.GetItemsInContainer();
for(int i = 0 ; i < count ; i++)
putpixel((int)(ag[i]->x),(int)(ag[i]->y),WHITE);
}
//////////////////////////////////////////////////////////////////////////////
int main()
{
randomize();
int ch, grmode, passcount, passlimit = 1;
// intro();
// drawhelp();
grmode = initgraf();
while(1)
{
passcount = 0;
while(1)
{
//----------------------------------------------------------------------------
// user input section
if(kbhit())
{
ch = tolower(getch());
switch(ch)
{
case 27: // user quit
ag.Flush(TShouldDelete::Delete);
nosound(); // just in case
closegraph();
return(0);
case 'a': autoclear = !autoclear; break;
case 'b': beepon = !beepon; break; // toggle sound effects
case 'c': clearviewport(); break; // clear screen this pass
case 'd': delayon = !delayon; break; // toggle delay
case 'h': // help
restorecrtmode();
drawhelp();
setgraphmode(grmode);
break;
case 'l': lookon = !lookon; break; // toggle showing where cell is looking
case 'p': getch(); break; // user pause
case 't': trailon = !trailon; break; // toggle trail display
}
} // end if(kbhit())
//----------------------------------------------------------------------------
if(autoclear)
if(++passcount >= passlimit)
{
clearviewport();
passcount = 0;
}
ag.Flush(TShouldDelete::Delete);
ag.Add(new agent);
ag[0]->move(); // plot its course until it crashes
} // end inner while(1)
} // end outer while(1)
} // end main()
|
|
|
|
|
|