@sulim000333

Как правильно написать этот код/как оптимизировать это «код»?

Не обращайте внимание на слишком тупо и неправильно написанный код, я только учусь и я хотел осуществить идею с мои уровнем знаний) Вопрос: Как сделать все тоже самое только чтобы было не гомнокод, а грамотно?))
using System;

namespace DefaultProject
{
	class MainClass
	{
		public static void Main (string[] args)
		{
			Console.WriteLine ("Всем привет! И это моя первая мини-программа для проверки ваших знаний языка программирования C#\nЕсли готовы пройти тест напишите Go, а если хотите выйти напишите Exit.");
			string st = Console.ReadLine ();
			switch (st) 
			{
			case "Go":
				Console.WriteLine ("1.Какая команда выводит текст в консоль? \n 1. if/else \n 2. System.out.println(); \n 3. Console.WriteLine(); \n 4. int x = 10;");
				string questionstr1 = Console.ReadLine ();
				int question1 = Convert.ToInt32 (questionstr1);
				if (question1 >= 0) {
					Console.WriteLine("2.Какой тип из списка целочисленный? \n 1. char \n 2. int \n 3. float \n 4. double");
					string questionstr2 = Console.ReadLine ();
					int question2 = Convert.ToInt32 (questionstr2);
					if (question2 >= 0) {
						Console.WriteLine ("3.Какой тип из списка строчный? \n 1. string \n 2. byte \n 3. char \n 4. decimal");
						string questionstr3 = Console.ReadLine ();
						int question3 = Convert.ToInt32 (questionstr3);
						if (question3 >= 0) {
							Console.WriteLine ("4.Какой тип переменной используется в коде: int a = 5? \n 1. Знаковое 32-бит целое \n 2. Знаковое 64-бит целое \n 3. Знаковое 8-бит целое \n 4. 1 байт");
							string questionstr4 = Console.ReadLine ();
							int question4 = Convert.ToInt32 (questionstr4);
							if (question4 >= 0) {
								Console.WriteLine ("5.Сколько условных конструкций есть в C#? \n 1 \n 2 \n 3 \n 4");
								string questionstr5 = Console.ReadLine ();
								int question5 = Convert.ToInt32 (questionstr5);
								if (question5 >= 0) {
									switch (question1) {
									case 3:
										Console.WriteLine ("На первый вопрос вы ответили: 3. Console.WriteLine(); \n Правильно!");
										break;
									default:
										Console.WriteLine ("Первый вопрос: Неправильный ответ!. Вы выбрали: " + question1);
										break;
									}
									switch (question2) {
									case 2:
										Console.WriteLine ("На второй вопрос вы ответили: 2. int \n Правильно!");
										break;
									default:
										Console.WriteLine ("Второй вопрос: Неправильный ответ!. Вы выбрали: " + question2);
										break;
									}
									switch (question3) {
									case 1:
										Console.WriteLine ("На третий вопрос вы ответили: 1. string \n Правильно!");
										break;
									default:
										Console.WriteLine ("Третий вопрос: Неправильный ответ!. Вы выбрали: " + question3);
										break;
									}
									switch (question4) {
									case 1:
										Console.WriteLine ("На четвертый вопрос вы ответили: 1. Знаковое 32-бит целое \n Правильно!");
										break;
									default:
										Console.WriteLine ("Четвертый вопрос: Неправильный ответ!. Вы выбрали: " + question4);
										break;
									}
									switch (question5) {
									case 3:
										Console.WriteLine ("На пятый вопрос вы ответили: 3 \n Правильно!");
										break;
									default:
										Console.WriteLine ("Пятый вопрос: Неправильный ответ!. Вы выбрали: " + question5);
										break;
										}
									if ((question1 != 3) && (question2 != 2) && (question3 != 1) && (question4 != 1) && (question5 != 3)) {
										Console.WriteLine ("Вы на все вопросы ответили неправильно! Тест пройден на 0% ");
									}
									if ((question1 == 3) && (question2 == 2) && (question3 == 1) && (question4 == 1) && (question5 == 3)) {
										Console.WriteLine ("Вы на все вопросы ответили правильно! Тест пройден на 100%! ");
									}
								}
							}
						}
					}
				}
				break;
			}
			Console.ReadKey ();
		}
	}
}
  • Вопрос задан
  • 799 просмотров
