Saving scores for multiple players in <list>

I am making a program to save scores in a dart game where you can enter x number of players and each player then throws 3 arrows in the order they enter their names and this repeats until someone reaches 501 points, ends the game. list

for players seems to work fine, but somehow I can't get the list

arrows / points to work. I don't see any errors in Visual Studio, and I can just run the program, but if I try to print the values ​​in arrowList

using a loop foreach

, nothing happens. As far as I can tell, I did arrowList

exactly the same as I did players

list

, which seems to work as intended, so why not arrowList

?

I came across this issue for my C # course for about a week - I found some questions here regarding a very similar task, but I still can't get these suggestions to work with my code (and I don't want to just copy and paste all my programs, as I here to learn). Code for my whole program:

class Program
{
    static void Main(string[] args)
    {
        Game game = new Game();
        game.PlayGame();
    }
}


class Game
{
    public Game()
    {
        //default  constructor that takes 0 arguments
    }


    int playernumber = 0;
    List<Player> players = new List<Player>();

    public void PlayGame()
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.Title = " Dartcounter 3000";
        Console.WriteLine("Welcome to the Dartcounter 3000!");

        NumberOfPlayers();

        Console.WriteLine("");

        foreach (var player in players)
        {
            if (player.ToString() == "Dator")
            {
                Console.WriteLine("Generating score for the NPC 'Dator'...");
                Random random = new Random();
                int randomThrow1 = random.Next(0, 60);
                int randomThrow2 = random.Next(0, 60);
                int randomThrow3 = random.Next(0, 60);
                Arrows arrows = new Arrows(randomThrow1, randomThrow2, randomThrow3);

                player.CalculatePoints();
            }
            else
            {
                Console.WriteLine("It {0} turn to throw", player.ToString());
                System.Threading.Thread.Sleep(500);
                Console.WriteLine("Enter your score for the first arrow: ");
                int arrowOne = int.Parse(Console.ReadLine());
                Console.WriteLine("Your second arrow: ");
                int arrowTwo = int.Parse(Console.ReadLine());
                Console.WriteLine("Your third arrow: ");
                int arrowThree = int.Parse(Console.ReadLine());
                Arrows arrows = new Arrows(arrowOne, arrowTwo, arrowThree);
                Console.WriteLine(arrows.ToString());

                player.CalculatePoints();
            }
        }

        Console.ReadLine();

    }

    // ------------ START of player methods in class Game ------------
    public void NumberOfPlayers()
    {
        Console.WriteLine("Please enter the number of players: ");
        start:
        string playernumberinput = Console.ReadLine();
        int value;

        if (int.TryParse(playernumberinput, out  value))
        {
            playernumber = int.Parse(playernumberinput);
            AddPlayer();
        }
        else
        {
            Console.WriteLine("You did not input a number. Please try again: ");
            goto start;
        }
    }


    public void AddPlayer()
    {

        for (int i = 0; i < playernumber; i++)
        {
            Console.WriteLine("Enter name of player {0}:", i + 1);
            players.Add(new Player(Console.ReadLine()));
        }
    }
    // ------------ END of player methods in class Game ------------
}





class Arrows
{
    public Arrows()
    {
        //default constructor that takes 0 arguements
    }

    public int roundScore;
    public Arrows(int roundScore)
    {
        this.roundScore = roundScore;
    }

    public int arrowOne { get; set; }
    public int arrowTwo { get; set; }
    public int arrowThree { get; set; }


    public Arrows(int Arrow1, int Arrow2, int Arrow3)
    {
        arrowOne = Arrow1;
        arrowTwo = Arrow2;
        arrowThree = Arrow3;

        Player player = new Player();
        player.AddArrows();
    }


    public int GetScore()
    {
            return arrowOne + arrowTwo + arrowThree;
    }

    public override string ToString()
    {
        return (string.Format("You got a total of {0} this round!", GetScore()));
    }

}


class Player
{
    public Player()
    {
        //default  constructor that takes 0 arguments
    }

    public string Name;
    public List<Arrows> arrowList = new List<Arrows>();

    public Player(string Name)
    {
        this.Name = Name;
    }

    public void AddArrows()
    {
        Arrows arrows = new Arrows();
        int roundScore = arrows.GetScore();
        arrowList.Add(new Arrows(roundScore));
    }

    public void CalculatePoints()
    {
        foreach (var arrow in arrowList)
        {
            //Calculation to sum up the entry in arrowList to see if someone has reached 501 points
        }
    }

    public override string ToString()
    {
        return (string.Format("{0}", Name));
    }

}

      

+3


source to share


3 answers


