I use the Fisher–Yates shuffle for my music-player, however when you play the previous song it's also a random song from the list. So, I added a list that saves the played songs PlayedSongs. It remembers all songs that were played. How do I play these songs backwards and forwards? If I would go back 3 songs, and then go forward again, it would still be the song that was on number 2. Yet thinking of this algorithm gives me a headache and I can't get it right. Here's what I have.
Again to be clear: I want a shuffle that can play forwards and backwards. When playing backwards all the songs had to be played recently in order. (It's temporary so this list gets cleared on exit).
This if-statement is from another method that normally plays songs and checks if Shuffle is enabled:
//after song was played
if (ShuffleIsOn)
{
ShuffledSongIndexes.Remove(ShuffledSongIndexes.FirstOrDefault());
PlayedSongs.Add(song);
}
Then:
public List<Song> PlayedSongs = new List<Song>();
public int CurrentShuffleIndex = 0;
public void PlayPreviousSongs()
{
PlayedSongs.Reverse();
CurrentShuffleIndex++;
try
{
MediaElement.Source = new Uri(PlayedSongs[CurrentShuffleIndex].FullFileName);
}
catch (ArgumentOutOfRangeException)
{
MediaElement.Source = new Uri(PlayedSongs[CurrentShuffleIndex - 1].FullFileName);
}
MediaElement.Play();
PlayedSongs.Reverse();
}
public void PlayNextSongs()
{
CurrentShuffleIndex++;
MediaElement.Source = new Uri(PlayedSongs[CurrentShuffleIndex].FullFileName);
MediaElement.Play();
}
I haven't found anything on the internet about this, does anyone know how to do this?
Thanks
There is no need to reverse the list. Add a bool like: PlayForward, and if it's true then index will increment by 1, otherwise decrement by 1, like:
CurrentIndex += (PlayForward ? 1 : -1)
Also, there is a way u can make ur life easier. Don't change the song whenever u change the index. Instead, combine the process like:
int _CurrentIndex;
public int CurrentIndex
{
get => _CurrentIndex;
set
{
if (value != _CurrentIndex)
{
_CurrentIndex = value;
MediaElement.Source = new Uri(PlayedSongs[CurrentIndex].FullFileName);
MediaElement.Play();
}
}
}
Don't forget to check the bounds too!
Related
Original Title: Get-Set to add Object w/multiple properties into a list C#
Edit: I had originally thought the issue was in setting up properties for the list objects, when it was an issue with regards to where I had initialized the list in my main code class.
Original Post:
New to coding, taking a C# course. We're working on encapsulation and get:set/properties.
The assignment says that we have to build a class that creates a die with an input number of sides, and "roll" the die for a random number. Easy!
In a second class, we have to build a function to add or remove any number of dice to the pool, and then roll them all for a result.
I'm assuming they want the dice pool to be a list that is private.
My logic going in was to create the single OneDie class, and then using a xDy notation in the main program prompt to add x number of die with y sides to the list. (ie: add 2d6)
I've built an AddDie function that should do that, but when I check my list count after it's done, the count is 0. The private list (_dicePool) seems to be re-setting to zero every time I try to add a new object to the list. I suspect I'm not building my property DicePool's get/set functionality correctly, but I'm not sure how to call my 2-parameter AddDice function from inside the DicePool{set}, or even if that's the approach I should take.
Assuming the list should be private, am I missing something to permanently add new objects to the list?
Edit to add: OR, would it be better to create a ManyDice object? But how do I build this.Sides and this.Roll from the OneDie object?
Here's my code that's applicable to adding objects (dice) to the list (dicepool).
class ManyDice
{
private List<OneDie> _dicePool = new List<OneDie>();
//What I think I might have to do:
public List<OneDie> DicePool
{
get
{
return this._dicePool;
}
set
{
//???????????? how do I call AddDice, when I need 2 parameters for it?
}
}
public void AddDie(int number, int sides)
{
for (int i = 0; i < number; i++)
{
this.dicePool.Add(new OneDie(sides));
}
}
}
class OneDie
{
private int _sides, _rolledValue;
public int Sides
{
get
{
return this._sides;
}
set
{
this._sides = value;
}
}
public int RollValue
{
get
{
return this._rolledValue;
}
set
{
this._rolledValue = value;
RollIt(value);
}
}
public OneDie()
{
}
public OneDie(int sides)
{
this.Sides = sides;
this.RollValue = sides;
}
private int RollIt (int sides)
{
Random random = new Random((int)DateTime.Now.Ticks);
return random.Next(1, (sides + 1));
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Let's roll some dice!");
Console.WriteLine("Please enter the number of dice you want to roll in the following format:");
Console.WriteLine("xdy, where \"x\" is the number of dice you want and \"y\" is how many sides they have.");
Console.WriteLine("Example: 2d6 is 2 6-sided dice. Perfect for playing Catan! (or Monopoly)");
Console.WriteLine("Please limit your dice to d4, d6, d8, d10, d12, d20");
Console.WriteLine("To add a die, type \"add xdy\" to add x number of y sided dice.");
Console.WriteLine("To remove a die, type \"remove xdy.\" to remove x number of y sided dice.");
Console.WriteLine("Type \"dice\" to see a list of all the dice in the pile.");
Console.WriteLine("Type \"roll\" to roll all the dice and see the results!");
Console.WriteLine("Type \"clear\" to clear all the dice and start agin!");
Console.WriteLine("Type \"exit\" to exit program\n");
PlayDice();
Console.ReadKey();
}
static void PlayDice()
{
do
{
string[] xDy = null;
int numberOfDice = 1;
int numberOfSides=1;
Console.WriteLine("\nEnter your command:");
string option = Console.ReadLine();
option = option.ToLower().Trim();
string[] words = option.Split(' ');
string command = words[0];
//CheckCommand(command);
if (words.Length > 1)
{
xDy = words[1].Split('d');
numberOfDice = int.Parse(xDy[0]);
numberOfSides = int.Parse(xDy[1]);
}
ManyDice die = new ManyDice();
if (command == "exit")
{
Console.WriteLine("Thank you, play again, soon!");
break;
}
else if (command == "add")
{
//numberOfSides=CheckDice(numberOfSides);
die.AddDie(numberOfDice, numberOfSides);
Console.WriteLine("You have {0}ed {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "remove")
{
Console.WriteLine("You have {0}d {1} {2}-sided dice.", command, numberOfDice, numberOfSides);
}
else if (command == "dice")
{
Console.WriteLine("These are your dice:");
die.Display();
}
else if (command == "roll")
{
Console.WriteLine("Here is your roll:");
}
else if (command == "clear")
{
Console.WriteLine("All dice have been cleared.");
}
} while (true);
}
static int CheckDice(int sides)
{
List<int> check = new List<int> {4,6,8,10,12,20};
while (!check.Contains(sides))
{
Console.WriteLine("{0}-sided dice are not available.\nPlease enter 4,6,8,10,12 or 20");
sides = int.Parse(Console.ReadLine());
}
return sides;
}
static string CheckCommand(string instructions)
{
List<string> check = new List<string> { "add", "remove", "dice", "roll","clear", "exit" };
while (!check.Contains(instructions))
{
Console.WriteLine("Command not recognized.\nPlease enter \"add\", \"remove\", \"dice\", \"roll\",\"clear\", or \"exit\"");
instructions = Console.ReadLine();
}
return instructions;
}
}
New Answer based on comments and updated question:
The line ManyDice die = new ManyDice(); is wiping your dice list clean every loop through your program. It's replacing your variable with a new instance of the class, with a fresh list and all.
Simply move that line before the start of the loop:
before the line do {
and then every iteration will use the same instance of ManyDice, and will all share the variable die, without overwriting it.
OLD ANSWER: From what I can see, your program only runs once. And then you need to start it again to put in another dice. Your main function only asks for input once. Whenever you start the program again, all the memory used in the program gets cleared. Unless I’m missing something, that is why your list continues to be reset. You’re actually running a completely new program the next time you try to add dice. So it has no knowledge of the previous runs.
One solution is to say (pseudo code)
While (running) {
// what you have now
if (option == “done”) running = false;
if (option == “roll”) // roll all dice.
}
This will keep prompting the user for commands until they type done. And it remains the same program so that you don’t lose the data from earlier commands.
Update based on comment: you’re recreating the ManyDice instance on each iteration, effectively starting from scratch. Save the instance outside the while loop and then reuse it.
Hint:
Your rolling should probably be done by manyDice.RollAll() And maybe should return a List of RollResults.
So as part of my training I have to create a program which utilizes the Xbox Api. It works so that the user searches for a Gamer tag and it brings up all of that users recently uploaded game clips. You can then select a clip and it will play within the Windows Media Player control I added.
Now I am trying to set it to auto play the next video. How would I fill out the following method to autoplay the next item in the list keeping in mind that it is not looping through all the videos in the list, just from where the user selects from.
private void wmpClip_PlayStateChange(object sender,
AxWMPLib._WMPOCXEvents_PlayStateChangeEvent e)
{
if (wmpClip.playState == WMPLib.WMPPlayState.wmppsMediaEnded)
{
//play next video in list
}
}
So the following code is part of the search method when searching for clips:
foreach (var video in videos)
{
ctrlSearchResults searchResult = new ctrlSearchResults();
searchResult.SetDetails(video);
flpSearchResults.Controls.Add(searchResult);
searchResult.OnVideoSelected += SearchResult_OnVideoSelected;
//Limit to save api requests
if (flpSearchResults.Controls.Count == 3)
{
break;
}
}
Then when the user clicks on a video from the list on the Flow Layout Panel, the following code is run:
private void SearchResult_OnVideoSelected(Video obj)
{
wmpClip.Visible = true;
wmpClip.URL = obj.gameClipUris[0].uri;
pnlVideoInfo.Visible = true;
lblClipName.Visible = true;
lblActualLength.Text = obj.durationInSeconds.ToString();
lblActualSize.Text = (obj.gameClipUris[0].fileSize / 1024 / 1024).ToString() + "mb";
lblActualDate.Text = obj.datePublished.ToString();
lblActualGame.Text = obj.titleName;
lblActualViews.Text = obj.views.ToString();
lblActualRating.Text = obj.rating.ToString();
lblActualLikes.Text = obj.likeCount.ToString();
lblClipName.Text = obj.clipName;
GamerCard gamer = _clipsApi.GetGamerCardByXUID(obj.xuid.ToString());
pnlGamerInfo.Visible = true;
pbGamerPic.Load(gamer.gamerpicLargeSslImagePath);
lblGamerTag.Text = gamer.gamertag;
lblGamerScore.Text = gamer.gamerscore.ToString();
lblActualLocation.Text = gamer.location;
txtBio.Text = gamer.bio;
}
I hope this makes sense.
You should work on your separation of concerns: divide your big problem into smaller problems, and invent for every smaller problem a separate fairly independent solution. This makes the implementations easier to understand, easier to reust, test, change etc.
So let's separate your concerns!
Apparently you have something to fetch all your videos as a sequence:
IEnumerable<Video> GetAllVideos() {...}
How this is implemented is up to you. I assume you have no duplicates in this: every Video is either selected or not selected, you can not have the same video selected as well as non-selected.
Let's create a collection class for videos where you can select and unselect videos.
In fact: let's make it reusable: a generic collection class that contains objects that
can be selected and unselected:
public SelectableCollection<T>
// only if desired
: IReadonlyCollection<T>, IReadonlyList<T>, IEnumerable<T>
{
private readonly Dictionary<T, bool> allItems;
public SelectableCollection() : this(Enumerable.Empty<T>()) {}
public SelectableCollection(IEnumerable<T> collection) : this (collection, null) {}
public SelectableCollection(IEnumerable<T> source, IEqualityComparer<T> comparer = null)
{
// TODO: check source not null
if (comparer == null) comparer = EqualityComparer<T>.Default;
// initially nothing selected:
this.AllItems = source.ToDictionary(video => video, false, comparer);
}
Select and Unselect:
bool IsSelected(T item)
{
// TODO: decide what if item does not exist
return this.allItems[item];
}
bool Select(T item)
{
// TODO: decide what if item does not exist
this.allItems[item] = true;
}
bool UnSelect(T item)
{
// TODO: decide what if item does not exist
this.allItems[item] = false;
}
IEnumerable<T> SelectedItems => this.allItems
// Key is T, Value is boolean Selected
.Where(keyValuePair => keyValuePair.Value)
.Select(keyValuePair => keyValuePair.Key);
TODO: implement IReadOnlyCollection, etc. Use this.allItems.Keys to get all items.
Your forms class:
private SelectableCollection<Video> videos = new SelectableCollection(this.GetAllVideos());
Select and Unselect:
bool IsSelected(Video video)
{
return this.videos.IsSelected(video);
}
bool Select(Video video)
{
this.videos.Select(video);
}
bool UnSelect(Video video)
{
this.videos.UnSelect(video);
}
IEnumerable<Video> SelectedVideos => this.videos.SelectedItems;
Start / Stop / Continue playing Videos:
class VideoPlayer
{
private List<Video> VideosToPlay {get; set;}
private int IndexNextVideoToPlay {get; set;}
void StartPlayingVideos(IEnumerable<Video> videos)
{
this.VideosToPlay = this.SelectedVideos.ToList();
this.IndexNextVideoToPlay = 0;
}
private void PlayNextVideo()
{
if (this.IndexNextVideoToPlay < this.VideosToPlay.Count)
{
this.Play(this.VideosToPlay[this.IndexNextVideoToPlay];
++this.IndexNextVideoToPlay;
}
}
}
Examples:
private void OnButtonClicked(object sender, EventArgs e)
{
// user finished selecting videos in the listbox. Mark them as selected
IEnumerable<Video> selectedVideos = this.listBox1.SelectedItems.Cast<Video>();
// TODO: select all selectedVideos; Unselect all not-selected videos.
}
private void OnVideoFinished(object sender, EventArgs e)
{
this.VideoPlayer.PlayNextVideo();
}
Conclusion: divide your problem into small sub problems. Don't hesitate to create a class that solves your sub problem. Usually these classes will only have one small task and barely depend on other classes. Hence your class will be easy to understande, easy to create, easy to reuse, easy to test and maintain.
I'm extremely new to programming so bear with me.
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
GameObject newItem = Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
newItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
newItem.GetComponent<ButtonSetter>().item_count += 1;
newItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
//SEE CONTINUED POST
}
}
So, within the else statement, I want to basically say,
They got their first wood, so we created a panel to display information about the wood they have. Now I want to say, since we have already instantiated the displayPanel, we just want to work from that and adjust the object's variable that controls the integer part of "Wood: 0"
This should be
newItem.GetComponent<ButtonSetter>().item_count
But if I try accessing that within the else statement as shown:
newItem.GetComponent<ButtonSetter>().item_count += 1;
It tells me that newItem does not exist within the current context.
How can I Instantiate something under certain conditions, then after the condition was met, take the instantiated object and work with its script/variables?
Visual representation of my issue
I think you need to restructure your code a little bit.
So here's how I would do it
public class Item : MonoBehaviour {
public string itemName;
public int count;
}
I will create a base class Item for wood, iron or whatever is there in your game.
Next, I will Create a Handler Script, that keeps track of all Items that are clicked/not Clicked
Handler.cs
public class Handler : MonoBehaviour {
public Item itemPrefab;
public Dictionary<string, Item> displayItems= new Dictionary<string, Item>();
public void OnWoodClicked()
{
OnItemClicked("wood");
}
public void OnIronClicked()
{
OnItemClicked("iron");
}
private void OnItemClicked(string itemKey)
{
if (displayItems.ContainsKey(itemKey))
{
displayItems[itemKey].count++;
}
else
{
Item item = Instantiate(itemPrefab);
item.count = 1;
item.itemName=itemKey;
displayItems.Add(itemKey, item);
}
}
}
So To keep track of created items, I have created a dictionary public Dictionary<string, Item> displayItems;
In this script OnItemClicked(string itemKey) Method will check If Item of this type is already created or not. (By checking if that key exists)
If that item is not created then we will Instantiate new Item (Prefab of display Item you want to show) and add that to the dictionary according to its key.
But if it already exists, then simply access that object as displayItems[itemKey] as you like
So for example, if you click on Wood, you will get access to wood display item as displayItems["wood"].
I hope this helps.
Just place a comment if you want me to make it clearer.
As it's currently written, newItem only lives in the "IF" clause of the code, so the "ELSE" clause can't see it.
If you assign it to a class-level field (like it appears you have _hasWood assigned at the class level) then you will be able to access it in either the "IF" or "ELSE" blocks, and maintain the count for the life of the object "clickWood()" is called from.
private bool _hasWood;
private GameObject _woodItem;
public void clickWood()
{
//Check If The Player Has Gotten Wood Previously
if(_hasWood == false)
{
_woodItem= Instantiate(wood_prefab) as GameObject;
//Initial Displayed Name
_woodItem.GetComponent<ButtonSetter>().setName("WOOD: ");
//Starts with 0 Wood, set to 1
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
_woodItem.transform.SetParent(GameObject.Find("Content").transform,false);
//Got their first wood, should run else from now on
_hasWood = true;
}
else
{
_woodItem.GetComponent<ButtonSetter>().item_count += 1;
}
}
I have a list of songs in a combobox. I am playing the song based on the selection if user. Here is my code.
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
wplayer.controls.pause();
song = comboBox1.SelectedItem.ToString();
index1 = comboBox1.SelectedIndex;
pictureBox4.Image=Image.FromFile(Application.StartupPath + "\\Input\\zeneszek.gif");
pictureBox4.SizeMode=PictureBoxSizeMode.StretchImage;
pictureBox4.Visible=true;
wplayer.PlayStateChange += new WMPLib._WMPOCXEvents_PlayStateChangeEventHandler(wplayer_PlayStateChange);
wplayer.URL = Application.StartupPath + "\\Input\\" + song + ".mp3";
wplayer.controls.play();
WMPLib.WMPPlayState playstate = WMPLib.WMPPlayState.wmppsMediaEnded;
label4.Text = "Playing" + song;
}
public void wplayer_PlayStateChange(int newstate)
{
if (newstate == (int)WMPLib.WMPPlayState.wmppsMediaEnded)
{
playstate = WMPLib.WMPPlayState.wmppsMediaEnded;
song = comboBox1.Items[index1++].ToString();
}
}
After the end of the song I wanted to play the next song which is the next one in the combobox selection. Can someone help on this? I tried playstate change event also. Still I am not able to play the next song. I increased the index of the combobox when the play state is wmppsMediaEnded. When do I need to play that song? I tried playing the song when the state is wmppsStopped and wmmppsTransitioning but in both the cases I am able to play only two songs continuously. after the end of the 2nd song I am not able to play the remaining.
Use the PlayStateChangeEvent to check for newstate showing that the media has ended, then move the selectedindex + 1 and play the new song.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd564079%28v=vs.85%29.aspx
This also might be some help to you: http://stereoclood.codeplex.com/SourceControl/latest#SteroMood/MediaPlayers/WmpMediaPlayer.cs
Hi i'm trying to play a specifc song for example if i have a list with 100 songs i want to play the song #20
to do this i use the following code
private void playSong(int n)
{
IsolatedStorageSettings.ApplicationSettings["currentSong"] = n;
if (BackgroundAudioPlayer.Instance.PlayerState == PlayState.Playing)
{
BackgroundAudioPlayer.Instance.Close();
BackgroundAudioPlayer.Instance.Play();
}
else
BackgroundAudioPlayer.Instance.Play();
}
And then in my AudioPlayer.cs i get the song
//get the song
currentSong = (int)IsolatedStorageSettings.ApplicationSettings["currentSong"];
It works fine but it takes too much time to load the song.
so please is there any other way to do this?
thanks.