Ролик WinForms

Я пишу приложение winforms для роликов в кости, используя C # 2012 VS. Ролик для кости создан для игры на настольном компьютере Shadowrun. Я чувствую, что в GUI может быть слишком много кода, но я не уверен, как его форматировать. Я также очень открыт для общих советов. Это моя первая программа, которую я пытаюсь построить самостоятельно после окончания школы. Я никогда не пытался использовать графическое приложение раньше.

GitHub

Пользовательский код управления:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace DiceRollerWinForms
{
    public partial class DiceRollerUserControl : Form
    {
        private RollDice diceRoll = new RollDice();
        private Roll currentRoll = new Roll();
        private Int32 rollNumber = 1;
        private Int32 currentNumDice = 1;
        private Int32 lastNumHit = 0;
        private Int32 lastNumDiceRolled = 0;
        private string numDiceText = "Number of Dice";

        public DiceRollerUserControl()
        {
            InitializeComponent();
        }

        private void newToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void splitContainer1_Panel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void mainSplitContainer_SplitterMoved(object sender, SplitterEventArgs e)
        {

        }

        private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
        {

        }

        private void diceToRollBox_TextChanged(object sender, EventArgs e)
        {
            if (Int32.TryParse(diceToRollBox.Text, out currentNumDice))
            {
                currentNumDice = Int32.Parse(diceToRollBox.Text);
            }
            else
            {
                currentNumDice = 1;
            }
            diceToRollBox.Text = currentNumDice.ToString();
        }

        private void subtractDiceButton_Click(object sender, EventArgs e)
        {
            if (currentNumDice > 1)
            {
                currentNumDice--;
            }
            diceToRollBox.Text = currentNumDice.ToString();
        }

        private void addDiceButton_Click(object sender, EventArgs e)
        {
            if (currentNumDice < 100)
            {
                currentNumDice++;
            }
            diceToRollBox.Text = currentNumDice.ToString();
        }

        private void RollDiceButton_Click(object sender, EventArgs e)
        {
            currentRoll = diceRoll.RollTheDice(currentNumDice, false, false);
            ListViewItem i = new ListViewItem(rollNumber.ToString());
            i.SubItems.Add(currentRoll.numHits.ToString());
            i.SubItems.Add(currentRoll.rawRoll);
            i.SubItems.Add(currentRoll.isGlitch.ToString());
            i.SubItems.Add(currentRoll.isCritGlitch.ToString());
            resultView.Items.Add(i);
            rollNumber++;
        }

        private void RollDiceWithEdgeButton_Click(object sender, EventArgs e)
        {
            currentRoll = diceRoll.RollTheDice(currentNumDice, false, false);
            ListViewItem i = new ListViewItem(rollNumber.ToString());
            i.SubItems.Add(currentRoll.numHits.ToString());
            i.SubItems.Add(currentRoll.rawRoll);
            i.SubItems.Add(currentRoll.isGlitch.ToString());
            i.SubItems.Add(currentRoll.isCritGlitch.ToString());
            resultView.Items.Add(i);
            rollNumber++;
        }

        private void reRollDiceWithEdgeButton_Click(object sender, EventArgs e)
        {
            currentRoll = diceRoll.RollTheDice(currentNumDice, false, false);
            ListViewItem i = new ListViewItem(rollNumber.ToString());
            i.SubItems.Add(currentRoll.numHits.ToString());
            i.SubItems.Add(currentRoll.rawRoll);
            i.SubItems.Add(currentRoll.isGlitch.ToString());
            i.SubItems.Add(currentRoll.isCritGlitch.ToString());
            resultView.Items.Add(i);
            rollNumber++;
        }
        private void resultView_SelectedIndexChanged(object sender, EventArgs e)
        {

        }
    }
}