To keep an eye on this, I thought I would go through the process that I use when developing and see if that helps. Plus, for some reason, it seemed like a fun project.

First, I copied / pasted the pseudocode from my previous answer into a new console project in the Main () method. Obviously a bunch of things were undefined, so I started defining them to get the code to compile.

The first thing that was undefined was Player

, so I created an empty Player class:

public class Player { }

      

Further, the method GetPlayers()

was undefined. This method will have to collect all the players for the game and return them to the list, so I created a bare-bones method that returns a list Players

:

public static List<Player> GetPlayers()
{
    var players = new List<Player>();
    return players;
}

      

Next, the method is AnnounceRound

undefined. I know this will just announce the start of a new round, and I decided to also clear the console window at the start of each round:

public static void AnnounceRound(int roundNumber)
{
    Console.Clear();

    var announcement = string.Format("Beginning Round #{0}", roundNumber);
    Console.WriteLine(announcement);

    // The next line writes out a list of dashes that is the 
    // exact length of the announcement (like an underline)
    Console.WriteLine(new string('-', announcement.Length));
}

      

Next, the method is AnnouncePlayer

undefined. This will let everyone know who is currently representing him:

private static void AnnouncePlayer(Player player)
{
    Console.WriteLine("{0}It now {1} turn.{0}", Environment.NewLine, player.Name);
}

      

But when I wrote the code the way I WANT to use it, there was a problem: the class Player

has no property Name

. Let's go back to the class Player

. I'll make the property read-only (by making the setter private) and take the name as a parameter to the constructor. This way we don't allow someone to create Player

, and then we change our name later (this is how I want to do it, but this is not necessary. If we later decide to make it read-write, we can easily change the setter for the public):

public class Player
{
    public string Name { get; private set; }

    public Player(string name)
    {
        Name = name;
    }
}

      

The next thing that needs to be defined is the method GetDarts

of the player object. Now that I had a day to sleep on it, I don't like the name. Methods starting with Get

tend to return some object, and my intention for this method is that it represents a player walking towards the dart board and grabbing the darts. Internally, I suppose it will just update the counter, which represents how many darts the player is holding. So I'll rename it to the original pseudocode as well as the implementation to "Player":

public class Player
{
    public string Name { get; private set; }
    private int dartsInHand;

    public void GrabDarts()
    {
        dartsInHand = 3;
    }
}

      

The next thing to implement is the HasUnthrownDarts property. It is a bool that simply represents if Player

any darts are in hand or not.

public class Player
{
    . . .

    public bool HasUnthrownDarts { get { return dartsInHand > 0; } }
}

      

Next, we have a method ThrowDart

. Now all of a sudden things get a little more complicated. I noticed that in your implementation you allow player-players to enter their own score, and NPC players have a random score. So, this means several things:

  • I must have a property (or some other means) to differentiate between Human and NPC players.
  • In this method, I have to do different things depending on this property.

The simplest thing at the moment is to simply create a bool property that defines the player's type (and add it to the constructor with a default value false

). If there were more than two types of players, I would probably create an enum to define the types and properties of an object of Player

that type. But for now this will do:

public class Player
{
    . . .

    public bool IsNpc { get; private set; }

    public Player(string name, bool isNpc = false)
    {
        . . .

        IsNPC = isNpc;
    }
}

      

Now let's implement the method ThrowDart

. This whole method would be to get the score from 0 to 60, add it to the player's score, and reduce the number of darts in the player's hand. After some further thought, I could also return the result generated in this method, so a "round point" can be calculated if necessary, and I decided to output the number of darts and points. As usual, I have written the code that I WANT to use, with a plan to implement it later.

public int ThrowDart()
{
    if (dartsInHand < 1) 
    {
        throw new Exception(string.Format("Player {0} has no darts to throw.", Name));
    }

    int dartScore;
    int thisDartNumber = (3 - dartsInHand) + 1;

    if (IsNpc)
    {
        // Get a random score for non-player characters
        dartScore = rnd.Next(0, 60);
        Console.WriteLine("{0} threw dart #{1} for {2} point{3}",
            Name, thisDartNumber, dartScore, dartScore == 1 ? "" : "s");
    }
    else
    {
        dartScore =
            ConsoleHelper.GetIntFromUser(string.Format(
                "{0}, please enter the score for dart #{1} (0-60): ",
                Name, thisDartNumber), "<Invalid score>", 
                (i) => i >= 0 && i <= 60);
    }

    Score += dartScore;
    dartsInHand--;
    return dartScore;
}

      



