How to use Thread.Sleep to animate a Binary Tree? - c#

I've been working on a program that visually outputs the contents of a binary tree (represented in turn by classes that I wrote myself). The last feature I want to include in this program is an animation of the postorder, inorder, and preorder construction of the tree.
This has proven much more challenging than I thought. Here's the original Draw Method:
private void DrawNode(int x, int y, BinaryTreeNode<T> node, int nodeLevel, int maxDepth, int connectX = -1, int connectY = -1, )
{
//calculate distance between the node's children
int distance = CalculateDistance(nodeLevel, maxDepth);
//draw the node at the specified coordinate
node.Draw(x, y, this.device);
if (node.Left != null)
{
DrawNode(x - distance / 2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node);
}
if (node.Right != null)
{
DrawNode(x + distance / 2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node);
}
//connect the node to its parent
if ((connectX != -1) && (connectY != -1))
{
node.Connect(connectX, connectY, device);
}
this.display.Image = surface;
}
My original idea was to simply put Thread.Sleep(1000) inside each of the first two if clauses - all I really needed to do was pause the execution of the program for 1 second before each drawing of a node.
I realized that the Sleep method was blocking the execution of the drawing code, so I gave up on that method.. I then tried to use Timers, but found it impossibly difficult when dealing with the tree.
My goal is to simply find a way to pause program execution without disrupting the GUI's responsiveness and without overly complicating the code..
Any help would be appreciated :).
Edit: Some potentially relevant information: The program runs on Winforms, all graphics are handled via GDI+. If you need any other information, just ask :)
Edit: For SLaks,
//draw the node's children
if (drawChildren)
{
if (node.Left != null)
{
if (this.timer2.Enabled)
{
this.timer2.Stop();
}
if (!this.timer1.Enabled)
{
this.timer1.Start();
}
this.count1++;
this.timer1.Tick += (object source, EventArgs e) =>
{
this.count1--;
DrawNode(x - distance / 2, y + 50, node.Left, nodeLevel + 1, maxDepth, x, y, node);
if (this.count1 == 0)
{
this.timer1.Stop();
}
};
}
else
{
this.timer1.Stop();
this.timer2.Start();
}
if (node.Right != null)
{
this.count2++;
this.timer2.Tick += (object source, EventArgs e) =>
{
this.count2--;
DrawNode(x + distance / 2, y + 50, node.Right, nodeLevel + 1, maxDepth, x, y, node);
if (this.count2 == 0)
{
this.timer2.Stop();
}
};
}
}

Use a timer and set an appropriate interval for updates. In the Tick event perform the next step of drawing and display it.

First, write yourself a DrawNodesForLevel(int level) function. Then start at the top level, start the timer, every time it ticks, call DrawNodesForLevel() with the appropriate level, and increment the level. When you get to the end, stop the timer.
EDIT: Updated with the understanding that you want to pause between each node, not each level.
Move the variables in your function to their own DrawNodeState class, and pass the instance of that class whenever you call DrawNode(). Then, instead of having DrawNode() call itself, have DrawNode() start a timer (also part of the DrawNodeState class). When that timer ticks, the tick function calls DrawNode() and passes it the state structure.
The state structure is also going to have to track whether it last drew the left or right node, so it can draw the appropriate node next.

Split your code into 2 parts - one traversing the tree, another rendering (which you already have).
Rewrite your "traverse tree" code to be IEnumerable<node> so you can pick nodes one by one. There are non-recursive verions of tree traversal for any order, so you can use "yield return" to make iterator. You should be able to create simple tests to verify the code (no UI necessary for this).
Than in the timer callback simply take next item from the iterator till all done.

One possible solution would be to spawn a separate thread, then use that thread to invoke the DrawNode function at a periodic interval. This way the UI thread will not be blocked.
Since the thread you spawned will not be the UI thread, you will need to explicitly invoke the DrawNode function on the UI thread. Here's one potential way of doing that:
How to update the GUI from another thread in C#?

Related

