diff --git a/.gitignore b/.gitignore
index 45fe24daba..c4a1e0a3be 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
*~
*#
+src_managed
project/plugins/project/
project/boot/*
*/project/build/target
diff --git a/akka-samples/akka-sample-ants/README.md b/akka-samples/akka-sample-ants/README.md
new file mode 100644
index 0000000000..2e55246a5c
--- /dev/null
+++ b/akka-samples/akka-sample-ants/README.md
@@ -0,0 +1,45 @@
+Ants
+====
+
+Ants is written by Peter Vlugter.
+
+Ants is based on the Clojure [ants simulation][ants.clj] by Rich Hickey, and ported to Scala using [Akka][akka] and [Spde][spde].
+
+[ants.clj]:http://clojure.googlegroups.com/web/ants.clj
+[akka]:http://akkasource.org
+[spde]:http://technically.us/spde/
+
+
+Requirements
+------------
+
+To build and run Ants you need [Simple Build Tool][sbt] (sbt).
+
+[sbt]: http://code.google.com/p/simple-build-tool/
+
+
+Running
+-------
+
+First time, 'sbt update' to get dependencies, then to run Ants use 'sbt run'.
+Here is an example. First type 'sbt' to start SBT interactively, the run 'update' and 'run':
+
+% sbt
+> update
+> run
+
+
+Notice
+------
+
+Ants is based on the Clojure ants simulation by Rich Hickey.
+
+Copyright (c) Rich Hickey. All rights reserved.
+The use and distribution terms for this software are covered by the
+Common Public License 1.0 ([http://opensource.org/licenses/cpl1.0.php][cpl])
+which can be found in the file cpl.txt at the root of this distribution.
+By using this software in any fashion, you are agreeing to be bound by
+the terms of this license.
+You must not remove this notice, or any other, from this software.
+
+[cpl]: http://opensource.org/licenses/cpl1.0.php
\ No newline at end of file
diff --git a/akka-samples/akka-sample-ants/src/main/resources/akka.conf b/akka-samples/akka-sample-ants/src/main/resources/akka.conf
new file mode 100644
index 0000000000..bcd5a09255
--- /dev/null
+++ b/akka-samples/akka-sample-ants/src/main/resources/akka.conf
@@ -0,0 +1,20 @@
+
+ level = "off"
+ console = off
+
+
+
+ version = "0.9"
+
+
+ timeout = 5000
+ serialize-messages = off
+
+
+
+ service = on
+ fair = on
+ max-nr-of-retries = 10
+ timeout = 1000
+
+
diff --git a/akka-samples/akka-sample-ants/src/main/scala/Ants.scala b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala
new file mode 100644
index 0000000000..85757f25e8
--- /dev/null
+++ b/akka-samples/akka-sample-ants/src/main/scala/Ants.scala
@@ -0,0 +1,207 @@
+/**
+ * Copyright (C) 2009-2010 Scalable Solutions AB
+ */
+
+package sample.ants
+
+import java.util.concurrent.TimeUnit
+import scala.util.Random.{nextInt => randomInt}
+import se.scalablesolutions.akka
+import akka.actor.{ActorRef, Transactor, Scheduler}
+import akka.actor.Actor.actorOf
+import akka.stm.{Vector => _, _}
+import akka.stm.Ref.Ref
+import akka.stm.Transaction.Local._
+
+object Config {
+ val Dim = 80 // dimensions of square world
+ val AntsSqrt = 7 // number of ants = AntsSqrt^2
+ val FoodPlaces = 35 // number of places with food
+ val FoodRange = 100 // range of amount of food at a place
+ val PherScale = 10 // scale factor for pheromone drawing
+ val AntMillis = 100 // how often an ant behaves (milliseconds)
+ val EvapMillis = 1000 // how often pheromone evaporation occurs (milliseconds)
+ val EvapRate = 0.99f // pheromone evaporation rate
+ val StartDelay = 1000 // delay before everything kicks off (milliseconds)
+}
+
+case class Ant(dir: Int, food: Boolean = false) {
+ def turn(i: Int) = copy(dir = Util.dirBound(dir + i))
+ def turnAround = turn(4)
+ def pickUp = copy(food = true)
+ def dropOff = copy(food = false)
+}
+
+case class Cell(food: Int = 0, pher: Float = 0, ant: Option[Ant] = None, home: Boolean = false) {
+ def addFood(i: Int) = copy(food = food + i)
+ def addPher(x: Float) = copy(pher = pher + x)
+ def alterPher(f: Float => Float) = copy(pher = f(pher))
+ def putAnt(antOpt: Option[Ant]) = copy(ant = antOpt)
+ def makeHome = copy(home = true)
+}
+
+object EmptyCell extends Cell
+
+class Place(initCell: Cell = EmptyCell) extends Ref(Some(initCell)) {
+ def cell: Cell = get.get
+ def food: Int = cell.food
+ def food(i: Int) = alter(_.addFood(i))
+ def hasFood = food > 0
+ def pher: Float = cell.pher
+ def pher(f: Float => Float) = alter(_.alterPher(f))
+ def trail = alter(_.addPher(1))
+ def ant: Option[Ant] = cell.ant
+ def ant(f: Ant => Ant): Cell = alter(_.putAnt(ant map f))
+ def enter(antOpt: Option[Ant]): Cell = alter(_.putAnt(antOpt))
+ def enter(ant: Ant): Cell = enter(Some(ant))
+ def leave = enter(None)
+ def occupied: Boolean = ant.isDefined
+ def makeHome = alter(_.makeHome)
+ def home: Boolean = cell.home
+}
+
+object World {
+ import Config._
+
+ val homeOff = Dim / 4
+ lazy val places = Vector.fill(Dim, Dim)(new Place)
+ lazy val ants = setup
+ lazy val evaporator = actorOf[Evaporator].start
+
+ def place(loc: (Int, Int)) = places(loc._1)(loc._2)
+
+ private def setup = atomic {
+ for (i <- 1 to FoodPlaces) {
+ place(randomInt(Dim), randomInt(Dim)) food (randomInt(FoodRange))
+ }
+ val homeRange = homeOff until (AntsSqrt + homeOff)
+ for (x <- homeRange; y <- homeRange) yield {
+ place(x, y).makeHome
+ place(x, y) enter Ant(randomInt(8))
+ actorOf(new AntActor(x, y)).start
+ }
+ }
+
+ def start = {
+ ants foreach (pingEvery(_, AntMillis))
+ pingEvery(evaporator, EvapMillis)
+ }
+
+ private def pingEvery(actor: ActorRef, millis: Long) =
+ Scheduler.schedule(actor, "ping", Config.StartDelay, millis, TimeUnit.MILLISECONDS)
+}
+
+object Util {
+ import Config._
+
+ def bound(b: Int, n: Int) = {
+ val x = n % b
+ if (x < 0) x + b else x
+ }
+
+ def dirBound(n: Int) = bound(8, n)
+ def dimBound(n: Int) = bound(Dim, n)
+
+ val dirDelta = Map(0 -> (0, -1), 1 -> (1, -1), 2 -> (1, 0), 3 -> (1, 1),
+ 4 -> (0, 1), 5 -> (-1, 1), 6 -> (-1, 0), 7 -> (-1, -1))
+ def deltaLoc(x: Int, y: Int, dir: Int) = {
+ val (dx, dy) = dirDelta(dirBound(dir))
+ (dimBound(x + dx), dimBound(y + dy))
+ }
+
+ def rankBy[A, B: Ordering](xs: Seq[A], f: A => B) = Map(xs.sortBy(f).zip(Stream from 1): _*)
+
+ def roulette(slices: Seq[Int]) = {
+ val total = slices.sum
+ val r = randomInt(total)
+ var i, sum = 0
+ while ((sum + slices(i)) <= r) {
+ sum += slices(i)
+ i += 1
+ }
+ i
+ }
+}
+
+trait WorldActor extends Transactor {
+ def act
+ def receive = { case "ping" => act }
+}
+
+class AntActor(initLoc: (Int, Int)) extends WorldActor {
+ import World._
+ import Util._
+
+ val locRef = Ref(initLoc)
+ val homing = (p: Place) => p.pher + (100 * (if (p.home) 0 else 1))
+ val foraging = (p: Place) => p.pher + p.food
+
+ def loc = locRef.get.getOrElse(initLoc)
+ def newLoc(l: (Int, Int)) = locRef swap l
+
+ def act = {
+ val (x, y) = loc
+ val current = place(x, y)
+ for (ant <- current.ant) {
+ val ahead = place(deltaLoc(x, y, ant.dir))
+ if (ant.food) { // homing
+ if (current.home) dropFood
+ else if (ahead.home && !ahead.occupied) move
+ else random(homing)
+ } else { // foraging
+ if (!current.home && current.hasFood) pickUpFood
+ else if (!ahead.home && ahead.hasFood && !ahead.occupied) move
+ else random(foraging)
+ }
+ }
+ }
+
+ def move = {
+ val (x, y) = loc
+ val from = place(x, y)
+ for (ant <- from.ant) {
+ val toLoc = deltaLoc(x, y, ant.dir)
+ val to = place(toLoc)
+ to enter ant
+ from.leave
+ if (!from.home) from.trail
+ newLoc(toLoc)
+ }
+ }
+
+ def pickUpFood = {
+ val current = place(loc)
+ current food -1
+ current ant (_.pickUp.turnAround)
+ }
+
+ def dropFood = {
+ val current = place(loc)
+ current food +1
+ current ant (_.dropOff.turnAround)
+ }
+
+ def random[A: Ordering](ranking: Place => A) = {
+ val (x, y) = loc
+ val current = place(x, y)
+ for (ant <- current.ant) {
+ val delta = (turn: Int) => place(deltaLoc(x, y, ant.dir + turn))
+ val ahead = delta(0)
+ val aheadLeft = delta(-1)
+ val aheadRight = delta(+1)
+ val locations = Seq(ahead, aheadLeft, aheadRight)
+ val ranks = rankBy(locations, ranking)
+ val ranked = Seq(ranks(aheadLeft), (if (ahead.occupied) 0 else ranks(ahead)), ranks(aheadRight))
+ val dir = roulette(ranked) - 1
+ if (dir == 0) move
+ else current ant (_.turn(dir))
+ }
+ }
+}
+
+class Evaporator extends WorldActor {
+ import Config._
+ import World._
+ val evaporate = (pher: Float) => pher * EvapRate
+ def act = for (x <- 0 until Dim; y <- 0 until Dim) place(x, y) pher evaporate
+}
diff --git a/akka-samples/akka-sample-ants/src/main/spde/Ants.spde b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde
new file mode 100644
index 0000000000..1d5bf4c39f
--- /dev/null
+++ b/akka-samples/akka-sample-ants/src/main/spde/Ants.spde
@@ -0,0 +1,46 @@
+import sample.ants._
+import sample.ants.Config._
+import se.scalablesolutions.akka.stm.Transaction.Local._
+
+val scale = 5
+
+size(Dim * scale, Dim * scale)
+smooth()
+
+override def setup() {
+ background(255)
+ World.start
+}
+
+def draw() {
+ for (x <- 0 until Dim; y <- 0 until Dim) {
+ val cell = atomic { World.place(x, y).cell }
+ val (rx, ry, rw, rh) = (x * scale, y * scale, scale, scale)
+ noStroke()
+ fill(255)
+ rect(rx, ry, rw, rh)
+ if (cell.pher > 0) fill(0, 255, 0, cell.pher * PherScale)
+ if (cell.food > 0) fill(255, 0, 0, 255 * (cell.food / FoodRange.floatValue))
+ rect(rx, ry, rw, rh)
+ for (ant <- cell.ant) {
+ if (ant.food) stroke(255, 0, 0) else stroke(0)
+ val (hx, hy, tx, ty) = antLine(ant.dir)
+ line(rx + hx, ry + hy, rx + tx, ry + ty)
+ }
+ stroke(0, 0, 255)
+ noFill()
+ val homeStart = World.homeOff * scale
+ val homeWidth = AntsSqrt * scale
+ rect(homeStart, homeStart, homeWidth, homeWidth)
+ }
+}
+
+val s = scale - 1
+val m = s / 2
+
+def antLine(dir: Int) = dir match {
+ case 0|4 => (m, 0, m, s)
+ case 1|5 => (s, 0, 0, s)
+ case 2|6 => (s, m, 0, m)
+ case _ => (s, s, 0, 0)
+}
diff --git a/project/build/AkkaProject.scala b/project/build/AkkaProject.scala
index abd0b2fce8..b484a90bf5 100644
--- a/project/build/AkkaProject.scala
+++ b/project/build/AkkaProject.scala
@@ -4,6 +4,7 @@
import sbt._
import sbt.CompileOrder._
+import spde._
import java.util.jar.Attributes
import java.util.jar.Attributes.Name._
@@ -198,7 +199,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
}
class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) {
- val redis = "com.redis" % "redisclient" % "2.8.0.RC2-1.4-SNAPSHOT" % "compile"
+ val redis = "com.redis" % "redisclient" % "2.8.0.Beta1-1.3" % "compile"
override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil
}
@@ -249,8 +250,9 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
//val atomikos_transactions_util = "com.atomikos" % "transactions-util" % "3.2.3" % "compile"
val jta_spec = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile"
}
+
+ // ================= TESTS ==================
- // examples
class AkkaFunTestProject(info: ProjectInfo) extends DefaultProject(info) {
val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile"
val stax_api = "javax.xml.stream" % "stax-api" % "1.0-2" % "compile"
@@ -264,7 +266,15 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
val jmock = "org.jmock" % "jmock" % "2.4.0" % "test"
}
+ // ================= EXAMPLES ==================
+
+ class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) {
+ val scalaToolsSnapshots = ScalaToolsSnapshots
+ override def spdeSourcePath = mainSourcePath / "spde"
+ }
+
class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
+
class AkkaSamplePubSubProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
class AkkaSampleLiftProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) {
@@ -300,6 +310,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
}
class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) {
+ lazy val akka_sample_ants = project("akka-sample-ants", "akka-sample-ants",
+ new AkkaSampleAntsProject(_), akka_core)
lazy val akka_sample_chat = project("akka-sample-chat", "akka-sample-chat",
new AkkaSampleChatProject(_), akka_kernel)
lazy val akka_sample_pubsub = project("akka-sample-pubsub", "akka-sample-pubsub",
@@ -351,14 +363,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
def deployPath: Path
lazy val dist = distAction
+
def distAction = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn(
`package`, packageDocs, packageSrc) describedAs("Deploying")
+
def deployTask(jar: Path, docs: Path, src: Path, toDir: Path,
genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task {
gen(jar, toDir, genJar, "Deploying bits") orElse
gen(docs, toDir, genDocs, "Deploying docs") orElse
gen(src, toDir, genSource, "Deploying sources")
}
+
private def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] =
if (flag) {
log.info(msg + " " + jar)
diff --git a/project/plugins/Plugins.scala b/project/plugins/Plugins.scala
index 4b21ea189b..f36c65ed13 100644
--- a/project/plugins/Plugins.scala
+++ b/project/plugins/Plugins.scala
@@ -1,6 +1,8 @@
import sbt._
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
+ val databinderRepo = "Databinder Repository" at "http://databinder.net/repo"
+ val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1"
// val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/"
// val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT"
}
\ No newline at end of file