在<list> </list>中存储多个玩家的分数

时间:2014-10-29 16:32:27

标签: c# list

我制作了一个程序,用于在飞镖游戏中保持分数,你可以输入x个玩家,然后每个玩家按照他们输入名字的顺序投掷3个箭头,然后重复自己,直到有人达到501结束游戏的点数。 对于玩家来说,list似乎工作得很好,但不知何故,我无法获得箭头/得分的list。我在Visual Studio中没有错误,我可以正常运行该程序,但是如果我尝试使用arrowList循环打印出foreach中的值,则不会发生任何事情。据我所知,我完全按照arrowList players完成了listarrowList 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's {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's in arrowList to see if someone has reached 501 points } } public override string ToString() { return (string.Format("{0}", Name)); } } 似乎按预期工作,所以为什么不是{{{1}} 1}}工作??

我已经为我的C#课程坚持了大约一个星期的任务 - 我在这里找到了几个关于非常类似任务的问题,但我仍然无法将这些建议付诸实践用我的代码(我不想复制和粘贴他们的整个程序,毕竟我在这里学习)。 我整个程序的代码:

{{1}}

3 个答案:

答案 0 :(得分:3)

为了跟进这一点,我想我会在开发过程中看到我使用的过程,看看它是否有帮助。另外,出于某种原因,这似乎是一个有趣的项目。

首先,我将前一个答案中的伪代码复制/粘贴到Main()方法中的新控制台项目中。很明显,一堆东西是未定义的,所以我开始定义它以便代码编译。

未定义的第一件事是Player,所以我创建了一个空的Player类:

public class Player { }

接下来,GetPlayers()方法未定义。这个方法需要获得游戏的所有玩家并将它们返回到List中,所以我创建了一个返回Players列表的裸骨方法:

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

接下来,AnnounceRound方法未定义。我知道这只会宣布新一轮开始,我决定在每轮开始时清除控制台窗口:

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));
}

接下来,AnnouncePlayer方法未定义。这将让每个人都知道目前是谁:

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

但是当我按照我想要的方式编写代码时,出现了一个问题:Player类没有Name属性。所以回到Player课程。我将使该属性只读(通过使setter私有),并将名称作为构造函数的参数。这样我们就不允许有人创建Player然后稍后更改他的名字(这就是我想要的方式,但是没有必要。如果我们以后决定让它读写,我们可以轻松地将setter更改为public):

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

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

接下来需要定义的是播放器对象上的GetDarts方法。现在我有一天睡觉,我不喜欢这个名字。以Get开头的方法通常返回一些对象,我对此方法的意图是它代表玩家走向飞镖板并抓住飞镖。在内部,我想它只会更新一个表示玩家持有多少飞镖的计数器。所以我会用原始的伪代码重命名它,也可以在&#39; Player&#39;中实现:

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

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

接下来要实施的是&#39; HasUnthrownDarts&#39;属性。这是一个bool,只是表示Player是否有任何飞镖。

public class Player
{
    . . .

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

接下来,我们有ThrowDart方法。现在突然事情变得有点棘手了。我注意到在你的实现中,你让人类玩家输入他们自己的分数,NPC玩家有一个随机生成的分数。所以这意味着一些事情:

  1. 我必须有一个属性(或其他方法)来区分人类和NPC玩家
  2. 我必须根据此属性在此方法中执行不同的操作。
  3. 现在最简单的方法是创建一个定义播放器类型的bool属性(并将其添加到构造函数中,默认值为false)。如果有两种以上类型的玩家,我可能会创建一个枚举来定义该类型的Player对象上的类型和属性。但就目前而言,这样做:

    public class Player
    {
        . . .
    
        public bool IsNpc { get; private set; }
    
        public Player(string name, bool isNpc = false)
        {
            . . .
    
            IsNPC = isNpc;
        }
    }
    

