Этот код представляет собой нестатический класс C # для игры в кости.

Привет всем!

Дорогие люди, сегодня я представляю вам мой сценарий игры в кости! Вот, в моей дилетантской попытке написать необработанный код C #. Возможно, это останется здесь, несмотря на десятки других скриптов броска кости.

Содержит цикл for, переключатель и метод, возвращающий значение.

using System;

namespace C_
{
    class RollDice : Program
    {


        //Variables

        int numOfDice;
        int numOfSides;

        int maxDice = 100;
        int minDice = 1;
        int maxSides = 100;
        int minSides = 1;
        int totalRolled;

        //Start method for number of Dice to throw - take the player input in InputMethod() which returns an int. If returning 0, then restart. Clamp the value between min/max. Continue to next function.

        public void GetDice()
        {
            Console.WriteLine("Welcome to Roll Dice!");
            Console.WriteLine("How many dice do you want to throw?");
            numOfDice = InputMethod();
            if(numOfDice == 0)
            {
                GetDice();
            }
            if (numOfDice > maxDice || numOfDice < minDice)
            {
                numOfDice = Math.Clamp(numOfDice, minDice, maxDice);
                Console.WriteLine($"Number of Dice set to {numOfDice}");
            }

            GetSides();
        }

        // Get the sides of each dice by using a similar process. Roll() for each dice, then sum the totals in RollTotal().

        public void GetSides()
        {
            Console.WriteLine("How many sides does each dice have?");
            numOfSides = InputMethod();
            if(numOfSides == 0)
            {
                GetSides();
            }
            if (numOfSides > maxSides || numOfSides < minSides)
            {
                numOfSides = Math.Clamp(numOfSides, minSides, maxSides);
                Console.WriteLine($"Number of Sides set to {numOfSides}");
            }

            for (int x = 1; x <= numOfDice; x++)
            {
                Roll();
            }

            RollTotal();
        }


        // Create a new Random() instance for the number of sides and add it to the totalRolled variable.

         public void Roll()
        {
            Console.WriteLine(".....");
            Console.WriteLine("Rolling");
            Random r = new Random();
            var result = r.Next(1, numOfSides + 1);
            Console.WriteLine($"You rolled {result}");
            totalRolled += result;
        }
        
        // Output the total and prompt to roll again or quit. Reset the total..

        public void RollTotal()
        {
            Console.WriteLine($"Total sum rolled is {totalRolled}");
            Console.WriteLine(" ");
            Console.WriteLine("Enter 1 to re-roll or 2 to quit.");
            var answer = InputMethod();

            switch (answer)
            {
                case 0:
                RollTotal();
                break; 
            
                case 1:
                totalRolled = 0;
                GetDice();
                break;           
            
                case 2:
                totalRolled = 0;
                Environment.Exit(0);
                break;
            
                default:
                Environment.Exit(0);
                break;
            }
        }

        //Convert the input to an integer if valid and return the value. Else return 0.

        public int InputMethod()
        {
            var input = Console.ReadLine();
            if(!Int32.TryParse(input, out int convertedStringToInt))

            {
                Console.WriteLine("You need to enter a valid number!");
                return 0;
            }
            
            else 
            
            {
                return Convert.ToInt32(input);
            }

        }

    }
}

2 ответа
2

У меня есть несколько советов:

  • Бесконечная рекурсия.

    Приложение никогда не закончит, пока вы не позвоните Environment.Exit потому что у вас бесконечная рекурсия. Результат бесконечной рекурсии всегда StackOveflowException, рано или поздно. Возьмите спусковой крючок: если вас заставили использовать Environment.Exit чтобы остановить приложение, то что-то пошло не так. О правильном использовании рекурсии: на каком-то уровне вызова рекурсивного метода должен return больше не выполнять рекурсивный вызов. Тогда рекурсия завершится нормально. Кстати, здесь вам не нужна рекурсия.

  • Именование

    Дайте всем элементам: классам, пространствам имен, переменным, полям и т. Д. Имена, объясняющие их назначение или содержащие значение. Это сделало бы код удобным для обслуживания. Также использую следующий стандарт.

    private int _fieldName;
    
    public int PropertyName { get; set; }
    
    public void DoSomething()
    {
        int localVariableName = 0;
    }
    

    Он позволяет явно различать поля и локальные переменные в коде, если я читаю его вне редактора IDE.

  • Инкапсуляция.
    Принцип ООП, который помогает вам в процессе разработки избегать популярных ошибок. Какой метод мне следует вызывать при создании экземпляра RollDice класс? Все методы public. Покинуть public только те компоненты класса, к которым вы разрешаете доступ за пределами класса.

  • Маленькие подсказки.

    Снять наследование class RollDice : Program, ничего не делает. Пусть методы и классы выполняют какую-то единственную работу. (видеть S в ТВЕРДЫЙ принципы для деталей).

Вот альтернативная реализация того же самого.

class Program
{
    static void Main(string[] args)
    {
        RollDice dice = new RollDice();
        dice.Run();
        Console.ReadKey();
    }
}

public class RollDice
{
    private const int maxDice = 100;
    private const int minDice = 1;
    private const int maxSides = 100;
    private const int minSides = 2;

    private Dice[] _dices;

    private List<string> _menuItems = new List<string>
    {
        "Exit.",
        "Setup Roll Dice.",
        "Roll!"
    };