Mouse Drag - C# bot

First time doing this. I am currently building a bot using C# and want my bot to be able to move the mouse to a given point in a way that looks human. By this I am referring to the dragging of the mouse when a human moves the cursor to a point they are trying to click on. Currently my C# bot moves the mouse instantly to the location which doesn't look human.
private static Point[] FindColor(Color color)
{
int searchValue = color.ToArgb();
List<Point> result = new List<Point>();
using (Bitmap bmp = GetScreenShot())
{
for (int x = 0; x < bmp.Width; x++)
{
for (int y = 0; y < bmp.Height; y++)
{
if (searchValue.Equals(bmp.GetPixel(x, y).ToArgb()))
result.Add(new Point(x, y));
}
}
}
return result.ToArray();
}
// FUNCTIONS OCCUR BELOW
// Error message if program could not find bitmap within screenshot show error message
Color myRgbColor = new Color(); // Creates new colour called myRgbColor
myRgbColor = Color.FromArgb(51, 90, 9); // This colour equals the RGB value
Point[] points = FindColor(myRgbColor); // Create an array called points which list all the points found in the screen where the RgB value matches.
if (points.Length > 0)
{
Cursor.Position = points[2]; // Move mouse cursor to first point (Point 0)
Thread.Sleep(0200);
MouseClick();
}
if (points.Length == 0)
{
MessageBox.Show("No matches!"); // Return error
goto checkore;
}
You're going to want to use some kind of Timer with a callback, to move the mouse incrementally, step by step. As for the movement itself, you have a world of possibilities, but it's all maths.
So, let's decompose the problem.
What is a natural mouse movement?
Position change rate
It doesn't necessarilly looks like it, but when you move your mouse, you're simply setting its position multiple times per seconds.
The amount of times the position changes per second is equivalent to the polling rate of your mouse. The default polling rate for USB mice is 125Hz (or 125 position changes per second, if you will). This is the value we'll use for our Timer: its callback will be called 125 times per second.
var timer = new Timer(1000 / 125d);
timer.Elapsed += MoveMouse;
void MoveMouse(object sender, ElpasedEventArgs e) { }
Speed and acceleration
When you move your mouse, the distance between two cursor positions is not constant, because you're fast when you start moving your mouse, but you slow down when you get close to the item you want your cursor to be on.
There are also two ways I personally usually move my mouse depending on the context/mood:
One fast uniform movement to get close to the destination, then one slow to correct and get on it (I'll usually go past the destination during the first move)
One medium-slow movement with a small deceleration, follow by a stronger deceleration at the end
The overall speed of the movement also depends on three factors:
The distance between your cursor and the destination
The size of the destination area
Your personal speed
I have absolutely NO IDEA how to work out the formula based on these factors, that's gonna be a work of trial and error for yourself.
This one is purely math and observation based, and will be tricky to get perfectly right, if ever; every person moves their mouse a different way.
The solution I can offer you is to simply forget about deceleration, correction and so on, and just divide your movement into equal steps. That has the merit of being simple.
using System;
using System.Timers;
using System.Drawing;
public class Program
{
static int stepCount = 0;
static int numberOfSteps = 0;
static float stepDistanceX = 0;
static float stepDistanceY = 0;
static PointF destinationPoint;
static Timer timer;
public static void Main()
{
int timerStepDurationMs = 1000 / 125;
PointF currentPoint = Cursor.Position;
destinationPoint = new PointF(2000, 1800); // or however you select your point
int movementDurationMs = new Random().Next(900, 1100); // roughly 1 second
int numberOfSteps = movementDurationMs / timerStepDurationMs;
stepDistanceX = (destinationPoint.X - currentPoint.X) / (float)numberOfSteps;
stepDistanceY = (destinationPoint.Y - currentPoint.Y) / (float)numberOfSteps;
timer = new Timer(timerStepDurationMs);
timer.Elapsed += MoveMouse;
timer.Start();
while (stepCount != numberOfSteps) { }
}
static void MoveMouse(object sender, ElapsedEventArgs e)
{
stepCount++;
if (stepCount == numberOfSteps)
{
Cursor.Position = destinationPoint;
timer.Stop();
}
Cursor.Position.X += stepDistanceX;
Cursor.Position.Y += stepDistanceY;
}
}
Note that I haven't tested with "Cursor", but with some PointF variable instead. It seems to work fine here: dotnetfiddle.

Improving UI performance for displaying dynamic real-time data

I'm currently working on a deep reinforcement learning implementation. To see how the training progresses, I created the UI seen below. The textbox and both charts are update each time at the end of a while-loop. This loops is run inside a thread, which simulates a slot machine and trains a neural network. The performance profiler indicates that 87% of CPU usage are consumed by the main thread (running the UI) and the rest is left for the simulation thread.
Does anybody know of a good approach to dramatically shrink down the cost of the UI?
private delegate void AppendChartCallback(Chart chart, double x, double y);
private void AppendChart(Chart chart, double x, double y)
{
if (chart.InvokeRequired)
{
AppendChartCallback d = new AppendChartCallback(AppendChart);
Invoke(d, new object[] { chart, x, y });
}
else
{
chart.Series[0].Points.AddXY(x, y);
if (chart.Series[0].Points.Count % 20 == 0)
{
chart.Refresh();
}
}
}
edit: I suspended the charts' updates and call individually refresh now as soon as some more amount of data is added (based on modulo).
I would not plot individual (x,y) points, you can bind to an array of values. There is an example here How To Create A Line Chart From Array Of Values?
Add the points to a list.
Have a timer invalidate the view every 16.66 ms.

Unity - Logic to pick up objects and drop them one by one at different positions

Please look at example - http://www.mathplayground.com/mancala.html
Can anyone suggest the logic to :
1) spawn objects at positions
2) Pick up all objects on click and distribute them one by one.
3) Is it better to create all objects or instantiate them on the fly. ?
I tried code below but it just instantiates all objects at once.
if (HoleHandler.gemCount_Hole1_Update_Flag == true)
{
foreach (GameObject g in gemList1)
{
Destroy(g);
//want to add a time delay of 2 secs here
}
if (gemCount_Hole1 > 0)
{
for (int i = 0; i < gemCount_Hole1; i++)
{
int Gem_prefabIndex = UnityEngine.Random.Range(0, 9);
gemList1.Add(Instantiate(Gem_prefabList[Gem_prefabIndex], new Vector2((xPos_Hole1 + (Random.Range(-20, 20))) * 2.0F, (-229 + (20 * i))), Quaternion.identity));
}
}
}
I'm not 100% sure of what you're trying to achieve but I will answer as best I can.
For a start, any gameobject you are going to be instantiating (spawning) at run time should ideally be done so from a prefab.
Secondly, to spawn them at random intervals you want to be checking if they should be spawned at different time frames. This can be achieved through a co-routine or the Update function. I would recommend Update if this is new to you.
Update is called every frame.. and it's with this that you can achieve timed events. You can use a variety of helper methods to determine the time since the last frame or the real time elapsed.
For example
public class MyGameObject : Monobehaviour {
void Start() {
//This is called first, use it to set up whatever you want.
}
void Update() {
//This will be called every frame.
//Each frame or time lapse will determine if I should spawn
// a new gameobject.
}
}
Update
After looking at the game you have linked in your post I can offer the following advice.
Something like the following may point you in the right direction.
public int[] gemsInCups = new int [] {4,4,4,4,4,4,0,4,4,4,4,4,4,0};
public void Distribute(int position){
int gems = gemsInCups[position];
for(int i = position + 1; gems > 0; i++){
gemsInCups[position] ++;
gems --;
//Check the end of the array has not been reached.
//If it has, start distributing again from the first position provided
// there are still gems to distribute.
}
}
You will need some additional logic to finish this.
What you should remember is, I usually find it much more manageable keeping my data and my view (gameobjects) under different scopes... but the view will change to reflect the data and does not directly alter it. Now you know how many gems there are in each cup, you can simply update this each frame.

