I'm working on a leaderboard for a game I am making.
In this code I create a list called 'players', it is a list of
objects from a custom class called 'Player'.
The code then reads all of the highscores from a file (which has
already been created), line by line. (Each line takes the format
of "Name Age Gender Score").
For each line it splits up the text into 4 parts and assigns each
of those parts to a variable.
Then it adds these variables to the list 'players'.
The problem I am having is that all of the items in the list end up with the same values.(The values on the last line of my highscores file). Any ideas how to fix this?
Here is the code:
private void LeaderboardScreen_Load(object sender, EventArgs e)
{
string name = "error";
string age = "error";
string gender = "error";
int score = 0;
List<Player> players = new List<Player>();
using (var fileStream = File.OpenRead(".\\InputInfo.bin"))
using (var streamReader = new StreamReader(fileStream, true))
{
string line;
line = streamReader.ReadLine();
}
foreach (var line in File.ReadLines(".\\InputInfo.bin")) // loop for every line in the inputinfo file
{
string[] words = line.Split(); //Splits the line into seperate words, and puts them into an array called 'words'
name = words[0]; // Makes the first word the name
age = words[1]; //Makes the second word the age
gender = words[2];//Makes the third word the gender
score = Convert.ToInt32(words[3]);//Makes the forth word the score
players.Add(new Player(name,age,gender,score)); **//This is where the problem is I think**
}
players = players.OrderByDescending(i => score).ToList(); //sorts list of players into descending order
}
Also if it helps at all here is the class:
public class Player
{
public static int score = 0;
public static string name;
public static string age;
public static string gender;
public Player(string aName, string aAge, string aGender, int aScore)
{
name = aName;
age = aAge;
gender = aGender;
score = aScore;
}
}
Remove static. It signifies that your member is shared by all instances of the class, but you actually want them per-instance.
It would help if you had included the file "InputInfo.bin". Could you please post that?
also you need to change this line:
players.Add(new Player(name,age,gender,score)); //This is where the problem is I think
to this:
players.Add(new Player(name,age,gender,score)); //** This is where the problem is I think**
It will not compile until the // are in front of the **
Related
I have a .txt file that has data in it on multiple likes, for example:
Line 1 has - Name: Tom
Line 2 has - Score 1: 10
Line 3 has - Score 2: 5
Line 4 has - Score 3: 5
Line 5 has - Score 4: 7
Line 6 has - Score 5: 10
Line 7 has - Total Score: 37
Line 8 has - Name: Ben
Line 9 has - Score 1: 5
Line 10 has - Score 2: 10
Line 11 has - Score 3: 10
Line 12 has - Score 4: 0
Line 13 has - Score 5: 5
Line 14 has - Total Score: 30
What I am trying to do is select the line 1, (Name: Tom) and line 7 (Total Score: 37) and line 8 and line 14 from the .txt file. Is there any way I can select these specific lines in the .txt file so that I can display them in a text box?
The result I want is to be able to put the chosen lines into a text box. Any help/Suggestions would be greatly appreciated.
I would create a class to store the name and the score. This allows you to fill a list with score information.
class Player
{
public string Name { get; set; }
public int Score { get; set; }
public override string ToString()
{
return $"{Name,-15}: {Score,4}";
}
}
You can enumerate the lines and look if a line starts with specific strings.
const string path = #"C:\Users\Me\Documents\TheFile.txt";
var players = new List<Player>();
Player currentPlayer = null;
foreach (string line in File.ReadLines(path)) {
if (line.StartsWith("Name:")) {
// We have a new person. Create a `Player` object and add it to the list.
currentPlayer = new Player { Name = line.Substring(6) };
players.Add(currentPlayer);
} else if (line.StartsWith("Total Score:") &&
Int32.TryParse(line.Substring(13), out int score)) {
// We have a score number. Convert it to `int` and assign it to the current player.
currentPlayer.Score = score;
}
}
Note that Player is a reference type and that therefore currentPlayer references the same player object already in the list. This means that currentPlayer.Score = score; also changes the score of the player in the list.
If you override the ToString method of the Player class, this enables you to add players to a ListBox. The listbox will then automatically use ToString to display them. This is more flexible than textboxes, because you always need only one listbox, no matter how many players you have.
listBox1.DataSouce = players; // Displays all the players with their scores.
Parse data into classes. The you can get names and scores :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
StreamReader reader = new StreamReader(FILENAME);
string line = "";
Player newPlayer = null;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
string[] splitLine = line.Split(new char[] { ':' }).ToArray();
if (line.StartsWith("Name"))
{
newPlayer = new Player();
Player.players.Add(newPlayer);
newPlayer.name = splitLine[1].Trim();
}
else
{
if (line.StartsWith("Score"))
{
if (newPlayer.scores == null) newPlayer.scores = new List<int>();
newPlayer.scores.Add(int.Parse(splitLine[1]));
}
}
}
}
string[] playersNames = Player.players.Select(x => x.name).ToArray();
Console.WriteLine(string.Join(" , ", playersNames));
Console.ReadLine();
}
}
public class Player
{
public static List<Player> players = new List<Player>();
public string name { get; set; }
public List<int> scores { get; set; }
}
}
Good day I have some problem regarding selecting a random string from my string array I am currently developing a guessing word game.
this is my string array:
string[] movie = {"deadpool", "batmanvssuperman", "findingdory", "titanic", "suicidesquad", "lordoftherings", "harrypotter", "jurassicpark", "hungergames", "despicableme" };
while this is the process in selecting a random string to my array, what should i do next, because I want to select the string not repeated.
e.g
when the program starts it will select a string then when i select random string again i want to not select the previous word that i've already selected previously.
string word = movie[r.Next(0, movie.Length)].ToUpper();
Thank you for response! Have a nice day.
Well, simply convert your array to list and shuffle it in random order :
var rand = new Random();
string[] movies = { "deadpool", "batmanvssuperman", "findingdory", "titanic", "suicidesquad", "lordoftherings", "harrypotter", "jurassicpark", "hungergames", "despicableme" };
List<string> randomMovies = movies.ToList();
for (int i = 0; i < movies.Length / 2; i++)
{
var randNum = rand.Next(i, randomMovies.Count);
var temp = randomMovies[randNum];
randomMovies[randNum] = randomMovies[i];
randomMovies[i] = temp;
}
Then you can just take random elements by :
var randomMovie = randomMovies.First();
randomMovies.Remove(randomMovie); // either remove it or use loop to iterate through the list
I sort of like to use Queue collection here :
var moviesQueue = new Queue<string>(randomMovies);
while (moviewQueue.Count > 0)
{
Console.WriteLine(moviewQueue.Dequeue());
}
P.S.
As suggested you don't really need to delete elements from randomMovie, you can save last used index in some field and use it later;
var lastIndex = 0;
var randomMovie = randomMovies[lastIndex++];
Just loop if it's been selected. This is untested code:
private string _last;
private string GetNonRepeatedMovie()
{
string selected = "";
do
{
selected = movie[r.Next(0, movie.Length)].ToUpper();
}
while (selected == this._last);
this._last = selected;
return selected;
}
This should work to select the initial string as well when the application starts.
If you need to keep a memory, convert your list to be a class that contains the name and a field of whether it has been chosen or not.
If you go through all of them, turn this semiphor off and begin again.
class GuessingName
{
public GuessingName(string name){Name = name;}
public string Name;
public bool chosen;
}
class RandomNamePicker{
private List<GuessingName> names;
public RandomNamePicker(){
names = new List<GuessingName>();
names.Add(new GuessingName("movie"));
}
string RandomPicker(){
if(names.All(c=>c.chosen))
names.ForEach(c=>c.chosen=false);
int r1 = r.Next(0, names.Length);
while(names[r1].chosen){
r1= r.Next(0,names.Length);
}
return names[r1].Name;
}
}
I'm trying to sort a list in descending order of the 'score' variable. How would I do this?
This is the code I used to set up the list:
private void LeaderboardScreen_Load(object sender, EventArgs e)
{
//GETTING VARIABLES FOR THE CLASS OBJECT
using (var fileStream = File.OpenRead(".\\InputInfo.bin"))
using (var streamReader = new StreamReader(fileStream, true))
{
string line;
while ((line = streamReader.ReadLine()) != null) ;
}
var lines = File.ReadLines(".\\InputInfo.bin");
foreach (var line in lines)
{
string[] words = line.Split(); //Splits the line into seperate words, and puts them into an array called 'words'
string name = words[0]; // Makes the first word the name
string age = words[1]; //Makes the second word the age
string gender = words[2];//Makes the third word the gender
int score = Convert.ToInt32(words[3]);//Makes the forth word the score
//SETTING UP THE LIST
List<Player> players = new List<Player>();
players.Add(new Player(name, age, gender, score));
}
}
THANKS!
using System.Linq;
players = players.OrderByDescending(i => i.Score).ToList();
Since you seems a little newbie at linq, here's an "optimized version"
lines.Select(line =>
{
string[] words = line.Split(); //Splits the line into seperate words, and puts them into an array called 'words'
string name = words[0]; // Makes the first word the name
string age = words[1]; //Makes the second word the age
string gender = words[2];//Makes the third word the gender
int score = Convert.ToInt32(words[3]);//Makes the forth word the score
return new Player(name, age, gender, score);
}).OrderByDescending(i => i.Score).ToList();
It avoid two list instantiations, and two loops over the whole set.
You can simply use the OrderBy statement:
players = players.OrderBy(x => -x.Score).ToList();
By using a minus (-) - I assume score is a numerical value - you reverse the order.
You however made an error by constructing a new List<Player>(); each time in your foreach loop so the list won't store the previous items. You should construct the List<Player> before entering the foreach loop and ordering it after the foreach loop.
While Linq is syntactically shiny, it is a bit wasteful. The final .ToList() is creating a copy the list.
One of many non-Linq solutions is to pass a custom comparison function into Sort(...)
public void DoStuff()
{
List<Player> players = new List<Player>();
foreach (var line in lines)
{
// Fill your players list
}
players.Sort(ComparePlayersDescending);
}
public int ComparePlayersDescending(Player p1, Player p2)
{
int scoreDiff = p2.Score - p1.Score;
if (scoreDiff != 0)
return scoreDiff;
else
return p2.Name.CompareTo(p1.Name);
}
Just for my own curiosity I ran the Linq method and this older one and measured the memory allocated on a list of 50,000 simple player objects. You can either have efficiency or small code but not both :)
players.Sort() allocated 8,192 bytes.
players.OrderByDescending allocated 1,857,296 bytes.
GC.Collect();
long memCur = GC.GetTotalMemory(false);
//players = players.OrderByDescending(i => i.Score).ToList();
players.Sort(ComparePlayersDescending);
long memNow = GC.GetTotalMemory(false);
MessageBox.Show(string.Format("Total Memory: {0} {1}, diff {2}", memCur, memNow, memNow - memCur));
public class TEST
{
static void Main(string[] args)
{
string Name = "";
double Value = 0;
Array test1 = new Array(Name, Value);
for (int i = 0; i < 2; i++)
{
Console.WriteLine("Enter A Customer:");
Name = Console.ReadLine();
Console.WriteLine("Enter {0} Insurance Value (numbers only):", Name);
Value = Convert.ToDouble(Console.ReadLine());
}
test1.Display();
Console.ReadLine();
}
}
So in another class, I have my arrays. The way it is set up, it adds one user at a time to the arrays and adds that users corresponding number in another array.
The part I am having problems with is the main coding. I prompt the user for input and want it to call my other class method and fill the array up one user at a time. BUT, I am stuck.
I know why the code above isn't working, because the object call is only called once and so the initial value is the one that is saved. But when I put the new Array(Name, Value); in the for loop it tells me that test1.Display(); is an unassigned variable.
Is there a way I can solve this. I know there is probably another easier way using list or something, but I haven't gotten that far yet. If you could explain or hint or anything, I'd appreciate it. :)
It is better in this situation to use List<T>.
You have to create a class and after that you can create a List<T> to save items:
public class Customer
{
public string Name {get;set;}
public double Value {get;set;}
}
and :
static void Main(string[] args)
{
List<Customer> customers = new List<Customer>;
for (int i = 0; i < 2; i++)
{
Console.WriteLine("Enter A Customer:");
Customer customer = new Customer(); // create new object
customer.Name = Console.ReadLine(); // set name Property
Console.WriteLine("Enter {0} Insurance Value (numbers only):", Name);
customer.Value = Convert.ToDouble(Console.ReadLine());// set Value Property
customers.Add(customer); // add customer to List
}
Console.ReadLine();
}
I have a little problem which I have no clue how to solve. I will give the who;e code from the class so you can see what its doing:
{
class CompetitorDataFile
{
//collection of DataFileRecords - people
public List<DataFileRecord> DataFileRecords;
public string FileName = "";
//called when DataFile created
public CompetitorDataFile(string FileName)
{
//creates the collection
DataFileRecords = new List<DataFileRecord>();
this.FileName = FileName;
}
//fill DataFileRecords with stuff from text file
public void ReadDataFile()
{
foreach (string s in File.ReadAllLines(FileName))
{
if (s == "") continue;
DataFileRecord dfr = new DataFileRecord();
dfr.Name = s.Split(',')[0];
dfr.CPSA = s.Split(',')[1];
dfr.PostCode = s.Split(',')[2];
dfr.Rank = s.Split(',')[3];
dfr.Score1 = s.Split(',')[4]; //new for score system
dfr.Score2 = s.Split(',')[5]; //new for score system
dfr.Score3 = s.Split(',')[6]; //new for score system
dfr.Score4 = s.Split(',')[7]; //new for score system
dfr.Score5 = s.Split(',')[8]; //new for score system
dfr.Score6 = s.Split(',')[9]; //new for score system
dfr.Score7 = s.Split(',')[10]; //new for score system
dfr.Score8 = s.Split(',')[11]; //new for score system
dfr.TotalSingleScore = s.Split(',')[12]; //new for score system
DataFileRecords.Add(dfr);
}
}
public int FindByCPSA(string CPSA)
{
//set index to 0
int index = 0;
//go through each record looking for CPSA number match
foreach (DataFileRecord dfr in DataFileRecords)
{
if (dfr.CPSA.ToLower() == CPSA.ToLower())
{
//if it's found return the current index
return index;
}
//increase index and move to next record
index++;
}
//not found returning -1
return -1;
}
//save DataFile records to text file
public void SaveDataFile()
{
//delete backup file if found
if (File.Exists(FileName+".bck"))
File.Delete(FileName+".bck");
//make backup of existing
if (File.Exists(FileName))
File.Move(FileName, FileName + ".bck");
//create a temporary array of string
List<string> stringy = new List<string>(DataFileRecords.Count);
//go through each DataFile record and create a single string line
foreach (DataFileRecord dfr in DataFileRecords)
stringy.Add(dfr.SingleString);
//saves all strings to file
File.WriteAllLines(FileName, stringy.ToArray());
}
}
//a single record - one person
public class DataFileRecord
{
public string Name;
public string CPSA;
public string PostCode;
public string Rank;
public string Score1; //new for score system
public string Score2; //new for score system
public string Score3; //new for score system
public string Score4; //new for score system
public string Score5; //new for score system
public string Score6; //new for score system
public string Score7; //new for score system
public string Score8; //new for score system
public Int64 TotalSingleScore; // used to get total score for one user
public string SingleString
{
get
{
return string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11}", Name, CPSA, PostCode, Rank, Score1, Score2, Score3, Score4, Score5, Score6, Score7, Score8); //,{4} and , Score are new for score system
}
}
public string PoshSingleString
{
get
{
return string.Format("{0,-20}|{1,-10}|{2,-9}|{3,-9}|{4,2}|{5,2}|{6,2}|{7,2}|{8,2}|{9,2}|{10,2}|{11,2}", Name, CPSA, PostCode, Rank, Score1, Score2, Score3, Score4, Score5, Score6, Score7, Score8);
//return SingleString.Replace(",0", ""); //may be used to replace 0
//return SingleString.Replace(",", ", ");
}
I have a problem with this line of code
dfr.TotalSingleScore = s.Split(',')[12];
TotalSingleScore is the only int (all others are strings) as a result I get this error:
"Error 1 Cannot implicitly convert type 'string' to 'long'"
How do I go about solving this? Code is C# and software is VS 2008 Pro
Thanks
use long.Parse or long.TryParse.
Since TotalSingleScore is declared as Int64, you have to use Int64.Parse.
Sample: dfr.TotalSingleScore = Int64.Parse(s.Split(',')[12]);
dfr.TotalSingleScore = Int64.Parse(s.Split(',')[12]);
Int64.TryParse()
Take your string and convert it. Like others stated the TryParse or Parse methods work great if the data might not be the casting type. But you can also do it this way using System.Convert.
dfr.TotalSingleScore = Convert.ToInt64(s.Split(',')[12]);
http://msdn.microsoft.com/en-us/library/system.convert.toint64.aspx
Just curious, why are you splitting the string everytime? Wouldn't it be better if you just split it once and reuse the array?
var myArray = s.split(',');
dfr.Name = myArray[0];
dfr.CPSA = myArray[1];
dfr.PostCode = myArray[2];
dfr.Rank = myArray[3];
dfr.Score1 = myArray[4]; //new for score system
dfr.Score2 = myArray[5]; //new for score system
dfr.Score3 = myArray[6]; //new for score system
dfr.Score4 = myArray[7]; //new for score system
dfr.Score5 = myArray[8]; //new for score system
dfr.Score6 = myArray[9]; //new for score system
dfr.Score7 = myArray[10]; //new for score system
dfr.Score8 = myArray[11]; //new for score system
:
etc
Not an answer for your question, but I thought I had to suggest this.