    public void Run()
    {
        Console.Write("Welcome to Roll Dice!");
        while (true)
        {
            ShowMenu(_menuItems);
            switch (ConsoleHelper.ReadInteger("Enter your selection: ", 0, _menuItems.Count - 1))
            {
                case 1:
                    Setup();
                    break;
                case 2:
                    Roll();
                    break;
                case 0:
                    Console.WriteLine("Bye.");
                    return;
                default:
                    Console.WriteLine("Error: Unexpected menu input!"); // this would never happen
                    break;
            }
        }
    }

    private void Roll()
    {
        if (_dices == null)
        {
            Console.WriteLine("Please setup the Roll Dice first.");
            return;
        }

        Console.WriteLine("Rolling...");
        for (int i = 0; i < _dices.Length; i++)
        {
            _dices[i].Roll();
        }
        Console.WriteLine($"You rolled: {string.Join(", ", (object[])_dices)}");

        int total = 0;
        for (int i = 0; i < _dices.Length; i++)
        {
            total += _dices[i].Value;
        }
        Console.WriteLine($"Total sum rolled is {total}");
    }

    private void Setup()
    {
        int diceCount = ConsoleHelper.ReadInteger("How many dice do you want to throw? ", minDice, maxDice);
        int sides = ConsoleHelper.ReadInteger("How many sides does each dice have? ", minSides, maxSides);
        _menuItems[1] = $"Change Roll Dice setup. (currently {diceCount} dice of {sides} sides).";
        _dices = CreateDice(diceCount, sides);
    }

    private Dice[] CreateDice(int diceCount, int sides)
    {
        Dice[] dices = new Dice[diceCount];
        for (int i = 0; i < diceCount; i++)
        {
            dices[i] = new Dice(sides);
        }
        return dices;
    }

    private void ShowMenu(List<string> items)
    {
        Console.WriteLine();
        for (int i = 1; i < items.Count; i++)
            Console.WriteLine($"[{i}] {items[i]}");
        Console.WriteLine($"[0] {items[0]}");
    }
}

public class Dice
{
    private static Random rndGen = new Random(); // one random generator for all dices would be enough

    private int _sides;

    public int Value { get; private set; }

    public Dice(int sides)
    {
        _sides = sides;
    }

    public void Roll()
    {
        Value = rndGen.Next(0, _sides) + 1;
    }

    public override string ToString()
    {
        return $"[{Value}]";
    }
}

public static class ConsoleHelper
{
    public static int ReadInteger(string prompt, int min, int max)
    {
        while (true)
        {
            Console.Write(prompt);
            if (int.TryParse(Console.ReadLine(), out int option) && option >= min && option <= max)
                return option;
            Console.WriteLine($"You need to enter a valid number from {min} to {max}!");
        }
    }
}

Примечание для других рецензентов: я не включаю const или же readonly предупреждения в обзоре, чтобы он оставался максимально простым.

Без рекурсии выше. Удачи!

    Добро пожаловать в CR. Хотя я ценю ваш энтузиазм и готовность учиться, ваша первая попытка здесь явно неудачна. Чтобы придать этому более позитивный оттенок, скажем, есть много возможностей для улучшения.

    Хорошей новостью является то, что отступы очень хороши. И вы используете { } даже для однострочного if заявления.

    В остальном — не очень хорошие новости.

    Ваше пространство имен плохо названо.

    Ваши комментарии совершенно бесполезны, так как они ничего не говорят нам о том, чего мы еще не знаем из чтения кода. Комментарии должны рассказывать нам, почему вы что-то делаете, а не как.

    Комментарий //Variables не нужен, но тоже немного неверен. В терминологии C # они называются fields поскольку они определены для класса. Что еще более важно, они должны быть определены как свойства, а не поля. Соглашения об именах свойств таковы, что они начинаются с заглавной буквы.

    Я надеюсь, что вы используете .NET 5, где использование Random() в твоей Roll() метод даст разные значения. Если вы используете .NET Framework, вы должны использовать свойство уровня класса для Random().

    Две мои самые большие проблемы с кодом связаны не с конкретными утверждениями, а с общим дизайном.

    Во-первых, вы смешали бизнес-логику (BL) игры в кости с пользовательским интерфейсом (UI). Хорошо спроектированное приложение разделяет их. С точки зрения разработчиков, мы называем это Separation of Concerns.

    И два, кроме поля totalRolled, который может быть собственностью TotalRolled, Я не вижу преимуществ нестатичности вашего класса. И его можно легко сделать статичным, отслеживая общее количество бросков вне класса. Если вы настаиваете на нестатическом классе, я бы посоветовал:

    А Dice класс, где он содержит только для чтения свойства игральных костей, то есть количество игральных костей и количество сторон. Эти свойства будут установлены в конструкторе, которого нет в вашем текущем коде. Некоторые из ваших текущих полей должны быть константами, например maxDice или же minDice, так как они не будут меняться ни в одном экземпляре Dice. Что меняется для каждого экземпляра Dice это снова количество игральных костей и количество сторон на кубике. В Roll метод будет принадлежать Dice. Этот класс не должен отслеживать общее количество бросков.

    А DiceGame класс, который является вашим пользовательским интерфейсом. Он запрашивает у пользователя ввод, создает Dice Экземпляр основан на количестве игральных костей и сторонах, а также отслеживает общее количество бросков.

    Желаю вам удачи и непременно сохраните желание учиться.

      Добавить комментарий

      Ваш адрес email не будет опубликован. Обязательные поля помечены *