Выпуск № 3
В этом выпуске:Ну что, продолжим?
Как уже говорилось в предыдущем выпуске, места на поле, где змейка совершает повороты, будем называть углами. Хранятся они в коллекции (поле fCorCol). Тогда, по определению, чтобы, поместить новый угол в коллекцию, нужно чтобы змейка совершила поворот (переменная fIsTurn приняла истинное значение). Для удаления угла из коллекции необходимо, чтобы его координаты совпали с координатами хвоста (последней клетки).
Вообще, такое "угловое" представление змейки возможно
несколько усложняет программу, но зато оно избавляет нас от
лишних вычислений (дальше это будет показано). Поле fCorCol
- это объект-коллекция. Подробно о коллекциях мы говорить не
будем, о них вы узнаете в последующих
выпусках рассылки
Как же вычислить координаты хвоста? Очень просто: нужно только изменить текущие его координаты в соответствии со входным направлением последнего угла. А вдруг змейка ползет по прямой, и никаких углов нет? Правильно возникнет ошибка. Можно предложить следующий выход: всегда хранить в коллекции углов один элемент, у которого для нас будет важным только входное направление. Оно будет соответствовать текущему направлению головы. Тогда без проблем вычисляем координаты хвоста змейки:
SetFillPattern(FP, fBkCol); {"затираем "} DrawOne(fXZ, fYZ); {хвост} SetFillPattern(FP, fCol); {змейки} {В соответствии со входным направлением последнего угла изменяем координаты хвоста} Case PCorner(fCorCol.At(fCorCol.Count-1))^.fInDir of kbUp : Dec(fYZ); kbDown : Inc(fYZ); kbLeft : Dec(fXZ); kbRight: Inc(fXZ); end; {Если координаты угла совпали с координатами хвоста, и это не последний элемент, то удаляем угол из коллекции} If (PCorner(fCorCol.At(fCorCol.Count-1))^.fX=fXZ) then If (PCorner(fCorCol.At(fCorCol.Count-1))^.fY=fYZ) then If fCorCol.Count <> 1 then fCorCol.AtDelete(fCorCol.Count-1);Тут, надеюсь, все понятно.
Теперь поговорим о том, что надо сделать, чтобы змейка изменила свое направление движения на противоположное. Очевидно, что координаты головы и координаты хвоста поменяются между, собой. Текущее направление головы станет противоположно входному направлению последнего угла. Входное направление после изменения станет противоположно выходному направлению до изменения направления движения. Порядок следования углов в коллекции (не считая самого первого, который по сути углом не является) также изменится на противоположный, т.е. последний станет вторым, предпоследний - третьим и т.д.. Все это делает метод ChangeDirections. Его реализация может быть вам непонятна (как никак работа с коллекциями, о которых вам будет рассказано позже), но это и неважно, главное понять, его идею, а с этим, уверен, проблем не будет. Используя все вышесказанное, можем назначить новые координаты головы:
{Если направление изменилось на пртивоположное, то вызываем метод ChangeDirections} If ((fVer and fPrevVer)and(fRule<>fPrev))or (not(fVer or fPrevVer) and (fRule <> fPrev)) then ChangeDirections; {Если совершили поворот, то помещаем в коллекцию углов новый элемент. Его координаты равны текущим координатам головы, входное определяется полем fPrev (предыдущее направ-ление), 1а выходное - fRule} If fIsTurn then begin fCorCol.AtInsert(1, New(PCorner, Init(fX, fY, fPrev, fRule))); PCorner(fCorCol.At(0))^.fInDir:=fRule; PCorner(fCorCol.At(0))^.fOutDir:=fRule; end; {В соответствии с текущим направлением изменяем координаты головы} Case fRule of kbLeft : Dec(fX); kbRight : Inc(fX); kbUp : Dec(fY); kbDown : Inc(fY); end;
И так, у нас есть все, чтобы змейка двигалась. Теперь нужно отработать проверку на то, не выходит ли она за поле, или не наползла ли на себя. За это отвечает метод Check. Изложим основные идеи его реализации.
В случае нарушения диапазона присвоим переменной fExitCode значение константы excKilled. Проверить, не вышла ли она за поле очень просто, нужно лишь сравнить координаты головы с координатами самого поля. Гораздо сложнее проверить, наползла ли змейка на себя. Но и эту проблему мы решим. Нетрудно заметить, что она наползет на себя лишь в том случае, если совершила хотя бы три поворота. Это очень важно так, как избавляет нас от лишних вычислений. Т.е., если в коллекции углов меньше четырех углов (с учетом первого, который, по сути, не является углом), то нет смысла осуществлять проверку. Еще можно отметить, что у змейки есть шанс наткнуться лишь на ту часть себя, которая на находится только между нечетным и четным углом, либо между нечетным углом и хвостом (нечетный угол тот, который ближе к голове). Теперь допустим, что змейка (ее голова) движется вертикально. Тогда имеет смысл проверить находится ли координата X головы между координатами X 3-го и 4-го углов, и, если нет, то проверять между 5-ым и 6-ым углами и т.д.. В последнюю очередь необходимо проверить для k-го угла (k - нечетно) и хвоста.
Если для какой-то пары углов окажется, что между их координатами Х находится координата Х головы, то проверим, не совпала ли координата Y головы с координатой Y любого из этих углов, и, если да, то получаем, что червячок наполз на себя, иначе продолжаем проверять. Проверяем до тех пор, пока не переберем все пары углов, либо не окажется что змейка наползла на себя.
Аналогично проверяем, если змейка ползет в горизонтальном направлении, только сначала проверяем координату Y. Все эти проверки необходимы для того, чтобы избавиться от лишних вычислений. Вот они - преимущества "углового" представления.
Надеюсь вышеизложенный материал не вызывает у вас затруднений. Если какие-то проблемы и возникают, то вы можете задать свои вопросы здесь. А вообще, советую побольше экспериментировать, "это … наш метод".
Всего хорошего!
[в начало] [Исходник игры] [Спрашивайте]© Олег Чарышкин