Прошу оценить код
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Написал я тут небольшую игрушку, Крепс называется (исполняемый файл находится здесь). Как всегда прошу оценить код. В данный момент, на предмет правильности построения класса и самой реализации (за интерфейс игры прошу камнями не закидывать, упор на него не делал :)).
Код исходников находится здесь.
ЗАРАНЕЕ СПАСИБО :)
почему решено было использовать указатель, а не ссылку?
Бытует мнение, что работа с адресами повышает производительность.
Но в данном случае
str
является объектом типаstring
, а с объектом лучше использовать ссылку, правильно?В подавляющем большинстве случаев код с указателями и с ссылками транслируется в одинаковый код ассемблера.
Получается нет никакой принципиальной разницы? :)
Удобней использовать. Без операции разыменовования при каждом использовании.
Это уже дело вкуса:)
Ссылка удобна, но при использовании указателя видно с чем имеешь дело.
Хотите вы песен? Их есть у меня... (с) ))
(1) Конструктор должен создавать объект, полностью готовый к употреблению. Почему для вывода на экран костей требуется некий внешний (по отношению к объекту) массив? Либо этот массив должен быть передан конструктору объекта как параметр, либо, что лучше, вообще не иметь внешнего массива, а использовать внутренний. (Также см. п.5)
(2) Почему константа размера массива
SIDE_OF_DICE
попала в перечислениеStatus
?? Да ещё таким образом, что две константы в перечислении (SIDE_OF_DICE
иCONTINUE
) имеют одинаковое значение 2.(3) Вне класса
Craps
используются только методы. Все переменные класса закрытые. Хорошо. Зачем нужны тривиальные геттеры и сеттеры? («Тривиальные» — в том смысле, что кроме доступа к переменной, они не выполняют больше никаких функций.) Вне методов класса они не используются. Зачем они используются внутри класса? Переменные класса в методах класса и так доступны.(4) Примерно то же можно сказать про метод
Craps::rollDie()
. Зачем он нужен?В свете (3) и (4) метод
Craps::rollDice()
будет выглядеть проще:И размер кода сократится наверное процентов на 50, если убрать всё лишнее.
(5) Если предположить, что в дальнейшем игра будет отображать кости каким-то более сложным образом (в чём я лично сомневаюсь (с) Ослик Иа), то массив функций для отображения каждого состояния кости ещё может быть оправдан. При этом, эти функции не должны быть видны ни где, кроме методов класса
Craps
. Как они могут быть реализованы? (а) Как свободные функции, как сейчас это сделано. (б) Или как закрытые статические методы класса. В обоих случаях в классе надо организовать массив указателей на функции (тоже статический член класса) и инициализировать его (статический член класса можно инициализировать вне конструктора).(6) Продолжая тему, мне кажется, что проще и логичнее было бы в классе иметь метод, который отрисовывал бы кость, исходя из числа очков, переданное в метод как параметр.
(7) Вместо массива указателей на функции для отрисовки костей напрашивается массив C-строк.
(8) Функцию
input()
надо перенести из файла craps.cpp в другое место. Она к классуCraps
отношения не имеет.Общее впечатление от кода: перемудрил.
PS-1. Большое желание забить камнями за интерфейс.....
PS-2. Вообще сама игра идиотская. Покер на костях — значительно интереснее ;-)
ЧЕРЕП, СПАСИБО!
Наверное, из-за переизбытка информации. Пока не совсем понятно, что и когда использовать и как это будет работать. Все хочется посмотреть и проверить. У меня такое впечатление, что когда начал знакомится с классами, как-будто С++ заново начал изучать. %)
Все методом проб и ошибок. Например:
Хотелось попробовать что-то с указателем на функции, в которых ты правильно заметил, можно разместить отрисовку кости. Кстати пробовал отрисовать границы кости — выходят они корявенькие, то границы выезжают, то дырочки относительно границ не симметричны.
В общем оставил пока одни дырочки :) дабы видно было работу программы.
Или
Перечисление
Status
хотел определить как константу в самом классе. Но функцияStatus Craps::getGameStatus() const;
возвращает одно из значений перечисления. В таком случае пришлось вынести определение перечисления за пределы класса, так как компилятор выдает ошибку "не существует типа данныхStatus
".Это я все к тому, что хочется все попробовать...
А насчет
SIDE_OF_DICE
иCONTINUE
, то дляCONTINUE
не принципиально какое значение в нем будет хранится, а дляSIDE_OF_DICE
это значение должно быть 2. Наверное определение перечисления можно переписать такЯ понимаю, что
SIDE_OF_DICE
не созвучна сStatus
, но не создавать же дляSIDE_OF_DICE
еще одно перечисление с названием , допустимDICE
?!В общем, обещаю добраться и до Покера на костях, как только приведу свой «серый стек» в порядок. )))
Для таких констант вообще не место в перечислении. Я дважды просмотрел код двух файлов, пока обнаружил где определяется
SIDE_OF_DICE
. Оно должно определяться либо черезconst
, либо через#define
.Если в твоём последнем варианте для
enum Status
изменить значениеBEGIN
или вставить доSIDE_OF_DICE
ещё одно состояние игры, то что получится? Правильно — ничего хорошего. Надо избегать кода, чреватого ошибками.Кстати, в программе про класс
List
ты использовал такой же приём: определение константы размера массива черезenum
. Лучше возможности языка использовать традиционно.Перечисление Status всё-таки не константа. Это во-первых.
Во-вторых, тип данных
Status
таки можно определить внутри определения классаCraps
.Спасибо, Череп, за объяснения.
В языке С++ столько возможностей, что просто запутаться можно :) как в шахматах, сделал один неправильный ход и через два хода пол доски отдал.
Череп, я тут переписал код в соответствии с твоими инструкциями (код здесь). Посмотри пожалуйста.
Добавил в
craps.cpp
функцию отображения случайных костей и вmainCraps.cpp
функцию прокрутки костей, которая собственно говоря меня и смущает.Здесь (и в функции
turnDice()
аналогично):ты объявляешь автоматический массив
pshow[]
, который экранирует одноимённый массив-член класса. С ним ты дальше и работаешь.Надо так:
Соответственно в
turnDice()
объявление массива надо просто удалить.Это — основная ляпа.
Из мелких улучшений ещё можно посоветовать:
(1) Вместо литерала 6 использовать константу
DICE
(я так понял, что это количество граней кости и, соответственно, максимальное количество очков на одной стороне кости).(2) Сделать приватную функцию:
поскольку это выражение встречается неоднократно,
или оптимизировать метод:
(3) Я бы код функции
turn()
перенёс вCraps::rollDice()
. Посколькуturn()
занимается только анимацией и вызывается всегда передCraps::rollDice()
. Или, если тебя смущает такое решение, сделал быturn()
приватным членом класса и вызывал вCraps::rollDice()
. Собственно и вызовCraps::showDice()
тоже просится вCraps::rollDice()
. Тогда получится, чтоCraps::rollDice()
сразу показывает анимацию, вычисляет результат броска и показывает его на экране. Вроде логично. Впрочем это, наверное, уже дело вкуса.По поводу пользовательского интерфейса и общего вида: попробуй посмотреть модуль
CScreen
из «Змейки». Ты ж вроде под Виндой живёшь?Первое что меня смущало, это то что при входе в функции
turnDice()
иshowDice()
постоянно происходит инициализация массива указателей.Второе: это то, что много функций отображения и прокрутки костей. В общем удалил я функцию
turnDice()
и оставилrollDice()
— так как ее основная функция получить значения выпавших костей, и оставилshowDice()
куда перенес функции «вращения» и отображения костей.И это третья проблема. Класс Craps получился кросс-платформенным (наш товарищ porshe об этом мечтает :) ) А тут в функции прокрутки костей нужно постоянно сбрасывать экран. Еще для задержки экрана, в коде я использовал
Sleep()
и дабы не привязывать реализацию методов класса к Винде, в main-файле объявил функциюturn(Craps & c)
, именно поэтому и функцияturnDice()
появилась, чтоб после нее сброс и задержку экрана делать.Короче говоря я попытался использовать
<ctime>
и с учетом твоих корректировок по инициализации указателя на функции и мелких ляпов переписал я функциюshowDice()
следующим образом, но... одинsystem("cls")
не захотел уйти :)Я об этом уже думал и бегло просматривал твой код. Мне хотелось бы:
— интерфейс игры сместить от полей консоли к центру;
— квадрат под «дырочками» закрасить в белый цвет, а «дырочки» сделать черными (типа кубики).
Это можно сделать? Я имею в виду цвет.
Если о кроссплатформенности, то тут porshe выложил ссылку на портированные исходники «Змейки» на Линукс.
Легко. И даже избавиться от постоянной очистки экрана легко: перезаписывать только изменяющуюся область экрана. И вывести всю информацию по игре: общую статистику, текущее положение в игре, сами кости...
Закрасить фон в белый цвет можно. «Дырочки» будут чёрные колечки на белом фоне (ты там вроде маленькую латинскую букву «о» используешь).
Метод
text_attr(WORD attr)
принимает слово, первый байт которого определяет цвет фона, а второй — цвет символа. Итого:WORD(0x0a)
— ярко-зелёный на чёрном фоне,WORD(0x70)
— чёрный на светло-сером фоне.