Need to change values in classes without changing its reference - c#

I am currently making an application which tracks information on players and monsters for a tabletop game.
Currently I've got classes for "Monsters". This class contains information such as its name, maxHP, speed, attacks etc. I've managed to make a Database which contains the default values for each type of monster. What I currently need to do is make it possible to change things such as name (Monster > Monster 1, Monster 2 etc), change its HP, and some other things.
I understand that I need to make a copy of such, but I am uncertain on how to do this.
What I currently tried is the following:
public class DatabaseService
{
public List<Player> Players { get; set; }
public List<Monster> AllMonsters { get; set; }
public List<Monster> ActiveMonsters = new List<Monster>();
public bool RollForHP = false;
//Main Database Service
public DatabaseService()
{
Players = GetPlayers();
AllMonsters = GetAllMonsters();
}
public void DoStuff()
{
AddMonsterByName("Goblin", 2);
AddMonsterByName("Adult White Dragon", 1);
AddMonsterByName("Duergar", 4);
foreach (Monster monster in ActiveMonsters) { Console.WriteLine(monster.Name); }
}
//Converts the .json list with all players to Classes, which are then stored in the list "Players" if the "IsInParty" is true
private List<Player> GetPlayers()
{
var path = #"C:\Users\MyName\source\repos\DndAdvancedInitiativeTracker\Logic\Database\Players.json";
var json = File.ReadAllText(path);
var players = JsonConvert.DeserializeObject<List<Player>>(json);
List<Player> inPartyPlayers = new List<Player>();
foreach (var player in players)
{
if (player.IsInParty == true) { inPartyPlayers.Add(player); }
}
return inPartyPlayers;
}
//Converts the .json list with all monsters to Classes, which are then stored in the list "AllMonsters"
private List<Monster> GetAllMonsters()
{
var path = #"C:\Users\MyName\source\repos\DndAdvancedInitiativeTracker\Logic\Database\Monsters.json";
var json = File.ReadAllText(path);
var monsters = JsonConvert.DeserializeObject<List<Monster>>(json);
return monsters;
}
//Adds a given monster to the "ActiveMonsters" list
public void AddMonsterByName(string monsterName, int amountOfMonsters)
{
for (int i = 0; i < amountOfMonsters; i++)
{
List<Monster> DatabaseCopy = AllMonsters.Clone();
DatabaseCopy = AllMonsters;
Monster monster = DatabaseCopy.Find(x => x.Name == monsterName);
Console.WriteLine(monster.Name);
var number = CheckIfNameExistsInList(monsterName);
monster.Name = monsterName + " " + (number + i).ToString();
ActiveMonsters.Add(monster);
}
}
private int CheckIfNameExistsInList(string monsterName)
{
var counter = 1;
foreach (var monster in ActiveMonsters)
{
if (monster.Name.Contains(monsterName))
{
counter += 1;
}
}
return counter;
}
}
In the "DoStuff" Method, I try to add 2 goblins, then a dragon, then a goblin again. The first goblin is named to "Goblin 1" correctly, but the second loop fails, because the AllMonsters' name for goblins is now "Goblin 1" because of the reference type, therefore, the second "Goblin" search in AllMonsters is never found, and returns null.

I'm not sure why you're copying your database (and doing so for every iteration of a for loop which is quite wasteful), but your current check code CheckIfNameExistsInList will always return 1 even if there are no matches in the list.
You can simplify your AddMonstersByName (and use a simple check for previous monster entries) as follows:
public void AddMonstersByName(string name, uint number = 1)
{
var count = AllMonsters.Count(x => x.Name.Contains(name));
for (var i = 1; i <= number; i++)
{
var num = count + i;
AllMonsters.Add(new Monster(){Name= name+num.ToString()});
}
}
This was tested in a simple Console App:
var dataService = new DataService();
dataService.AddMonstersByName("Goblin", 2);
dataService.AddMonstersByName("Dragon", 2);
dataService.AddMonstersByName("Goblin", 2);
foreach (var monster in dataService.AllMonsters)
{
Console.WriteLine($"{monster.Name}");
}
where
public class DataService
{
public List<Monster> AllMonsters = new List<Monster>();
public void AddMonstersByName(string name, uint number = 1)
{
var count = AllMonsters.Count(x => x.Name.Contains(name));
for (var i = 1; i <= number; i++)
{
var num = count + i;
AllMonsters.Add(new Monster(){Name= name+num.ToString()});
}
}
}
public class Monster
{
public string Name { get; set; }
}

