How to transfer data from a list to another list - c#

I'm trying to build a mission system which randomly pick 5 element from a list that store all the missions. But it seems I can't transfer the object from one list to another. Here is what I got so far.
void Awake()
{
Object[] getScriptableObject = Resources.LoadAll("Scriptable Objects/Missions/sideMission");
temp = new List<ScriptableObject>();
for (int i = 0; i < getScriptableObject.Length; i++)
{
temp.Add((ScriptableObject)getScriptableObject[i]);
}
if (missionContainer.mission.Count < 5)
{
missionContainer.mission.Add(temp[Random.Range(0, temp.Count)]);
}
else
return;
}
the temp List search every mission scriptable object in the folder and store them in the temp list, and I want the mission List to randomly pick 5 from the temp list and store them. What should I do?
public class missionContainer : ScriptableObject
{
public List<ScriptableObject> mission = new List<ScriptableObject>();
}
This is the mission list.

Actually you could use the generic typed version
// Or even better use the actual type of your ScriptableObject ;)
ScriptableObject[] getScriptableObject = Resources.LoadAll<ScriptableObject>("Scriptable Objects/Missions/sideMission");
and then simply do
temp = new List<ScriptableObject>(getScriptableObject);
And then it sounds like you rather wanted to either allow duplicate entries and use e.g.
// Note though that you could also simply directly use
// "getScriptableObject" array instead of "temp" in this case
while (missionContainer.mission.Count < 5)
{
missionContainer.mission.Add(temp[Random.Range(0, temp.Count)]);
}
or if you need unique entries do something like e.g.
while (missionContainer.mission.Count < 5)
{
if(temp.Count == 0)
{
Debug.LogWarning("Didn't find enough resources!", this);
break;
}
var random = Random.Range(0, temp.Count);
var item = temp[random];
missionContainer.mission.Add(item);
temp.Remove(item);
}
In case the missionContainer is actually also a ScriptableObject you might want to first call
missionContainer.mission.Clear();
in order to start with an empty list.

That would never add more than one mission. Don't you mean:
while (missionContainer.mission.Count < 5)
{
missionContainer.mission.Add(temp[Random.Range(0, temp.Count)]);
}
But you are not removing the mission from the temp list, so you might add it again.

Related

Making a simple change function for a database app C#

