//------------------------------------------------------------------------------ // cosporen.jc //------------------------------------------------------------------------------ // // Taking the "Sporen" applet to the next level... // This implementation uses a cofunction to run the simulation code in it's own // cooperative thread. It also has some improvements to the Sporen simulation, // such as mutation, development of new families, etc... // To run this script, simply drag it onto the ScriptApp's icon. // //------------------------------------------------------------------------------ import stdlib; import math; import time; import Screen; import RectLayer; using stdlib, math, time; //------------------------------------------------------------------------------ // global variables and constants //------------------------------------------------------------------------------ const int kMaxLevel = 255; // maximum energy a spore can have const int kEnergyDec = 3; // defines how fast spore grows old (and dies) const int kEatBonus = 155; // how much energy eating brings const int kReproCost = 15; // how much energy reproduction costs const int kEatLevel = 92; // level at which spore starts to eat const int kReproLevel = 181; // level at which spore starts to reproduce const int kCellSize = 8; // width and height of a cell in pixel const int kViewWidth = 640; // width in pixel of the spore view const int kViewHeight = 480; // height in pixel of the spore view const int kRedrawSpeed = 30; // redraw interval in msec const int kSimulationSpeed = 30; // simulation speed, 1 (slow) ~ 60 (fast) const int kMutateSpeed = 25; // mutate every n generations const int kUseSync = true; // if 'true' fullscreen will attempt to sync to display //------------------------------------------------------------------------------ // forward declarations //------------------------------------------------------------------------------ class World; class Spore; class Cell; cofunction SporeLife(World); //------------------------------------------------------------------------------ // class SporeApplet //------------------------------------------------------------------------------ class SporeApplet : Applet { method SporeApplet (Screen scr); method OnOpen (); method OnClose (); method OnKey (int key); method Idle (); Screen m_Screen; World m_World; SporeLife m_SporeLife; time m_Timer; } //------------------------------------------------------------------------------ // class World //------------------------------------------------------------------------------ // The World is a two dimensional array where the Spores live. // This class uses the built-in array to create a two-dimensional array. class World { method World (int width, int height); method PlaceSpore (Point pos, Spore s); method RemoveSpore (Point pos); method Spore GetSpore (Point pos); method SetSpore (Point pos, Spore s); method Wrap (Point pos); int dimX; int dimY; Cell[] matrix; } //------------------------------------------------------------------------------ // class Spore //------------------------------------------------------------------------------ // A spore has a fixed life-cycle: It gets born into a cell, eats one or more of it's neighbors, // reproduces itself to into a free cell next to it, and eventually dies. class Spore { method Spore (const Color c, int s); method Spore (const Spore dad); method Life (World world); method SpinToXY (Point pt); int energy; // decreases continually, if 0, it dies Point pos; // position in matrix Color color; // color of the spore int shape; // "family" of the spore int gen; // spore generation int spin; // direction of the spore int ispin; // spin increment int ieat; // eat bonus int irepro; // repro cost } //------------------------------------------------------------------------------ // class Cell //------------------------------------------------------------------------------ // A simple data class that stores a reference to a Spore and a RectLayer, which // is used to display the cell on the screen. class Cell { method Cell (Point pos); method Update (); Spore spore; RectLayer layer; } //------------------------------------------------------------------------------ // function CreateApplet //------------------------------------------------------------------------------ // The Application calls this function to create our applet. // We need to implement this function and return an instance of our applet. function Applet CreateApplet(Screen screen) { return new SporeApplet(screen); } //------------------------------------------------------------------------------ // cofunction SporeLife //------------------------------------------------------------------------------ // We use a cofunction to simulate our spores in it's own cooperative thread. // This thread is created by our SporeApplet class and resumed each time // SporeApplet::Idle() is called. cofunction SporeLife(World world) { // infinitely run main loop for(;;) { // wait a little... for the dramatic effect for( int i = 0; i < 50; i++ ) yield; // place four spores of different colors and shape as start population Point pos = new Point(world.dimX / 2, world.dimY / 2); world.PlaceSpore(pos, new Spore(new Color( 64, 64,255), 0)); world.PlaceSpore(pos, new Spore(new Color(255, 64,64 ), 8000)); world.PlaceSpore(pos, new Spore(new Color( 64,255,64 ), 16000)); world.PlaceSpore(pos, new Spore(new Color(255,255,64 ), 24000)); // wait again... for( int i = 0; i < 100; i++ ) yield; int alive = true; int ycnt = 0; // go through all spores and simulate their life while( alive ) { alive = false; for( int y = 0; y < world.dimY; y++ ) { for( int x = 0; x < world.dimX; x++ ) { Cell cell = world.matrix[x, y]; Spore spore = cell.spore; if( spore != null ) { spore.Life( world ); cell.Update(); alive = true; } } // yield every few rows ycnt++; if( ycnt >= kSimulationSpeed ) { yield; ycnt = 0; } } } } } //------------------------------------------------------------------------------ // function Mutate //------------------------------------------------------------------------------ // Darwin's little helper function. function int Mutate(int val, int min, int max) { int strength = (abs(min) + abs(max)) / 10 + 1; val += rand(-strength, strength); if( val < min ) val = min; else if( val > max ) val = max; return val; } //------------------------------------------------------------------------------ // class SporeApplet //------------------------------------------------------------------------------ // constructor method SporeApplet::SporeApplet(Screen screen) { // store reference to screen m_Screen = screen; // set fixed size m_Screen.FixedSize( kViewWidth, kViewHeight ); // set background color m_Screen.BackColor( new Color(0, 0, 0) ); // try to make 60 fps m_Screen.SetDesiredFPS( 60 ); // set window title m_Screen.Caption( "Sporen II - JewelScript demo applet" ); // create our world m_World = new World( kViewWidth / kCellSize, kViewHeight / kCellSize ); // init members m_Timer = new time(); m_SporeLife = new SporeLife( m_World ); // init random generator randInit(); } //------------------------------------------------------------------------------ // OnOpen //------------------------------------------------------------------------------ // method SporeApplet::OnOpen() { // add all Cell layers to our Screen object for( int y = 0; y < m_World.dimY; y++ ) { for( int x = 0; x < m_World.dimX; x++ ) { m_Screen.AddLayer( m_World.matrix[x, y].layer ); } } } //------------------------------------------------------------------------------ // OnClose //------------------------------------------------------------------------------ // method SporeApplet::OnClose() { m_Screen.RemoveAllLayers(); } //------------------------------------------------------------------------------ // OnKey //------------------------------------------------------------------------------ // method SporeApplet::OnKey(int key) { Point pos = new Point(rand(0, m_World.dimX - 1), rand(0, m_World.dimY - 1)); switch( key ) { case '1': m_World.PlaceSpore(pos, new Spore(new Color( 64, 64,255), 0)); break; case '2': m_World.PlaceSpore(pos, new Spore(new Color(255, 64,64 ), 8000)); break; case '3': m_World.PlaceSpore(pos, new Spore(new Color( 64,255,64 ), 16000)); break; case '4': m_World.PlaceSpore(pos, new Spore(new Color(255,255,64 ), 24000)); break; case kKeyF11: m_Screen.FullScreen(kViewWidth, kViewHeight, kUseSync); break; } } //------------------------------------------------------------------------------ // Idle //------------------------------------------------------------------------------ // method SporeApplet::Idle() { // resume our thread m_SporeLife(); // check if time to update screen if( m_Timer.isTick(kRedrawSpeed) ) { m_Screen.Redraw(); } } //------------------------------------------------------------------------------ // class World //------------------------------------------------------------------------------ // Implementation method World::World(int width, int height) { dimX = width; dimY = height; matrix = new array(width, height); for( int y = 0; y < height; y++ ) { for( int x = 0; x < width; x++ ) { matrix[x, y] = new Cell(new Point(x, y)); } } } //------------------------------------------------------------------------------ // PlaceSpore //------------------------------------------------------------------------------ // Place a spore into a free cell around the given cell. method World::PlaceSpore(Point pos, Spore s) { int x = pos.x; int y = pos.y; if( matrix[x, y].spore == null ) { SetSpore(pos, s); } else { int a = rand(0, 7); int b = a + 8; for( int c = a; c < b; c++ ) { int d = c % 8; if( d >= 4 ) d++; int nx = x + d % 3 - 1; int ny = y + d / 3 - 1; if( matrix[nx, ny].spore == null ) { SetSpore(new Point(nx, ny), s); break; } } } } //------------------------------------------------------------------------------ // RemoveSpore //------------------------------------------------------------------------------ // Remove a Spore from the world method World::RemoveSpore(Point pos) { Cell cell = matrix[pos.x, pos.y]; cell.layer.Hide(); cell.spore = null; } //------------------------------------------------------------------------------ // GetSpore //------------------------------------------------------------------------------ // Check if a cell contains a Spore and return it method Spore World::GetSpore(Point pos) { return matrix[pos.x, pos.y].spore; } //------------------------------------------------------------------------------ // SetSpore //------------------------------------------------------------------------------ // Set a Spore into specific cell in the world method World::SetSpore(Point pos, Spore s) { int x = pos.x; int y = pos.y; Cell cell = matrix[x, y]; Color c = s.color; int e = s.energy; int r = (c.red * e) >> 8; int g = (c.green * e) >> 8; int b = (c.blue * e) >> 8; cell.layer.SetColor( new Color(r, g, b) ); cell.layer.Show(); cell.spore = s; s.pos = pos; } //------------------------------------------------------------------------------ // Wrap //------------------------------------------------------------------------------ // Wrap around coords method World::Wrap(Point pos) { if( pos.x < 0 ) pos.x += dimX; else if( pos.x >= dimX ) pos.x -= dimX; if( pos.y < 0 ) pos.y += dimY; else if( pos.y >= dimY ) pos.y -= dimY; } //------------------------------------------------------------------------------ // class Cell //------------------------------------------------------------------------------ // Constructor method Cell::Cell(Point pos) { spore = null; layer = new RectLayer(); layer.ResizeTo( kCellSize - 1, kCellSize - 1 ); layer.MoveTo( pos.x * kCellSize, pos.y * kCellSize ); } //------------------------------------------------------------------------------ // Update //------------------------------------------------------------------------------ // method Cell::Update() { if( spore != null ) { int e = spore.energy; Color c = spore.color; int r = (c.red * e) >> 8; int g = (c.green * e) >> 8; int b = (c.blue * e) >> 8; layer.SetColor(new Color(r, g, b)); } } //------------------------------------------------------------------------------ // class Spore //------------------------------------------------------------------------------ // Constructor method Spore::Spore(const Color c, int s) { energy = kMaxLevel; pos = new Point(); color = c; shape = s; gen = 0; spin = rand(0, 7); ispin = 1; ieat = kEatBonus; irepro = kReproCost; } //------------------------------------------------------------------------------ // class Spore //------------------------------------------------------------------------------ // copy constructor method Spore::Spore(const Spore dad) { energy = dad.energy * 85 / 100; // 85% of dad's energy pos = new Point(); color = new Color(dad.color); shape = dad.shape; gen = dad.gen + 1; spin = dad.spin; ispin = dad.ispin; ieat = dad.ieat; irepro = dad.irepro; // every few generations, mutate... if( gen >= kMutateSpeed ) { gen = 0; ispin = Mutate(ispin, -3, 3); ieat = Mutate(ieat, 120, 150); irepro = Mutate(irepro, 10, 30); shape += rand(-1, 1); color.red = Mutate(color.red, 64, 255); color.green = Mutate(color.green, 64, 255); color.blue = Mutate(color.blue, 64, 255); } } //------------------------------------------------------------------------------ // Life //------------------------------------------------------------------------------ // Simulate the Spores life method Spore::Life(World world) { int turn = true; Point spoPt; energy -= kEnergyDec; if( energy <= 0 ) { // spore is dead, remove from world world.RemoveSpore( pos ); return; } if( energy < kEatLevel ) { // peek around to find something to eat... SpinToXY(spoPt); world.Wrap(spoPt); // get the spore from the cell Spore enemy = world.GetSpore(spoPt); // only eat if it's from other family if( enemy != null ) { // found a meal? if( abs(enemy.shape - shape) >= 16 ) { // yum! energy += enemy.ieat; if( energy > kMaxLevel ) energy = kMaxLevel; world.RemoveSpore(spoPt); turn = false; } } } else if( energy >= kReproLevel ) { // peek around to find a free place SpinToXY(spoPt); world.Wrap(spoPt); // found a spot? if( world.GetSpore(spoPt) == null ) { // place kid in cell world.PlaceSpore(spoPt, new Spore(this) ); // kid inherits from us energy -= irepro; if( energy < 0 ) energy = 0; turn = false; } } if( turn ) { spin += ispin; if( spin > 7 ) spin -= 8; else if( spin < 0 ) spin += 8; } } //------------------------------------------------------------------------------ // SpinToXY //------------------------------------------------------------------------------ // Computes cell coords from the spin index method Spore::SpinToXY(Point pt) { int s = spin; if( s >= 4 ) s++; pt.x = pos.x + s % 3 - 1; pt.y = pos.y + s / 3 - 1; }