    现在,实现ThrowDart方法。所有这些方法都会得到0到60之间的分数,将其添加到玩家的分数中,并减少玩家手中的飞镖数量。进一步思考之后,我可能还想要返回这个方法中产生的分数,这样一个&#34;圆形分数&#34;如有必要可以计算,我决定输出飞镖数和分数。像往常一样,我编写了我想要使用的代码,并计划在之后实现它。

    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;
    }
    

    正如我写的那样,我意识到我需要从用户那里得到一个整数。这听起来很简单,但实际上需要一些代码来进行验证。用户可以输入非整数字符串,在这种情况下,我必须再次询问它们。他们也可以输入一个超出我们界限的数字(0-60),在这种情况下我也必须再次问他们。因为这段代码是半复杂的,并且因为我有一种感觉,我们可能需要从用户那里得到其他整数(我们可能需要询问他们想要玩多少NPC玩家),我决定创建一个名为{的新类。 {1}}并在那里添加方法ConsoleHelper。此方法只是从控制台获取一个字符串,将其转换为整数,应用一些自定义验证(如果需要),然后返回它。我已经添加了一些评论来帮助描述它是如何工作的:

    GetIntFromUser

    我也意识到我们还需要为NPC玩家获取随机数。为此,我创建了一个私有随机属性并在构造函数中设置它:

    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;
        }
    }
    

    我也正在更新此方法中的虚构Score属性,所以现在让我们实现它:

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

    这也是利用public class Player { . . . public int Score { get; private set; } } 返回该镖号得分这一事实的好时机。对于每一位球员,对于每轮比赛,我都可以向他们概述他们在那轮比赛中的表现:

    ThrowDarts

    我决定添加的另一件事是&#39; MaxDarts&#39;属性给玩家。这使我可以在一个地方存储飞镖的数量,而不是在任何地方都有硬编码的飞镖。所以我将它添加到. . . 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); . . . 类并更新了硬编码值。

    Player

    所以,既然一切都在编译,那剩下要做的最后一件事就是实现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; . . . } 方法。为了从用户那里收集人类和计算机播放器信息而不编写重复的代码,我创建了第二个GetPlayers方法,它接受一个布尔值,说明它是否应该是计算机播放器。然后,GetPlayers方法只会调用此重载两次 - 一次使用GetPlayers(),一次使用false。以下是两种方法:

    true

    我决定做的另一件事是,在每轮开始时,显示当前的排名。此代码通常是游戏结束时代码的副本(显示最终分数)。因为如果可能的话我们永远不应该编写重复的代码,我将它包装在一个名为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; } 的方法中:

    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);
        }
    }
    

    现在我们到了那里。我决定将整个游戏包装在另一个循环中,以便允许用户在每个会话中玩多个游戏。要做到这一点,我做了一些事情。首先,向玩家类添加了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); . . . } 方法,以便他们的分数回归到零:

    Reset()

    然后我将代码包裹在一个循环中,并重置玩家的分数(如果它们已经存在):

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

    希望通过我的大脑这次旅行有所帮助! :)

答案 1 :(得分:1)

该代码需要...一些工作。

要回答你的问题,你问为什么foreach没有打印任何东西。我假设你指的是这个:

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

添加到该集合的唯一内容是AddArrows这很奇怪,因为您使用默认构造函数创建箭头;调用GetScore(因为你从未初始化箭头,它将始终返回0),然后用该分数创建一个 new Arrows对象。

无论如何,调用 函数的唯一事情就是Arrows重载构造函数,它甚至是 weirder ;特别是因为你在这里构造了一个新的Player对象:

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

所以你的“新”箭头的范围是该构造函数;然后它们就会超出范围并消失。

你应该在其他地方调用该功能;而且大约有一百万个其他东西应该是不同的(开始时没有goto语句)。无需为您重新编写代码(您不想要;对您有用!)很难说如何来修复它。老实说,该计划非常小;我会重新开始,也许可以和我的导师讨论如何正确设计它。或许可以在这里提出一些与如何设置相关的好问题。

答案 2 :(得分:1)

你学习编程真是太棒了,我想我花一点时间分享一种方法来解决过去曾帮助过我的问题。

了解场景和关键部分

在编写真实世界事件和对象的模拟时(这几乎就是所有程序所做的),我发现首先在脑海中播放场景是有帮助的,弄清楚对象是什么,它们的相关属性是什么并且将采取行动,然后尝试编写代码来代表它们。如果该程序是为其他人编写的,那么这将来自现实世界中的讲师或客户。

编写流程图

在编写任何代码之前,请为代码所代表的方案创建流程图。对于飞镖游戏的例子(让我们假设他们正在玩301),我会想象它将如何在现实生活中发生。许多朋友聚在一起,每人拿起3个飞镖,他们决定他们将投掷的顺序,然后,每次一个,每个玩家投掷他们的飞镖并加上他们的分数。这个'回合'分数被添加到他们的总分中。当玩家投掷飞镖使其总分为501或更高时,游戏结束并且该玩家是胜利者。

创建UML图

定义对象以及它们彼此之间的关系。定义关系(一对多,多对多,is-a,has-a等)。

写入伪代码

编写一些示例代码,使用想象的对象来表示您喜欢使用它们的方式。这应该真正让您了解哪些对象应具有哪些属性和方法。

这是我可以想象编写代码的一种方式:

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);
}

现在你必须定义'GetPlayers()'方法如何使用类似的技术,ThrowDart()方法如何更新玩家的得分和他的'HasUnthrownDarts'属性(并且可能输出特定投掷的结果得分) ),以及AnnounceRound()和AnnouncePlayer()方法将输出到屏幕的内容。

希望有所帮助。