Sorry if this is an ultra beginner question but, I need to be able to change all field values except the item number by giving its item number not array index. I already have a add and delete feature.
using System;
struct ItemData
{
public int ItemNumber;
public string Description;
public double PricePerItem;
public int QuantityOnHand;
public double OurCostPerItem;
public double ValueOfItem;
}
class Program
{
public static void Main()
{
int invItems = 0;
var items = new ItemData[10];
while (true)
{
Console.Write("1. Add, 2. Change, 3. Delete, 4. List:");
string strx = Console.ReadLine();
var choice = int.Parse(strx);
Console.WriteLine();
switch (choice)
{
This is what I have so far but not sure where to start
case 2: //change items
{
Console.Write("Please enter an item ID No:");
string input = Console.ReadLine();
int changeItemNumber = int.Parse(input);
bool foundItem = false;
for (int x = 0; x < invItems; x++)
{
if (items[x].ItemNumber == changeItemNumber)
{
//code
}
}
if (!foundItem)
{
Console.WriteLine("Item {0} not found", changeItemNumber);
}
break;
}
It's still a little unclear what you're asking so if this is wide of the mark then feel free to let me know.
Start by mapping out the flow of the process and then implement one piece at a time. Here's a basic series of operations you'll want to do, not necessarily in this order:
Input ItemNumber.
Find index of matching record in array.
If not found report error and stop.
Get record from user.
Set ItemNumber in record to entered value.
Store record in array at same index as original.
If you think about the way the rest of the program works you should see that some of those parts - maybe most of them - are already done somewhere in your code. Your add operation probably already has the Get record from user code. Your delete operation probably has Input ItemNumber and Find index of matching record in array done already.
So pull those parts out into methods and reuse them where it makes sense. Anywhere you're writing the same code multiple times with minor changes try to see if you can extract the code into a small method you can call with maybe some parameters. Your "input a number" code for instance - used for operation selection, item number entry and presumably whatever you're doing for delete - can be pulled out to a supporting method like this one:
static int InputNumber(string prompt)
{
Console.Write($"{prompt}: ");
var inputText = Console.ReadLine();
return int.Parse(inputText);
}
Now when you want to change the way you're doing number input you have a central place to make the changes and you don't have to worry about tracking down all the places you wrote the same code.
The same goes for the rest of the pieces. When two operations share some of the same code then pull that code out to supporting methods. Finding the index of an item in the array by some criteria can - and should - be one of those:
// Return array index of matching item or -1 if not found
int ItemIndex(int itemNumber)
{
for (int i = 0; i < invItems; i++)
{
if (items[i].ItemNumber == itemNumber)
return i;
}
return -1;
}
Likewise the code - whatever that looks like - that your add operation uses to get a record from the user.
Once you do that your change item case looks a lot simpler:
case 2: // change items
var itemNumber = InputNumber("Please enter an item ID");
var index = ItemIndex(itemNumber);
if (index == -1)
{
Console.WriteLine($"Item {itemNumber} not found.");
}
else
{
var newItem = InputItemData();
newItem.ItemNumber = itemNumber;
items[index] = newItem;
}
break;
For bonus points you can actually provide the current values to the InputItemData method so the user can re-use them instead of just asking for a complete new record each time.

Convert user entered string into an object in c#

I have a battleship like terminal game, the user enters a coordinate like e2, and the program checks one of the instance variables of my object Box, it checks whether hasShip is true, if its true then it will make the coordinate e2 false, and give the output "Ship destroyed"
The problem is that all my objects are called a1,a2,a3,a4,a5,b1,b2 and so on.
I have created 25 instances of the Box class. All names as such.
Once the program gets input, either e4 ,e5 etc. I want to convert that string into an object.
For example( I want to do something like this )
target = Console.ReadLine();
target.hasShip == true;
I want to convert target into an object, then use target to use the methods of the Box class.
Because the other approach requires me to make loads of if statements, which isn't clean code, doesn't look good, and is a waste if you ask me.
Thanks in advance,
New Answer: use an Array
I am slow. I did not pay attention that you are making a battleship-like game, and that we know that the "boxes" make a rectangle. We can store this efficiently in an array.
Why I did not catch up to this fact earlier? I guess I need to wake up properly.
So, use an array:
var board = new Box[5, 5];
Now, to populate it, we can do a double for loop:
for(var indexRow = 0; indexRow < 5; indexRow++)
{
for(var indexCol = 0; indexCol < 5; indexCol++)
{
board[indexRow, indexCol] = new Box();
}
}
Note: pay attention that the indexes go from 0 to 4. For a total of 5 values: {0, 1, 2, 3, 5}.
And to query from it, we will need the indexes...
Addendum on populating the array
In comments, OP has said that each Box has an id and the ship positions are picked at random.
We can give the id in the loop:
for(var indexRow = 0; indexRow < 5; indexRow++)
{
for(var indexCol = 0; indexCol < 5; indexCol++)
{
var box = new Box();
box.vhID = (((char)(((int)'a') + indexRow))).ToString() + ((char)(((int)'1') + indexCol)).ToString();
board[indexRow, indexCol] = box;
}
}
What I am doing here is constructing the id from the indexes. Basically taking the value of 'a' and adding the indexRow will give us 'a' when indexRow is 0, 'b' when it is 1 and so on. Similarly, we get the digit that represents the column.
Note: We convert the char to int, do the addition, then convert back to char... and then from char to string. Once we have string, we can concatenate them.
I do not think we need this id. But, hey, you can do it like this.
OP also mentions that he will pick 4 ship positions at random. Fair enough:
var random = new Random();
for (var ships = 0; ships < 4; ships++)
{
board[random.Next(0, 4), random.Next(0, 4)].hasShip = true;
}
Since the user inputs an string, I suggest to create a function to convert it to the index pair:
var input = Console.ReadLine();
if (TryGetCoordinates(input, out int irow, out int icol))
{
var target = board[irow, icol];
}
else
{
Console.WriteLine("The cell {0} does not exist.", input);
}
// ...
bool TryGetCoordinates(string cell, out int indexRow, out int indexCol)
{
// ...
}
Start by validating null:
bool TryGetCoordinates(string cell, out int indexRow, out int indexCol)
{
indexRow = -1;
indexCol = -1;
if (cell == null)
{
return false;
}
// ...
}
Note: Feel free to use Trim, ToUpper or ToUpperInvariant.
We know that must be a letter followed by a digit, we can validate the length:
bool TryGetCoordinates(string cell, out int indexRow, out int indexCol)
{
indexRow = -1;
indexCol = -1;
if (cell == null)
{
return false;
}
if (cell.Length != 2)
{
return false;
}
// ...
}
We extract the characters and from them the coordinates. Noting that the first one is a letter, and the other a digit. We can also validate they are withing bounds.
bool TryGetCoordinates(string cell, out int indexRow, out int indexCol)
{
indexRow = -1;
indexCol = -1;
if (cell == null)
{
return false;
}
if (cell.Length != 2)
{
return false;
}
indexRow = (int)cell[0] - (int)'a';
indexCol = (int)cell[1] - (int)'1';
return indexRow < 5 && indexRow >= 0 && indexCol < 5 && indexCol >= 0;
}
And of course, you can do a loop of the validation similar to what was explained in the old answer.
Note: the issue with value types I describe in the old answer still applies with the array.
Old Answer: Use a Dictionary
I believe you do not want to convert the string to an object (the string is an object by the way), you want to pick the Box object you previously created based on the string. And you want to do it without using if statements. What you need is a dictionary.
So, you would have Dictionary<string, Box> meaning that it is a dictionary that you can query by string and stores Box.
Addendums:
In this case, string is the key type, by which we will access the dictionary. When we add an object to the dictionary we identify it with a key, and when we retrieve it, we also use the key. The key does not have to be string, you can choose a different type. string is convenient in this case because it is what you get from Console.ReadLine().
You can create the dictionary to store whatever type you need. If you do not need Box, you can create a dictionary that stores something else.
Creating and populating the Dictionary
Then, you add to the Dictionary all your Box objects, like this:
var dict = new Dictionary<string, Box>();
// ...
dict.Add("a1", CreateBoxA1());
Where CreateBoxA1 represents whatever means you have to create the object. No, you do not need to create a method for each Box... you can do it like this:
dict.Add("a1", new Box());
Or whatever. I do not know how you create them, so consider that a placeholder, ok? ok.
Querying and retrieving values from the Dictionary
Once you have all your Box instances in your dictionary, you can get the one you need using the string:
Console.WriteLine("Enter the name of the Box:");
var name = Console.ReadLine();
var target = dict[name];
Addendum: The value you get from dict[name] is the value that you added to the dictionary with that key. So, if the user typed "a1" it dict[name] will be the value that we added with "a1" (dict.Add("a1", new Box());). Again, if what you need is not Box you can create a dictionary to store a different type.
Input validation
You can also use the Dictionary to validate if the string corresponds to a Box that exists, for example:
Console.WriteLine("Enter the name of the Box:");
var name = Console.ReadLine();
if (dict.KeyExists(name))
{
var target = dict[name];
// ...
}
else
{
Console.WriteLine("The Box {0} does not exist", name);
}
It goes without saying, but... you can make a loop based on that, for example:
Box target = null;
while(true)
{
Console.WriteLine("Enter the name of the Box:");
var name = Console.ReadLine();
if (dict.KeyExists(name))
{
target = dict[name];
break;
}
Console.WriteLine("The Box {0} does not exist", name);
}
Also, it goes without saying, but... you can add your own validations and sanitation steps. For example using ToUpper, ToUpperInvariant or Trim. And I would remind you that changing strings to lower or upper case is culture sensitive.
See also: Best Practices for Using Strings in .NET.
Editing an removing objects from the dictionary
Once you have the object you retrieved from the Dictionary...
var target = dict[name];
We can use it, and even modify it:
var target = dict[name];
if (target.hasShip) // no need for "== true" if hasShip bool
{
target.hasShip = false;
Console.WriteLine("Ship Destroyed");
}
An special note must be done if Box is value type. For a custom type that means that it is not a class but a struct. The problem with value types is that they are copied on assignment, meaning that when you do var target = dict[name]; with a value type, you get a copy. You must then update the dictionary once you manipulated it:
var target = dict[name];
if (target.hasShip) // no need for "== true" if hasShip bool
{
target.hasShip = false;
dict[name] = target;
Console.WriteLine("Ship Destroyed");
}
Note: As I said above, this is only needed for value types.
And you can even remove the Box from the dictionary if that is necesary:
dict.Remove(name);

C# error with converting string to int in unity3d

I'm having a problem in Unity with C#, I'm trying to create a List or Array that has all the information I need.
0000, 0001,0002, etc.
I want to put this into an List/Array and use this information to instantiate a model on a character selection screen. However, this is the part of code where everything starts to get messed up.
I'm just trying to read out the numbers and add them into the list.
void Start () {
gestureListener = this.GetComponent<GestureListener>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
List<string> mylist = new List<string>(new string[b]);
Debug.Log (mylist);
break;
}
}
I get this error:
error CS0029: Cannot implicitly convert type `string' to `int'
The error happens on line 5, but to me this seems an to be an irreplaceable line...
The variable B is a string so I wouldn't know why the lists sees it as an int.
Please let me know if you can help, much appreciated!
If you are trying to instantiate a list to then add elements to this list then you got it wrong. You are currently instantiating a new list with just one element every time you iterate. In other words, you are not putting the list to use, you are just creating a new one every time you loop.
Create your List of Strings outside the loop then add to it from inside the loop.
You should have something like this to populate the list.
void Start()
{
List<string> mylist = new List<string>();
gestureListener = this.GetComponent<GestureListener>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
myList.Add(b);
Debug.Log (mylist);
break;
}
}
With that said, at the end of your for-loop your myList will have a collection of models per say. You can then iterate that collection to see all the elements you have pushed.
foreach(var item in mylist)
{
//Do whatever with each Item.
}
If you need more examples, take a look at DotNetPerls List Examples
and this video example with Unity in mind.
void Start () {
gestureListener = this.GetComponent<GestureListener>();
List<string> myList = new List<string>();
for (int i = 0; i < numberOfModels; i++) {
string b = i.ToString("0000");
myList.Add(b);
Debug.Log (mylist);
break;
}
//myList is populated with all the numberOfModels here.
}
Don't create a new list inside the loop. The way you are doing it now, you are trying to create a new list (which you throw away anyway), that has progressively larger empty string arrays. For example with a numberOfModels of 100, you would have 100! empty string elements in the list (if you saved it).
Just create a list outside of the for loop, and add the string b to the list inside the loop.

Please explain me behaviour of C#. Object from reference

I have method with one argument which is instance of my created class - Chessfield which contain three tables - one of integers and two of bools.
In the method I have also List of Chessfield which is the return object of my method.
I am modifying chessfield (so the object from reference) and add it on list List.Add(Chessfield) several times (one time after each change).
In the end return object (so list contain several object of Chessfield) all instances are the same unrespecting my changes !
I have read similar topic and try to put 'ref' before argument and the place where I proceed this method.
Without success also I tried to create instance of Chessfield inside the method and assign to it Chessfield object from reference, then making changes on internal created object.
How I can solve it ? In the end I need to receive list of objects which each is a little bit modified from original one (from reference).
Have a nice day !!
p.s. If the code will be helpful then I cut and paste general idea code.
EDIT:
Joel undrestand me Good ! The difference is that inside object are tables and it makes the problem more complex, because I do few changes in these tables in compare to this original object. To be more clear I paste my code:
public class Chessfield
{
public int[] pieces = new int[64];
public bool[] blacks = new bool[64];
public bool[] whites = new bool[64];
public Chessfield(int[] pieces, bool[] blacks, bool[] whites)
{
this.pieces = pieces;
this.blacks = blacks;
this.whites = whites;
}
public Chessfield()
{
}
}
And method look like this:
static public List<Chessfield> MakeAllMovesForWhites(Chessfield chessfieldModel)
{
List<Chessfield> listOfPossibleMoves = new List<Chessfield>(); // list containing chessfields with changed position of figures
int indexOfCurrentPosition = 0; //start with field 0 (most top left)
foreach (bool singleEnemyChecker in chessfieldModel.whites) //iterate all fields, table of whites contain information if white field stand on the field (true, otherwise false),
{
if (singleEnemyChecker == true) //so algorithm will proceed only fields with white figure
{
int kindOfPiece = chessfieldModel.pieces[indexOfCurrentPosition]; // (table pieces contain information which kind of figure stand on particular field 0 -> empty, 1 -> soldier, 2-> tower, 3 -> horse etc...
switch (kindOfPiece)// (based on figure at field it is going to predict all possible moves
{
case 2: // tower case
if (indexOfCurrentPosition % 8 != 0) // check if the field is not most left, otherwise leave
{
int localIndexIterator = indexOfCurrentPosition; //localIndex iterate all possible moves in left direction
while (localIndexIterator % 8 != 0) // checking if tower is standing on the most left field
{
localIndexIterator = localIndexIterator - 1; //iterate all possible moves of tower for left direction
if (chessfieldModel.pieces[localIndexIterator] == 0) //if there are no figures on checking field proceed:
{
chessfieldModel.pieces[indexOfCurrentPosition] = 0; // erase tower from original position
chessfieldModel.whites[indexOfCurrentPosition] = false; // and mark that white tower is not standing there anymore
chessfieldModel.pieces[localIndexIterator] = 2; // put tower on new place
chessfieldModel.whites[localIndexIterator] = true; // and mark that on new place there is white figure
listOfPossibleMoves.Add(chessfieldModel); // here I add changed object of chessfield to list
chessfieldModel.pieces[indexOfCurrentPosition] = 2; // here I come back to original chessfield
chessfieldModel.whites[indexOfCurrentPosition] = true;
chessfieldModel.pieces[localIndexIterator] = 0;
chessfieldModel.whites[localIndexIterator] = false;
}
else //if there is figure at checking field
break; //leave this case
}
}
if (indexOfCurrentPosition % 8 != 7) // right direction case
{
// here is similar code to the sample above
}
if (indexOfCurrentPosition / 8 != 0) //top direction case
{
// here is similar code to the sample above
}
if (indexOfCurrentPosition / 8 != 7) //bottom direction case
{
// here is similar code to the sample above
}
break;
// here are another figures horse and so on...
}
}
indexOfCurrentPosition++; // go to next field...
}
return listOfPossibleMoves; //return list of changed chessfields
}
and here I call method
Logic.MakeAllMovesForWhites(currentChessfield);
I understand what is the problem. And Joel - you are explaining very good ! (y) Thank you.
My first attemp to solve it was (before I ask here):
Chessfield abc = new Chessfield();
abc = chessfieldModel;
abc.pieces[indexOfCurrentPosition] = 0;
abc.whites[indexOfCurrentPosition] = true;
abc.pieces[localIndexIterator] = 2;
abc.whites[localIndexIterator] = false;
listOfPossibleMoves.Add(abc);
Fail. I tried this in every case (create for every figure and every direction). By the way there are 33 different cases how figure can move in chess, so I had this piece of code above in this 33 places (but sometimes I put different things to tables..). But figure like tower can move left for 1,2,3,4,5,6,7 fields if there are not pieces on the left.. and this is problem that I must to create always new instance and I do not know how, because I must to create unique instance, change it a bit, and add to list.. always unique, but in different cases.
Also I have tried your solution Joel, but the problem is that I need to do some changes to original chessfield (4 rows always, but different changes for different figures).
But I tried to create a new instance, add it to list, and then change it while is already on list. Doesn't work and logic is not proper even then.
listOfPossibleMoves.Add(new Chessfield() { pieces = chessfieldModel.pieces, blacks = chessfieldModel.blacks, whites = chessfieldModel.whites });
listOfPossibleMoves[listOfPossibleMoves.Count - 1].pieces[indexOfCurrentPosition] = 0;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].whites[indexOfCurrentPosition] = false;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].pieces[localIndexIterator] = 2;
listOfPossibleMoves[listOfPossibleMoves.Count - 1].whites[localIndexIterator] = true;
EDIT: So maybe coming back to my first method, but how I can create unique name for objects creating in the same place ? May you recommend some technique or what I can do in this situation ?
Thanks Joel and all :)
Have a nice day (or night) everybody !
It would really help to see code in the question, but it sounds like you're doing something like this:
public class ChessField
{
public bool b1;
public bool b2;
public int i1;
}
public List<ChessField> Method(ChessField c)
{
var result = new List<ChessField>();
for (int i = 0;i<3;i++)
{
c.i1 = i;
result.Add(c);
}
return result;
}
Here's the problem: you are adding the same object to the list. result[0] refers to the same object instance as result[1] refers to the same object instance as result[2] refers to the same object instance as c. Changing a property in c changes it everywhere else in this code, because they all are variables for the same object in memory. If you need the objects to be different, you must do something to create new object instances, like this:
public List<ChessField> Method(ChessField c)
{
var result = new List<ChessField>();
for (int i = 0;i<3;i++)
{
result.Add(new ChessField() {b1 = c.b1, b2 = c.b2, i1 = i});
}
return result;
}
or my preferred style would do this:
public IEnumerable<ChessField> Method(ChessField c)
{
return Enumerable.Range(0, 3)
.Select(i => new ChessField() {b1 = c.b1, b2 = c.b2, i1 = i});
}

