How to create a grid based map? - c#

I'm trying to make a map like this in C#:
0 1
0 [ ]---[ ]
| |
| |
1 [ ]---[ ]
Simple grid with rooms at (0,0), (1,0), (0,1) and (1,1)
I have tried doing this and have an example here https://dotnetfiddle.net/3qzBhy
But my output is:
[ ]|||| [ ]
I don't get why and not sure if calling .ToString() on a StringBuilder makes it lose its formatting such as new lines.
I also had trouble finding a way to store coordinates
Public SortedList<int, int> Rooms ()
{
var roomList = new SortedList<int, int>();
roomList.Add(0,0);
roomList.Add(1,0);
//roomList.Add(0,1);
//roomList.Add(1,1);
return roomList;
}
roomList.Add(0,1) and roomList.Add(1,1) are duplicates because the keys 0 and 1 are already used. How can I store a list of coordinates?

Instead of spreading my opinions via comments I'll just dump em all here as an answer:
I also had trouble finding a way to store coordinates
SortedLists, Dictionaries, etc. won't work. It would be best to just use a regular List filled with Tuples, Points or a class of your own until you find a better solution.
Since those rooms maybe won't stay empty you could write your own classes, e.g.:
class Tile
{
public int X { get; set; }
public int Y { get; set; }
public virtual void Draw(StringBuilder map)
{
map.Append(" ");
}
}
class Room : Tile
{
public int EnemyType { get; set; }
public int Reward { get; set; }
public override void Draw(StringBuilder map)
{
map.Append("[ ]");
}
}
// etc.
I don't get why and not sure if calling .ToString() on a StringBuilder makes it lose its formatting such as new lines.
It doesn't. You didn't have any newlines in your example because all rooms are at Y = 0
The current attempt won't work if you draw multiple lines at once and string them together. Instead you could use something like a "half-step" grid.
You can find a small (ugly, non-optimzed, makeshift) example here as a fiddle.

Besides the problems with text-formatting and the console, and also the options Manfred already mentioned, here is an example which uses an array instead of a list. A room is a true-value at any given index, "no room" is represented by false.
Your Rooms()method looks like this then:
public bool[,] Rooms ()
{
var roomList = new bool[,]{{true,false},{true,true}};
return roomList;
}
Which makes you need to change IsRoom(int, int)to look like this:
public bool IsRoom(int x, int y)
{
var rooms = Rooms();
if(x<rooms.GetLength(0) && y<rooms.GetLength(1))
{
return (rooms[x,y]);
}
return false;
}
Also there was a line-break missing, which lead to having a vertcal connector on the same line as the next room. I changed it to be
if (IsRoom(x, y + 1))
{
buildMap.Append("\r\n".PadLeft(4) + DrawVerticalConnector().PadRight(4));
buildMap.Append("\r\n".PadLeft(4) + DrawVerticalConnector().PadRight(4)+ "\r\n".PadLeft(4));
}

Related

Trying to append an array value inside a Dictionary<string,string[]> [duplicate]

