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;
}
}
}
Related
Good day everyone. To my shame dont understand how to make it. I have a class like:
public class Worker
{
public int ID { get; set; }
}
And Data class with workers List. Have a method to work with it:
public Department GetWorkerByID (int id)
{
foreach (Worker worker in Workers)
{
if (worker.ID == id)
return worker;
}
return null;
{
The problem is i need to give the user ability to choose the current worker from list by its ID number and i want program to handle invalid input like random simbols, letters etc and check if the input ID is exist. Usually i use boolean property for this:
string input = Console.ReadLine();
bool isInt = int.TryParse(input, out _);
And so on. But in case with class properties it doesnt work like i expected and when i try to do this:
while (!isInt && Data.GetWorkerByID(int.Parse(input)) == null)
{
Console.Write("Wrong input. Try again: ");
input = Console.ReadLine();
}
Its not working, throwing me the exceptions of invalid format for ID number or pass the number but than trying to get the non existing instance of worker. Any advice welcome.
Try do something like that:
C#:
public Worker TryGetWorker()
{
string input = Console.ReadLine();
int index = -1;
bool isInt = int.TryParse(input,out index);
Worker worker = Data.GetWorkerByID(index);
if (!isInt || worker == null)
{
Console.Write("Wrong input. Try again: ");
return TryGetWorker();
}
else
return worker;
}
I need to allow the users choose their own path by picking between two simple choices displayed on their screen in order to progress to the next set of choices, until they get to one of the endings, i.e something like this should be achieved:
I have tried the following code, but only the left side is evaluated each time. I am wondering how can I achieve a results like the above image (covering all the branches)? For instance, if the user selects "No" the application shouldn't ask any further question from the user and just simply shows the "Maybe you want a Pizza" message. I have done this with decision tree algorithm and need to fix it, so that it covers both the left and right sides just like the above image.
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
var decisionTree = MainDecisionTree();
var client = new Client();
Console.WriteLine("Do you want a book? true/false");
client.Answer[0] = bool.Parse(Console.ReadLine());
Console.WriteLine("Do you like it? true/false");
client.Answer[1] = bool.Parse(Console.ReadLine());
Console.WriteLine("Are you sure? true/false");
client.Answer[2] = bool.Parse(Console.ReadLine());
decisionTree.Evaluate(client);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
private static DecisionQuery MainDecisionTree()
{
//Decision 2
var wantBranch = new DecisionQuery
{
Title = "Do you want a book?",
Test = (client) => client.Answer[0],
Positive = new DecisionResult { Result = true },
Negative = new DecisionResult { Result = false }
};
//Decision 1
var deserveBranch = new DecisionQuery
{
Title = "Do you like it?",
Test = (client) => client.Answer[1],
Positive = wantBranch,
Negative = new DecisionResult { Result = false }
};
//Decision 0
var sureBranch = new DecisionQuery
{
Title = "Are you sure?",
Test = (client) => client.Answer[2],
Positive = deserveBranch,
Negative = new DecisionResult { Result = false }
};
return sureBranch;
}
}
public class DecisionResult : Decision
{
public bool Result { get; set; }
public override void Evaluate(Client client)
{
Console.WriteLine("\r\nThe result: {0}", Result ? "Buy it" : "You need to wait");
}
}
public class DecisionQuery : Decision
{
public string Title { get; set; }
public Decision Positive { get; set; }
public Decision Negative { get; set; }
public Func<Client, bool> Test { get; set; }
public override void Evaluate(Client client)
{
bool result = this.Test(client);
string resultAsString = result ? "yes" : "no";
Console.WriteLine($"\t- {this.Title}? {resultAsString}");
if (result) this.Positive.Evaluate(client);
else this.Negative.Evaluate(client);
}
}
public abstract class Decision
{
public abstract void Evaluate(Client client);
}
public class Client
{
public bool[] Answer { get; set; } = new bool[3];
}
}
If I understand your issue, here is your code corrected.
I renamed some things.
I called MakeDecisionTree the method that initializes the expert system with the conditions tree and it returns the root condition.
Each condition contains a sentence to evaluate and it can be a query or a result.
For a result, the evaluate display the sentence.
For a query, the evaluate method asks the user to answer the question by yes or no. And using this answer, it calls the corresponding evaluate of the next child condition.
Sorry for my english here, it is not my native language, and I'm not involved in AI.
static private void DecisionTreeTest()
{
Console.WriteLine("Please, answer a few questions with yes or no.");
Console.WriteLine();
MakeDecisionTree().Evaluate();
}
static private bool GetUserAnswer(string question)
{
Console.WriteLine(question);
string userInput;
while ( true )
{
userInput = Console.ReadLine().ToLower();
if ( userInput == "yes" )
return true;
else
if ( userInput == "no" )
return false;
else
Console.WriteLine("Your answer is not supported, retry please." +
Environment.NewLine + Environment.NewLine +
question);
}
}
static private DecisionTreeQuery MakeDecisionTree()
{
var queryAreYouSure
= new DecisionTreeQuery("Are you sure?",
new DecisionTreeResult("Buy it."),
new DecisionTreeResult("You need to wait."),
GetUserAnswer);
var queryIsItAGoodBook
= new DecisionTreeQuery("Is it a good book?",
new DecisionTreeResult("What are you waiting for? Just buy it."),
new DecisionTreeResult("Find another one."),
GetUserAnswer);
var queryDoYouLikeIt
= new DecisionTreeQuery("Do you like it?",
queryAreYouSure,
queryIsItAGoodBook,
GetUserAnswer);
var queryDoYouWantABook
= new DecisionTreeQuery("Do you want a book?",
queryDoYouLikeIt,
new DecisionTreeResult("Maybe you want a pizza."),
GetUserAnswer);
return queryDoYouWantABook;
}
abstract public class DecisionTreeCondition
{
protected string Sentence { get; private set; }
abstract public void Evaluate();
public DecisionTreeCondition(string sentence)
{
Sentence = sentence;
}
}
public class DecisionTreeQuery : DecisionTreeCondition
{
private DecisionTreeCondition Positive;
private DecisionTreeCondition Negative;
private Func<string, bool> UserAnswerProvider;
public override void Evaluate()
{
if ( UserAnswerProvider(Sentence) )
Positive.Evaluate();
else
Negative.Evaluate();
}
public DecisionTreeQuery(string sentence,
DecisionTreeCondition positive,
DecisionTreeCondition negative,
Func<string, bool> userAnswerProvider)
: base(sentence)
{
Positive = positive;
Negative = negative;
UserAnswerProvider = userAnswerProvider;
}
}
public class DecisionTreeResult : DecisionTreeCondition
{
public override void Evaluate()
{
Console.WriteLine(Sentence);
}
public DecisionTreeResult(string sentence)
: base(sentence)
{
}
}
You decision tree design is very good, just the way you ask the user is not.
You ask all the questions in advance, and go to the decision tree later on.
Of course the tree cannot make asked questions unasked.
It's not a decision tree problem.
you would have to put these pairs of lines:
Console.WriteLine("Are you sure? true/false");
client.Answer[2] = bool.Parse(Console.ReadLine());
into the Evaluate method, or you do a callback or whatever architectural design to separate logic from user-interface.
The decision-tree has to ask, or at least know the question.
The answer does not need to be stored, you can evaluate it right after input.
Maybe you pass something to Ask the user to the evaluate Method
public interface IAsk
{
bool Question(string message);
}
And for your sample you instantiate that with
public class ConsoleAsk : IAsk
{
bool Question(string message)
{
Console.WriteLine(message);
return bool.Parse(Console.ReadLine());
}
}
and you modify your Evaluate like this:
public override void Evaluate(IAsk ask)
{
bool result = ask.Question("do you feel good or bad");
string resultAsString = result ? "yes" : "no";
Console.WriteLine($"\t- {this.Title}? {resultAsString}");
if (result) this.Positive.Evaluate(ask);
else this.Negative.Evaluate(ask);
}
of course you replace the question with your title property.
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)
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.
Ok, I have smashed in my head for the better of the day now.
I have two lists of custom objects with one property the same at both lists. I need to iterate through both lists and see if the property is the same.
I could do it with nested for-each loops, but I'm rather reluctant if I can do the same with LINQ (and I'm sure I can do that).
I have tried almost everything but I simply can't get to the solution I am looking for.
Here is the code for the objects I am using for the Lists.
public class Game
{
// Fields
private short maxPlayers;
private Team axis;
private Team allies;
// Properties
public string Name { get; set; }
public short MaxPlayers
{
get
{
return maxPlayers;
}
set
{
if (value > 8)
maxPlayers = 8;
else if (value < 2)
maxPlayers = 2;
else
maxPlayers = value;
}
}
public short CurrentPlayers
{
get
{
int players = axis.Players.Count + allies.Players.Count;
return (short)players;
}
}
public bool IsFull
{
get
{
if (CurrentPlayers == MaxPlayers)
return true;
else
return false;
}
}
public Team Axis { get; set; }
public Team Allies { get; set; }
public List<Player> Players
{
// Somehow this does not work either, so I had to stick with one single team in the for-each loops. Ideas to fix?
get
{
if (allies.Players.Count == 0)
return axis.Players.Concat(allies.Players).ToList();
else
return allies.Players.Concat(axis.Players).ToList();
}
}
//Constructor
public Game()
{
axis = new Team();
allies = new Team();
}
}
public class Team
{
public List<Player> Players { get; set; }
public EFaction Faction { get; set; }
public enum EFaction
{
Allies,
Axis,
Random
}
public Team()
{
Players = new List<Player>();
Faction = EFaction.Random;
}
}
public class Player
{
private int skillRange = 200;
public string Name { get; set; }
public int Skill { get; set; }
public int SkillRange
{
get
{
return skillRange;
}
set
{
if (value >= 200)
skillRange = value;
else
skillRange = 200;
}
}
}
At startup I populate the List from a database and do the same with the List. What I want to do is iterate through the game list and compare the skill property for each player in the game with the skill property of each player on a team. Here is the for-each loop I used. this worked. But you clearly can see why I want to cut it down so badly.
// Loop through each player in the Automatch queue.
foreach (Team team in match.TeamsInQueue)
{
// Loop through every game in the Atomatch queue.
foreach (Game game in match.AvailableGames)
{
int teamPlayersInSkillRange = 0;
// Loop through every player in the team and loop through every player in the game.
foreach (Player teamPlayer in team.Players)
{
int gamePlayersInSkillRange = 0;
foreach (Player gamePlayer in game.Allies.Players)
{
// Compare beoth skill values. If they are in a certain range increase the counter.
if (Math.Abs(teamPlayer.Skill - gamePlayer.Skill) <= 200) // The range is currently set for 200, but I want to make it variable later.
gamePlayersInSkillRange++;
}
// Check if the player in the team is in skill range of the game he wants to join. If yes increase the counter.
if (gamePlayersInSkillRange == game.Allies.Players.Count)
teamPlayersInSkillRange++;
}
// Check if the whole team is in skill range of the game they want to join. If yes return true.
if (teamPlayersInSkillRange == team.Players.Count)
{
// ToDo: Implement join process here.
}
}
}
Any help would be appreciated. Thanks.
For a given team and game, you want the team to join the game if all of the team's player's are in the skill range of all the game's players. This sounds like a job for the All() method!
// Loop through each player in the Automatch queue.
foreach (Team team in match.TeamsInQueue)
{
// Loop through every game in the Atomatch queue.
foreach (Game game in match.AvailableGames)
{
bool allInSkillRange = team.Players.All(t =>
game.Allies.Players.All(g => Math.Abs(t.Skill - g.Skill) <= 200));
if(allInSkillRange)
{
// ToDo: Implement join process here.
}
}
}
If you're interested in an automated way to convert code to LINQ, look at Resharper.
Here's how I came up with the solution. Using this process along with becoming familiar with LINQ's methods and acquiring experience can make refactoring much easier. I didn't just look at the code and immediately think of using All(). I started by refactoring smaller sections and then going from there. After reading over all of the code I focused on the inner-most loop.
int gamePlayersInSkillRange = 0;
foreach (Player gamePlayer in game.Allies.Players)
{
// Compare beoth skill values. If they are in a certain range increase the counter.
if (Math.Abs(teamPlayer.Skill - gamePlayer.Skill) <= 200) // The range is currently set for 200, but I want to make it variable later.
gamePlayersInSkillRange++;
}
This counts the number of players that satisfy a condition, which can be refactored into a Count() call:
int gamePlayersInSkillRange = game.Allies.Players.Count(g =>
(Math.Abs(teamPlayer.Skill - g.Skill) <= 200);
The following if statement checks if gamePlayersInSkillRange equals the number of items in game.Allies.Players, which is the list that we were originally counting. Oh, so we're checking if all of those list members satisfy the predicate! We can include that step in the LINQ call by changing Count() to All(). Here is what the next innermost loop looks like now:
foreach (Player teamPlayer in team.Players)
{
bool allGamePlayersInRange = game.Allies.Players.All(g =>
(Math.Abs(teamPlayer.Skill - g.Skill) <= 200);
// Check if the player in the team is in skill range of the game he wants to join. If yes increase the counter.
if (allGamePlayersInRange)
teamPlayersInSkillRange++;
}
Now this loop looks like it could be refactored into a Count() call. But if we examine the code just after it closely, we see it is the exact same pattern as the loop we just refactored, meaning we can jump right into refactoring it into an All() call, completing the refactoring.
Try this:
foreach(Team team in match.TeamsInQueue)
{
if(team.Players.Insersect(match
.SelectMany(m => m.AvailableGames, g=> g.Allies.Players),
new PlayerSkillComparer().Count() == team.Players.Count()) {
// ToDO: Implement join process here.
}
}
Where PlayerSkillComparer implements IEqualityComparer<Player> and its Equals method returns true if the two given Player objects have a skill difference <= 200. See an example here.