Manipulating Values in Dictionary

So I have a dictionary whose index is an int, and whose value is a class that contains a list of doubles, the class is built like this:
public class MyClass
{
public List<double> MyList = new List<double>();
}
and the dictionary is built like this:
public static Dictionary<int, MyClass> MyDictionary = new Dictionary<int, MyClass>();
I populate the dictionary by reading a file in line by line, and adding the pieces of the file into a splitstring, of which there is a known number of parts (100), then adding the pieces of the string into the list, and finally into the dictionary. Here's what that looks like:
public void DictionaryFiller()
{
string LineFromFile;
string[] splitstring;
int LineNumber = 0;
StreamReader sr = sr.ReadLine();
while (!sr.EndOfStream)
{
LineFromFile = sr.ReadLine();
splitstring = LineFromFile.Split(',');
MyClass newClass = new MyClass();
for (int i = 1; i < 100; i++)
{
newClass.MyList.Add(Convert.ToDouble(splitstring[i]));
}
MyDictionary.Add(LineNumber, MyClass);
LineNumber++;
}
}
My question is this: is I were to then read another file and begin the DictionaryFiller method again, could I add terms to each item in the list for each value in the dictionary. What I mean by that is, say the file's 1st line started with 10,23,15,... Now, when I read in a second file, lets say its first line begins with 10,13,18,... what I'm looking to have happen is for the dictionary to have the first 3 doubles in its value-list (indexed at 0) to then become 20,36,33,...
Id like to be able to add terms for any number of files read in, and ultimately then take their average by going through the dictionary again (in a separate method) and dividing each term in the value-list by the number of files read in. Is this possible to do? Thanks for any advice you have, I'm a novice programmer and any help you have is appreciated.
Just Replace
newClass.MyList.Add(Convert.ToDouble(splitstring[i]))
with
newClass.MyList.Add(Convert.ToDouble(splitstring[i]) + MyDictionary[LineNumber].GetListOfDouble()[i])
and then replace
MyDictionary.add(Linenumber, Myclass)
with
MyDictionary[linenumber] = MyClass
Just makes sure that the MyDictionary[LineNumber] is not null before adding it :)
Something like this would work
If(MyDictionary[LineNumber] == null)
{
MyDictionnary.add(LIneNUmber, new List<double>());
}
If(MyDictionary[LineNUmber][i] == null)
{
return 0;
}
My solution does not care about list size and it done at reading time not afterward, which should be more efficient than traversing your Dictionary twice.
var current = MyDictionary[key];
for(int i = 0; i < current.MyList.Length; i++)
{
current.MyList[i] = current.MyList[i] + newData[i];
}
Given both lists have same length and type of data.
You can get the custom object by key of the dictionary and then use its list to do any operation. You need to keep track of how many files are read separately.

Categories

Resources