Решения вопроса 2
@Free_ze
Пишу комментарии в комментарии, а не в ответы
Первое, что нужно сделать - определить доменные сущности, с которыми будет работать программа. В вашем случае это:
  • тест, который состоит из вопросов;
  • вопрос, который состоит из текста и ответов (упорядоченные по индексам);
  • ответ, котрый состоит из текста и признака правильности.
Код
public class Test
{
    public IList<Question> Questions { get; set; }
}

public class Question
{
    public string Text { get; set; }
    public IList<Answer> Answers { get; set; }
}

public class Answer
{
    public string Text { get; set; }
    public bool IsRight { get; set; }
}



Необходимо декомпозировать задачу на атомарные подзадачи. Каждую подзадачу можно унести в отдельную функцию, чтобы ограничить сферы ответственности кода (в духе SOLID в общем и SRP в частности), чтобы обезопасить себя от "эффекта бабочки", когда небольшое изменение отдого кусочка кода в одной части приложения рушит остальную работу в другой части. Да и читать код будет проще, не путаясь в деталях реализации из огромной портянки неразрывного кода с неочевидными связями переменных. Получается примерно так:

  1. логика "главного меню";
  2. логика теста:
    1. создание контента теста;
    2. прохождение теста:
      1. распечатка вопроса;
      2. получение ввода пользователя;
      3. проверка ответа;
      4. подсчет и вывод статистики.



Затем необходимо сформировать структуру теста. Для этого можно создать функцию-фабрику, которая возвращает новый объект теста (данные хардкодятся как здесь, читаются из базы, ходят за ними в интернет... whatever, нас интересует само тестирование):
Код
public static Test CreateTest()
{
    return new Test
    {
        Questions = new List<Question>
        {
            new Question
            {
                Text = "Какая команда выводит текст в консоль?",
                Answers = new List<Answer>
                {
                    new Answer { Text = "if/else" },
                    new Answer { Text = "System.out.println();" },
                    new Answer { Text = "Console.WriteLine();", IsRight = true },
                    new Answer { Text = "int x = 10;" }
                }
            },
            new Question
            {
                Text = "Какой тип из списка целочисленный?",
                Answers = new List<Answer>
                {
                    new Answer { Text = "char" },
                    new Answer { Text = "int", IsRight = true },
                    new Answer { Text = "float" },
                    new Answer { Text = "double" }
                }
            }
            //,...
        }
    };
}


Пусть программа, как у вас, приветствует пользователя, а затем, пока он не введет какой-то из двух корректных выводов (регистронезависимо (...IngoreCase)), будет показывать приглашение с подсказкой. Если Go - вызываем создание теста и заходим в игру. Если игра завершилась или пользователь изначально ввел Exit - печатаем знаменитое Press any key... и выходим из программы.
Код
public static void Main()
{
    Console.WriteLine("Всем привет! И это моя первая мини-программа для проверки ваших знаний языка программирования C#");
    string input;
    
    do
    {
        Console.WriteLine("Если готовы пройти тест напишите Go, а если хотите выйти напишите Exit.");
        input = Console.ReadLine();

        if (input.Equals("Go", StringComparison.InvariantCultureIgnoreCase))
        {
            var test = CreateTest();
            PlayGame(test); // <----- Логика тестирования находится здесь
        }
    } while (   !input.Equals("Go", StringComparison.InvariantCultureIgnoreCase)
             && !input.Equals("Exit", StringComparison.InvariantCultureIgnoreCase));

    Console.WriteLine("Для выхода нажмите любую клавишу...");
    Console.ReadKey();
}


