Merge: curl: basic Unix domain socket support
[nit.git] / lib / bucketed_game.nit
index eba5d69..213d875 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Provides basic game logic utilities using buckets to coordinate and
-# optimize actions on game turn ends. Supports both action at each
-# end of turn as well as actions on some end of turns.
+# Game framework with an emphasis on efficient event coordination
 #
-# Allows for fast support of a large number of entities with rare actions,
-# such as a forest with many individual trees.
-module bucketed_game
+# Provides basic game logic entities to manage a game where the logic is executed by turns:
+# `Game`, `GameTurn`, `GameEvent`, `Turnable`.
+# Also offers a bucket system to plan future events at a known number of turns in the future:
+# `Bucketable` and the services `act_next` and `act_in`.
+#
+# Efficiently support large number of entities with rare or sparse actions,
+# such as a forest with many individual trees growing slowly.
+module bucketed_game is serialize
+
+import serialization
+import counter
 
 # Something acting on the game
-class Turnable[G: Game]
+abstract class Turnable[G: Game]
 
        # Execute `turn` for this instance.
        fun do_turn(turn: GameTurn[G]) is abstract
 end
 
 # Something acting on the game from time to time
-class Bucketable[G: Game]
+abstract class Bucketable[G: Game]
        super Turnable[G]
+
        private var act_at: nullable Int = null
 
        # Cancel the previously registered acting turn
@@ -49,7 +56,7 @@ class Buckets[G: Game]
        type BUCKET: HashSet[Bucketable[G]]
 
        private var next_bucket: nullable BUCKET = null
-       private var current_bucket_key: Int = -1
+       private var current_bucket_key = -1
 
        # Number of `buckets`, default at 100
        #
@@ -59,6 +66,9 @@ class Buckets[G: Game]
        private var buckets: Array[BUCKET] =
                [for b in n_buckets.times do new HashSet[Bucketable[G]]] is lazy
 
+       # Stats on delays asked when adding an event with `act_in` and `act_next`
+       private var delays = new Counter[Int]
+
        # Add the Bucketable event `e` at `at_tick`.
        fun add_at(e: Bucketable[G], at_tick: Int)
        do
@@ -100,6 +110,26 @@ class Buckets[G: Game]
                        end
                end
        end
+
+       # Get some statistics on both the current held events and historic expired events
+       fun stats: String
+       do
+               var entries = 0
+               var instances = new HashSet[Bucketable[G]]
+               var max = 0
+               var min = 100000
+               for bucket in buckets do
+                       var len = bucket.length
+                       entries += len
+                       instances.add_all bucket
+                       min = min.min(len)
+                       max = max.max(len)
+               end
+               var avg = entries.to_f / buckets.length.to_f
+
+               return "{buckets.length} buckets; uniq/tot:{instances.length}/{entries}, avg:{avg.to_precision(1)}, min:{min}, max:{max}\n" +
+                       "history:{delays.sum}, avg:{delays.avg}, min:{delays[delays.min.as(not null)]}, max:{delays[delays.max.as(not null)]}"
+       end
 end
 
 # Game related event
@@ -117,20 +147,20 @@ end
 # Game logic on the client
 class ThinGame
 
-       # Game tick when `self` should act.
+       # Current game tick
        #
        # Default is 0.
-       var tick: Int = 0 is protected writable
+       var tick: Int = 0 is writable
 end
 
 # Game turn on the client
 class ThinGameTurn[G: ThinGame]
 
-       # Game tick when `self` should act.
+       # Game tick when happened this turn
        var tick: Int is protected writable
 
-       # List of game events occured for `self`.
-       var events = new List[GameEvent] is protected writable
+       # Game events occurred for `self`.
+       var events = new Array[GameEvent] is protected writable
 end
 
 # Game turn on the full logic
@@ -147,10 +177,18 @@ class GameTurn[G: Game]
        end
 
        # Insert the Bucketable event `e` to be executed at next tick.
-       fun act_next(e: Bucketable[G]) do game.buckets.add_at(e, tick + 1)
+       fun act_next(e: Bucketable[G])
+       do
+               game.buckets.add_at(e, tick + 1)
+               game.buckets.delays.inc(1)
+       end
 
        # Insert the Bucketable event `e` to be executed at tick `t`.
-       fun act_in(e: Bucketable[G], t: Int) do game.buckets.add_at(e, tick + t)
+       fun act_in(e: Bucketable[G], t: Int)
+       do
+               game.buckets.add_at(e, tick + t)
+               game.buckets.delays.inc(t)
+       end
 
        # Add and `apply` a game `event`.
        fun add_event( event : GameEvent )