Привет всем!
Дорогие люди, сегодня я представляю вам мой сценарий игры в кости! Вот, в моей дилетантской попытке написать необработанный код 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 ответа
У меня есть несколько советов:
Бесконечная рекурсия.
Приложение никогда не закончит, пока вы не позвоните
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
Экземпляр основан на количестве игральных костей и сторонах, а также отслеживает общее количество бросков.
Желаю вам удачи и непременно сохраните желание учиться.