C# - String Arrays - c#

I am attempting to code a mini-game. I am new to C#. The first thing I want to do is have a string array that holds names of enemies.
string[] Opponents = new string[]
{
"enemy1", "enemy2", "enemy3", "enemy4", "enemy5", "enemy6", "enemy7", "enemy8", "enemy9", "enemy10", "enemy11", "enemy12", "enemy13", "enemy14", "enemy15", "enemy16", "enemy17", "enemy18", "enemy19", "enemy20", "enemy21", "enemy22", "enemy23", "enemy24", "enemy25", "enemy26", "enemy27", "enemy28", "enemy29", "enemy30", "enemy31", "enemy32", "enemy33", "enemy34", "enemy35", "enemy36", "enemy37"
};
I want to take this string and use the values to make waves of 5 enemies. Each time a wave is displayed, I would like to have 3 enemies removed and the next wave include 2 new enemies, placing them at the beginning of the wave. If the wave only has 3 enemies left, the next wave will have a new set of 5 (or whatever is left in he array) I would like it to look like this:
Wave 1: enemy1, enemy2, enemy3, enemy4, enemy5
Wave 2: enemy6, enemy7, enemy4, enemy5
Wave 3: enemy8, enemy9, enemy5
Wave 4: enemy10, enemy11, enemy12, enemy13, enemy14
etc...
This should be possible with any length of the array as the number of enemies later on will change.
I know I need to use a loop to do so, but I am having trouble...
This is what I have started with:
class Program
{
static void Main()
{
Game game = new Game();
game.Waves();
Console.ReadKey();
}
class Game
{
public void Waves()
{
string[] Opponents = new string[]
{
"enemy1", "enemy2", "enemy3", "enemy4", "enemy5", "enemy6", "enemy7", "enemy8", "enemy9", "enemy10", "enemy11", "enemy12", "enemy13", "enemy14", "enemy15", "enemy16", "enemy17", "enemy18", "enemy19", "enemy20", "enemy21", "enemy22", "enemy23", "enemy24", "enemy25", "enemy26", "enemy27", "enemy28", "enemy29", "enemy30", "enemy31", "enemy32", "enemy33", "enemy34", "enemy35", "enemy36", "enemy37"
};
string[] activeWave = new string[5];
Array.Copy(Opponents, 0, activeWave, 0, 5);
Console.WriteLine("Current wave of opponents: " + activeWave[0] + ", " + activeWave[1] + ", " + activeWave[2] + ", " + activeWave[3] + ", " + activeWave[4]);
}
}
}
Any help would be appreciated. Am I even on the right path?

I've drafted up a solution using queues. I'm not really that good at C#, so I'm sure there's a couple of syntax errors and typos, but I hope this gets you on the right track.
class Program{
string [] Opponents = ...;
int currentPlace = 0;
int curWaveNum = 0;
int newWaveSize = 5;
int newEnemiesPerWave = 2;
int enemiesRemovedPerWave = 3;
// Creates a queue, or a "FIFO" (First In, First Out) data structure.
Queue myQ = newQueue();
void addWave(){
// Remove enemies until you've gotten to enemiesRemovedPerWave, or until the
// wave is empty
for(int i = 0 ; i < enemiesRemovedPerWave ; i++){
if(myQ.count == 0){
break;
}
myQ.Dequeue();
}
// Set toAdd to be newWaveSize if the queue is empty, or newEnemiesPerWave if
// it's not empty
int toAdd = (myQ.count == 0 ? newWaveSize : newEnemiesPerWave);
// Add the enemies to the queue, from the Opponents array
for(int i = 0 ; i < toAdd ; i++){
// If there's only one enemy left in the Opponents array, only attempt
// to add one enemy to myQ
if(currentPlace + i >= Opponents.count){
break;
}
else{
// Actually add to the queue
myQ.enqueue(Opponents[currentPlace + i]);
}
}
// Update currentPlace. Note that you could have the above for loop be:
// for( ; currentPlace < currentPlace + toAdd ; currentPlace++)
// but I think how I wrote it is easier to understand
currentPlace += toAdd;
}
void printWave(){
Console.write("Wave %d: ", waveNum);
// Print everything but the last one
for(int i = 0 ; i < myQ.count - 1 ; i++){
Console.write(myQ[i] + ", ");
}
Console.write(myQ[myQ.count - 1] + "\n");
}
public void play(){
while(currentPlace < Opponents.count){
addWave();
printWave();
curWaveNum++;
}
}
}
The key to this is a data structure called a Queue. Think of it as an actual queue (line) at a store--the first people to get in line are served first. This is in contrast to a "LIFO" data structure, known as a Stack. A Stack behaves like, well, a stack of pancakes. When you make pancakes, you typically put the newest cooked ones on top, meaning the first pancake to bee taken will be the last one that was made, hence "LIFO," or "Last In, First Out."
You can read more about the functions defined for queues here!
Edit: Added printWave and play

