При установке точки останова щелкните правой кнопкой, и необходимо получить несколько опций о том, как точка останова обрабатывается (зарегистрируйте Вар и продолжите, выполнение паузы, и т.д.)
Также удостоверяются, что "Символы загрузки лениво" не выбраны в предпочтениях отладки.
(Относится к Xcode 3.1, не уверенному в прошлом/будущих версиях)
Измените единицу измерения TChessPiece, чтобы она выглядела следующим образом:
TYPE
tBaseChessBoard = class;
TChessPiece = class
procedure GetMoveTargets (BoardPos : TPoint; Board : TBaseChessBoard; ...
...
end;
затем измените модуль, определяющий TChessBoard, чтобы он выглядел следующим образом:
USES
unit_containing_tBaseChessboard;
TYPE
TChessBoard = class(tBaseChessBoard)
private
FBoard : array [1..8, 1..8] of TChessPiece;
...
end;
Это позволяет вам передавать конкретные экземпляры в шахматную фигуру, не беспокоясь о круговой ссылке. Поскольку доска использует Tchesspieces как частное лицо, на самом деле она не должна существовать до объявления Tchesspiece, просто как заполнитель. Любые переменные состояния, о которых должен знать tChessPiece, конечно, должны быть помещены в tBaseChessBoard, где они будут доступны обоим.
Вот некоторые мысли, которые у меня были по этому поводу. В конце концов, вам может потребоваться как можно более разделить активы и код. Я могу придумать несколько возможных стратегий:
Активы в одном репо и код в другом.
Инструменты DVCS не отслеживают других репозиториев, кроме их собственного, поэтому нет никакой прямой поддержки BOM (Bill of Materials), т.е. нет четкого способа определить, когда оба репозитория синхронизированы. (Думаю, это то, для чего предназначен git-submodule или repo ).
Пример: художник добавляет новое изображение в один репозиторий, а программист добавляет функцию для использования Представьте себе, однако, когда кому-то приходится откатывать версии, они вынуждены каким-то образом отслеживать эти изменения самостоятельно.
Накладные расходы на хранилище активов, даже если они влияют только на тех, кто его использует.
Ресурсы и код находятся в одном репозитории, но в двух разных каталогах.
Обе стратегии, перечисленные выше, по-прежнему имеют недостаток в виде больших накладных расходов, поскольку вам необходимо клонировать большое хранилище ресурсов. Одним из решений этой проблемы является вариант первой стратегии, описанной выше, два репозитория; хранить код в распределенном репозитории VCS, а активы - в централизованном репозитории VCS (например, SVN, Alienbrain и т. д.).
Учитывая то, как большинство графических дизайнеров работают с двоичными файлами, обычно нет необходимости в ветвлении, если это действительно не необходимо (новые функции, требующие большого количества ресурсов, которые не понадобятся намного позже). Недостатком является то, что вам нужно будет найти способ резервного копирования центрального репозитория. Отсюда третья стратегия:
Код в репозитории как обычно, а активы не в репозитории. Вместо этого активы должны быть помещены в какую-то систему управления контентом / медиа / активами или, по крайней мере, в папку, для которой регулярно создается резервная копия. Это предполагает, что есть очень небольшая потребность в обратном отслеживании версий с графикой. Если есть необходимость в обратном отслеживании, графические изменения незначительны.
Не похоже, что TChessBoard.FBoard должен быть массивом TChessPiece, он также может быть из TObject и быть понижен в ChessPiece.pas.
Другой подход:
Сделайте свою доску из tBaseChessPiece. Это абстрактно, но содержит определения, на которые вам нужно сослаться.
Внутренняя работа находится в tChessPiece, который происходит от tBaseChessPiece.
Я действительно согласен с тем, что Delphi плохо обрабатывает вещи, которые ссылаются друг на друга - о худшей особенности языка. Я давно призывал к форвардным объявлениям, которые работают в разных единицах. У компилятора будет вся необходимая информация, он не нарушит однопроходный характер, который делает его таким быстрым.
С помощью Delphi Prism вы можете распределить свои пространства имен по отдельным файлам, чтобы вы могли решить эту проблему простым способом.
Модули работают следующим образом. просто фундаментально нарушены их текущей реализацией Delphi. Просто посмотрите, как db.pas нужно было иметь TField, TDataset, TParam и т. Д. В одном чудовищном файле .pas, потому что их интерфейсы ссылаются друг на друга.
В любом случае, вы всегда можете переместить код в отдельный файл и включить его, например, с помощью {$ include ChessBoard_impl.inc}
. Таким образом, вы можете разделить материал по файлам и получить отдельные версии через свой vcs. Однако редактировать файлы таким способом немного неудобно.
Лучшим долгосрочным решением было бы убедить эмбаркадеро отказаться от некоторых идей, имевших смысл в 1970 году, когда родился паскаль, но в наши дни это не более чем заноза в заднице для разработчиков. Однопроходный компилятор - один из них.
Одним из решений может быть введение третьего модуля, который содержит объявления интерфейсов (IBoard и IPiece).
Тогда разделы интерфейса двух модулей с объявлениями классов могут ссылаться на другой класс по его интерфейсу:
TChessBoard = class(TInterfacedObject, IBoard)
private
FBoard : array [1..8, 1..8] of IPiece;
...
end;
и
TChessPiece = class abstract(TInterfacedObject, IPiece)
public
procedure GetMoveTargets (BoardPos: TPoint; const Board: IBoard;
MoveTargetList: TList <TPoint>);
...
end;
(модификатор const в GetMoveTargets позволяет избежать ненужного подсчета ссылок)
Модули Delphi не "принципиально сломаны". А TChessPiece не требует ссылки на игру или доску для определения допустимых целей для перемещения из данной текущей позиции. Ограничения доски (8 рангов и файлов) являются константами предметной области, а не свойствами данного экземпляра доски.
Таким образом, требуется TChessGame , которая инкапсулирует знания, объединяющие понимание доски, фигур и - что особенно важно - правила, но доска и фигуры не нуждаются в знании друг друга ИЛИ игры.
Может показаться заманчивым поместить правила, относящиеся к разным фигурам в классе, для самого типа фигуры, но это это ошибка, imho, поскольку многие правила основаны на взаимодействии с другими фигурами, а в некоторых случаях и с конкретными типами фигур. Такое поведение «большой картины» требует определенной степени недальновидности (читайте: обзор) общего состояния игры, которое не подходит для определенного класса фигур.
например, TChessPawn может определить, что допустимая цель хода - на один или два квадрата вперед или на одно квадратное поле по диагонали вперед, если любой из этих диагональных квадратов занят. Однако, если движение пешки подвергает короля ситуации ПРОВЕРКИ, то пешка вообще не может быть перемещена.
Я бы подошел к этому, просто позволив классу пешки указывать все ВОЗМОЖНЫЕ цели для хода - на 1 или 2 поля вперед. и оба по диагонали вперед квадраты. Затем TChessGame определяет, какой из них является действительным, исходя из занятости этих перемещаемых целей и состояния игры. 2 клетки вперед возможны только в том случае, если пешка находится на своем домашнем ранге, поля вперед заняты БЛОКИРОВАТЬ ход = недействительная цель, незанятые диагональные клетки УПРАВЛЯЮТ ходом, и если какой-либо другой допустимый ход раскрывает короля, то этот ход также недействителен.
Опять же, может возникнуть соблазн поместить общеприменимые правила в базовый класс TChessPiece (например, раскрывает ли данный ход король?), но применение этого правила требует осведомленности об общем состоянии игры, т. е. о размещении других фигур, так что оно более точно относится как обобщенное поведение класса TChessGame , imho
В дополнение к Чтобы перемещать цели, фигуры также должны указывать CaptureTargets, что в случае большинства фигур одинаково, но в некоторых случаях сильно отличается - хорошим примером является пешка. Но опять же, что - если вообще есть - из всех возможных захватов является эффективным для любого данного хода, это - имхо - оценка правил игры, а не поведение фигуры или класса фигур.
как насчет этого подхода:
блок шахматной доски:
TBaseChessPiece = class
public
procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>); virtual; abstract;
...
TChessBoard = class
private
FBoard : array [1..8, 1..8] of TChessPiece;
procedure InitializePiecesWithDesiredClass;
...
блок фигур:
TYourPiece = class TBaseChessPiece
public
procedure GetMoveTargets (BoardPos : TPoint; Board : TChessBoard; MoveTargetList : TList <TPoint>);override;
...
В этом подходе блок шахматной доски будет включать ссылку на блок фигур только в разделе реализации (из-за к методу, который фактически будет создавать объекты), а модуль фигур будет иметь ссылку на модуль шахматной доски в интерфейсе. Если я не ошибаюсь, это поможет вам решить вашу проблему ...