As I wrote this, I realized that I needed to get an integer from the user. It sounds simple, but it actually takes a little code to validate. The user might enter a non-integer string, in which case I would have to ask them again. They can also enter a number that is outside our bounds (0-60), in which case I also have to ask them again. Since this code is semi-complex, and because I have a feeling that we might need to get other integers from the user (we might need to ask how many NPC players they want to play against), I decided to create a new class called ConsoleHelper

and add a method thereGetIntFromUser

... This method will just get a string from the Console, convert it to an integer, apply some random check (if needed) and return it. I've added a few comments to help describe how this works:

public static class ConsoleHelper
{
    /// <summary>
    /// Gets an integer from the user
    /// </summary>
    /// <param name="prompt">A prompt to display to the user. Can be null.</param>
    /// <param name="errorMessage">An error message to display if 
    /// the user enters an invalid integer. Can be null</param>
    /// <param name="intValidator">A function to run which will validate 
    /// the integer. The integer  will be passed to it, and it should 
    /// return true if the integer is valid. Can be null</param>
    /// <returns>The integer entered by the user</returns>
    public static int GetIntFromUser(string prompt, string errorMessage, 
        Func<int, bool> intValidator)
    {
        int intEntered;

        while (true)
        {
            if (prompt != null) Console.Write(prompt);
            var input = Console.ReadLine();
            if (int.TryParse(input, out intEntered))
            {
                if (intValidator == null || intValidator(intEntered))
                {
                    break;
                }
            }

            if (errorMessage != null) Console.WriteLine(errorMessage);
        }

        return intEntered;
    }
}

      

I also realized that we also need to get random numbers for the NPC players. To do this, I created a private random property and set it in the constructor:

public class Player
{
    . . .       
    private readonly Random rnd;

    public Player(string name, bool isNpc = false)
    {
        . . .
        rnd = new Random();
    }
}

      

And I also update the imaginary Score property in this method, so now we also implement this:

public class Player
{
    . . .
    public int Score { get; private set; }
}

      

It's as good as any to take advantage of what ThrowDarts

returns the score for that dart number. For each player, for each round, I can give them a summary of how well they threw in that round:

. . .
var roundScore = 0;

while (p.HasUnthrownDarts)
{
    roundScore += p.ThrowDart();
    . . .
}

if (winner != null) break;

Console.WriteLine("{0} threw for {1} points this round.", p.Name, roundScore);
. . .

      

Another thing I decided to add is the MaxDarts property for the Player. This allows me to store the number of darts in one place, rather than a hard-coded "3". So I added it to the class Player

and updated the hardcoded values.

public class Player
{
    . . .
    public int MaxDarts { get; set; }

    public Player(string name, bool isNpc = false)
    {
        . . .
        MaxDarts = 3;
    }
}

public void GrabDarts()
{
    dartsInHand = MaxDarts;
}

public int ThrowDart()
{
    . . .
    int thisDartNumber = (MaxDarts - dartsInHand) + 1;
    . . .
}

      

So now that everything is compiling, the last thing left to do is implement the method GetPlayers

. To collect information about a person and a computer player from a user without writing duplicate code, I created a second method GetPlayers

that takes a boolean value that says whether it should be a computer player or not. The method then GetPlayers()

simply calls this overload twice - once with false

and once with true

. Here are two methods:

public static List<Player> GetPlayers()
{
    var players = GetPlayers(false);
    Console.WriteLine();
    players.AddRange(GetPlayers(true));            
    return players;
}

private static List<Player> GetPlayers(bool npcPlayers)
{
    var players = new List<Player>();
    var playerType = npcPlayers ? "NPC" : "human";

    int numberOfPlayers = ConsoleHelper.GetIntFromUser(
        string.Format("How many {0} players will be playing? ", playerType),
        "<Invalid number>", (x) => x >= 0);

    for (int i = 1; i <= numberOfPlayers; i++)
    {
        string name;
        if (npcPlayers)
        {
            // Generate computer player names
            name = string.Format("ComputerPlayer{0}", i);
        }
        else
        {
            // Get human names from the user
            Console.Write("Enter the name for player #{0}: ", i);
            name = Console.ReadLine();
        }

        players.Add(new Player(name, npcPlayers));
    }

    return players;
}

      

Another thing I decided to do is show the current position at the beginning of each round. This code will usually be a copy of the code at the end of the game (which shows the final scores). Since we should never write duplicate code if possible, I wrapped it in a method called ShowScores

:

public static void ShowScores(string message, List<Player> players)
{
    if (message != null) Console.WriteLine(message);

    foreach (var p in players.OrderByDescending(p => p.Score))
    {
        Console.WriteLine(" {0}: {1}", p.Name, p.Score);
    }
}

      