Related

What should I do in order to display how many loops it takes to get a favourable number?

Im new to programming and Im currently attempting to make a dice program, where the user can input how many throws they would like to do and then a list will display how many throws it took to get a specific number, in this case that number is 6 (later on I'd like to make it for all numbers 1-6) How should I go about doing this?
Im currently trying to use an if-statement to recognize when a specific number is rolled, currently I want the program to recognize the number 6, but im a bit unsure how to display the amount of rolls it took to get that number, in a list, and also keeping the loop going until all rolls have been executed.
private void Btnkast_Click(object sender, EventArgs e)
{
bool throws;
int numberofthrows = 0;
int dice;
Random dicethrow = new Random();
throws = int.TryParse(rtbantal.Text, out numberofthrows);
int[] list = new int[numberofthrows];
for (int i = 0; i <= numberofthrows; i++)
{
dice = dicethrow.Next(1, 7);
if (dice == 6)
{...}
}
}
Also, the only reason I use tryparse is to prevent crashes when having to handle with string-values.
I have written this for you using a C# Console Application, but I'm sure you will be able to edit it to fit your requirements for Windows Forms.
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main()
{
Random rnd = new Random(); // create Random object
Console.WriteLine("Enter a number between 1 and 6: "); // prompt user to enter a number between 1 and 6
int chosenNumberInt;
var chosenNumber = int.TryParse(Console.ReadLine(), out chosenNumberInt); // check to see if user actually entered a number. If so, put that number into the chosenNumberInt variable
Console.WriteLine("How many rolls would you like?"); // prompt user to enter how many rolls they would like to have
int chosenRollsInt;
var chosenRolls = int.TryParse(Console.ReadLine(), out chosenRollsInt);
Console.WriteLine(); // to create space
Console.WriteLine(); // to create space
Console.WriteLine("Chosen Number = " + chosenNumberInt + " --- Chosen Rolls = " + chosenRollsInt); // show user what they entered
Console.WriteLine("------------");
int count = 0;
int numberRolled = 0;
var lstRolls = new List<int>(); // create list object
for(int i = 1; i <= chosenRollsInt; i++)
{
count++;
int dice = rnd.Next(1, 7);
numberRolled = dice;
lstRolls.Add(numberRolled); // add each roll to the list
Console.WriteLine("Roll " + i + " = " + numberRolled); // show each roll
}
var attempts = lstRolls.Count; // how many rolls did you do
var firstIndexOfChosenNumber = lstRolls.FindIndex(x => x == chosenNumberInt) + 1; // have to add 1 because finding the index is 0-based
Console.WriteLine("-------------");
if(firstIndexOfChosenNumber == 0)
Console.WriteLine("The chosen number was " + chosenNumberInt + " and that number was NEVER rolled with " + chosenRollsInt + " rolls.");
else
Console.WriteLine("The chosen number was " + chosenNumberInt + " and the number of rolls it took to hit that number was " + firstIndexOfChosenNumber);
}
}
Something that I didn't add would be the validation to ensure that the user does indeed enter a number between 1 and 6, but you can do that I'm sure.
I have created a DotNetFiddle that proves this code does work and even shows you each roll.
Let me know if this helps or if you need any more assistance.
UPDATE
Based on your comment on my original post, I have edited my code to allow the user to enter the number they want, along with how many rolls. Then, once all of the rolls have been completed, I find the index of the first occurrence of the number they selected in the beginning.
Let me know if this is what you want.
Read the comments I added in the code
private void Btnkast_Click(object sender, EventArgs e)
{
bool throws;
int numberofthrows = 0;
int dice;
Random dicethrow = new Random();
throws = int.TryParse(rtbantal.Text, out numberofthrows);
List<int> list = new List<int>(); //I changed this to a list
for (int i = 0; i < numberofthrows; i++)
{
dice = dicethrow.Next(1, 7);
list.Add(dice); //add every roll to the array to check later the values if you want
if (dice == 6)
{
//Print that you found 6 at the roll number list.Count
Console.WriteLine("Found 6 at roll number: " + list.Count);
break; //probably break the loop so you won't continue doing useless computation
}
}
}