Для игры нам потребуется массив с номерами "проваленных" вопросов (которые пригодятся для статистики в конце - CalculateResult). Мы обходим все вопросы из теста, сначала выводя их текст с вариантами на экран (PrintQuestion), затем спрашивая юзера его выбор (GetUserChoice). Затем ответ проверяется (IsCorrectAnswer), если он неверен - добавляем номер ответа (i - это индекс массива с нуля, поэтому +1) в массив ошибок. После всех вопросов - подсчитываем и выводим статистику.
Код
private static void PlayGame(Test test)
{
    var mistakes = new List<int>();
    for (int i = 0; i < test.Questions.Count; ++i)
    {
        var question = test.Questions[i];
        PrintQuestion(question, i);

        int choice = GetUserChoice(question.Answers.Count);

        if (!IsCorrectAnswer(choice, question.Answers))
        {
            mistakes.Add(i + 1);
        }
    }

    CalculateResults(test.Questions.Count, mistakes);
}


Вопрос распечатывается элементарно: сначала его текст, затем варианты ответов, перед которыми ставиться табуляция (\t), чтобы сформировался отступ списка:

Код
private static void PrintQuestion(Question question, int questionIndex)
{
    Console.WriteLine($"{questionIndex + 1}. {question.Text}");

    for (int i = 0; i < question.Answers.Count; ++i)
    {
        Console.WriteLine($"\t{i + 1}. {question.Answers[i].Text}");
    }
}


Получение варинта юзера делается циклом аналогичным вводу Go/Exit в начале. Его завершением управляет переменная isCorrectInput, которая ставится равной true только в самом конце, когда значение успешно спарсится и пройдет проверку на валидность номера ответа (от 1 до максимального номера ответа):
Код
private static int GetUserChoice(int answersCount)
{
    bool isCorrectInput = false;
    int choice = -1;
    do
    {
        try
        {
            Console.WriteLine("Введите номер ответа:");
            string input = Console.ReadLine();
            choice = Convert.ToInt32(input);
            if (choice >= 1 && choice <= answersCount) isCorrectInput = true;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Неправильный формат ввода");
        }
    } while (!isCorrectInput);

    return choice;
}


Для проверки правильности сначала выделяем правильные ответы вопроса (rightAnswers), а потом сравниваем их порядковые индексы с выбранным пользователем индексом. Если хоть один ответ совпал (да, их может быть несколько) - вернется true:
Код
private static bool IsCorrectAnswer(int choice, IList<Answer> answers)
{
    var rightAnswers = answers.Where(x => x.IsRight);
    int chosenAnswerIndex = choice - 1;
    return rightAnswers.Any(x => answers.IndexOf(x) == chosenAnswerIndex);
}


Ну и вишенка - распечатка результатов. Если ошибок нет - все гуд, 100% решений. Если ошибки есть, то мы решаем простую пропорцию, вычисляя сколько процентов ошибочных ответов было дано, а затем вычитаем их из 100%, получая процент успеха.
Код
private static void CalculateResults(int questionsCount, IList<int> mistakes)
{
    if (mistakes.Count == 0)
    {
        Console.WriteLine("Вы на все вопросы ответили правильно! Тест пройден на 100%! ");
    }
    else
    {
        string invalidQuestionsString = string.Join(", ", mistakes.Select(x => $"#{x}"));
        int progressPercentage = 100 - (int)Math.Ceiling(100 * (double)mistakes.Count / questionsCount);
        Console.WriteLine($"Среди ваших ответов есть неправильные ({mistakes.Count}: {invalidQuestionsString})). Тест пройден на {progressPercentage}% ");
    }
}



ЗЫ Вы можете поиграть с кодом, попробовать добавить больше вопросов, принудительное прерывание после первого неправильного ответа, обработку множественных корректных ответов (добавлять или баллы за частичное угадывание), разнообразить типы вопросов и прочее.

Полный листинг на ideone
Ответ написан
Комментировать
Therapyx
@Therapyx
Data Science
- текстовый фаил с вопросам и ответами или тоже самое в базе данных
- класс игра с контейнером классами вопросов (1, *). Класс вопросов с контейнером классами ответов(1,*). Класс ответ обязательно имеет булеан аттрибут правильного ответа (да, нет).
- При старте игры в классе игра считывается текстовый фаил, создаются и распарсиваются классы по контейнерам (array, list, map...)
- Дальше фантазия безгранична.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@youkerni
Unity3D developer
Представьте что в вашем тесте 1000 вопросов, а не 5. Вы немного устанете, мне кажется.

Ну а если из примитивных ошибок - что будет если в качестве ответа вам дадут строку, а не число?
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы