public void CurrentAndFutureItems() { var source = new LifetimeSource(); var p1 = new Perishable<int>(1, source.Lifetime); var p2 = new Perishable<int>(1, Lifetime.Immortal); var p = new PerishableCollection<int>(); var li0 = new List<Perishable<int>>(); p.CurrentAndFutureItems().Subscribe(e => { li0.Add(e); e.Lifetime.WhenDead(() => li0.Remove(e)); }); li0.AssertSequenceEquals(); p.Add(p1); li0.AssertSequenceEquals(p1); var li1 = new List<Perishable<int>>(); p.CurrentAndFutureItems().Subscribe(e => { li1.Add(e); e.Lifetime.WhenDead(() => li1.Remove(e)); }); li1.AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); li0.AssertSequenceEquals(p1, p2); li1.AssertSequenceEquals(p1, p2); source.EndLifetime(); li0.AssertSequenceEquals(p2); li1.AssertSequenceEquals(p2); }
public void CurrentAndFutureItems() { var source = new LifetimeSource(); var p1 = new Perishable <int>(1, source.Lifetime); var p2 = new Perishable <int>(1, Lifetime.Immortal); var p = new PerishableCollection <int>(); var li0 = new List <Perishable <int> >(); p.CurrentAndFutureItems().Subscribe(e => { li0.Add(e); e.Lifetime.WhenDead(() => li0.Remove(e)); }); li0.AssertSequenceEquals(); p.Add(p1); li0.AssertSequenceEquals(p1); var li1 = new List <Perishable <int> >(); p.CurrentAndFutureItems().Subscribe(e => { li1.Add(e); e.Lifetime.WhenDead(() => li1.Remove(e)); }); li1.AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); li0.AssertSequenceEquals(p1, p2); li1.AssertSequenceEquals(p1, p2); source.EndLifetime(); li0.AssertSequenceEquals(p2); li1.AssertSequenceEquals(p2); }
///<summary>Handles periodically adding children to existing balls.</summary> public static void SetupPeriodicChildSpawning(this Game game, double expectedPerBallPerSecond, int maxChildrenPerBall, int maxGeneration) { game.Balls.CurrentAndFutureItems().Subscribe( ball => { if (ball.Value.Generation > maxGeneration) { return; } // keep track of children var curChildCount = 0; var children = new PerishableCollection <Ball>(); children .CurrentAndFutureItems() .ObserveNonPerishedCount(completeWhenSourceCompletes: true) .Subscribe(e => curChildCount = e, ball.Lifetime); // spawn children periodically at random game.StochasticRate( expectedPerBallPerSecond, ball.Lifetime, () => { if (curChildCount >= maxChildrenPerBall) { return; } var child = game.SpawnBall(parent: ball.Value); game.Connectors.Add(new Connector { Child = child, Parent = ball.Value }, child.Life.Lifetime); children.Add(child, child.Life.Lifetime); }); }, game.Life); }
///<summary>Handles periodically adding children to existing balls.</summary> public static void SetupPeriodicChildSpawning(this Game game, double expectedPerBallPerSecond, int maxChildrenPerBall, int maxGeneration) { game.Balls.CurrentAndFutureItems().Subscribe( ball => { if (ball.Value.Generation > maxGeneration) return; // keep track of children var curChildCount = 0; var children = new PerishableCollection<Ball>(); children .CurrentAndFutureItems() .ObserveNonPerishedCount(completeWhenSourceCompletes: true) .Subscribe(e => curChildCount = e, ball.Lifetime); // spawn children periodically at random game.StochasticRate( expectedPerBallPerSecond, ball.Lifetime, () => { if (curChildCount >= maxChildrenPerBall) return; var child = game.SpawnBall(parent: ball.Value); game.Connectors.Add(new Connector {Child = child, Parent = ball.Value}, child.Life.Lifetime); children.Add(child, child.Life.Lifetime); }); }, game.Life); }
public void CurrentAndFutureItems_EndingDuringEnumerationDoesNotLockup() { var s = new LifetimeSource(); var p = new PerishableCollection<int>(); for (var i = 0; i < 1000; i++) { p.Add(i, Lifetime.Immortal); } var t = Task.Factory.StartNew( () => p.CurrentAndFutureItems().Subscribe( item => s.EndLifetime(), s.Lifetime)); t.Wait(TimeSpan.FromMilliseconds(100)); t.AssertRanToCompletion(); }
public void ToPerishableCollection() { var source = new LifetimeSource(); var p1 = new Perishable<int>(1, source.Lifetime); var p2 = new Perishable<int>(1, Lifetime.Immortal); var p = new PerishableCollection<int>(); var q = p.CurrentAndFutureItems().ToPerishableCollection(); q.CurrentItems().AssertSequenceEquals(); p.Add(p1); q.CurrentItems().AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); q.CurrentItems().AssertSequenceEquals(p1, p2); source.EndLifetime(); q.CurrentItems().AssertSequenceEquals(p2); }
public void CurrentAndFutureItems_EndingDuringEnumerationDoesNotLockup() { var s = new LifetimeSource(); var p = new PerishableCollection <int>(); for (var i = 0; i < 1000; i++) { p.Add(i, Lifetime.Immortal); } var t = Task.Factory.StartNew( () => p.CurrentAndFutureItems().Subscribe( item => s.EndLifetime(), s.Lifetime)); t.Wait(TimeSpan.FromMilliseconds(100)); t.AssertRanToCompletion(); }
public void ToPerishableCollection() { var source = new LifetimeSource(); var p1 = new Perishable <int>(1, source.Lifetime); var p2 = new Perishable <int>(1, Lifetime.Immortal); var p = new PerishableCollection <int>(); var q = p.CurrentAndFutureItems().ToPerishableCollection(); q.CurrentItems().AssertSequenceEquals(); p.Add(p1); q.CurrentItems().AssertSequenceEquals(p1); p.Add(p2.Value, p2.Lifetime); q.CurrentItems().AssertSequenceEquals(p1, p2); source.EndLifetime(); q.CurrentItems().AssertSequenceEquals(p2); }
public void IsObservationThreadSafe() { foreach (var repeat in Enumerable.Range(0, 5)) { var n = 5000; var nt = 4; var p = new PerishableCollection <int>(); var queues = Enumerable.Range(0, 4).Select(e => new Queue <LifetimeSource>()).ToArray(); var li = new List <int>(); p.CurrentAndFutureItems().Subscribe( e => { lock (li) li.Add(e.Value); e.Lifetime.WhenDead(() => { lock (li) li.Remove(e.Value); }); }); TestUtil.ConcurrencyTest( threadCount: nt, callbackCount: n, repeatedWork: (t, i) => { var r = new LifetimeSource(); p.Add(i, r.Lifetime); if (i % 2 == 0) { queues[t].Enqueue(r); } if (queues[t].Count > 20) { queues[t].Dequeue().EndLifetime(); } }, finalWork: t => { while (queues[t].Count > 0) { queues[t].Dequeue().EndLifetime(); } }); var expected = Enumerable.Range(0, n).Where(e => e % 2 != 0).SelectMany(e => Enumerable.Repeat(e, nt)); li.OrderBy(e => e).AssertSequenceEquals(expected); } }
private void SetupAndRunGame(Game game, bool initial) { // controls added to this collection should be displayed on the canvas until they perish var controls = new PerishableCollection <UIElement>(); controls.CurrentAndFutureItems().Subscribe( e => { canvas.Children.Add(e.Value); e.Lifetime.WhenDead(() => canvas.Children.Remove(e.Value)); }, game.Life); // balls should move and bounce off borders game.SetupMoveAndBounceBalls( playArea: () => new Rect(0, 0, canvas.ActualWidth, canvas.ActualHeight)); // connected balls should be be gently tugged towards each other game.SetupAttractBalls( deadRadius: 50, accellerationPerSecondChild: 10, accellerationPerSecondParent: 5); // balls should periodically spawn dependent children game.SetupPeriodicChildSpawning( expectedPerBallPerSecond: 0.2, maxChildrenPerBall: 2, maxGeneration: 5); // balls should be drawn using ellipse controls and have death animations game.SetupDrawBallsAsControls( controls, deathFadeOutDuration: 800.Milliseconds(), deathFinalRadiusFactor: 3); // ball connectors should be drawn using line controls and have cut and death animations game.SetupDrawConnectorsAsControls( controls, deathFadeDuration: 800.Milliseconds(), deathFinalThicknessFactor: 6, propagateBangColor: Colors.Green, propagateBangDuration: 400.Milliseconds(), propagateBangRotationsPerSecond: 3, propagateBangMaxRadius: 10, cutBangColor: Colors.Red, cutBangDuration: 400.Milliseconds(), cutBangMaxRadius: 15); // connectors that touch the cursor should die SetupMouseCutter(game, controls); // text displays of game state should track that state if (!initial) { SetupEnergyAndTime( game, energyLossForCutting: 2.5, energyGainPerConnectorBroken: 1); } // there should be a few root balls to start with foreach (var repeat in 5.Range()) { game.SpawnBall(parent: new Ball { Pos = new Point(game.Rng.NextDouble() * canvas.ActualWidth, game.Rng.NextDouble() * canvas.ActualHeight), Radius = 10, Life = new LifetimeSource(), Hue = game.Rng.NextDouble() * 3 }); } // run the game loop until the game is over game.Loop().ContinueWith(e => { // exceptions? }); }
public void IsObservationThreadSafe() { foreach (var repeat in Enumerable.Range(0, 5)) { var n = 5000; var nt = 4; var p = new PerishableCollection<int>(); var queues = Enumerable.Range(0, 4).Select(e => new Queue<LifetimeSource>()).ToArray(); var li = new List<int>(); p.CurrentAndFutureItems().Subscribe( e => { lock (li) li.Add(e.Value); e.Lifetime.WhenDead(() => { lock (li) li.Remove(e.Value); }); }); TestUtil.ConcurrencyTest( threadCount: nt, callbackCount: n, repeatedWork: (t, i) => { var r = new LifetimeSource(); p.Add(i, r.Lifetime); if (i % 2 == 0) queues[t].Enqueue(r); if (queues[t].Count > 20) queues[t].Dequeue().EndLifetime(); }, finalWork: t => { while (queues[t].Count > 0) queues[t].Dequeue().EndLifetime(); }); var expected = Enumerable.Range(0, n).Where(e => e % 2 != 0).SelectMany(e => Enumerable.Repeat(e, nt)); li.OrderBy(e => e).AssertSequenceEquals(expected); } }
private void SetupAndRunGame(Game game, bool initial) { // controls added to this collection should be displayed on the canvas until they perish var controls = new PerishableCollection<UIElement>(); controls.CurrentAndFutureItems().Subscribe( e => { canvas.Children.Add(e.Value); e.Lifetime.WhenDead(() => canvas.Children.Remove(e.Value)); }, game.Life); // balls should move and bounce off borders game.SetupMoveAndBounceBalls( playArea: () => new Rect(0, 0, canvas.ActualWidth, canvas.ActualHeight)); // connected balls should be be gently tugged towards each other game.SetupAttractBalls( deadRadius: 50, accellerationPerSecondChild: 10, accellerationPerSecondParent: 5); // balls should periodically spawn dependent children game.SetupPeriodicChildSpawning( expectedPerBallPerSecond: 0.2, maxChildrenPerBall: 2, maxGeneration: 5); // balls should be drawn using ellipse controls and have death animations game.SetupDrawBallsAsControls( controls, deathFadeOutDuration: 800.Milliseconds(), deathFinalRadiusFactor: 3); // ball connectors should be drawn using line controls and have cut and death animations game.SetupDrawConnectorsAsControls( controls, deathFadeDuration: 800.Milliseconds(), deathFinalThicknessFactor: 6, propagateBangColor: Colors.Green, propagateBangDuration: 400.Milliseconds(), propagateBangRotationsPerSecond: 3, propagateBangMaxRadius: 10, cutBangColor: Colors.Red, cutBangDuration: 400.Milliseconds(), cutBangMaxRadius: 15); // connectors that touch the cursor should die SetupMouseCutter(game, controls); // text displays of game state should track that state if (!initial) SetupEnergyAndTime( game, energyLossForCutting: 2.5, energyGainPerConnectorBroken: 1); // there should be a few root balls to start with foreach (var repeat in 5.Range()) { game.SpawnBall(parent: new Ball { Pos = new Point(game.Rng.NextDouble()*canvas.ActualWidth, game.Rng.NextDouble()*canvas.ActualHeight), Radius = 10, Life = new LifetimeSource(), Hue = game.Rng.NextDouble()*3 }); } // run the game loop until the game is over game.Loop().ContinueWith(e => { // exceptions? }); }