Then I added code to call this method at the start of each round, and at the end of the game:

private static void Main()
{
    . . .
            while (winner == null)
            {
                round++;
                AnnounceRound(round);
                ShowScores("The current standings are:", players);
                . . .
            }

            Console.Clear();
            Console.WriteLine("We have a winner! Congratulations, {0}!!", winner.Name);
            ShowScores("The final scores are:", players);
            . . .
}

      

Now we get there. I decided to wrap the whole game in a different loop so that users can play more than one game per session. To do this, I did several things. First we added a method Reset()

to the player class so that their score goes back to zero:

public void Reset()
{
    Score = 0;
    dartsInHand = 0;
}

      

Then I wrapped the code in a loop and reset the player scored (if they already existed):

private static void Main()
{
    Console.Write("Would you like to play a game of darts (y/n)? ");
    var playGame = Console.ReadKey();
    List<Player> players = null;

    while (playGame.Key == ConsoleKey.Y)
    {
        Console.WriteLine(Environment.NewLine);
        if (players == null)
        {
            players = GetPlayers();
        }
        else
        {
            players.ForEach(p => p.Reset());
        }

        . . .

        Console.Write("{0}Would you like to play another game (y/n)? ", 
            Environment.NewLine);
        playGame = Console.ReadKey();
    }

    Console.Write("{0}Thanks for playing! Press any key to quit...", 
        Environment.NewLine);
    Console.ReadKey();
}

      

Hope this trip through my brain helped in some way! :)

+2


source


This code needs some work.

To answer your question, you asked why foreach

doesn't print anything. I assume you mean this:

   foreach (var arrow in arrowList)
   {
      //Calculation to sum up the entry in arrowList to see if someone has reached 501 points
   }

      

The only thing that adds to this collection is AddArrows

, which is odd since you are creating arrows with a standard constructor; call GetScore

(which will always return 0, since you never initialized the arrows) and then create a new object Arrows

with that count.



Regardless, the only function that calls this function is the overloaded constructor Arrows

, which is even weirder; especially because here you are creating a new object Player

:

Player player = new Player();
player.AddArrows();

      

So your "new" arrows are bound to this constructor; and then they fall out of scope and disappear.

You must call this function somewhere else; and about a million other things should be different (no instructions goto

to start). Without rewriting your code for you (which you don't want is good for you!) It's hard to say how to fix it. To be honest, the program is very small; I would just start and maybe talk to my instructor on how to design it properly. Maybe ask some good questions here that deal with how to set this up.

+1


source


It's great that you are learning programming, and I thought I would take a minute to share one way to solve a problem like this that has helped me in the past.

Know the script and key pieces

When writing simulations of real-world events and objects (which pretty much depends on what all the programs do), it's helpful for me to play out the scenario in my head first, figure out what those objects are, what their respective properties and actions would be, and then try to write the code to represent them. If the program is written for someone else, it will come from an instructor or client in the real world.

Write a flow diagram

Before writing any code, create a flowchart of the script that represents the code. Using the example of playing darts (assuming they are playing 301), I would imagine how this would happen in real life. Several friends get together, each chooses 3 darts, they choose the order in which they will throw, and then, one at a time, each player throws all their darts and adds up their score. This "round" account is added to their total. The moment a player throws a dart that gives them a total score of 501 or more, the game is over and that player is the winner.

Create UML Diagram

Define objects and how they will relate to each other. Define relationships (one to many, many to many, is-a, has-a, etc.).

Writing pseudocode

Write some sample code using imaginary objects to imagine how you might use them. This should really give you an idea of ​​what objects should have what properties and methods.

Here's one way to represent the code:

List<Player> players = GetPlayers();
Player winner = null;
int round = 0;

while (winner == null)
{
    round++;
    AnnounceRound(round);

    foreach(Player p in players)
    {
        AnnouncePlayer(p);

        p.GetDarts();

        while (p.HasUnthrownDarts)
        {
            p.ThrowDart();

            if (p.Score >= 501)
            {
                winner = p;
                break;
            }
        }

        if (winner != null) break;
     }
}

Console.WriteLine("We have a winner! Congratulations, {0}!!", winner.Name);

Console.WriteLine("The final scores are:");

foreach(Player p in players.OrderByDescending(p => p.Score))
{
    Console.WriteLine(" {0}: {1}", p.Name, p.Score);
}

      

Now you need to define how the GetPlayers () method will work using similar methods, how the ThrowDart () method will update the player's score and its "HasUnthrownDarts" property (and possibly display the final score of a particular throw), and which AnnounceRound () methods and AnnouncePlayer () will be displayed.

Hope it helps.

+1


source







All Articles