I really do like it. Some other things I have thought of doing is first, make an Erlang style library in C++ to do concurrency in a similar pattern, could even make it usable into a normal Erlang node as well (because the normal Erlang C++ Node library makes it so that the entire C++ process acts as one Erlang process, even though it (I think!) does transfer process information as well so you could emulate a full Erlang system, but written purely in C++ as well, that would allow the speed of C++ for critical functions (database, pathfinding, etc...), but allow a purely fault-tolerant and featurefull Erlang for running near everything else, only thing is that you could not be able to run arbitrary things on a C Node so any Erlang process communicating with it would need to know it is a C Node and not a normal Erlang node (which would be rather obvious since you would only do specific things, like pathfinding and database on the nodes, and not generic processing like on Erlang Nodes).
To do it in C++, the concurrency aspect could be written like a standard jump table hidden with macro's so you could literally write in C++ as you do in Erlang or Pylang, and although this would be more 'pretty', it is very difficult to do in Microsoft's compiler, whereas it is a synch in others (like GCC or MingW). Or, could go the generic route of a class with an event loop, the event loop would not have to be a function though, but rather a function pointer so you could still do the other unique tests and flow controls like in Erlang and Pylang, just a little bit more brain-wracking in structure.

Here, this is a normal python example that I ported to Pylang, a little simulation (I'm sure you can find plenty of Erlang examples out there, I have, there is even a modeler, Wings3D, made in Erlang, so client-side apps are very possible):
And you need to install the Genshi plugin for SMF so that it can colorize code snippets if I put a language="Python" parameter in the code tag, this is far less readable without it:
I do not actually encourage this style, but it works, and this is a well known example that I ported (and it runs correctly)
"""
From "http://members.verizon.net/olsongt/stackless/why_stackless.html", remade
the 'Killer Robots!' example, not so much more scaffolding needed, eh?
Although, do not need the network stuff either... Hmm, a distributed robot
fight, perhaps add in an input for a human or something as well on multiple
ends of the network mesh. That will be a later project, just remaking this one
for now.
"""
from ..core import Process, getMessage, getSelfPid
from ..nameserver import NameServer
from ..stacklessExtensions import Sleep
import pygame
import pygame.locals
import os
import sys
import math
import time
import random
# Messages do not have to be classes, just makes testing faster since
# an isinstance is usually faster then most other tests, although it pickles
# up slower, a tuple, ala Erlang style, would probably be the fastest you
# could get, but just using classes here to make it clean.
class ActorMovementUpdate(object):
def __init__(self, angle, velocity, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.angle = angle
self.velocity = velocity
class ActorInfo(object):
def __init__(self, name,location=(-1,-1),angle=0,
velocity=0,height=-1,width=-1,hitpoints=1,physical=True,
public=True, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.name = name
self.location = location
self.angle = angle
self.velocity = velocity
self.height = height
self.width = width
self.physical = physical
self.public = public
self.hitpoints = hitpoints
def updateMovement(self, movementUpdate):
self.angle = movementUpdate.angle
self.velocity = movementUpdate.velocity
class ActorInfoJoin(object):
def __init__(self, actorInfo, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.actorInfo = actorInfo
class WorldState(object):
def __init__(self, updateRate, time, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.updateRate = updateRate
self.time = time
self.actors = []
class Collision(object):
def __init__(self, actor, collidedWith, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.actor = actor
self.collidedWith = collidedWith
class KillActor(object):
pass
class KillMe(object):
def __init__(self, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
class Damage(object):
def __init__(self, amt, actorPid=None):
self.actorPid = actorPid
if self.actorPid is None: self.actorPid = getSelfPid()
self.amt = amt
# Not making a class for world, probably should, would be cleaner, but I am
# trying to make things look excessively procedural here.
def worldProcess():
import math
NameServer.__nameserver__.registerNamedPid(getSelfPid(), 'world')
registeredActors = {}
updateRate = 15
maxupdateRate = 30
# Internal functions
def testForCollision(x, y, item, otherItems=[]):
if x < 0 or x + item.width > 496:
return getSelfPid()
elif y < 0 or y+ item.height > 496:
return getSelfPid()
else:
ax1,ax2,ay1,ay2 = x, x+item.width, y,y+item.height
for item,bx1,bx2,by1,by2 in otherItems:
if registeredActors[item].physical == False: continue
for x,y in [(ax1,ay1),(ax1,ay2),(ax2,ay1),(ax2,ay2)]:
if x >= bx1 and x <= bx2 and y >= by1 and y <= by2:
return item
for x,y in [(bx1,by1),(bx1,by2),(bx2,by1),(bx2,by2)]:
if x >= ax1 and x <= ax2 and y >= ay1 and y <= ay2:
return item
return None
def killDeadActors():
toKill = []
for (actor, actorInfo) in registeredActors.iteritems():
if actorInfo.hitpoints <= 0:
print "ACTOR DIED", actorInfo.name, actorInfo.hitpoints
actor(KillActor())
toKill.append(actor)
for actor in toKill:
del registeredActors[actor]
def updateActorPositions():
actorPositions = []
for (actor, actorInfo) in registeredActors.iteritems():
if actorInfo.public and actorInfo.physical:
x,y = actorInfo.location
angle = actorInfo.angle
velocity = actorInfo.velocity
VectorX,VectorY = (math.sin(math.radians(angle)) * velocity,
math.cos(math.radians(angle)) * velocity)
x += VectorX/updateRate
y -= VectorY/updateRate
collision = testForCollision(x, y, actorInfo, actorPositions)
if collision:
#don't move
actor(Collision(actor, collision))
if collision and collision is not getSelfPid():
collision(Collision(actor, collision))
else:
actorInfo.location = (x,y)
actorPositions.append( (actor,
actorInfo.location[0],
actorInfo.location[0] + actorInfo.height,
actorInfo.location[1],
actorInfo.location[1] + actorInfo.width))
def sendStateToActors(starttime):
worldState = WorldState(updateRate, starttime)
for (actor, actorInfo) in registeredActors.iteritems():
if actorInfo.public:
worldState.actors.append(actorInfo)
for (actor, actorInfo) in registeredActors.iteritems():
actor(worldState)
while True:
e=getMessage(timeout=0)
initialStartTime = time.clock()
startTime = time.clock()
if e is None: # Timeout, no messages, so send info back out to all
killDeadActors()
updateActorPositions()
sendStateToActors(startTime)
calculatedEndTime = startTime + 1.0/updateRate
doneProcessingTime = time.clock()
percentUtilized = (doneProcessingTime - startTime) / (1.0/updateRate)
if percentUtilized >= 1:
updateRate -= 1
print "TOO MUCH LOWERING FRAME RATE: " , updateRate
elif percentUtilized <= 0.6 and updateRate < maxupdateRate:
updateRate += 1
print "TOO MUCH FREETIME, RAISING FRAME RATE: " , updateRate
Sleep(calculatedEndTime-time.clock())
startTime = calculatedEndTime
Sleep(0.0)
elif isinstance(e, ActorInfoJoin):
print 'ADDING ', e.actorInfo.name, e.actorInfo
registeredActors[e.actorPid] = e.actorInfo
elif isinstance(e, ActorMovementUpdate):
registeredActors[e.actorPid].updateMovement(e)
elif isinstance(e, Collision):
pass # Known, but ignored
elif isinstance(e, KillMe):
registeredActors[e.actorPid].hitpoints = 0
else:
print '!!!! WORLD GOT UNKNOWN MESSAGE ', e
def display():
import pygame
import os
world = NameServer.__nameserver__.getNamedPid('world')
icons = {}
pygame.init()
window = pygame.display.set_mode((496,496))
pygame.display.set_caption("Actor Demo")
world(ActorInfoJoin(ActorInfo("display", public=False)))
datapath = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
# Function declarations here:
def getIcon(iconName):
if icons.has_key(iconName):
return icons[iconName]
else:
iconFile = os.path.join(datapath,"%s.bmp" % iconName)
surface = pygame.image.load(iconFile)
surface.set_colorkey((0xf3,0x0a,0x0a))
icons[iconName] = surface
return surface
def updateDisplay(actors):
for event in pygame.event.get():
if event.type == pygame.QUIT: NameServer.__nameserver__.shutdown()
screen = pygame.display.get_surface()
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((200, 200, 200))
screen.blit(background, (0,0))
for item in actors:
itemImage = getIcon(item.name)
itemImage = pygame.transform.rotate(itemImage,-item.angle)
screen.blit(itemImage, item.location)
pygame.display.flip()
while True:
e=getMessage(timeout=-1)
if isinstance(e, WorldState):
updateDisplay(e.actors)
else:
print "display: UNKNOWN MESSAGE", e
def basicRobot(location=(0,0), angle=135, velocity=1, hitpoints=20, name="basicRobot"):
world = NameServer.__nameserver__.getNamedPid('world')
world(ActorInfoJoin(ActorInfo(name,
location=location,
angle=angle,
velocity=velocity,
height=32,width=32,hitpoints=hitpoints)))
selfpid = getSelfPid()
while True:
e=getMessage(timeout=-1)
if isinstance(e, WorldState):
for actor in e.actors:
if actor.actorPid is selfpid: break
location = actor.location
angle += 3.0 * (1.0 / e.updateRate)
if angle >= 360:
angle -= 360
world(ActorMovementUpdate(angle, velocity))
elif isinstance(e, Collision):
angle += 73
if angle >= 360:
angle -= 360
hitpoints -= 1
if hitpoints <= 0:
Explosion(location, angle)
world(KillMe())
elif isinstance(e, Damage):
hitpoints -= e.amt
if hitpoints <= 0:
Explosion(location, angle)
world(KillMe())
else:
print "%s: UNKNOWN MESSAGE"%name, e
def Explosion(location=(0,0), angle=0):
time = 0.0
world = NameServer.__nameserver__.getNamedPid('world')
world(ActorInfoJoin(ActorInfo('explosion',
location=location,
angle=angle,
velocity=0,
height=32, width=32, hitpoints=1,
physical=False)))
selfpid = getSelfPid()
while True:
e=getMessage(timeout=-1)
if isinstance(e, WorldState):
if time == 0.0:
time = e.time
elif e.time >= time + 3.0:
world(KillMe())
else:
print "Explosion: UNKNOWN MESSAGE", e
def Mine(location=(0,0)):
world = NameServer.__nameserver__.getNamedPid('world')
world(ActorInfoJoin(ActorInfo('Mine',
location=location,
angle=0,
velocity=0,
height=2,width=2,hitpoints=1)))
selfpid = getSelfPid()
while True:
e=getMessage(timeout=-1)
if isinstance(e, WorldState):
pass # Known about, but unused
elif isinstance(e, Collision):
if e.collidedWith is selfpid:
other = e.actor
else:
other = e.collidedWith
other(Damage(25))
world(KillMe())
print "MINE COLLISION"
else:
print "Mine: UNKNOWN MESSAGE", e
def minedropperRobot(location=(0,0), angle=135, velocity=1, hitpoints=20, name="minedropperRobot"):
world = NameServer.__nameserver__.getNamedPid('world')
selfpid = getSelfPid()
height=32.0
width=32.0
delta = 0.0
deltaDirection = "up"
nextMine = 0.0
world(ActorInfoJoin(ActorInfo(name,
location=location,
angle=angle,
velocity=velocity,
height=height,width=width,hitpoints=hitpoints)))
while True:
e=getMessage(timeout=-1)
if isinstance(e, WorldState):
for actor in e.actors:
if actor.actorPid is selfpid:
break
location = actor.location
if deltaDirection == "up":
delta += 60.0 * (1.0/e.updateRate)
if delta > 15.0:
delta = 15.0
deltaDirection = "down"
else:
delta -= 60.0 * (1.0/e.updateRate)
if delta < -15.0:
delta = -15.0
deltaDirection = "up"
if nextMine <= e.time:
nextMine = e.time + 1.0
mineX,mineY = (location[0] + (width / 2.0),
location[1] + (width / 2.0))
mineDistance = (width / 2.0 ) ** 2
mineDistance += (height / 2.0) ** 2
mineDistance = math.sqrt(mineDistance)
VectorX,VectorY = (math.sin(math.radians(angle + delta)),
math.cos(math.radians(angle + delta)))
VectorX,VectorY = VectorX * mineDistance, VectorY * mineDistance
x,y = location
x += width / 2.0
y += height / 2.0
x -= VectorX
y += VectorY
Process(Mine)((x, y))
world(ActorMovementUpdate(angle+delta, velocity))
elif isinstance(e, Collision):
angle += 73
if angle >= 360:
angle -= 360
hitpoints -= 1
if hitpoints <= 0:
Explosion(location, angle)
world(KillMe())
elif isinstance(e, Damage):
hitpoints -= e.amt
if hitpoints <= 0:
Explosion(location, angle)
world(KillMe())
else:
print "%s: UNKNOWN MESSAGE"%name, e
def spawner(location=(0,0), robots=[basicRobot, minedropperRobot]):
world = NameServer.__nameserver__.getNamedPid('world')
t = time.time()+0.5
world(ActorInfoJoin(ActorInfo('spawner',
location=location,
angle=0,
velocity=0,
height=32,width=32,hitpoints=1,
physical=False)))
while True:
e=getMessage(timeout=t-time.time())
if e is None:
t = time.time()+(random.random()*5.0)+3.0
angle = random.random() * 360.0
velocity = random.random() * 1000.0
newRobot = random.choice(robots)
Process(newRobot)(location, angle, velocity)
elif isinstance(e, WorldState):
pass # Known about, but unused
# My generic process that will completely kill a system to make sure a test app
# does not run excessivly long.
def tempKillAll(waitTime=50, ns=None):
if waitTime==-1: return # never die by this process
if not ns:
ns = NameServer.__nameserver__
Sleep(waitTime%10)
for i in xrange(int(waitTime)-(waitTime%10), 0, -10):
print "%i seconds until death" %(i)
Sleep(10)
print "dieing"
ns.shutdown()
def runTest(timeout=60):
NameServer()
Process(tempKillAll)(timeout)
Process(worldProcess)()
Process(display)()
Process(spawner)( (32,32) )
Process(spawner)( (432,32) )
Process(spawner)( (32,432) )
Process(spawner)( (432,432) )
Process(spawner)( (232,232) )
NameServer.__nameserver__.run()
""" Run this at a command prompt at the directory that contains Pylang if Pylang is not properly installed, or just run it anywhere, like the interpreter after you add the Pylang path to import sys;sys.path.append(pathToPylangParentDirectory)
F:\Python25\ODL1 Temp Scripts>..\python -c "from Pylang.tests.KillerRobots import runTest;runTest()"
"""