Pull to refresh

Comments 39

UPD. Прошу прощения, пока редактировал текст под макросы, сделал ошибку в функции MoveCard — цикл не до 52, а до 53 должен быть. А я чего-то решил, что правильно всё-таки до 52. Нет. :) Архив перезалит.

Скачавшие старую версию не смогут её выиграть — там из-за этой ошибки теряется карта,
Им не надо «учиться» пользоваться. Надо просто начать и выучить две команды — git commit и git push. Ну хорошо, один раз будет нужно сделать git init

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

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 — жизненно необходимый навык для любого программиста.

Есть фирмы, где программу пишут один-два человека, и никаких систем контроля версий никто никогда не применял и не планирует применять.
И я инженер, а не программист или инженер-программист.
Знаю инженеров или инженеров-программистов которые используют fossil (но не вижу смысла и не рекомендую)
Сам использую 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.
И кстати, git нужен интернет или можно поднять на локальной машине? Потому как clone я делал как раз для локальной машины (вместо url вбивал путь к папке). Я бы git внедрил бы, но мне нужно, чтобы работало без интернета и без сетки. Потому как разрабатываемое ПО не предназначено для публичного доступа от слова совсем, ибо относится к военной аппаратуре.
Для git вообще «сервер» не нужен. Это просто толпа файлов в папке .git и сценарии для их обработки. Git может использовать некоторые протоколы для того, чтобы синхронизировать изменения между хранилищами.

Ничего «поднимать» для одиночного использования не нужно — достаточно
1) сделать git init в папке с исходниками (создаст пустую структуру в папке .git),
2) добавить файл .gitignore, в котором перечислить маски имен файлов, за которыми следить не нужно
3) перед каждым коммитом делать git add --all
А как разделить проекты? Я же не могу добавить все файлы в одну кучу. А как брать репозиторий в таком случае?
У каждого проекта в корне сделать git init.

Если есть зависимости между проектами, есть две стратегии соединения репозиториев — submodule и subtree

Под «брать репозиторий» вы имеете в виду совместную работу? или «как получить из репозитория исходники для работы, что-то с ними сделать и положить результат обратно?»

Если второе, то это и не нужно. Вы просто работаете с файлами прямо в своей локальной копии (там, где у вас папка .git лежит). Вы можете приказать git выудить из репозитория конкретнyю ветку (git checkout) — тогда все отслеживаемые файлы у вас заменятся на нужные.
А, понятно, спасибо. Меня интересует и как совместную работу организовать и как получать исходники для работы и отправлять обратно. И ещё есть идея поставить на некий сетевой компьютер.

В книжке автор запрашивает git clone с сервера .project. А откуда этот ".project" возьмётся в папке .git? Или это просто переименованный .git?
У git есть настройка «origin» — это как раз репозиторий (может быть сетевым), с которым он пытается синхронизироваться по умолчанию, когда ему командуют git pull/push

Штатным средством сделать git сетевым является взять машину с линуксом и ssh, раздать всем ключи RSA подлиннее и создать на ней bare — репозиторий (git --bare init)