Related

several instantiation of a class depending of user input

I'm creating a bingo game where i have "Player" class with different variables in it. I want to ask the user how many players does he/she wants and depending of the input create as many instantiations as desired but i don't know how to do it. Here is my code:
int players = int.Parse(Console.ReadLine());
for(int i = 0; i <= players; i++)
{
Player player +1 = new Player((int[5, 3]), "Player " + i);
}
The expected result is to be able to instantiate a class as many times as a variable says.
Edit: This is the Player class:
public class Player
{
private int[,] playerTicket;
private int ticketId;
private string name;
public int[,] PlayerTicket
{
get { return playerTicket; }
set { playerTicket = value; }
}
public int TicketId
{
get { return ticketId; }
set { ticketId = value; }
}
public string Name
{
get { return Name; }
set { Name = value; }
}
public Player(int[,] playerTicket, int ticketId, string name)
{
this.playerTicket = PlayerTicket;
this.ticketId = TicketId;
this.name = Name;
}
}
int numberOfPlayers = Int32.Parse(Console.ReadLine());
//List which will contain each Player object
//Why use a list? A List is container which holds a reference to each
//Player object which is created in the loop,
//Reference to the List object
//https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=netframework-4.7.2
List<Player> players = new List<Player>();
for(int i = 0; i <= numberOfPlayers; i++)
{
//Just instantiate a new Player object, make sure to pass in an integer for the second parameter of your constructor
Player player = new Player(new int[5, 3], i, "Player " + i);
//Add the Player to the List
players.Add(player);
}
Edit your Player class:
Either add a constructor which accepts the two parameters or make sure to pass in an integer for the second parameter of your constructor:
//You should be setting your class fields to the ones passed into the constructor
public Player(int[,] playerTicket, int ticketId, string name)
{
this.playerTicket = playerTicket;
this.ticketId = ticketId;
this.name = name;
}
I think you should use List and .Add(T) method. e.g.:
int players = int.Parse(Console.ReadLine());
List<Player> activePlayers = new List<Player>();
for(int i = 0; i <= players; i++)
{
activePlayers.Add(new Player((int[5, 3]), "Player " + i));
}

Processing int to name of list