assigning and moving instances in a array

void InsertScoreAndLeaderBoard(int pointageCurrent, string nameCurrent)
{
int savePosition;
string saveName;
if (IsBetterScore(pointageCurrent))
{
for (int i = 0; i < leaderBoardNum.Length; i++)
{
if (pointageCurrent > leaderBoardNum[i])
{
savePosition = leaderBoardNum[i] ;
saveName = leaderBoardName[i];
leaderBoardNum[i] = pointageCurrent;
leaderBoardName[i] = nameCurrent;
for (int j = leaderBoardNum.Length; j > 0; j--)
{
}
}
}
}
}
so i am kinda stuck in this code i am trying to write. I have to put a username and his score to this leader board. The thing is that i never modified an array in a way so, for example, if i replace the 2nd place with the current numbers and name, the ancient score and name is transferred to 3rd place and the old score of the 3rd place moves to 4th place and so on. The only data that has to be destroyed while moving the arrays is the last place (or position 0).
As in the comments, using arrays for this purpose is probably not a correct choice. It will make your code unreadable/complex.
There may be a valid case of using an array for this purpose: when you are trying to be very memory efficient and performance is crucial.
Suggested solutions (Lists, LINQ) allow clean (easy to read and maintain) code, but add some overhead.
Here's a sample console app, which shows how it could be done with an array, to be memory efficient (first) and also fast (second):
using System;
namespace ScoreBoardApp
{
// How a single scoreboard entry looks like.
struct ScoreboardEntry
{
public ushort scoredPoints;
public string playerName;
public ScoreboardEntry( ushort points, string name )
{
scoredPoints = points;
playerName = name;
}
}
public class Program
{
// This is our scoreboard array. Most games start with fake entries,
// to set some expectations, so are we. (Make sure you order them
// correctly here and the data is clean and neat.)
// To start empty use:
// scoreboard = new ScoreboardEntry[10];
private readonly ScoreboardEntry[] scoreboard = new [] {
new ScoreboardEntry( 7777, "Winner" ),
new ScoreboardEntry( 6666, "Hans" ),
new ScoreboardEntry( 5555, "Anna" ),
new ScoreboardEntry( 4444, "Sven" ),
new ScoreboardEntry( 3333, "Elsa" ),
new ScoreboardEntry( 2222, "Kurt" ),
new ScoreboardEntry( 1111, "Ollie" ),
new ScoreboardEntry( 999, "Bertha" ),
new ScoreboardEntry( 888, "Joana" ),
new ScoreboardEntry( 777, "Javaid" )
};
// What to show, when no player name given.
private const string playerNamePlaceholder = "<none>";
// In case we need to start from scratch.
public void ClearBoard()
{
// We could just do (after we strip "readonly" from our field):
// scoreboard = new ScoreboardEntry[scoreboard.Length];
// But this way we're re-using memory, and are still relatvely
// fast:
for (var i = 0; i < scoreboard.Length; i++)
scoreboard[i] = new ScoreboardEntry();
}
// This shows current state of the scoreboard (not very fast
// nor efficient).
public void PrintScoreboard()
{
Console.WriteLine("---+-----------------+-------");
Console.WriteLine("{0,2} | {1,-15} | {2,6}", "#", "Name", "Score");
Console.WriteLine("---+-----------------+-------");
var len = scoreboard.Length;
for (var i = 0; i < len; i++)
Console.WriteLine(
"{0,2:N0} | {1,-15} | {2,6:N0}",
i + 1,
scoreboard[i].playerName ?? playerNamePlaceholder,
scoreboard[i].scoredPoints
);
Console.WriteLine("---+-----------------+-------");
}
// This checks if the player's score reached the scoreboard
// tells us so, and if yes, then places his entry on the board.
// Should be quite efficient & fast (apart from console output).
public void RecordScore(ushort playerScore, string playerName)
{
// Cleaning the input data.
if (playerName != null)
{
// TODO: Deal with pesky control characters inside!
playerName = playerName.TrimStart();
playerName = playerName.Substring(0, Math.Min(15, playerName.Length)).TrimEnd();
}
if (string.IsNullOrWhiteSpace(playerName))
playerName = null;
// Let's compare to the last scoreboard entry.
var place = scoreboard.Length - 1;
if (playerScore <= scoreboard[place].scoredPoints)
{
Console.WriteLine(
"{0} did not make it with score {1:N0}",
playerName ?? playerNamePlaceholder,
playerScore
);
return;
}
// We'll go from bottom, to the top, to find correct landing
// spot, and at the same time, move the beaten entries down.
while (place > 0 && playerScore > scoreboard[place - 1].scoredPoints)
{
place--;
scoreboard[place + 1] = scoreboard[place];
}
// Let's record our winner.
scoreboard[place].scoredPoints = playerScore;
scoreboard[place].playerName = playerName;
Console.WriteLine(
"{0} is #{1:N0} with score {2:N0}",
playerName ?? playerNamePlaceholder,
place + 1,
playerScore
);
}
// Let's play.
public static void Main(string[] args)
{
var p = new Program();
// Initial state.
p.PrintScoreboard();
// This player should not reach the board.
p.RecordScore(666, null);
p.PrintScoreboard();
// This one scored same as the #10, which is not enough.
p.RecordScore(777, "Almost there");
p.PrintScoreboard();
// This one should land as #5.
p.RecordScore(4000, " Fifth ");
p.PrintScoreboard();
// This is the #1.
p.RecordScore(ushort.MaxValue, "Best !!!!!!!!!!!!!!!!!!!!!!!");
p.PrintScoreboard();
// No app is considered worthy, without accepting some user
// input. This one uses execution parameters.
if (args.Length >= 1 && args.Length <= 2)
{
ushort score = 0;
string name = null;
if (ushort.TryParse(args[0], out score))
{
if (args.Length > 1)
name = args[1];
p.RecordScore(score, name);
p.PrintScoreboard();
}
else
Console.Error.WriteLine("Give <score> and <name> as parameters.");
}
// Let's sweep everything before we go to sleep.
p.ClearBoard();
p.PrintScoreboard();
}
}
}
To make it faster, but a bit less efficient, we could use a class instead of a struct (and adjust code accordingly).