Но так как git работает просто с папками, такой репозиторий можно создать хоть на машине с Windows, на общем сетевом диске. Примапить его на какую-то букву и дальше push/pull туда.
Ну, посмотрим, что получится.
Когда то в начале 90-стых тоже с другом занимался, на ассемблере на Profi клон ZX Spectrum, основная проблема не хватало скорости чтобы тащить карту по столу, несколько дней переписывали процедуру высчитывая машинные такты.
В штатном видеорежиме спектрума или в каком-то своём у профи? В штатном должно хватать скорости на асме — там спрайт ведь небольшой.
в расширенном насколько помню при таскании карта дергалась вот и ужимали, а еще портировали музыку с писишных игрушек и крутили ее вовремя игры через AY, так еще у по продавать пасьянс, через разработчика Profi (кондор вроде), умудрились, правда пришлось к игрушке лепить защиту от копирования с записью секретного ключа в межсекторной области дискеты.
Золотые времена! :)
Надо же, кому-то не понравился этот мой комментарий. :) Удивительно.
помню я писала шахматы… ох были времена, я тогда еще студенткой была, это мой диплом был такой, хэх сейчас даже смешно немного
А про шахматы у меня статья уже была. :) Вот эта.
Видел исходники оригинальной косынки в числе утекших исходников Win2k.
Там же есть Сапёр и Реверси.
Исходников оригинального Паука не видел, декомпилирую потихоньку в качестве хобби…
Не, я только косынку умею играть. Научил друг в далёком 2004 году. С тех пор и играю. :)
Кстати, сапёр у меня тоже есть самодельный. Может, и по нему какую-нибудь статью напишу. Вдруг кому столь простые игры интересны будут.
Если собрать все самодельны саперы, и еще те что под Linux получится целая БСЭ.
Но сам по себе сапер… непродуман и малоинтересен. Нацельтесь хотя-бы на пазлы от разработчика putty (в низу они под Windows, *nix, Andoid, Palm, iOS).
«Паука» много раскладывал с выходом XP и до сих пор балуюсь, параллельно с с просмотром каких-н. видео.
До компилябельного состояния еще не довёл…

Реверси увлекался в институте, в 90-е гг. Реверси шел в комплекте древней Windows 3.0, но запускался еще на XP. Хоть он и 16-разрядный, под wine-ом работает нормально. Немного рефакторнул реверси, отделил геймплей от гуи (и сказал, что это хорошо). Гуи перевел на Qt. Теперь и для малины и для андроида его собрать можно… Выкладывать не могу — нарушение прав всё-таки…
Перво-наперво, нам понадобится графика.

хм, для консольного варианта можно псевдографикой воспользоваться :)

Стоит похвалить за сделанную игру. Но…

Но эту статью скинул своим ученикам как пример кода, за который они могут остаться без башки. Вы понимаете, что по таким статьям могут захотеть учиться новички? Неужели сложно хотя бы вылизать код перед тем как залить его на хабр? Это всё-таки серьёзный ресурс, а не своя стена в вк
Если не секрет, что конкретно вам не нравится в коде? Я не понимаю. Покажите, если не сложно, пример хорошего кода.
Описанное gbg, мягко говоря, субъективное мнение. Если ему нравятся константы, то я их терпеть не могу — минимум глобальных переменных. Если его смущает функция больше 20 строчек, то это тоже маразм — линейная последовательность без ветвлений не требует сокращения. Функциональное деление? Вообще-то, оно там есть. Странные переменные? В них есть логика. s — source, d-destination, n,m,k,l — переменные циклов исторически (в таком порядке), 1-начальная точка, 2-конечная, d_чего-либо — приращение величины. xl-левая граница, xr-правая. Ну и так далее. Можно, конечно, макросами задать всякие 53 и коды ящиков. Но не сделал.
Я не являюсь программистом по специальности — моя специальность квантовая электроника и лазерная техника. Однако, я очень часто пишу программы. Я пишу их около 20 лет на самых разных языках (от бейсика до Си через паскаль и ассемблеры разных процессоров) под разные операционные системы от Windows до QNX. Насколько сложные я пишу программы? Трудно сказать, но аппаратными комплексами уровня изделий военного назначения они вполне управляют. И я не очень понимаю, чего конкретно вы ждёте от кода? Я видел код многих изделий — там нет также ничего понятного стороннему человеку. Не зная идеи работы программы, невозможно в ней разобраться. А идея может быть очень непростой. Для этого и существуют комментарии.
Вот смотрите, давайте возьмём вот эту функцию:

//----------------------------------------------------------------------------------------------------
//переместить карту из ящика 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;
 }
}


По-моему, они прозрачны донельзя. Или я ошибаюсь?
случайно промазал и не туда ответил)
https://habrahabr.ru/post/330470/#comment_10265140
Прошу прощения что резко ответил утром, формулировка некрасивая вышла.

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.

Sign up to leave a comment.

Articles