This question already has answers here:
C# dictionary - one key, many values
(15 answers)
Closed 12 days ago.
For some reason I cannot append a string variable to string[] value in Dictionary<string;string[]>
I am trying to make a Graph C# class for practice and i ran into a problem: I use Dictionary<string, string[]> graph to make a structure like this:
"Node1":[connection1,connection2,connection3]
"Node2":[connection1,connection2,connection3]
"Node3":[connection1,connection2,connection3]
...
I have a method to append a connections array value:
// from Graph class
private Dictionary<string, string[]> graph;
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Append(EdgeName);
}
And use it like this:
//from Main
Graph g = new Graph();
string[] Nodes = { "node1", "node2", "node3" };
string[][] Edges = { new[] { "node1", "nodeToEdgeTo" }, new[] { "node2", "nodeToEdgeTo" } };
//nodeToEdgeTo are nodes like "node2" or "node3"
foreach (var i in Edges)
{
g.AddEdge(i[0], i[1]);
}
But as a result i get empty values for some reason:
"Node1":[]
"Node2":[]
"Node3":[]
I have no idea why
As people already said, an array (as in most languages) are not resizeables.
I think it's interesting to see how you instanciate an array in C to understand that.
In C either you instanciate like that
int[5] myArray
or you choose the dynamic way with malloc pretty much like so
int *myArray = malloc(5 *sizeof(int));
What you can see is that when you instanciate them you have to give it a size and it will "lock" a chunck of memory for it.
Now if you want to add more things to it you have to recreate a new one bigger and copy the content from one to another.
Lists work in a different way. each element of a list are alocated separatly and each of them contain a pointer to the next element.
That's why it's way easier to add an element to a list. What happens under the hood is that it creates that element and alocate memory for it and make the last (or first it depends) element of the list to it (you can see it as a chain)
It should remind you of what you're trying to achieve with your tree. Graphs and lists aren't much different but graph doesn't already exist int c# so you have to recreate it.
Simple answer
So the easy solution for you case is to swap the array by a list like so:
Dictionary<string, List<string>> graph
and then you use it like so
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Add(EdgeName);
}
More complicated answer
So following what we said about list, we should assume that each element of your graph should reference other elements.
That will look like so:
using System.Text.Json;
var edge0 = new Edge() { WhatEverData = "level0 : edge0" }; //Your base node/edge
var edge1 = new Edge() { WhatEverData = "level1 : edge1" };
var edge2 = new Edge() { WhatEverData = "level1 : edge2" };
var edge3 = new Edge() { WhatEverData = "level1 : edge3", Edges = new List<Edge> { new Edge() { WhatEverData = "level2" } } };
edge0.AddEdge(edge1);
edge0.AddEdge(edge2);
edge0.AddEdge(edge3);
Console.WriteLine(JsonSerializer.Serialize(edge0));
public sealed class Edge
{
public string? WhatEverData { get; set; }
public List<Edge>? Edges { get; set; }
public Edge AddEdge(Edge toAdd)
{
Edges ??= new List<Edge>();
Edges.Add(toAdd);
return this;
}
}
In this example everything is an edge, Edge0 being the root of your graph but your root could also be a list of edge if you really want to.
The point is that now that it's an object you can put pretty much everything you want in there.
You can also easily serialize it! Here it's what the output of this tiny code looks like:
{
"WhatEverData": "level0 : edge0",
"Edges": [
{ "WhatEverData": "level1 : edge1", "Edges": null },
{ "WhatEverData": "level1 : edge2", "Edges": null },
{
"WhatEverData": "level1 : edge3",
"Edges": [{ "WhatEverData": "level2", "Edges": null }]
}
]
}
Hope it helped
Instead of an array, you might want a List isntead: Dictionary<string, List<string>>.
Then you could do this:
public void AddEdge(string NodeName, string EdgeName)
{
graph[NodeName].Add(EdgeName);
}

How can I list the keys in a nested dictionary?

I'm just starting to learn c# by creating my own program that is basically a damage calculator for the game Risk of Rain 2. As such, right now I am trying to create the user selection for a ability that a survivor has. I am currently using this format for how I store the ability and it's information:
Dictionary<string, Dictionary<string, double[]>> survivorAbilities =
new Dictionary<string, Dictionary<string, double[]>>();
Dictionary<string, double[]> acridAbilities =
new Dictionary<string, double[]>();
An example of some of the data being stored is:
//Epidemic: Special 1
double[] acridEpidemic = new double[3];
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
acridAbilities.Add("epidemic", acridEpidemic);
//Initializing Acrid and his abilities
survivorAbilities.Add("acrid", acridAbilities);
The problem is that the survivor "Acrid" is only one of the four survivors that I decided to add. The other survivors have different dictionary names specific to the survivor. For example, instead of "acridAbilities", there is "commandoAbilities". I want to print each key in these dictionaries, but I'm not sure how to specify which survivor's dictionary to pick from, from the dictionary "survivorAbilities".
Sorry if its a bit confusing and long, not quite sure what to put.
This is not a direct answer to your question, but some advice since you're learning C# as well as an alternate approach.
A dictionary of nested dictionaries is, of course, perfectly valid code, but it can be confusing. Instead, why not create some classes to easily encapsulate your data? For example, you have this in your sample code:
acridEpidemic[0] = (1); //instances
acridEpidemic[1] = (1); //damage %
acridEpidemic[2] = (1); //proc
Notice you're using comments to explain what these keys in the dictionary are supposed to represent? This is not good; the code should be able to explain what it's doing without needing comments like these.
Here is an example, based on my understanding of your problem, of what I'd consider a better approach:
public void SomeExample()
{
var survivors = new List<Survivor>(); // all your survivors go in here
var acrid = new Survivor
{
Name = "Acrid",
};
acrid.Abilities.Add(new Ability
{
Name = "epidemic",
Instances = 1,
Damage = 1,
Proc = 1,
});
survivors.Add(acrid);
}
public class Survivor
{
public string Name { get; set; }
public List<Ability> Abilities { get; set; } = new List<Ability>();
}
public class Ability
{
public string Name { get; set; }
public double Instances { get; set; }
public double Damage { get; set; }
public double Proc { get; set; }
}

Search in list of strings by name of the string

