original post.
I made a very simple sand dune simulator that runs from the terminal it's very fun to watch. Because of an artifact of the code the dunes do not like to get above a height of 9. Also dunes move upwind.
basically my code goes like this.
h= your height. The chance of one unit of sand being blown away from you is 1/h
If you get to a height of 9 or greater, one unit of your sand is added to every square touching you.
'?' to show controls
SPACE to dismiss
[spoiler]#include <ctime>
#include <cstdlib>
#include <curses.h>
#include <iostream>
using namespace std;
/***************************/
const int viewY=23; //height of view
const int viewX=30; //1/2 width of veiw
const int Y=53; //height of world
const int X=101; //width of world
int SAND[Y][X];
int sand[Y][X];
bool wall[Y][X];
int WATER[Y][X];
int water[Y][X];
int CHART[Y][X];
int chart[Y][X]; //up down left right
int tree[Y][X];
int cursorX=0;
int cursorY=0;
char inchar;
int windY=-1; //wind direction
int windX=-1;
int showSlope=0;
int sloWind=1; //inverse wind speed
bool instructions=0;
int COUNT=0;
int Time=16384*4;
char view[Y][X];
/***************************/
void sleep();
void print();
void init();
void wind();
void input();
void flow();
void land(int y, int x, int a, int b);
void grow();
void calc();
/***************************/
int main()
{
nodelay(initscr(),1); // don't wait for user input
srand(time(NULL)); // initializes the random number generator
clear(); // clear the window
noecho(); // don't show typed characters on the screen
init();
for(int i=0; i<5; i++)
calc();
int i=0;
while(inchar!='q') //press q to quit
{
print();
if(instructions==0)
{
i++;
if(sloWind>0) //when sloWind is - it executes once every so often
i=i%sloWind;
else
i=sloWind;
for(; i<=0;i++) //when sloWind is - it executes many times
wind();
flow();
grow();
}
input();
// sleep(); // wait a bit
COUNT++;
}
refresh();
echo(); // turn echoing back on before exiting
endwin(); // end curses control of the window
}
/***************************/
void init()
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
view[y][x]='?';
WATER[y][x]=0;
SAND[y][x]=rand()%3;
wall[y][x]=0;
CHART[y][x]=rand()%4;
tree[y][x]=0;
}
}
}
/***************************/
void print()
{
int a,b;
for(int y=0; y<Y; y++)
{
a= (y+Y+cursorY)%Y;
for(int x=0; x<X; x++)
{
b= (x+X+cursorX)%X;
if(CHART[a][b]==4)
view[y][x]='o';
//*/
if(SAND[a][b]>4)
view[y][x]=';';
else if(SAND[a][b]>1)
view[y][x]=',';
else if(SAND[a][b]>0)
view[y][x]='.';
//*/
if(WATER[a][b]>1)
view[y][x]='~';
else if(WATER[a][b]>0)
view[y][x]='-';
if(tree[a][b]==1)
view[y][x]='%';
else if(tree[a][b]==2)
view[y][x]='*';
if(wall[a][b]==1)
view[y][x]='#';
if(showSlope==1)
{
if(CHART[a][b]==0)
view[y][x]='^';
else if(CHART[a][b]==1)
view[y][x]='<';
else if(CHART[a][b]==2)
view[y][x]='v';
else if(CHART[a][b]==3)
view[y][x]='>';
}
}
}
if(COUNT%2)
view[viewY/2][viewX/2]='@';
clear();
if(instructions==0)
{
for(int y=0; y<viewY; y++)
{
for(int x=0; x<viewX; x++)
{
mvaddch(y, x*2, view[y][x]);
view[y][x]=' ';
}
}
}
else
{
char strng[][34]={
"\'w\' \'a\' \'s\' \'d\' to move ",
"SPACE to place or remove a wall ",
"\'z\' to change the wind direction ",
"\'t\' to slow down the game ",
"\'T\' to speed up the game ",
"\'r\' to slow down the wind ",
"\'R\' to speed up the wind ",
"\'q\' to quit ",
"\'x\' to dig a hole ",
"\'v\' to plant a tree ",
" ",
" . is blowing sand ",
" , is more sand ",
" ; is piled sand ",
" - is shallow water ",
" ~ is deeper water ",
" # is a wall ",
" % is a tree ",
" * is a dead tree ",
" @ is you ",
" ",
"Press SPACE to return to the game"};
for(int y=0; y<viewY; y++)
{
for(int x=0; x<viewX; x++)
{
if(y<22 && x<33)
mvaddch(y, x, strng[y][x]);
view[y][x]=' ';
}
}
}
refresh(); // refresh the screen
mvaddch(viewY-1,0,' ');
cout<<sloWind<<' '<<Time;
}
/***************************/
// function that waits a little while, keeps the game slow
void sleep()
{
clock_t goal = Time + clock();
while (goal > clock());
}
/***************************/
void input()
{
int y=(cursorY+viewY/2)%Y;
int x=(cursorX+viewX/2)%X;
inchar = char(getch());
if(inchar != ERR)
{
if(instructions==0)
{
if(inchar=='w')
cursorY=(cursorY+Y-1)%Y;
else if(inchar=='s')
cursorY=(cursorY+1)%Y;
else if(inchar=='a')
cursorX=(cursorX+X-1)%X;
else if(inchar=='d')
cursorX=(cursorX+1)%X;
else if(inchar=='v')
tree[y][x]=1;
else if(inchar=='f')
showSlope^=1;
else if(inchar=='g')
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
WATER[y][x]=0;
}
}
}
else if(inchar=='y')
calc();
else if(inchar=='x')
{
CHART[y][x]=4;
CHART[(y+1)%Y][x]=0;
CHART[y][(x+1)%X]=1;
CHART[(y+Y-1)%Y][x]=2;
CHART[y][(x+X-1)%X]=3;
}
else if(inchar=='r')
sloWind++;
else if(inchar=='R')
{
sloWind--;
// if(sloWind <1)
// sloWind=1;
}
else if(inchar=='t')
Time/=2;
else if(inchar=='T')
{
if(Time==0)
Time=1;
Time*=2;
}
else if(inchar=='z')
{
if(windX==1)
{
windY+=2;
windY=windY%3-1;
}
windX+=2;
windX=windX%3-1;
}
else if(inchar=='?')
instructions=1;
else if(inchar==' ')
wall[y][x]^=1;
}
else
{
if(inchar==' ')
instructions=0;
}
}
}
/***************************/
void wind()
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
sand[y][x]=SAND[y][x]; //get a copy of the perminent record
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
for(int a=(y+Y-1)%Y; a!=(y+2)%Y; a=(a+1)%Y)
{
//choose a square one above this and iterate until one below
for(int b=(x+X-1)%X; b!=(x+2)%X; b=(b+1)%X)
{
//choose a square one to the left of this and iterate until one to the right
int temp=SAND[y][x]-SAND[a][b];
//the sand falls over if it piles too high
if(temp/10>0 && !wall[a][b] && !tree[a][b])
{
sand[y][x]--;
sand[a][b]++;
}
}
}
// erode
int temp = SAND[y][x];
if(temp!=0 && rand()%temp==0&& WATER[y][x]==0 &&(!tree[(y+windY+Y)%Y][(x+windX+X)%X] || rand()%2) && !wall[(y+windY+Y)%Y][(x+windX+X)%X])
{
sand[(y+windY+Y)%Y][(x+windX+X)%X]++;
sand[y][x]--;
}
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
SAND[y][x] = sand[y][x]; //record it on the perminent record
}
}
}
/***************************/
void flow()
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
water[y][x]=WATER[y][x];
chart[y][x]=CHART[y][x];
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
if(WATER[y][x] &&(!tree[y][x] ||rand()%2))
{
for(int a=(y+Y-1)%Y; a!=(y+3)%Y; a=(a+2)%Y)
{
land(y,x,a,x);
}
for(int b=(x+X-1)%X; b!=(x+3)%X; b=(b+2)%X)
{
land(y,x,y,b);
}
}
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
if(water[y][x]>0 && (SAND[y][x]-water[y][x])>0) //evaporate
{
if((rand()%(SAND[y][x]-water[y][x]))/5)
water[y][x]--;
}
water[0][0]++; //a spring
WATER[y][x] = water[y][x];
CHART[y][x]=chart[y][x];
}
}
}
/***************************/
void land(int y, int x, int a, int b)
{
int c;
int temp=0;
if(a==(y+Y-1)%Y)
c=0;
else if(b==(x+X-1)%X)
c=1;
else if(a==(y+1)%Y)
c=2;
else if(b==(x+1)%X)
c=3;
if(CHART[y][x]==c)
temp++;
else if(CHART[y][x]==(c+10)%8) //if we slope away from that direction
temp--;
else if(CHART[a][b]==(c+10)%8) //if that direction slopes toward us
temp--;
temp+=(WATER[y][x]+SAND[Y][X]-WATER[a][b]-SAND[a][b])/5;
if(temp>0 && WATER[y][x]>4 && !wall[a][b])
{
water[y][x]--;
water[a][b]++;
chart[y][x]=c;
if(SAND[y][x]>0)
{
sand[y][x]--;
sand[a][b]++;
}
}
}
/***************************/
void grow()
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
if(WATER[y][x]==1 && SAND[y][x]>1 && rand()%64==0)
tree[y][x]=1;
else if(tree[y][x]==1 && (WATER[y][x]==0 || WATER[y][x]>2) && rand()%128==0)
tree[y][x]=0;
// else if(tree[y][x]==2 &&rand()%512==0)
// tree[y][x]=0;
}
}
}
/***************************/
void calc()
{
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
chart[y][x]=CHART[y][x];
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
for(int a=(y+Y-1)%Y; a!=(y+2)%Y; a=(a+1)%Y)
{
for(int b=(x+X-1)%X; b!=(x+2)%X; b=(b+1)%X)
{
if(CHART[y][x]==(CHART[a][b]+10)%8 && CHART[y][x]!=5)
chart[y][x]=rand()%4; //reshufles any arrows that don't fit
}
}
}
}
for(int y=0; y<Y; y++)
{
for(int x=0; x<X; x++)
{
CHART[y][x]=chart[y][x];
}
}
}
/***************************/