Thread for my user control

I have a user control that shows a speed in a dial format (An image).
It has a single method: SetSpeed(int speed);
It then makes the dial move to the desired speed, from the last set speed. It does then in incriments. That is, it moves to the new speed.
Problem is, if the car is going 10km/h, and then goes (very quickly) to 100km/h, it takes the dial maybe 2 seconds to reach that speed... (It's not 100% real time - which is realistic .. it moves to the new speed).
But, if, before the dial gets to 100km/h, the person slows to 50km/h, I need to interupt the movement to 100, and start moving to 50. Regardless of the current position (not speed) of the dial, I need to change the 'target speed'.
My control is using a BackgroundWorker to handle the refreshing, and not lock the UI.
So, my call is very basic from the UI:
dial1.SetSpeed(int.Parse(value));
And then the user control does this:
BackgroundWorker bw = new BackgroundWorker();
public void SetSpeed(int speed)
{
while(bw.IsBusy)
{
Thread.Sleep(50);
}
bw.RunWorkerAsync(speed);
}
And the method that does the work:
private void UpdateSpeed(object sender, DoWorkEventArgs e)
{
var oldSpeed = airSpeed;
var newSpeed = (int) e.Argument;
if(oldSpeed <= newSpeed)
{
for (int i = oldSpeed; i < newSpeed; i++)
{
airSpeed++;
this.Invoke(new MethodInvoker(Refresh));
}
}
else
{
for (int i = oldSpeed; i > newSpeed; i--)
{
airSpeed--;
this.Invoke(new MethodInvoker(Refresh));
}
}
airSpeed = newSpeed;
}
It locks when I send it two values in quick succession...
How do I interrupt the thread, if it's running, and change the value?
(Also, I think my code to change the speed is bad - can that be made neater?)
You do not. You handle it in a proper way, with locks, and checking whether the value needs changing. You do NOT interrupt it.
Basically, you need a lock area between SetSpeed and the Refresh method, so that one blocks the other. Then, when you set speed and the thread is currently in a critical area, it simply waits until the update is finished.
And your UpdateSpeed makes no sense - the change (airspeed-- and airspeed++) should be timer driven... you currently change them in a "arbitrary" speed, depending on processor speed. No timing.

Implementing a method onto text in XNA

So I have started to mess around with XNA today and I am still learning C#. I am trying to produce a main menu for a game.
I have made a sprite font file and am producing the text I want. The code for this is:
spriteBatch.DrawString(font, ">://Start Game [1]", new Vector2(0, 0), Color.LimeGreen);
My question is that I have a method to make a typing effect from the "computer" (which I asked a question about a few days ago), but this is in C++. I have an idea how to convert it into C#, but even if I do convert the code correctly, how do I apply the method onto the text being created? Is their a more efficient way to print text in XNA?
The code for the typing effect in C++ is:
void typeOutput(string displayString){
for(int i = 0; i < displayString.length(); i++){
cout << displayString[i];
Sleep((rand() + 1)%typeSpeed);
}
}
There are various ways to do this which is discussed in this thread. One example from that thread is:
// our string will take 3 seconds to appear
private const float timerLength = 3f;
private float timer = 0f;
Then in your Draw method you add to the timer and use that to determine how much of the string to draw:
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
// if the timer is passed timerLength, we just draw the whole string
if (timer >= timerLength)
{
spriteBatch.DrawString(myFont, myString, stringPosition, stringColor);
}
// otherwise we want to just draw a substring
else
{
// figure out how many characters to show based on
// the ratio of the timer to the timerLength
int numCharsToShow = (int)(myString.Length * (timer / timerLength));
string strToDraw = myString.Substring(0, numCharsToShow);
// now just draw the substring instead
spriteBatch.DrawString(myFont, strToDraw, stringPosition, stringColor);
}

Categories

Resources