I try to write a simple console application with Hanoi towers. But I am stuck at one point.
In my program I ask a person to write from to which tower wants to put the disk, but: I have 3 lists as towers, I'll ask for a number from gamer and now how I can build a "compare method"? Because I don't want to copy the same piece of code 6 times...
class Towers : Disks
{
//public Towers(int Value, int WhereItIs) : base(Value, WhereItIs) { }
public List<Disks> Tower1 = new List<Disks>();
public List<Disks> Tower2 = new List<Disks>();
public List<Disks> Tower3 = new List<Disks>();
public void Compare(int dyskFrom, int dyskWhere) {
}
public void Display() {
foreach(var i in Tower1){
Console.WriteLine("Where: {0} | Value: {1}", i.WhereItIs, i.Value);
}
}
public void Build(int TowerHigh) {
for (int i = TowerHigh; i > 0; i--) {
Tower1.Add(new Disks {Value = i, WhereItIs = 1 });
}
}
}
class Disks
{
public int Value; //wartosc krazka
public int WhereItIs; //na ktorej wiezy sie on znajduje
}
class Program
{
static void Main(string[] args)
{
Towers Tower = new Towers();
int TowerHigh;
Console.Write("Podaj wysokość wieży: ");
TowerHigh = int.Parse(Console.ReadLine());
Tower.Build(TowerHigh);
Tower.Display();
Tower.Compare(1, 2);
}
}
It's easier to create an array of Stack<Disk>(). This way you can select a tower by index.
I would use a Stack, because thats typically the functionality you need.
Something like: PSEUDO (typed in browser, no syntax checked)
class Disk
{
public int Size {get; set;}
}
static void Main(string[] args)
{
// create the Stack<Disk> array
Stack<Disk>[] towers = new Stack<Disk>[3];
// create each tower
for(int i=0;i<towers.Length;i++)
{
towers[i] = new Stack<Disk>();
// fill the towers.
for(int j=3;j>0;j--)
towers[i].Enqueue(new Disk { Size = j });
}
bool isHoldingDisk = false;
while(true)
{
DisplayTowers(towers);
if(isHoldingDisk)
Console.WriteLine("On what tower do you want to place it?");
else
Console.WriteLine("Choose a tower to pick a disk");
var key = Console.ReadKey(true);
var chosenTowerIndex = 0;
switch(key)
{
case(Key.Escape):
break;
case(Key.D1):
chosenTowerIndex = 0;
break;
case(Key.D1):
chosenTowerIndex = 1;
break;
case(Key.D1):
chosenTowerIndex = 2;
break;
// etc...
}
if((chosenTowerIndex < 1) || (chosenTowerIndex >= towers.Length))
continue;
// check here if you are holding a disk
if(isHoldingDisk)
{
// logic for testing if you can place the disk here...
// using towers[chosenTowerIndex]
// check if it is completed...
if(completed)
break;
isHoldingDisk = false;
}
else
{
// check if you can pickup a disk....(if there is any)
isHoldingDisk = true;
}
}
// final display...
DisplayTowers(towers);
Console.WriteLine("Thanks for playing");
}
static void DisplayTowers(Stack<Disk>[] towers)
{
// draw some fancy asc-ii art...
}

Do different things on each iteration of a loop c#

