// 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 #include #include #include #include "BenchmarkBase.h" #include "react/Domain.h" #include "react/Signal.h" #include "react/Event.h" #include "react/logging/EventLog.h" using namespace react; using std::cout; using std::string; using std::unique_ptr; using std::vector; // FIXME: GCC doesn't like enum class in MakeEventSource enum { summer, winter }; enum { enter, leave }; template struct Incrementer { T operator()(Token,T v) const { return v+1; } }; using PositionT = std::pair; using BoundsT = std::tuple; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Time /////////////////////////////////////////////////////////////////////////////////////////////////// template class Time { public: USING_REACTIVE_DOMAIN(D) EventSourceT<> NewDay = MakeEventSource(); SignalT TotalDays = Iterate(NewDay, 0, Incrementer()); SignalT DayOfYear = TotalDays % 365; SignalT Season = MakeSignal( DayOfYear, [] (int day) -> int { return day < 180 ? winter : summer; }); }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Region /////////////////////////////////////////////////////////////////////////////////////////////////// template class Region { Time& theTime; public: USING_REACTIVE_DOMAIN(D) BoundsT Bounds; EventSourceT EnterOrLeave = MakeEventSource(); SignalT AnimalCount = Iterate( EnterOrLeave, 0, [] (int m, int count) { return m == enter ? count + 1 : count - 1; }); SignalT FoodPerDay = MakeSignal( theTime.Season, calculateFoodPerDay); SignalT FoodOutputPerDay = MakeSignal( With(FoodPerDay,AnimalCount), calculateFoodOutputPerDay); EventsT FoodOutput = Pulse(theTime.NewDay, FoodOutputPerDay); Region(Time& time, int x, int y) : theTime( time ), Bounds( x*10, x*10+9, y*10, y*10+9 ) {} PositionT Center() const { using std::get; return PositionT(get<0>(Bounds) + 5, get<2>(Bounds) + 5); } PositionT Clamp(PositionT pos) const { using std::get; pos.first = get<0>(Bounds) + (abs(pos.first) % 10); pos.second = get<2>(Bounds) + (abs(pos.second) % 10); return pos; } bool IsInRegion(PositionT pos) const { using std::get; return get<0>(Bounds) <= pos.first && pos.first <= get<1>(Bounds) && get<2>(Bounds) <= pos.second && pos.second <= get<3>(Bounds); } private: static int calculateFoodPerDay(int season) { return season == summer ? 20 : 10; } static int calculateFoodOutputPerDay(int food, int count) { return count > 0 ? food/count : 0; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// World /////////////////////////////////////////////////////////////////////////////////////////////////// template class World { public: USING_REACTIVE_DOMAIN(D) 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("Out of bounds %d %d\n", pos.first, pos.second); assert(false); return nullptr; } PositionT Clamp(PositionT pos) const { pos.first = abs(pos.first) % 10*w_; pos.second = abs(pos.second) % 10*w_; return pos; } private: int w_; }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Animal /////////////////////////////////////////////////////////////////////////////////////////////////// template class Animal { Time& theTime; World& theWorld; std::mt19937 generator; public: USING_REACTIVE_DOMAIN(D) VarSignalT*> CurrentRegion; EventsT FoodReceived = REACTIVE_PTR(CurrentRegion, FoodOutput); SignalT ShouldMigrate = Hold(FoodReceived, 0) < 10; EventsT Moving = Pulse(theTime.NewDay, ShouldMigrate); SignalT Position; SignalT*> NewRegion; EventsT*> RegionChanged = Monitor(NewRegion); SignalT Age = Iterate(theTime.NewDay, 0, Incrementer()); SignalT Health = Iterate(FoodReceived, 100, calculateHealth); Animal(Time& time, World& world, Region* initRegion, unsigned seed) : theTime( time ), theWorld( world ), generator( seed ), CurrentRegion( MakeVar(initRegion) ), Position ( Iterate(Moving, initRegion->Center(), With(CurrentRegion), [this] (bool moving, PositionT position, Region* region) { std::uniform_int_distribution dist(-1,1); // Wander randomly for (int i=0; i<100; i++) { position.first += dist(generator); position.second += dist(generator); } // Should migrate? if (moving) return theWorld.Clamp(position); else return region->Clamp(position); }) ), NewRegion ( MakeSignal(Position, [this] (PositionT pos) { return theWorld.GetRegion(pos); }) ), regionChangeContinuation_ ( MakeContinuation(RegionChanged, With(CurrentRegion), [this] (Region* newRegion, Region* oldRegion) { oldRegion->EnterOrLeave(leave); newRegion->EnterOrLeave(enter); // Change region in continuation CurrentRegion <<= newRegion; }) ) { initRegion->EnterOrLeave(enter); } private: Continuation regionChangeContinuation_; static int calculateHealth(int food, int health) { auto newHealth = health + food - 10; return newHealth < 0 ? 0 : newHealth > 10000 ? 10000 : newHealth; } }; /////////////////////////////////////////////////////////////////////////////////////////////////// /// Run simulation /////////////////////////////////////////////////////////////////////////////////////////////////// struct BenchmarkParams_LifeSim { BenchmarkParams_LifeSim(int n, int w, int k) : N( n ), W( w ), K( k ) {} void Print(std::ostream& out) const { out << "N = " << N << ", K = " << K << ", W = " << W; } const int N; const int W; const int K; }; template struct Benchmark_LifeSim : public BenchmarkBase { double Run(const BenchmarkParams_LifeSim& params) { Time theTime; World theWorld( theTime, params.W ); vector>> animals; std::mt19937 gen( 2015 ); std::uniform_int_distribution dist( 0u, theWorld.Regions.size()-1 ); for (int i=0; i>(new Animal(theTime, theWorld, r, i+1))); } // WHEEL IN THE SKY KEEPS ON TURNING auto t0 = tbb::tick_count::now(); for (int i=0; i