Where is the flaw in my algorithm for consolidating gold mines?

The setup is that, given a list of N objects like
class Mine
{
public int Distance { get; set; } // from river
public int Gold { get; set; } // in tons
}
where the cost of moving the gold from one mine to the other is
// helper function for cost of a move
Func<Tuple<Mine,Mine>, int> MoveCost = (tuple) =>
Math.Abs(tuple.Item1.Distance - tuple.Item2.Distance) * tuple.Item1.Gold;
I want to consolidate the gold into K mines.
I've written an algorithm, thought it over many times, and don't understand why it isn't working. Hopefully my comments help out. Any idea where I'm going wrong?
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Mine
{
public int Distance { get; set; } // from river
public int Gold { get; set; } // in tons
}
class Solution
{
static void Main(String[] args)
{
// helper function for reading lines
Func<string, int[]> LineToIntArray = (line) => Array.ConvertAll(line.Split(' '), Int32.Parse);
int[] line1 = LineToIntArray(Console.ReadLine());
int N = line1[0], // # of mines
K = line1[1]; // # of pickup locations
// Populate mine info
List<Mine> mines = new List<Mine>();
for(int i = 0; i < N; ++i)
{
int[] line = LineToIntArray(Console.ReadLine());
mines.Add(new Mine() { Distance = line[0], Gold = line[1] });
}
// helper function for cost of a move
Func<Tuple<Mine,Mine>, int> MoveCost = (tuple) =>
Math.Abs(tuple.Item1.Distance - tuple.Item2.Distance) * tuple.Item1.Gold;
// all move combinations
var moves = from m1 in mines
from m2 in mines
where !m1.Equals(m2)
select Tuple.Create(m1,m2);
// moves in ascending order of cost
var ordered = from m in moves
orderby MoveCost(m)
select m;
int sum = 0; // running total of move costs
var spots = Enumerable.Repeat(1, N).ToArray(); // spots[i] = 1 if hasn't been consildated into other mine, 0 otherwise
var iter = ordered.GetEnumerator();
while(iter.MoveNext() && spots.Sum() != K)
{
var move = iter.Current; // move with next smallest cost
int i = mines.IndexOf(move.Item1), // index of source mine in move
j = mines.IndexOf(move.Item2); // index of destination mine in move
if((spots[i] & spots[j]) == 1) // if the source and destination mines are both unconsolidated
{
sum += MoveCost(move); // add this consolidation to the total cost
spots[i] = 0; // "remove" mine i from the list of unconsolidated mines
}
}
Console.WriteLine(sum);
}
}
An example of a test case I'm failing is
3 1
11 3
12 2
13 1
My output is
3
and the correct output is
4
The other answer does point out a flaw in the implementation, but it fails to mention that in your code, you aren't actually changing the Gold values in the remaining Mine objects. So even if you did re-sort the data, it wouldn't help.
Furthermore, at each iteration all you really care about is the minimum value. Sorting the entire list of data is overkill. You can just scan it once to find the minimum-valued item.
You also don't really need the separate array of flags. Just maintain your move objects in a list, and after choosing a move, remove the move objects that include the Mine you would otherwise have flagged as no longer valid.
Here is a version of your algorithm that incorporates the above feedback:
static void Main(String[] args)
{
string input =
#"3 1
11 3
12 2
13 1";
StringReader reader = new StringReader(input);
// helper function for reading lines
Func<string, int[]> LineToIntArray = (line) => Array.ConvertAll(line.Split(' '), Int32.Parse);
int[] line1 = LineToIntArray(reader.ReadLine());
int N = line1[0], // # of mines
K = line1[1]; // # of pickup locations
// Populate mine info
List<Mine> mines = new List<Mine>();
for (int i = 0; i < N; ++i)
{
int[] line = LineToIntArray(reader.ReadLine());
mines.Add(new Mine() { Distance = line[0], Gold = line[1] });
}
// helper function for cost of a move
Func<Tuple<Mine, Mine>, int> MoveCost = (tuple) =>
Math.Abs(tuple.Item1.Distance - tuple.Item2.Distance) * tuple.Item1.Gold;
// all move combinations
var moves = (from m1 in mines
from m2 in mines
where !m1.Equals(m2)
select Tuple.Create(m1, m2)).ToList();
int sum = 0, // running total of move costs
unconsolidatedCount = N;
while (moves.Count > 0 && unconsolidatedCount != K)
{
var move = moves.Aggregate((a, m) => MoveCost(a) < MoveCost(m) ? a : m);
sum += MoveCost(move); // add this consolidation to the total cost
move.Item2.Gold += move.Item1.Gold;
moves.RemoveAll(m => m.Item1 == move.Item1 || m.Item2 == move.Item1);
unconsolidatedCount--;
}
Console.WriteLine("Moves: " + sum);
}
Without more detail in your question, I can't guarantee that this actually meets the specification. But it does produce the value 4 for the sum. :)
When you consolidate mine i into mine j, the amount of gold in the mine j is increased. This makes consolidations from mine j to other mines more expensive potentially making the ordering of the mines by the move cost invalid. To fix this, you could re-sort the list of mines at the beginning of each iteration of your while-loop.