Другой код:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace DiceRollerWinForms
{
    class Roll
    {
        public string rawRoll { get; set; }
        public Int32 numHits {get; set;}
        public bool isGlitch {get; set;}
        public bool isCritGlitch{get; set;}
        public Int32 lastNumDiceRolled { get; set; }
        public Int32 lastNumHitsRolled { get; set; }
        public bool lastRollWasEdge { get; set; }


        internal void FinalRollResults(Int32[] resultsRaw, Int32 numDice)
        {
            Int32[] rollResults = new Int32[6];

            for (Int32 i = 0; i < numDice; i++)
            {
                switch(resultsRaw[i])
                {
                    case 1:
                        rollResults[0]++;
                        break;
                    case 2:
                        rollResults[1]++;
                        break;
                    case 3:
                        rollResults[2]++;
                        break;
                    case 4:
                        rollResults[3]++;
                        break;
                    case 5:
                        rollResults[4]++;
                        break;
                    case 6:
                        rollResults[5]++;
                        break;
                }
            }

            numHits = rollResults[4] + rollResults[5];
            //If more than half the dice you rolled show a one, then you’ve got problems. This is called a glitch.
            if ((numDice / 2) < rollResults[0])
            {
                this.isGlitch = true;

                if (numHits == 0)
                {
                    this.isCritGlitch = true;
                }
            }

            rawRoll = string.Join(",", resultsRaw);

        }
        public Roll()
        {
             //six the number of sides on a dice
            rawRoll = ""; //its a magic number
            numHits = 0;
            isGlitch = false;
            isCritGlitch = false;
        }
    }
    class RollDice : Roll
    {
        private Int32 const_Delay = 0;

        private RNGCryptoServiceProvider RNGProvider = new RNGCryptoServiceProvider();

        public Roll RollTheDice(Int32 numberOfDiceToRoll, bool edgeRoll, bool reRollEdge)
        {
            Roll currentRoll = new Roll();
            Int32[] results = new Int32[numberOfDiceToRoll];



            for (int i = 0; i < numberOfDiceToRoll; i++)
            {
                System.Threading.Thread.Sleep(const_Delay);
                results[i] = RNGDiceRoll(RNGProvider);
            }

            currentRoll.FinalRollResults(results, numberOfDiceToRoll);
            currentRoll.lastNumDiceRolled = numberOfDiceToRoll;
            currentRoll.lastNumHitsRolled = currentRoll.numHits; 
            if (edgeRoll) 
            {
                lastRollWasEdge = true;
            }

            return currentRoll;
        }
        private Int32 RNGDiceRoll(RNGCryptoServiceProvider Provider)
        {
            byte[] arr = new byte[4];
            Int32 rand = 0;
            do
            {
                Provider.GetBytes(arr);
                rand = BitConverter.ToInt32(arr, 0);
            }
            while (rand < 1);
            Int32 roll = (rand % 6) + 1;
            return roll;
        }
    }
}
11 голосов | спросил Andy Hoffman 22 +04002013-10-22T18:48:18+04:00312013bEurope/MoscowTue, 22 Oct 2013 18:48:18 +0400 2013, 18:48:18

2 ответа


14

Как дополнение к эпическому ответу @retailcoder я предлагаю ...

Объектно-ориентированные кости

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

Класс умирания

Одиночная Dice, то есть.

public class Die {
    protected int sides = 6;
    protected Random generate = new Random();

    public int Roll() { return generate.Next(1,(sides+1)); }
}

Примечания

  • Это одна 6-сторонняя вещь. Его можно перевернуть.
  • Случайный класс - См. озабоченность OP по этому вопросу

Класс Dice

У нас есть понятие «прокатные кости». Итак, давайте сделаем кубики.

public class Dice {
    List<Die> dice;

    public int Count { get { return dice.Count; }

    public Dice (params Die[] theDice) { // see notes.
        foreach (Die die in theDice) { dice.Add(die); }
    }

    // this is an indexer. see notes.
    public Die this[int i] { get { return dice[i]; } }

    public int Roll() {
        int total = 0;

        foreach (Die die in dice) { total =+ die.Roll(); }

        return total;
    }
}

Примечания

  • Теперь перекатывание одного штампа красиво абстрагируется. Код читается так, как он делает.
  • params позволяет иметь переменное количество параметров. Таким образом, мы можем пройти 1, 2, 10 кубиков, если хотим
  • Индексатор классный. Прочтите это.

DiceGame

Просто эскиз. Это может быть крэпс, яхтзи, лирские кости ...

public class DiceGame { 
    protected Dice dice;

    public DiceGame(Dice theDice) { dice = theDice; }

    public void Play() { // TBD
         int total = dice.Roll();  rolling all the dice at once

         // indexer allows individual Rolls.

         int firstDieRoll = dice[0].Roll();
         int secondDieRoll = dice[1].Roll();

         // when I don't care how many there are
         for (int i=0; i<dice.Count; i++) {
             dice[i].Roll();
         }
    }
}

Примечания