I have the following list I'm populating as I go through my class:
List<string> stringCollection = new List<string>();
I have a lot of static strings that I have declared before going to my class.
These strings are added to my list based on a collection of conditional expressions, meaning that it varies what kind of strings that I put into my list e.g.:
static string DescriptionText1 = "blabla",
DescriptionText2 = "blabla",
MagnitudeText1 = "blabla",
MagnitudeText2 = "blabla";
if(number < 2)
{
stringcollection.Add(DescriptionText1)
}
else
{
stringcollection.Add(DescriptionText2)
}
//Magnitude
if(magnitude > 128 && number < 256)
{
stringcollection.Add(MagnitudeText1)
}
else
{
stringcollection.Add(MagnitudeText2)
}
...
I then pass the list to my method in which I want to retrieve the strings like so:
public void Collection(List<string> ts)
{
string Description = ts.Find(DescriptionText); <--- my guess
string Magnitude = ts.Find(MagnitudeText);
}
How do I find the correct strings in my list, and write it to my newly declared strings in my method? - Even though they are appended hence 1,2,3 ... 6,7
Since you always put in Description first and then Magnitude, you can just do:
ts[0] // description
ts[1] // magnitude
Alternatively, consider writing a class that has the two properties:
// I don't know what these two things represent, please name it properly in your code!
class DescriptionMagnitude {
public string Description { get; }
public string Magnitude { get; }
public DescriptionMagnitude(string description, string magnitude) {
Description = description;
Magnitude = magnitude;
}
}
And then create an instance of this class and pass it around.
EDIT:
From your comment:
and then i would be able to search for my int variable?
It seems like you want to find the integer associated with the string. However, the 1 in DescriptionText1 is just part of an identifier. Why not just store the integer instead of the string?
Depending on what you are doing with the strings, an enum may be suitable:
enum Descriptions {
Foo = 0,
Bar = 1
Fizz = 2
}

Dynamically add values to List<double> using get & set

Is it possible to do somethink like
public class TestClass
{
public List<double> preTvoltage
{
get
{
return preTvoltage;
}
set
{
preTvoltage.Add(this); //how to add to the List??
}
}
}
The reason I want to do this (I do not know if this is a best method, just as far as my knowledge allows) because I have to get data from xml files that do not have always same number of data in them.
Later I want to fill a ListView rows and using list I can count how many items are and how many columns will be needed.
Here is a schematic of xml file:
and there are also Trigger and PostTrigger nodes in xml file with same data sorting.
and here is the listview I want to achive:
Link to full size image
So, there are some pin groups and each pingroup has lots of data, the above code I gave, was just to hold 1 of the voltage nodes in xml file.
I am pretty much listening for your ideas!
Thanks.
No, and it defies usage of properties - you should implement it as an Add (or similarly aptly named) method.
You can't add this, because this is a TestClass, not a double; and you can't add value, as otherwise suggested, because that is a List<double>, and Add requires a double.
It's not clear how you would use this, but it looks like a very bad idea to me. Setting a collection as a property is slightly unusual already, but it's even odder for that set operation to mutate the list. It's additionally weird that you're not using the value variable within the setter... why not?
You should consider what the calling code would look like, and whether that's really the clearest way of expressing the semantics you want.
set { preTvoltage.AddRange(value); }
As Jon Skeet is saying, this is not what you should do. Instead, do
TestClass t = new TestClass();
t.PreTvoltage.Add(...);
declaring the property as
public List<double> PreTvoltage
{
get { return preTvoltage; }
}
The type of a getter and setter must match.
You could have:
public List<double> preTvoltage
{
get
{
return preTvoltage;
}
set
{
preTvoltage.AddRange(value); //add all items in list assigned.
}
}
However, this seems like a bad idea as it would be confusing to users why the value got did not match the value just set. I would have the two operations as separate members, and the setter either not exist or else overwrite the existing preTvoltage entirely.
You can not implement it like this, the preferable way is to make collection controls like:
private IList<double> _preTvoltage = new List<double>();
public IEnumerable<double> preTvoltage
{
get
{
return preTvoltage.AsEnumerable();
}
}
public void AddTvoltage(double item)
{
_preTvoltage.Add(item);
}
Well I managed to solve my problem this way:
public class ITestData
{
public string pinName { get; set; } //Name of the pin
public double stressLevel { get; set; } //Stress level for latchup
public int psuCount { get; set;} //Number of PSU's
public List<double[]> preTrigger = new List<double[]>();
public List<double[]> inTrigger = new List<double[]>();
public List<double[]> postTrigger = new List<double[]>();
public void AddPreTrigger(double volt, double curr)
{
double[] data = new double[2];
data[0] = volt;
data[1] = curr;
preTrigger.Add(data);
}
public void AddInTrigger(double volt, double curr)
{
double[] data = new double[2];
data[0] = volt;
data[1] = curr;
inTrigger.Add(data);
}
public void AddPostTrigger(double volt, double curr)
{
double[] data = new double[2];
data[0] = volt;
data[1] = curr;
postTrigger.Add(data);
}
}