C# Reverse a string array without using sort( ) or reverse( )

Hi I am trying to write a method that will reverse a string array onced called. I finished my code , but only get half of the array reversed, leaving the rest unchanged, being stuck on this for hours. so I had to ask on stack as last resort.
int start;
string[] sArray = {
"Reverse", "this", "string", "type", "array"
};
int end = (sArray.Length - 1);
for (start = 0; start < sArray.Length; start++) {
sArray[start] = sArray[end - start];
Console.Write(sArray[start] + ",");
}
// The output supposed to be : array type string this Reverse
// However, I keep getting array type string type array.
// The output supposed to be : array type string this Reverse
// However, I keep getting array type string type array.
Any ideas would be appreciated.
You are missing swapping. And you can do it with half of len of array:
string[] sArray = { "Reverse", "this", "string", "type", "array" };
for (int start = 0; start < sArray.Length/2; start++ )
{
var temp = sArray[start];
sArray[start] = sArray[sArray.Length - 1 - start];
sArray[sArray.Length - 1 - start] = temp;
}
There are lots of way to do this.
First, you can use recursion. In C#-like pseudocode this will look like this:
T[] Reverse<T>(T[] input)
{
if(input.Length <= 1) return input;
return Reverse(input[0..input.Length - 1]) + input[..input.Length];
}
Next is an in-place reverse; almost what you've already done, except your for loop is twice as big as it needs to be. See what happens when you change one of the parts of your loop to start < sArray.Length / 2. Plus, you really need to swap the elements.
You are rewriting items after the HALF to the first half. try this:
int start;
string[] sArray = { "Reverse", "this", "string", "type", "array" };
string[] temp = new string[sArray.Length];
int end = (sArray.Length-1);
for (start = 0; start < sArray.Length; start++ )
{
temp[start] = sArray[end - start];
Console.Write(sArray[start]+",");
}
sArray = temp; // putting back to the original varible
Hopefully this answer makes sense, you can replace T by string if you don't understand generics. The end variable starts at the end, starts starts at the front of the array, and they progress closer to eachother until they point to the same element (for an odd sized array) or the end pointer points to something before the start pointer(for an even sized array), in either case start < end will return false, and the loop will stop.
private static void Reverse<T>(T[] items)
{
for (int start = 0, end = items.Length - 1; start < end; start++, end--)
{
Swap(items, start, end);
}
}
private static void Swap<T>(T[] items, int a, int b)
{
var help = items[a];
items[a] = items[b];
items[b] = help;
}
After half of the table you're swapping elements with already swapped.
The proper solution is:
static void Reverse()
{
string[] sArray = { "Reverse", "this", "string", "type", "array" };
int end = sArray.Length - 1;
for (int start = 0; start < (sArray.Length / 2); ++start)
{
string tmp = sArray[start];
sArray[start] = sArray[end - start];
sArray[end - start] = tmp;
}
foreach (var s in sArray)
Console.Write(s + ",");
}
Sometimes I wonder why code looks more difficult than it should be. Try `
string[] words = new string[] {"reverse", "this", "string", "type", "array"};
string[] reverse = new string[words.Length];
int start = 0;`
for(int i = words.Length - 1; i >= 0; i--){
reverse[start] = words[i];
s++;
}
foreach(string s in reverse){
Console.Write(s + ", ");
}
Hope this helps =) or use another for loop inside the for loop counting up instead of using start.