  • Все в терминах Dice, и это звучит логично. DiceGame красиво абстрагируется относительно обработки костей.
  • Игра имеет дело только с Dice, а не индивидуальным Die s.
  • Он не знает, как бросить кости. Кости знают, мы просто говорим, что кости это делают.
  • Dice, в свою очередь, не знает, как свернуть индивидуальный Die s, мы просто скажем, что кубик скатился.

Инверсия управления

Нам все еще нужен класс, чтобы собрать все это вместе. Но сначала МОК ...

Метод knee-jerk заключается в создании экземпляра DiceGame, внутри которого мы «обновляем» некоторые Dice, внутри которого мы создаем несколько объектов Die. Лучший способ - инвертировать эту конструктивную иерархию. Сначала сделайте наименьшие бит, затем передайте их в конструктор вещи, в которую он входит, и так далее ...

public DiceGameBuilder {
    protected Die die1 = new Die();
    protected Die die2 = new Die();

    protected Dice dice = new Dice(die1, die2); //params doing it's thing

    // DiceGameBuilder actually does not need die1, die2 objects
    // so the constructor call would look like this
    protected Dice dice = new Dice (new Die(), new Die());

    protected DiceGame craps = new DiceGame(dice);

    craps.Play();
}

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

IOC упрощает изменение

  

Итак, мы катаемся с 6-сторонними кубиками. Что делать, если вы хотите изменить свой код, чтобы также поддерживать кости размером 8, 12 или 20? (Я не знаю, что такое ShadowRun, но, может быть, правила могут быть настроены?) И кто говорит, что все кости должны иметь одинаковое количество лиц? Это делает довольно сложную кодировку 6 для исправления!

Добавить новый конструктор Die

public Die (int sides = 6) { this.sides = sides; }

Drumroll, пожалуйста

  • Дополнительные параметры конструктора:
    • Существующие вызовы без параметров не нужно изменять
    • Обязательно задокументируйте поведение по умолчанию! XML-комментарии будут в порядке.
  • ТОЛЬКО коснется класс Die.
  • Каждый Die может иметь разное количество сторон
  • БезDie класс такой модификации был бы кошмаром
  • Я не сделал класс Die, потому что знал, что я собираюсь сделать это изменение. Die - это логичный, значимый объект в мире игры в кости. Вот почему.
  • Dice не нуждается в модификации
  • DiceGame не нуждается в модификации
  • Изменение очень мало, потому что только Die должен знать , сколько сторон оно имеет.
  • Классы имеют тенденцию быть маленькими, потому что каждый класс отвечает за выполнение своей собственной вещи.
  • Методы имеют тенденцию быть небольшими - это симптом хорошо продуманных классов. Dice.Roll() - это 3 строки кода!
  • Мы создали весь этот бизнес-уровень с нулевым учетом пользовательского интерфейса. Это хорошо.
  • Вы должны иметь возможность «управлять» игрой в кости без интерфейса.
  • Вышеуказанные точки означают, что при его написании будет свободная связь с пользовательским интерфейсом.
  • При построении, вождении, сначала тестировании бизнес-уровня (по крайней мере, некоторой минимально функционирующей части) пользовательский интерфейс будет намного проще и менее ошибочным.

Обслуживание кода

Здесь описывается этот комментарий о прокатке N Dice.

Извлеченные уроки

  • Перегрузка метода намного чище и понятнее.
  • Добавление нового метода менее подвержено ошибкам, а затем изменению существующего.
    • Существующий код клиентского кода Roll() не подвержен риску нарушения.
  • Хороший дизайн OO, который «разделяет проблемы», который следует Принципу единой ответственности, делает изменения намного лучше во всех отношениях.
  • Невидимая рука хорошего дизайна работает здесь, как и вышеприведенный пример модификации конструктора Die.

.

public class Dice {
    public int Roll() { } // don't need to touch this

    public int Roll (int thisMany) {
        if ( this.Count < thisMany ) return 0;

        int total = 0;

        for (int i = 0; i < thisMany; i++)
            total += dice[i].Roll();

        return total;
    }
}
ответил radarbob 24 +04002013-10-24T07:26:52+04:00312013bEurope/MoscowThu, 24 Oct 2013 07:26:52 +0400 2013, 07:26:52
4

ваш Do..While бросил меня за цикл. Я видел оператор While, а не Do, предшествующее ему, и задался вопросом, где был код.

вы уже установили переменную rand на что-то меньшее, чем 1 перед циклом, поэтому вы должны просто написать его

while (rand < 1)
{
  Provider.GetBytes(arr);
  rand = BitConverter.ToInt32(arr,0);
}

это более читаемо и меньше кода для записи.

моим инструкторам не нравились циклы Do-While, им понравилось, когда мы использовали While Петли. сложнее попасть в бесконечный цикл с циклом while, а не с помощью do-while

Разница между Do и While

Две петли различны.

Цикл Do будет проходить через блок, а затем решить, следует ли снова пройти цикл на основе while. цикл While проверит условие перед прохождением цикла.

ответил Malachi 22 +04002013-10-22T19:19:06+04:00312013bEurope/MoscowTue, 22 Oct 2013 19:19:06 +0400 2013, 19:19:06

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132