// Copyright Sebastian Jeckel 2014. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #pragma once #include #include #include #include #include #include "BenchmarkBase.h" #include "react/ReactiveObject.h" #include "react/logging/EventLog.h" using namespace react; using std::vector; using std::atomic; using std::tuple; using std::unique_ptr; using std::move; using std::make_tuple; using std::cout; using std::string; using std::pair; using std::make_pair; using std::get; enum class Seasons { summer, winter }; enum class Migration { enter, leave }; typedef pair PositionT; template class Time : public ReactiveObject { public: EventSourceT NewDay = MakeEventSource(); SignalT TotalDays = Iterate(0, NewDay, Incrementer()); SignalT DayOfYear = TotalDays % 365; SignalT Season = DayOfYear ->* [] (int day) { return day < 180 ? Seasons::winter : Seasons::summer; }; }; template class Region : public ReactiveObject { Time& theTime; public: using BoundsT = tuple; BoundsT Bounds; EventSourceT EnterOrLeave = MakeEventSource(); SignalT AnimalCount = Fold(0, EnterOrLeave, [] (int count, Migration m) { return m == Migration::enter ? count + 1 : count - 1; }); SignalT FoodPerDay = theTime.Season ->* [] (Seasons season) { return season == Seasons::summer ? 20 : 10; }; SignalT FoodOutputPerDay = (FoodPerDay, AnimalCount) ->* [] (int food, int count) { return count > 0 ? food/count : 0; }; EventsT FoodOutput = Pulse(FoodOutputPerDay, theTime.NewDay); Region(Time& time, int x, int y): theTime { time }, Bounds { make_tuple(x*10, x*10+9, y*10, y*10+9) } { } PositionT Center() { return make_pair(get<0>(Bounds) + 5, get<2>(Bounds) + 5); } PositionT Clamp(PositionT pos) { pos.first = get<0>(Bounds) + (abs(pos.first) % 10); pos.second = get<2>(Bounds) + (abs(pos.second) % 10); return pos; } bool IsInRegion(PositionT pos) { return get<0>(Bounds) <= pos.first && pos.first <= get<1>(Bounds) && get<2>(Bounds) <= pos.second && pos.second <= get<3>(Bounds); } }; template class World : public ReactiveObject { int w_; public: vector>> Regions; World(Time& time, int w) : w_( w ) { for (int x=0; x>(new Region(time, x, y))); } Region* GetRegion(PositionT pos) { for (auto& r : Regions) { if (r->IsInRegion(pos)) return r.get(); } printf("FATAL ERROR %d %d\n", pos.first, pos.second); return nullptr; } PositionT Clamp(PositionT pos) { pos.first = abs(pos.first) % 10*w_; pos.second = abs(pos.second) % 10*w_; return pos; } }; template class Animal : public ReactiveObject { Time& theTime; World& theWorld; std::mt19937 generator; public: SignalT Position; VarSignalT*> CurrentRegion; SignalT*> NewRegion; Animal(Time& time, World& world, Region* initRegion, unsigned seed) : theTime( time ), theWorld( world ), CurrentRegion( MakeVar(initRegion) ), generator( seed ) { Position = Fold(initRegion->Center(), Moving, [this] (PositionT position, bool shouldMigrate) { std::uniform_int_distribution dist(-1,1); // Wander randomly for (int i=0; i<100; i++) { position.first += dist(generator); position.second += dist(generator); } if (shouldMigrate) return theWorld.Clamp(position); else return CurrentRegion.Value()->Clamp(position); }); NewRegion = (Position) ->* [this] (PositionT pos) { return theWorld.GetRegion(pos); }; initRegion->EnterOrLeave << Migration::enter; Observe(NewRegion, [this] (Region* newRegion) { CurrentRegion.Value()->EnterOrLeave << Migration::leave; newRegion->EnterOrLeave << Migration::enter; CurrentRegion <<= newRegion; //Migrating << true; }); } EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); SignalT Age = Iterate(0, theTime.NewDay, Incrementer()); SignalT Health = Fold(100, FoodReceived, [] (int health, int food) { auto newHealth = health + food - 10; return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; }); //Signal YoungAndHealthy = Age < 1000 && Health > 0; //Event Migrating = EventSource(); SignalT ShouldMigrate = Hold(0, FoodReceived) < 10; EventsT Moving = Pulse(ShouldMigrate, theTime.NewDay); }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run simulation /////////////////////////////////////////////////////////////////////////////////////////////////// struct BenchmarkParams_LifeSim { BenchmarkParams_LifeSim(int n, int w, int k) : N( n ), W( w ), K( k ) { } const int N; const int W; const int K; void Print(std::ostream& out) const { out << "N = " << N << ", K = " << K << ", W = " << W; } }; template struct Benchmark_LifeSim : public BenchmarkBase { double Run(const BenchmarkParams_LifeSim& params) { auto theTime = Time(); auto theWorld = World(theTime, params.W); auto animals = vector>>(); std::mt19937 gen(2015); std::uniform_int_distribution dist(0,theWorld.Regions.size()-1); for (int i=0; i>(new Animal(theTime, theWorld, r, i+1))); } atomic c(0); int s = 0; //for (const auto& e : animals) //{ // Observe(e->Migrating, [&c] (bool _) { // c++; // }); //} //Observe(theEnvironment.SummerIsComing, [] (int v) { // printf("=== SUMMER ===\n"); //}); //Observe(theEnvironment.WinterIsComing, [] (int v) { // printf("=== WINTER ===\n"); //}); // WHEEL IN THE SKY KEEPS ON TURNING auto t0 = tbb::tick_count::now(); for (int i=0; i