Комментарии 39
Скачавшие старую версию не смогут её выиграть — там из-за этой ошибки теряется карта,
И еще. Я ваши статьи узнаю по внешнему виду исходников. Да. По этим ужасным простыням кода низкого качества, в котором отсутствует функциональное деление, использование констант и логичное именование переменных. Ваш код (если на него смотреть как на самостоятельную единицу ценности) годится только на выброс. Ну или в качестве наказания для других разработчиков.
1) у вас имеются длииииннные методы (эмпирическое правило — размер метода должен быть не более одного «экрана» текста — той области, которую можно охватить зрением, не переводя взгляд). Разбивка простыней на части помогает их структурировать.
2) многие данные по смыслу являются векторами. Вот и работайте с ними, как с векторами. Этим вы исключите копипаст и эффект последней строки при копипасте. Не волнуйтесь, компиляторы уже умеют раскручивать циклы. Посмотрите хотя бы на нашу с haqreu библиотеку для векторов и матриц.
3) если вместе с выполнением (2) начать именовать переменные содержательно, программу можно будет вообще читать как сказку, а не продираться через x1; x2; dx; и прочее.
4)константные данные следует помещать в const, а не толкать в дефайны.
Им не надо «учиться» пользоваться. Надо просто начать и выучить две команды — git commit и git push. Ну хорошо, один раз будет нужно сделать git init
Я чтобы попробовать поставил git отдельно на компьютер и скачал книжку. Так вот, не получилось. В книжке написано — сделайте git add *.cc. Хорошая идея. Только вот как добавить несколько проектов? Как добавить папку? Не написано в той главе, где упоминается git add. А git clone вызывало ошибку kerlen322.dll — у меня Windows XP, а оно, которое скачанное, желает как минимум висту.
По этим ужасным простыням кода низкого качества, в котором отсутствует функциональное деление, использование констант и логичное именование переменных.
По-моему, всё это есть как в этом коде, так и в коде остальных статей. Констант, конечно, нет. Я всё-таки предпочитаю define. Но уж именование переменных и функциональное деление там точно есть.
Разбивка простыней на части помогает их структурировать.
Не соглашусь. От множества мелких функций будет в глазах рябить.
многие данные по смыслу являются векторами. Вот и работайте с ними, как с векторами.
Для шахмат такого лучше не делать. В движке это сделано и так. А эта программа порт с PSP и там я точно их не использовал, потому как, помнится, компилятор там был использован C, а не C++.
а не продираться через x1; x2; dx; и прочее.
Но в данном случае как раз x1,x2 и dx очень даже содержательны. x1;y1-начальные координаты, dx-размер по x.
Исключение составляют функции текстурирования в 2.5D движке — им 15 лет и я тогда написал именно так, а разбираться заново стало лень. Там действительно t1-индекс внутри текстуры, x1-координата, ну и т.д.
4)константные данные следует помещать в const, а не толкать в дефайны.
Не люблю глобальные переменные. А вот define смотрится органично и находится в тексте очень легко — большие буквы. А недостатки есть у многих вещей, но это не повод от них отказываться.
многие данные по смыслу являются векторами. Вот и работайте с ними, как с векторами.
А, вы не про std:vector. Тогда зачем координаты представлять векторами?
зачем координаты представлять векторами?
Дело в том, что программа от этого сокращается раза эдак в два (когда 2D). Кроме того, данная абстракция позволяет лучше понять суть производимых с координатами операций.
А git clone вызывало ошибку kerlen322.dll — у меня Windows XP

