Board game pawn movement algorithm - c#

I've been working on a board game with a friend of mine. We have managed to get most of it working. The game is called 'jeu de barricade'. Maybe you know it.
The whole board gets created using a Linked List so every field has a 'LinkNorth', 'LinkEast', 'LinkSouth' and 'LinkWest' variable.
Here is the board to give you an idea of how it looks like.
Now, there is one thing we just can't seem to figure out how to do. Currently, a pawn can be selected, and it can be moved to any field on the board. This of course is not good.
What we need to do now, is write a method with some sort of algorithm that returns an array or list of the fields the pawn is able to move to. This way, we can check if the selected pawn is actually able to move to the field you clicked on, with the thrown dice number. (a random number from 1 till 6 is generated)
One more thing though. Every 'Field' class has a barricadePawn variable. When this variable contains a barricadePawn object. (barricadePawn != null) the pawn should not be able to move over it. The pawn should be allowed to move onto that field, but not any further.
(when the player lands exactly on a barricade, he can move it. But we already implemented that, so don't worry about that)
So, in short.
- I want to create a method in our 'Pawn' class, which returns an array or list of all the fields that the pawn should be able to move to.
- The pawn should be able to move on barricades but NOT over them.
- The pawn has to move exactly the amount thrown by the dice. So, to get on the finish or on a barricade, you have to throw exactly the right amount.
We have these in our 'Pawn' class:
'currentLocation' contains the field that the selected pawn is currently standing on.
private Model.Field startLocation;
private Model.Field currentLocation;
public List<Model.Field> getPossibleMoves(Model.Field curSpot, int remainingMoves, List<Model.Field> moveHistory)
{
List<Model.Field> retMoves = new List<Model.Field>();
if( remainingMoves == 0 )
{
retMoves.Add(curSpot);
return retMoves;
}
else
{
moveHistory.Add(curSpot);
if( curSpot.LinkNorth != null && !moveHistory.Contains(curSpot.LinkNorth) )
{
retMoves.AddRange( getPossibleMoves( curSpot.LinkNorth, remainingMoves - 1, moveHistory ));
}
if (curSpot.LinkEast != null && !moveHistory.Contains(curSpot.LinkEast))
{
retMoves.AddRange( getPossibleMoves( curSpot.LinkEast, remainingMoves - 1, moveHistory ));
}
if (curSpot.LinkSouth != null && !moveHistory.Contains(curSpot.LinkSouth))
{
retMoves.AddRange( getPossibleMoves( curSpot.LinkSouth, remainingMoves - 1, moveHistory ));
}
if (curSpot.LinkWest != null && !moveHistory.Contains(curSpot.LinkWest))
{
retMoves.AddRange( getPossibleMoves( curSpot.LinkWest, remainingMoves - 1, moveHistory ));
}
}
}
And this is our 'Field' class:
public class Field
{
protected Field linkNorth, linkEast, linkSouth, linkWest;
protected Controller.Pawn pawn;
protected Model.BarricadePawn barricadePawn;
protected int x, y;
//Properties:
public Field LinkNorth
{
get { return linkNorth; }
set { linkNorth = value; }
}
public Field LinkEast
{
get { return linkEast; }
set { linkEast = value; }
}
public Field LinkSouth
{
get { return linkSouth; }
set { linkSouth = value; }
}
public Field LinkWest
{
get { return linkWest; }
set { linkWest = value; }
}
public Controller.Pawn Pawn
{
get { return pawn; }
set { pawn = value; }
}
public BarricadePawn Barricade
{
get { return barricadePawn; }
set { barricadePawn = value; }
}
public int X
{
get { return x; }
set { x = value; }
}
public int Y
{
get { return y; }
set { y = value; }
}
}
If anyone could help us with this, it would be much appreciated. We haven't been able to come up with anything.

Try creating a recursive method.
List<Spot> CheckMoves(Spot curSpot, int remainingMoves, List<Spot> moveHistory)
{
List<Spot> retMoves = new List<Spot>();
if( remainingMoves == 0 )
{
retMoves.Add(curSpot);
return retMoves;
}
else
{
moveHistory.Add(curSpot);
if( !moveHistory.Contains(Spot.North) )
{
retMoves.AddRange( CheckMoves( Spot.North, remainingMoves - 1, moveHistory );
}
/* Repeat for E, W, S */
}
}
This will return a List (where spot is the class that represents a position on the board) that contains all potential final positions. Of course you'll need to be a bit more thorough in making sure the Spot.North is a valid spot, but this is a basic idea for you.
The method above won't allow the user to move into the same spot twice in a single move, but doesn't look for barricades or other obstructions. It also doesn't handle any spots which halt movement or have no possible movement in a particular direction.
It should, however, give you an idea of how it should go.

Something like this should do it,
It recusively enumerates each link and adds the Fields to a HashSet to prevent duplicates. The recursion stops when, the worp counts down to 0, the link is null or a barrier pawn is located.
public IList<Field> GetPossibleMoves(int worp)
{
var valid = new HashSet<Field>();
foreach (var f in GetPossibleMoves(current.LinkNorth, worp))
{
valid.Add(f);
}
foreach (var f in GetPossibleMoves(current.LinkEast, worp))
{
valid.Add(f);
}
foreach (var f in GetPossibleMoves(current.LinkSouth, worp))
{
valid.Add(f);
}
foreach (var f in GetPossibleMoves(current.LinkWest, worp))
{
valid.Add(f);
}
return valid.ToList();
}
private static IEnumerable<Field> GetPossibleMoves(Field current, int worp)
{
if (current == null)
{
yield break;
}
yield return current;
if (worp == 0 || current.BarricadePawn) // is that a bool?
{
yield break;
}
foreach (var f in GetPossibleMoves(current.LinkNorth, nextWorp))
{
yield return f;
}
foreach (var f in GetPossibleMoves(current.LinkEast, nextWorp))
{
yield return f;
}
foreach (var f in GetPossibleMoves(current.LinkSouth, nextWorp))
{
yield return f;
}
foreach (var f in GetPossibleMoves(current.LinkWest, nextWorp))
{
yield return f;
}
}
It could be optimised by omitting the calculation of backward moves which would prevent a lot of Adds being discarded from the HashSet.

Related

Be able to show the next value in a Linked List by clicking a button

This is probably a simple task however I am unable to solve.
So currently I have set up a form which contains a textbox and a button and I want to be able to click the button and the first value within the LinkedList will show up in the textbox. If I click the button again then the next value will show up etc.
I currently go it so that the first value will show up but then I am unable to proceed to the next value.
This is the code I have currently:
public class Node
{
public string data;
public Node next;
public Node(string newData)
{
data = newData;
next = null;
}
public void AddEnd(string data)
{
if (next == null)
{
next = new Node(data);
}
else
{
next.AddEnd(data);
}
}
}
public class myList
{
public void AddEnd(string data)
{
if (headnode == null)
{
headnode = new Node(data);
}
else
{
headnode.AddEnd(data);
}
}
public string getFirst() // this gets the first value within the list and returns it
{
if (headnode == null)
{
throw new Exception("List is empty");
}
Node node = headnode;
while (node.next != null)
{
node = node.next;
}
return node.data;
}
I also tried using this:
public class NavigationList<T> : List<T>
{
private int _currentIndex = -1;
public int CurrentIndex
{
get
{
if (_currentIndex == Count)
_currentIndex = 0;
else if (_currentIndex > Count - 1)
_currentIndex = Count - 1;
else if (_currentIndex < 0)
_currentIndex = 0;
return _currentIndex;
}
set { _currentIndex = value; }
}
public T MoveNext
{
get { _currentIndex++; return this[CurrentIndex]; }
}
public T Current
{
get { return this[CurrentIndex]; }
}
}
However, I am not really familiar with something like this so I wasn't sure on how to use it.
So you have a sequence of items, and the only thing that you want, is to get the first item, and once you've got an item, every time your ask for it, you want the next item, until there are no more items left.
In .NET this is called an IEnumerable, or if you know what kind of items are in your sequence, for instance items of MyClass, it is called an IEnumerable<MyClass>. In your case you need an IEnumerable<string>.
Luckily .NET is loaded with classes that implement IEnumerable. Two of the most used ones are array and list. You seldom have to create an enumerable class yourself, re-use the existing ones and enumerate over it.
List<string> myData = ... // fill this list somehow.
IEnumerator<string> myEnumerator = null // we are not enumerating yet.
string GetNextItemToDisplay()
{ // returns null if there are no more items to display
// if we haven't started yet, get the enumerator:
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
// get the next element (or if we haven't fetched anything yet: get the first element
// for this we use MoveNext. This returns false if there is no next element
while (this.myEnumerator.MoveNext())
{
// There is a next string. It is in Current:
string nextString = enumerator.Current();
return nextString;
}
// if here: no strings left. return null:
return null;
}
This looks like a lot of code, but if you remove the comments it is in fact just a few lines of code:
string GetNextItemToDisplay()
{
if (this.myEnumerator == null) this.myEnumerator = this.myData.GetEnumerator();
while (this.myEnumerator.MoveNext())
return enumerator.Current();
return null;
}
Your ButtonClick event handler:
void OnButtonClick(object sender, eventArgs e)
{
string nextItemToDisplay = this.GetNextItemToDisplay();
if (nextItemToDisplay != null)
this.Display(nextItemToDisplay);
else
this.DisplayNoMoreItems():
}
If you want to start over again with the first element, for instance after changing the List
void RestartEnumeration()
{
this.myEnumerator = null;
}

Working with getter and setter in c#

I am new to C# and was wondering what I am doing wrong while working with get and set.
I have a VendingMachine that, when I input a number for the beverage that I want, it subtracts from the total.
private int _coke = 2;
public int Beer = 20;
public int LemonLime = 20;
public int Grape = 20;
public int CreamSoda = 20;
public bool AnohterBevrage;
The problem is that it keeps subtracting even after it reaches 0. Well, there cant be -1 Coke left in the Machine. So, I tried this.
public int Coke
{
get => _coke;
set
{
if (_coke == 0)
{
_coke = value;
Console.WriteLine("No more Coke Left!");
}
}
}
But it doesn't work, so I'm not sure where I'm getting stuck. I'm not sure if the Math function is relevant here.
If there is something missing, let me know. I will try and adjust. This getter and setter gets me all confused.
EDIT: Add function
public void Math()
{
var input = Console.ReadKey();
while (input.Key == ConsoleKey.D1)
{
do
{
_coke--;
Console.WriteLine("\nTotal Coke Left: " + Coke);
Console.Write("\nWould You like Another Bevrage ?y/n: ");
if (Console.ReadLine() == "y")
{
AnohterBevrage = true;
Content();
Math();
}
else
{
AnohterBevrage = false;
}
break;
} while (AnohterBevrage == true);
}
}
if (_coke == 0)
You're checking the current value.
That means that you can only set the property if it is currently 0.
When you start trying to do something, you need to build a model of what you are doing. Using getters/setters as your UI is not a great design (as #Rufus L pointed out). Instead, picture a Coke machine in your head. There are racks of various drinks (that get restocked), and then a mechanical system that implements a user interface that allows someone to pick a drink and get it delivered. You should separate your design into those two parts.
One hint that your design needs work is that your main function (that's doing all the work) is named "Math". If you were to look at this code in two months, you'd see "Math" and not have a clue it represented a Coke machine.
I wrote a quickie coke machine that matches your needs (well, I think it does). It uses that same mental model, a set of beverage racks and a controller. I did this with a simple class that represents a rack of drinks and a controller written in a console app's main routine (so everything is static).
The RackOfBeverage class (it includes a restock method that's never called):
class RackOfBeverages
{
public string Label { get; }
public int Count { get; private set; }
public RackOfBeverages(string label, int initialCount)
{
Label = label;
Count = initialCount;
}
public void Restock(int howMany)
{
Count += howMany;
}
public bool Dispense()
{
if (Count > 0)
{
--Count;
return true;
}
else
{
return false;
}
}
}
It has a label (indicating what kind of drink it is), an inventory/count and a Dispense method. The Dispense call will return false if there are no drinks left.
Then I wrote a simple controller (as the Main method in a console app along with a few other bits that I put in the Program class):
class Program
{
private static readonly Dictionary<string, RackOfBeverages> Beverages =
new Dictionary<string, RackOfBeverages>
{
{"COKE", new RackOfBeverages("Coke", 2)},
{"SPRITE", new RackOfBeverages("Sprite", 20)},
//etc.
};
static void Main(string[] args)
{
Console.WriteLine("Welcome to the machine\r\nType a selection (type \"Exit\" to quit)");
while (true)
{
var selection = Console.ReadLine();
//did the user enter the name of a drink
if (Beverages.Keys.Contains(selection, StringComparer.OrdinalIgnoreCase))
{
var beverage = Beverages[selection.ToUpper()];
//was there enough to dispense a drink
if (beverage.Dispense())
{
Console.WriteLine($"Here's your {beverage.Label}");
}
else
{
Console.WriteLine($"Sorry, no {beverage.Label} for you");
}
}
//or, perhaps, the user chose to exit the app
else if (selection.ToUpper() == "EXIT")
{
Console.WriteLine("Exiting");
break;
}
//finally, if the user didn't enter anything I understand
//let him/her know and then let him/her try again
else
{
Console.WriteLine("Pick a valid selection");
}
}
}
}
Separating out the "concerns" in an application is important. Here, the UI is completely separate from the beverage storage. If you want to add another beverage, just add it to the Dictionary up at the top, and the UI will continue to function with the old beverages and the new beverage you added.
I guess you want this code, If _coke less than 0 show message ,otherwise subtract _coke value.
public int Coke
{
get { return _coke; }
set
{
if (_coke <= 0)
{
Console.WriteLine("No more Coke Left!");
}
else
{
_coke = value;
}
}
}

How to find all the possible words using adjacent letters in a matrix

I have the following test matrix:
a l i
g t m
j e a
I intend to create an algorithm that helps me find every possible word from a given minimum length to a maximum length using adjacent letters only.
For example:
Minimum: 3 letters
Maximum: 6 letters
Based on the test matrix, I should have the following results:
ali
alm
alg
alt
ati
atm
atg
...
atmea
etc.
I created a test code (C#) that has a custom class which represents the letters.
Each letter knows its neighbors and has a generation counter (for keeping track of them during traversal).
Here is its code:
public class Letter
{
public int X { get; set; }
public int Y { get; set; }
public char Character { get; set; }
public List<Letter> Neighbors { get; set; }
public Letter PreviousLetter { get; set; }
public int Generation { get; set; }
public Letter(char character)
{
Neighbors = new List<Letter>();
Character = character;
}
public void SetGeneration(int generation)
{
foreach (var item in Neighbors)
{
item.Generation = generation;
}
}
}
I figured out that if I want it to be dynamic, it has to be based on recursion.
Unfortunately, the following code creates the first 4 words, then stops. It is no wonder, as the recursion is stopped by the specified generation level.
The main problem is that the recursion returns only one level but it would be better to return to the starting point.
private static void GenerateWords(Letter input, int maxLength, StringBuilder sb)
{
if (input.Generation >= maxLength)
{
if (sb.Length == maxLength)
{
allWords.Add(sb.ToString());
sb.Remove(sb.Length - 1, 1);
}
return;
}
sb.Append(input.Character);
if (input.Neighbors.Count > 0)
{
foreach (var child in input.Neighbors)
{
if (input.PreviousLetter == child)
continue;
child.PreviousLetter = input;
child.Generation = input.Generation + 1;
GenerateWords(child, maxLength, sb);
}
}
}
So, I feel a little stuck, any idea how I should proceed?
From here, you can treat this as a graph traversal problem. You start at each given letter, finding each path of length min_size to max_size, with 3 and 6 as those values in your example. I suggest a recursive routine that builds the words as paths through the grid. This will look something like the following; replace types and pseudo-code with your preferences.
<array_of_string> build_word(size, current_node) {
if (size == 1) return current_node.letter as an array_of_string;
result = <empty array_of_string>
for each next_node in current_node.neighbours {
solution_list = build_word(size-1, next_node);
for each word in solution_list {
// add current_node.letter to front of that word.
// add this new word to the result array
}
}
return the result array_of_string
}
Does that move you toward a solution?
When solving these kind of problems, I tend to use immutable classes because everything is so much easier to reason about. The following implementation makes use of a ad hoc ImmutableStack because its pretty straightforward to implement one. In production code I'd probably want to look into System.Collections.Immutable to improve performance (visited would be an ImmutableHashSet<> to point out the obvious case).
So why do I need an immutable stack? To keep track of the current character path and visited "locations" inside the matrix. Because the selected tool for the job is immutable, sending it down recursive calls is a no brainer, we know it can't change so I don't have to worry about my invariants in every recursion level.
So lets implement an immutable stack.
We'll also implement a helper class Coordinates that encapsulates our "locations" inside the matrix, will give us value equality semantics and a convenient way to obtain valid neighbors of any given location. It will obviously come in handy.
public class ImmutableStack<T>: IEnumerable<T>
{
private readonly T head;
private readonly ImmutableStack<T> tail;
public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>(default(T), null);
public int Count => this == Empty ? 0 : tail.Count + 1;
private ImmutableStack(T head, ImmutableStack<T> tail)
{
this.head = head;
this.tail = tail;
}
public T Peek()
{
if (this == Empty)
throw new InvalidOperationException("Can not peek an empty stack.");
return head;
}
public ImmutableStack<T> Pop()
{
if (this == Empty)
throw new InvalidOperationException("Can not pop an empty stack.");
return tail;
}
public ImmutableStack<T> Push(T value) => new ImmutableStack<T>(value, this);
public IEnumerator<T> GetEnumerator()
{
var current = this;
while (current != Empty)
{
yield return current.head;
current = current.tail;
}
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
struct Coordinates: IEquatable<Coordinates>
{
public int Row { get; }
public int Column { get; }
public Coordinates(int row, int column)
{
Row = row;
Column = column;
}
public bool Equals(Coordinates other) => Column == other.Column && Row == other.Row;
public override bool Equals(object obj)
{
if (obj is Coordinates)
{
return Equals((Coordinates)obj);
}
return false;
}
public override int GetHashCode() => unchecked(27947 ^ Row ^ Column);
public IEnumerable<Coordinates> GetNeighbors(int rows, int columns)
{
var increasedRow = Row + 1;
var decreasedRow = Row - 1;
var increasedColumn = Column + 1;
var decreasedColumn = Column - 1;
var canIncreaseRow = increasedRow < rows;
var canIncreaseColumn = increasedColumn < columns;
var canDecreaseRow = decreasedRow > -1;
var canDecreaseColumn = decreasedColumn > -1;
if (canDecreaseRow)
{
if (canDecreaseColumn)
{
yield return new Coordinates(decreasedRow, decreasedColumn);
}
yield return new Coordinates(decreasedRow, Column);
if (canIncreaseColumn)
{
yield return new Coordinates(decreasedRow, increasedColumn);
}
}
if (canIncreaseRow)
{
if (canDecreaseColumn)
{
yield return new Coordinates(increasedRow, decreasedColumn);
}
yield return new Coordinates(increasedRow, Column);
if (canIncreaseColumn)
{
yield return new Coordinates(increasedRow, increasedColumn);
}
}
if (canDecreaseColumn)
{
yield return new Coordinates(Row, decreasedColumn);
}
if (canIncreaseColumn)
{
yield return new Coordinates(Row, increasedColumn);
}
}
}
Ok, now we need a method that traverses the matrix visiting each position once returning words that have a specified minimum number of characters and don't exceed a specified maximum.
public static IEnumerable<string> GetWords(char[,] matrix,
Coordinates startingPoint,
int minimumLength,
int maximumLength)
That looks about right. Now, when recursing we need to keep track of what characters we've visited, That's easy using our immutable stack, so our recursive method will look like:
static IEnumerable<string> getWords(char[,] matrix,
ImmutableStack<char> path,
ImmutableStack<Coordinates> visited,
Coordinates coordinates,
int minimumLength,
int maximumLength)
Now the rest is just plumbing and connecting the wires:
public static IEnumerable<string> GetWords(char[,] matrix,
Coordinates startingPoint,
int minimumLength,
int maximumLength)
=> getWords(matrix,
ImmutableStack<char>.Empty,
ImmutableStack<Coordinates>.Empty,
startingPoint,
minimumLength,
maximumLength);
static IEnumerable<string> getWords(char[,] matrix,
ImmutableStack<char> path,
ImmutableStack<Coordinates> visited,
Coordinates coordinates,
int minimumLength,
int maximumLength)
{
var newPath = path.Push(matrix[coordinates.Row, coordinates.Column]);
var newVisited = visited.Push(coordinates);
if (newPath.Count > maximumLength)
{
yield break;
}
else if (newPath.Count >= minimumLength)
{
yield return new string(newPath.Reverse().ToArray());
}
foreach (Coordinates neighbor in coordinates.GetNeighbors(matrix.GetLength(0), matrix.GetLength(1)))
{
if (!visited.Contains(neighbor))
{
foreach (var word in getWords(matrix,
newPath,
newVisited,
neighbor,
minimumLength,
maximumLength))
{
yield return word;
}
}
}
}
And we're done. Is this the most elegant or fastest algorithm? Probably not, but I find it the most understandable and therefore maintainable. Hope it helps you out.
UPDATE Based upon comments below, I've run a few test cases one of which is:
var matrix = new[,] { {'a', 'l'},
{'g', 't'} };
var words = GetWords(matrix, new Coordinates(0,0), 2, 4);
Console.WriteLine(string.Join(Environment.NewLine, words.Select((w,i) => $"{i:00}: {w}")));
And the outcome is the expected:
00: ag
01: agl
02: aglt
03: agt
04: agtl
05: at
06: atl
07: atlg
08: atg
09: atgl
10: al
11: alg
12: algt
13: alt
14: altg

Search Integer Array for duplicates

I'm currently creating a very basic game in C# and I have an inventory system created which using a very simple command (Items.Add(id, amount)) you can add items to said inventory. What I want to be able to do, which my current system does not do is be able to effectively "search" my inventory array which is a 2D array holding the item id and item amount. My current system is like this:
public static void add(int id, int amount)
{
for (int i = 0; i < Ship_Builder.Player.invCount; i++)
{
if (Ship_Builder.Player.inv[i, 0] == 0)
{
Ship_Builder.Player.inv[i, 0] = id;
Ship_Builder.Player.inv[i, 1] = amount;
}
}
Ship_Builder.Player.invCount++;
}
and I want it to (in an else if) be able to search the array. I did have this:
else if (Ship_Builder.Player.inv[i, 0] == Ship_Builder.Player.inv[i + 1, 0])
{
//Do
}
Before, but it didn't work how I wanted it to.
Any help would be greatly appreciated thanks,
Laurence.
As comments suggest, you should use a Dictionary for such a task. But if you have to use a 2-d array, which is (i presume) pre-populated with zeros before we add any items to it, then an if-else statement like you propose won't do the trick. What you need to do is iterate through the array looking for a matching id first and each time your ids don't match, you have to check if the id that you're currently checking is equal to 0. If it is, then you have traversed all "slots" which had some items in them without finding a match, which means this item must go into another, empty slot.
public static void add(int id, int amount)
{
for (int i = 0; i < Ship_Builder.Player.invCount; i++)
{
if (Ship_Builder.Player.inv[i, 0] != id)
{
if (Ship_Builder.Player.inv[i, 0] == 0)
{
Ship_Builder.Player.inv[i, 0] = id;
Ship_Builder.Player.inv[i, 1] = amount;
Ship_Builder.Player.invCount++;
continue;
}
}
else
{
Ship_Builder.Player.inv[i, 1] += amount;
continue;
}
}
}
Warning! My answer assumes that you locate new items in the empty slot with the smallest possible index. Also, if you are removing items and setting the id to zero as a result, then you'll have to traverse the whole array first in search of a matching index before you can allocate a new item. Which might get very expensive time-wise if the array is large.
There's a lot going on here (and there isn't enough detail to give any answer except in broad strokes), but how I would approach something like this would be to start with using object oriented designs rather than relying on indexed positions in arrays. I'd define something like this:
public class InventoryItem
{
public int Id { get; set; }
public int Amount { get; set; }
// Now I can add other useful properties here too
// ...like Name perhaps?
}
Now I'd make my inventory a Dictionary<int,InventoryItem> and adding something to my inventory might look something like this:
public void Add(int id, int amount)
{
// assuming myInventory is our inventory
if (myInventory.ContainsKey(id)) {
myInventory[id].Amount += amount;
}
else {
myInventory[id] = new InventoryItem()
{
Id = id,
Amount = amount
};
}
}
Now it's not necessary that you actually use the InventoryItem class, you could just stick with Dictonary<int,int>, but you'll probably find as you work through it that you'd much rather have some objects to work with.
Then you could probably have a master dictionary of all objects and just add them to your inventory, so you end up with something like:
public void Add(InventoryItem item, int amount)
{
// assuming myInventory is our inventory
if (myInventory.ContainsKey(item.Id)) {
myInventory[item.Id].Amount += amount;
}
else {
myInventory[item.Id] = new InventoryItem(item) // assuming you added a
// copy constructor, for example
{
Amount = amount
};
}
}
Depending on speed performance requirements (using arrays should be only slightly faster than this) you could just skip the hard coded values and arrays all together. This has a few semi-advanced topics:
public abstract class InventoryItem
// or interface
{
public abstract string Name { get; }
public int Count { get; set; }
}
public class InventoryGold : InventoryItem
{
public string Name { get { return "Gold" } }
}
public abstract class InventoryWeapon : InventoryItem { }
public class OgreSlayingKnife : InventoryWeapon
{
public string Name { get { return "Ogre Slaying Knife"; } }
public int VersusOgres { get { return +9; } }
}
public UpdateCount<Item>(this ICollection<Item> instance,
int absoluteCount)
{
var item = instance.OfType<Item>().FirstOrDefault();
if (item == null && absoluteCount > 0)
{
item = default(Item);
item.Count = absoluteCount;
instance.add(item);
}
else
{
if (absoluteCount > 0)
item.Count = absoluteCount;
else
instance.Remove(item);
}
}
// Probably should be a Hashset
var inventory = new List<InventoryItem>();
inventory.UpdateCount<InventoryGold>(10);
inventory.UpdateCount<OgreSlayingKnife(1)

How to check if an item is already in a ListBox

Say I have a view model defined this way
public class DataVM
{
public int number { get; set; }
public string name { get; set; }
}
Then somewhere in my code I want to do this to populate DataListbox:
List<DataVM> data = new List<DataVM>();
for (int i = 0; i < data.Count; i++)
{
if (DataListbox.Items.Contains(data[i]))
{
//do nothing
}
else
{
DataListbox.Add(data[i]);
}
}
However, this line if (DataListbox.Items.Contains(data[i])) always evaluate to false even when that item is already in DataListbox and it should evaluate to true. I don't get why it doesn't work.
What am I doing wrong here and how do I fix it?
The reason why your code always evaluates false is because the .NET framework compares the pointers to the memory and not the variables content by default when using checking for equality of two objects.
So instead of using the built in Contains function you should iterate through all elements of the listbox and check by comparing an unique property if the item was already added to the listbox:
You would have to do something like this (using LINQ; Replace data[i].name and item.Value with the unique property):
bool listContainsItem = DataListbox.Items.Any(item => item.Value == data[i].name);
Or by using "old" coding style:
for (int i = 0; i < data.Count; i++)
{
bool itemAlreadyAdded = false;
foreach (var item in DataListbox.Items)
{
if (item.Value == data[i].name)
{
itemAlreadyAdded = true;
break;
}
}
if (itemAlreadyAdded)
{
//do nothing
}
else
{
DataListbox.Add(data[i]);
}
}
The Contains method uses the Equals method of the class being checked.
In this case the DataVM class needs to override the Equals method
public class DataVM
{
public int number { get; set; }
public stringname { get; set; }
public override bool Equals(object obj)
{
bool areEqual ;
areEqual = false ;
if((obj != null) && (obj instanceOf DataVM))
{
//compare fields to determine if they are equal
areEqual = (DataVM(obj)).number == this.number ;
}
return areEqual ;
}
public override int GetHashCode()
{
//calculate a hash code base on desired properties
return number ;
}
}
When you override the Equals method is necessary for you to override also the GetHashCode method
You cannt match your own classes. Even if the Properties are the same, it´s baisicaly not the same Object (In your case its not the same DataVM). It´s like trying to match your Blue car with an ather one, baisicaly it´s the same but the location for examle is different.
Try matching properties of the object or write an own function. (I made a function)
List<DataVM> data = new List<DataVM>();
for (int i = 0; i < data.Count; i++)
{
if (ListContainsDataVM(DataListbox.Items, data[i]))
{
//do nothing
}
else
{
DataListbox.Add(data[i]);
}
}
public bool ListContainsDataVM(List<DataVM> DataVMList, DataVM myDataVM)
{
foreach (var dataVm in DataVMList)
{
if (dataVm.number == myDataVM.number && dataVm.stringname == DataVM.stringname)
{
return true;
}
}
return false;
}
I cannot reply to previous answer, but this is the version as it should be I believe
bool listContainsItem = DataListbox.Items.Cast<DataVM>().Any(item => item.Value == data[i].name);
it was missing the cast...
On my case, I just want to check if my selected item on ComboBox exists in ListBox
This code works for me:
string selected_i = comboBox.SelectedItem; //selected_i = "selected item"
if(!listbox.Items.Contains(selected_i))
{
listbox.Items.Add(selected_i);
}
else{
MessageBox.Show("Item already exists in Listbox!");
}

Categories

Resources