Card Game without using arrays

We have a task to create a random hand of cards (5 cards). Where the cards can not be the same. We have yet not learnd how to use arrays so it would be nice if anyone could help us to get started without using arrays.
This is how we have started, but we can not figure out how to not get the same card twice.
static void Cards()
{
var rnd = new Random();
var suit, rank, count = 0;
while (count < 5)
{
rank = rnd.Next(13) + 1;
suit = rnd.Next(4) + 1;
if (suit == 1)
{
Console.WriteLine("Spader " + rank);
}
else if (suit == 2)
{
Console.WriteLine("Hjärter " + rank);
}
else if (suit == 3)
{
Console.WriteLine("Ruter " + rank);
}
else
{
Console.WriteLine("Klöver " + rank);
}
count++;
}
}
Thanks!
This is the sort of problem that arrays can deal with, so it would be easier to learn how to use them. Without them you need to store your 5 cards in variables (string card1, string card2, etc) then on each iteration check to see if the card matches any of these and discard it if it does, else save it. But then you have a whole bunch of conditional code to see which variable to store it in...
Much easier to just have an array
string[] cards = new string[5];
then you can just loop over the array looking for a match (something like this)
for(int idx=0; idx<5; idx++){
if(cards[idx]==thecardyouhavejustcreatedwithrandomcode){
break; //bail out of the for on a match
}
cards[iAntalKort]=thecardyouhavejustcreatedwithrandomcode;
}

Categories

Resources