Дело в том, что программа от этого сокращается раза эдак в два (когда 2D). Кроме того, данная абстракция позволяет лучше понять суть производимых с координатами операций.
Всё может быть. Но по-моему, программа и так достаточно прозрачна и вполне себе понятна.
У меня работает
А у меня portable-версия не работает.
Плохому танцору…
В нынешние времена знание git/svn — жизненно необходимый навык для любого программиста.
И я инженер, а не программист или инженер-программист.
Сам использую git чтобы… иногда посмотреть на предыдущую версию исходников.
Чтобы использовать github, git вообще не нужен. (ну чтобы выкладывать исходники и релизы)
1. git init
2. в .gitignore пишем обычными масками какие файлы в git не ложить
3. git add.; git commit -a -m «bla-bla changes» (у меня не windows и там есть alias-ы и выглядит как commit «bla-bla changes») и забываем про git add
4. gitk (gitgui) удобно чтобы просто глянуть на предыдущие версии исходников
5. git log — список версий
6. git checkout 1234567890 — переходим на предыдущую версию, где 123 — хэш из п.5
7. git chackout master — вернутся на самую последнюю версию
Проблемы могут возникнуть если файлы отредактировать, незакоммитить и перейти на другую версию, но все решается гуглом и stackoverflow.
Ничего «поднимать» для одиночного использования не нужно — достаточно
1) сделать git init в папке с исходниками (создаст пустую структуру в папке .git),
2) добавить файл .gitignore, в котором перечислить маски имен файлов, за которыми следить не нужно
3) перед каждым коммитом делать git add --all
Если есть зависимости между проектами, есть две стратегии соединения репозиториев — submodule и subtree
Под «брать репозиторий» вы имеете в виду совместную работу? или «как получить из репозитория исходники для работы, что-то с ними сделать и положить результат обратно?»
Если второе, то это и не нужно. Вы просто работаете с файлами прямо в своей локальной копии (там, где у вас папка .git лежит). Вы можете приказать git выудить из репозитория конкретнyю ветку (git checkout) — тогда все отслеживаемые файлы у вас заменятся на нужные.
В книжке автор запрашивает git clone с сервера .project. А откуда этот ".project" возьмётся в папке .git? Или это просто переименованный .git?
Штатным средством сделать git сетевым является взять машину с линуксом и ssh, раздать всем ключи RSA подлиннее и создать на ней bare — репозиторий (git --bare init)
Но так как git работает просто с папками, такой репозиторий можно создать хоть на машине с Windows, на общем сетевом диске. Примапить его на какую-то букву и дальше push/pull туда.
Там же есть Сапёр и Реверси.
Исходников оригинального Паука не видел, декомпилирую потихоньку в качестве хобби…
Кстати, сапёр у меня тоже есть самодельный. Может, и по нему какую-нибудь статью напишу. Вдруг кому столь простые игры интересны будут.
Но сам по себе сапер… непродуман и малоинтересен. Нацельтесь хотя-бы на пазлы от разработчика putty (в низу они под Windows, *nix, Andoid, Palm, iOS).
До компилябельного состояния еще не довёл…
Реверси увлекался в институте, в 90-е гг. Реверси шел в комплекте древней Windows 3.0, но запускался еще на XP. Хоть он и 16-разрядный, под wine-ом работает нормально. Немного рефакторнул реверси, отделил геймплей от гуи (и сказал, что это хорошо). Гуи перевел на Qt. Теперь и для малины и для андроида его собрать можно… Выкладывать не могу — нарушение прав всё-таки…
Перво-наперво, нам понадобится графика.
хм, для консольного варианта можно псевдографикой воспользоваться :)
Но эту статью скинул своим ученикам как пример кода, за который они могут остаться без башки. Вы понимаете, что по таким статьям могут захотеть учиться новички? Неужели сложно хотя бы вылизать код перед тем как залить его на хабр? Это всё-таки серьёзный ресурс, а не своя стена в вк
Описанное gbg, мягко говоря, субъективное мнение. Если ему нравятся константы, то я их терпеть не могу — минимум глобальных переменных. Если его смущает функция больше 20 строчек, то это тоже маразм — линейная последовательность без ветвлений не требует сокращения. Функциональное деление? Вообще-то, оно там есть. Странные переменные? В них есть логика. s — source, d-destination, n,m,k,l — переменные циклов исторически (в таком порядке), 1-начальная точка, 2-конечная, d_чего-либо — приращение величины. xl-левая граница, xr-правая. Ну и так далее. Можно, конечно, макросами задать всякие 53 и коды ящиков. Но не сделал.
Вот смотрите, давайте возьмём вот эту функцию:
//----------------------------------------------------------------------------------------------------
//переместить карту из ящика s в ячейку d
//----------------------------------------------------------------------------------------------------
bool CWnd_Main::MoveCard(long s,long d)
{
long n;
long s_end=0;
long d_end=0;
//ищем первые свободные места в ящиках
for(n=0;n<53;n++)
{
s_end=n;
if (sCard_Box[s][n].Value<0) break;
}
for(n=0;n<53;n++)
{
d_end=n;
if (sCard_Box[d][n].Value<0) break;
}
if (s_end==0) return(false);//начальный ящик пуст
//иначе переносим карты
sCard_Box[d][d_end]=sCard_Box[s][s_end-1];
sCard_Box[s][s_end-1].Value=-1;//карты там больше нет
return(true);
}
Вам лично чего в этой функции не нравится (про магические числа не стоит — я согласен, что их можно было и убрать)?
А в этой функции?
//----------------------------------------------------------------------------------------------------
//переместить карты с учётом правил
//----------------------------------------------------------------------------------------------------
void CWnd_Main::ChangeCard(long s_box,long s_index,long d_box,long d_index)
{
if (d_box>=2 && d_box<9)//если ящик на игровом поле
{
//если он пуст, то класть туда можно только короля
if (d_index<0)
{
if (sCard_Box[s_box][s_index].Value==12) ChangeBox(s_box,s_index,d_box);//наша карта - король, перемещаем её
return;
}
//иначе, класть можно в порядке убывания и разных цветовых мастей
if (sCard_Box[d_box][d_index].Value<=sCard_Box[s_box][s_index].Value) return;//значение карты больше, чем та, что есть в ячейке ящика
if (sCard_Box[d_box][d_index].Value>sCard_Box[s_box][s_index].Value+1) return;//можно класть только карты, отличающиеся по значению на 1
CARD_SUIT md=sCard_Box[d_box][d_index].Suit;
CARD_SUIT ms=sCard_Box[s_box][s_index].Suit;
if ((md==CARD_SUIT_SPADES || md==CARD_SUIT_CLUBS) && (ms==CARD_SUIT_SPADES || ms==CARD_SUIT_CLUBS)) return;//цвета масти совпадают
if ((md==CARD_SUIT_HEARTS || md==CARD_SUIT_DIAMONDS) && (ms==CARD_SUIT_HEARTS || ms==CARD_SUIT_DIAMONDS)) return;//цвета масти совпадают
ChangeBox(s_box,s_index,d_box);//копируем карты
return;
}
if (d_box>=9 && d_box<13)//если ящик на поле сборки
{
//если выбрано несколько карт, то так перемещать карты нельзя - только по одной
if (GetCardInBox(s_box)>s_index+1) return;
//если ящик пуст, то класть туда можно только туза
if (d_index<0)
{
if (sCard_Box[s_box][s_index].Value==0)//наша карта - туз, перемещаем её
{
DrawMoveCard(s_box,s_index,d_box);
}
return;
}
//иначе, класть можно в порядке возрастания и одинаковых цветовых мастей
if (sCard_Box[d_box][d_index].Value>sCard_Box[s_box][s_index].Value) return;//значение карты меньше, чем та, что есть в ячейке ящика
if (sCard_Box[d_box][d_index].Value+1<sCard_Box[s_box][s_index].Value) return;//можно класть только карты, отличающиеся по значению на 1
CARD_SUIT md=sCard_Box[d_box][d_index].Suit;
CARD_SUIT ms=sCard_Box[s_box][s_index].Suit;
if (ms!=md) return;//масти не совпадают
DrawMoveCard(s_box,s_index,d_box);
return;
}
}
По-моему, они прозрачны донельзя. Или я ошибаюсь?
1) стиль именования функций и переменных, который разный. Это не способствует удобному прочтению кода. Где-то есть подчёркивания, где-то нету, тоже не всегда ясно с большими/маленькими буквами. Так же вопрос к названиям переменных, которые не всегда прозрачны
2) Я посоветую немного попрактиковаться в написании чего-то ООПшного. Не обязательно с наследованием и т.д., а именно с позиции «ООП как стиль мышления».
К примеру, возьмём наш «ящик». В коде был такой фрагмент
//ищем первые свободные места в ящиках
for(n=0;n<53;n++)
{
s_end=n;
if (sCard_Box[s][n].Value<0) break;
}
for(n=0;n<53;n++)
{
d_end=n;
if (sCard_Box[d][n].Value<0) break;
}
И с точки зрения быстродействия, и с точки зрения читабельности было бы круто вынести весь функционал «ящика» в структуру, в которой инкапсулировать весь нужный функционал. В итоге, написать что-то типа
s_end=sCard_Box[s].get_border_number();
И это упростит код не только тут, но и в других частях.
П.с.: потом увидел в коде ремарку именно про эту часть. Но тут дело не только в быстродействии, но и в читабельности и удобстве.
3) я бы посоветовал не использовать «магические константы». А допустим мы придумаем как изменить или разнообразить нашу игру;) Захотим добавить пятую-шестую масти, да и размер поля увеличить тогда. А вдруг. Захотелось) Нет смысла закладывать преждевременную основу для всех-всех модификаций. Но в тех местах, где это можно сделать легко — так стоит сделать. Например, ввести переменные для номеров 0,1,2,8,9,12. Опять-таки, даже риск механической ошибки тогда меньше. Когда кода станет много — некоторые такие заразки можно долго ловить по всей программе.
Ну и макросов многовато, это не считается хорошей практикой. Их можно заменить константами
4) в ChangeCard есть такое
CARD_SUIT md=sCard_Box[d_box][d_index].Suit;
CARD_SUIT ms=sCard_Box[s_box][s_index].Suit;
это удобно, ведь мд и мс будет явно легче читать, и ясно что оно такое, как их ввели(хоть и можно придраться к названиям: р). Читать полотно из ифов с кучей индексов сложно. Как вариант, использовать указатели на карты, что бы не писать индексы постоянно.
Прошу прощения что резко ответил утром, формулировка некрасивая вышла.
Да обычная самая формулировка! :) Спасибо за комментарии! Но я думал, что проблемы будут более необычными – перечисленное мне известно, но в основном проходят как рекомендации и дело вкуса (например, класс ящиков – это во много дело вкуса – слишком простой объект, эти ящики).
1) Стиль именования переменных следующий: структуры и классы начинаются с большой буквы S и C. Экземпляр структуры или класса начинаются с маленькой буквы: SCard sCard. Подчёркивание указывает в данном случае индивидуальное имя объекта: CMain cMain_Local. Если класс унаследован от кого-либо, то тот, от кого унаследован класс пишется перед именем наследника, а между ними ставится подчёркивание: class CWnd_Main:public CWnd. Параметры функции чаще всего идут маленькими буквами, но могут быть и большими (если, как мне кажется, они выглядят лучше). В данном случае подчёркивание может разделять части названия или функциональное назначение: dx_1; Имена функций всегда начинаются с больших букв и в тексте большими буквами разделяются отдельные слова в имени функции.
2) Данная программа изначально была написана на Си. Чистом Си (потому что с cpp компилятором для PSP я разобрался позже). Поэтому из ООП тут только обёртка
3) Ну, с магическими числами я согласен. А вот с константами вместо макросов не соглашусь. Это дело вкуса. Да, макрос проходит через единицу трансляции, но не для этого ли он макросом и делался? Он потому и макрос, что должен быть виден и доступен во всём коде. Константа же – локальная фишка. Локальность требуется редко. К тому же непонятно, как удобно именовать константы, чтобы в коде сразу было видно, что это не переменная, с которой можно что-то делать и при этом легко её найти. Макрос же у меня пишется всегда большими буквами и легко выделяется в коде. И сразу же ясно, что это – макрос, не переменная.
4) Дело в том, что ради одного-двух считываний делать указатель на элемент с индексом бессмысленно. Он не добавит читабельности, ибо будет использован те же один-два раза.
Уважаемый автор!
По ключевым словам QNX и по комментариям я понял, что мы - коллеги. Сфера "применения" у нас одна. Хочу написать отдельное СПАСИБО за исходный код и за пояснения к программе.
Спасибо!
У меня на github (есть ссылка в профиле) есть исходники для QNX и Windows для некоторых программ (и там пасьянс под Windows с гораздо лучшим кодом - конкретно в этой статье пасьянс был изначально сделан на Си для PSP, а потому к коду и придрались тут).
А ещё вот вам мои игры для QNX 6. Когда-то делал, ибо играть там особо не во что было. Установки не требуют, но требуют копирования данных (они лежат в каталоге с игрой и имеют такое же название, как каталог с игрой) в usr/share.
Пишем пасьянс «Косынка»