I am making a game for children on the Sifteo Cubes. I am trying to load a sentence on the cubes, word by word. I have some experience with the Sifteo Cubes but how can I solve this in a more beautiful way? More generic if that's possible.
foreach (CubeWrapper wrapper in mWrappers) {
if (i == 0) {
wrapper.setWord (w1);
} else if (i == 1) {
wrapper.setWord (w2);
} else if (i == 2) {
wrapper.setWord (w3);
} else if (i == 3) {
wrapper.setWord (w4);
} else if (i == 4) {
wrapper.setWord (w5);
}
i++;
}
I will always add w1 to the first wrapper and so on.
Any help would be appreciated!
You can do the following:
String[] words = new String[] {w1,w2,w3,w4,w5};
foreach (CubeWrapper wrapper in mWrappers)
{
wrapper.setWord(words[i++]);
}
This solution is working, but keep in mind, if the words array is smaller than the mWrappers collection you will receive a IndexOutOfBoundsException. So syncing the access to the array is now your work.
If you can't\don't want to change your class then you could use reflection to do something like this (Even though it's probably not the right thing to do here):
foreach (CubeWrapper wrapper in mWrappers) {
var name = "w" + (i+1).ToString();
var value = this.GetType().GetProperty(name).GetValue(this);
wrapper.setWord(value);
}
Example:
public class ABC
{
public int w1 { get; set; }
public int w2 { get; set; }
public int w3 { get; set; }
public ABC()
{
w1 = 7;
w2 = 8;
w3 = 9;
}
public void DoSomething()
{
var i = 1;
var name = "w" + (i + 1).ToString(); // w2
var value = (int)this.GetType().GetProperty(name).GetValue(this);
DoSomething(value);
}
public void DoSomething(int curr)
{
Console.WriteLine(curr); // 8
}
}
Usage:
ABC abc = new ABC();
abc.DoSomething();
You can get a random word from the array, using Random class like this...
String[] words = new String[] { "w1", "w2", "w3", "w4", "w5" };
Random r = new Random(0);
int randomNumber = r.Next(0, words.Length);
foreach (CubeWrapper wrapper in mWrappers)
{
wrapper.setWord(words[randomNumber]);
}

Creating list/array of class instances?

I'm fairly new to C# and I have just learned about creating custom classes. The problem is, I can't figure out how to take the 40~65 instances of this class and put them in a list/array (whichever one I need) where I can locate and choose one based on an attribute defined in it.
Here's the class I have created right now:
public class Team
{
protected int teamNum;
protected double averageMatchPoints;
protected string location;
protected int matchesPlayed;
protected int matchesPending;
protected int blowouts;
//Team Number
public void SetNumber(int num)
{
teamNum = num;
}
public int GetNumber()
{
return teamNum;
}
//Average Points per match
public void AverageMatchPoints(double p)
{
averageMatchPoints = p;
}
public double GetAverageMatchPoints()
{
return averageMatchPoints;
}
//location information
public void SetLocation(string l)
{
location = l;
}
public string GetLocation()
{
return location;
}
//Number of Played Matches
public void PlayedMatches(int mat)
{
matchesPlayed = mat;
}
public int GetPlayedMatches()
{
return matchesPlayed;
}
//Number of matches pending (not played)
public void PendingMatches(int pen)
{
matchesPending = pen;
}
public int GetPendingMatches()
{
return matchesPending;
}
//Number of Blowouts (matches where the robot was disbaled for any number of reasons)
public void SetBlowouts(int b)
{
blowouts = b;
}
public int GetBlowouts()
{
return blowouts;
}
}
Now, if I had 40~65 of these teams competing at an event and I made an instance of this class for each one, how would I populate a combobox with each team number (teamNum) and then locate one specific team out of all the instances in the program by their team numbers?
I recommend a dictionary!
// Declared somewhere
private Dictionary<int, Team> _teamDictionary = new Dictionary<int, Team>();
.
.
.
//Initialization code - I assume you have gotten your teams from a database or somewhere?
foreach (var team in myTeamsList)
{
_teamDictionary.Add(team.teamNum, team);
}
.
.
.
// Later when you want to locate a team:
var team = _teamDictionary[selectedTeamNum];
Have you tried creating a List yet?
List<Team> Teams { get; set; }
You can then bind your combobox to the list/collection/IEnumerable of all the teams that you have. To initialize the teams up to 40/60 do the following?
for(int i = 0; i < 60; i++)
{
Team t = new Team();
t.Name = "Team 1";
t.TeamNumber = i + 1;
Teams.Add(t);
}
List<Team> allTheTeams = new List<Team>();
for(var i = 0; i < 65; i++){
allTheTeams.Add(new Team { teamNum = i });
}
And to get the team with number 34:
allTheTeams.FirstOrDefault(x => x.teamNum == 34);
Like this:
Add a constructor to your class that takes the teamnumber:
(this is the best solution if every team needs to have a number. So you can not forget to set the team number as you can not create an object of type team without setting the number in the constructor)
public class Team
{
protected int _teamNum;
public Team(int teamNum)
{
_teamNum = teamNum;
}
public int getTeamNum()
{
return _teamNum;
}
//more logic
}
Populate a dictionary, the comboBox and get a team for its number:
Dictionary<int, Team> dictionary = new Dictionary<int, Team>();
int teamNum = 1;
// Add your Teams to a dictionary (example)
dictionary.Add(teamNum ,new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
dictionary.Add(teamNum, new Team(teamNum++));
// Populate a comboBox
foreach(KeyValuePair<int,Team> kvp in dictionary)
{
comboBox1.Items.Add(kvp.Value.getTeamNum().ToString());
}
// get a team for a given teamNumer
int targetTeamNumber = 2;
if (dictionary.ContainsKey(targetTeamNumber))
{
Team team = dictionary[targetTeamNumber];
// do something with the team
}

Variable not returning actual values

I want correctly return some variables (arrays)
kazkas.Ads[n]; (n = how many ads are)
kazkas.Ads[n].id;
kazkas.Ads[n].Days[m].Stats.Clicks; // every day have his own clicks
kazkas.Ads[n].Days[m].Stats.Impresons; // every day have his own impresions
from this method and use these variables in other class.
public static void GetAdsStats(string Ticket, DateTime start, DateTime end, int CamId)
{
var client = new CampaignStatsServiceClient();
var id = new CampaignIdFilter();
id.CampaignId = CamId;
var statsdata = new GetAdStatsData();
var kazkas = new Campaign();
kazkas = client.GetAdStats(Ticket, new GetAdStatsData
{
IdFilter = id,
StartDate = start,
EndDate = end
});
long AllClicks = 0;
long AllImpresions = 0;
int reklamos = kazkas.Ads.Length;
long[] statistikaClikai = new long[reklamos];
long[] statistikaImpresions = new long[reklamos];
for (int i = 0; i < reklamos; i++)
{
int dienos = kazkas.Ads[i].Days.Length;
for (int lop = 0; lop < dienos; lop++)
{
AllClicks = AllClicks + kazkas.Ads[i].Days[lop].Stats.Clicks;
AllImpresions = AllImpresions + kazkas.Ads[i].Days[lop].Stats.Impressions;
}
statistikaClikai[i] = AllClicks;
statistikaImpresions[i] = AllImpresions;
}
}
I know that void type can't return anything, but this how I know that my method works ( from debugging). Like you see I was trying do that with for loop. Here i have 9 Ads and every ad have one day.
Like I says I want return every Ads id[in array], and every days.stats.impresions and days.stats.click
how can I do that ? Ore how return more variables/arrays from method to other class, I am using webservises, so i cant use database ore something like that.
As can be seen by the downvotes of the question, you need to design the return value and then code against it.
Your query almost does it (now):
kazkas.Ads[n]; (n = how many ads are)
kazkas.Ads[n].id;
kazkas.Ads[n].Days[m].Stats.Clicks; // every day have his own clicks
kazkas.Ads[n].Days[m].Stats.Impressions; // every day have his own impressions
Your existing code show this should be expanded to include:
kazkas.Ads[n].Total.Clicks;
kazkas.Ads[n].Total.Impressions;
So now you're ready to design. First you want a Stat Class that just contains CLicks and Impressions:
public class Stat
{
public long Impressions { get; set; }
public long Clicks { get; set; }
}
An optimisation here may be to use a struct, but I won't go into that.
As you currently have defined it each Day has just a Stats property:
public class DayStat
{
public Stat Stats { get; set; }
}
Now finally we can define the top level AdStat:
public class AdStat
{
public int id { get; set; }
public DayStat Day[];
public Stat Total { get; set; }
}
Etc... There's further issues here, such as ensuring arrays are created and Stat instances are never null (which is why making some of these classes structs is an option). But I'm really a VB programmer so I'll stop here before I get caught typing crap into the SO IDE :-)
Create a class or struct with members you need
public class Stat
{
public int Id { get; set; }
public long Clicks { get; set; }
...
}
Change the signature of your method from void GetAdsStats to IEnumberable<Stat> GetAdsStats and either return a collection of stats or use yield keyword to return the stat object.
Also if you do not want your method to return anything (return type void) do not use a name starting with Get.
Example:
public static IEnumerable<Stat> GetAdsStats(...)
{
...
var statList = new List<Stat>();
for (int i = 0; i < reklamos; i++)
{
var stat = new Stat();
statList.Add(stat);
int dienos = kazkas.Ads[i].Days.Length;
for (int lop = 0; lop < dienos; lop++)
{
AllClicks = AllClicks + kazkas.Ads[i].Days[lop].Stats.Clicks;
AllImpresions = AllImpresions + kazkas.Ads[i].Days[lop].Stats.Impressions;
}
stat.Clicks = AllClicks;
stat.Impression = AllImpresions;
}
return statList;
}
Change your void to the type you want to return, say Campaign, and return the appropriate variable. The variables you define in your method, only live in your method and are not accessible from another method or class.

Categories