which c# collection to use instead of List<KeyValuePair<string, double>>?

I want to store data such as
{
{"apple",15 }
{"pear",12.5 }
{"", 10 }
{"", 0.45 }
}
Data will be plotted on a bar chart (string will be the legend and double will be the value)
Insert order is important.
Perfs don't matter.
Strings could be duplicated or empty. (values could be duplicated too)
I need to get min and max values (easily if possible) to set the scale.
I use
List<KeyValuePair<string, double>> data = new List<KeyValuePair<string, double>>();
data.Add(new KeyValuePair<string,double>("",i));
Quite boring and unreadable.
Is there a cleaner way to do it ?
StringDoubleCollection data = new StringDoubleCollection();
data.add("apple",15);
data.add("",10);
double max = data.values.Max();
double min = data.values.Min();
if not how to get the max value of List<KeyValuePair<string, double>> without too much hassle
NameValueCollection looks nice but its a <string,string> I need a <string,double>
You could create a class like the following:
class X
{
public string Name { get; set; }
public double Value { get; set; }
// name is an optional parameter (this means it can be used only in C# 4)
public X(double value, string name = "")
{
this.Name = name;
this.Value = value;
}
// whatever
}
And then get maximum and minimum values using LINQ with a selector:
var data = new List<X>();
data.Add(new X(35.0, "Apple"))
data.Add(new X(50.0));
double max = data.Max(a => a.Value);
double min = data.Min(a => a.Value);
EDIT: if the code above still seems unreadable to you try to improve it using an operator for cases in which you want to have just the value.
// Inside X class...
public static implicit operator X(double d)
{
return new X(d);
}
// Somewhere else...
data.Add(50.0);
To determine which data structure you really want, lets look at your usage patterns.
Insert order matters.
You don't access your items by key.
You want min and max.
A heap offers min or max, but doesn't preserve order. A hash based dictionary also doesn't preserve order. A List is actually a good choice for your data structure. It is available and offers excellent support.
You can prettify your code by defining classes for both the data structure and your bar data. And you can add min/max functionality to the collection. Note: I didn't use the Linq Min/Max functions, because they return the minimum value, not the minimum element.
public class BarGraphData {
public string Legend { get; set; }
public double Value { get; set; }
}
public class BarGraphDataCollection : List<BarGraphData> {
// add necessary constructors, if any
public BarGraphData Min() {
BarGraphData min = null;
// finds the minmum item
// prefers the item with the lowest index
foreach (BarGraphData item in this) {
if ( min == null )
min = item;
else if ( item.Value < min.Value )
min = item;
}
if ( min == null )
throw new InvalidOperationException("The list is empty.");
return min;
}
public BarGraphData Max() {
// similar implementation as Min
}
}
Have you looked at LookUp?
The only problem is that it's immutable, so you need to be able to create your collection in one go.
As Anthony Pegram notes, it's a bit of a pain to create one. It depends on where your data is coming from. Have a look at the ToLookup method.
If it's worth it for usability (i.e. you're using awkward collections of List<KeyValuePair<string, double>> everywhere, it might just be worth it to implement StringDoubleCollection. It wouldn't be that difficult to wrap the underlying collection with the friendlier syntax you've described in your example.
And, as other comments / answers are suggesting, the Framework doesn't seem to provide a simpler solution that matches all of your requirements...
As for "max value", I assume you mean the Key-Value Pair with the greatest value. It can be retrieved like so:
var max = list.Select(kvp => kvp.Value).Max();
Just define your own model class to hold the data instead of depending on a KeyValuePair and everything becomes cleaner:
using System;
using System.Collections.Generic;
public class Fruit
{
public string Name {get; set;}
public double Price {get; set;}
}
public class Program
{
public static void Main()
{
List<Fruit> _myFruit = new List<Fruit>();
_myFruit.Add(new Fruit{Name="apple", Price=15 });
_myFruit.Add(new Fruit{Name="pear", Price=12.5 });
_myFruit.Add(new Fruit{Name="", Price=10 });
_myFruit.Add(new Fruit{Name="", Price=0.45 });
// etc...
}
}
What about implementing the StringDoubleCollection to work like you want...
public class StringDoubleCollection
{
private List<KeyValuePair<string, double>> myValues;
public List<double> values
{
get { return myValues.Select(keyValuePair => keyValuePair.Value).ToList(); }
}
public void add(string key, double value)
{
myValues.Add(new KeyValuePair<string,double>(key,value));
}
}
You can implementing Dictionary<key, value>
Dictionary<string, string> openWith = new Dictionary<string, string>();
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
https://learn.microsoft.com/pt-br/dotnet/api/system.collections.generic.dictionary-2?view=net-5.0

Categories