Это адаптация Core War , программирования KOTH, начиная с 20-го века. Чтобы быть более конкретным, он использует невероятно упрощенный набор инструкций, в основном на основе исходного предложения .
Задний план
В Core War есть две программы, сражающиеся за контроль над компьютером. Цель каждой программы - победить, найдя и остановив противоборствующую программу.
Битва происходит в основной памяти компьютера. Эта память называется Ядром и содержит 8192 адреса. Когда начинается битва, код для каждого участника (так называемый воин) помещается в случайный кусок памяти. Выполнение программы чередуется между воинами, выполняя одну инструкцию каждому. Каждая инструкция способна модифицировать часть Ядра, что приводит к возможности самоизменения программ.
Цель состоит в том, чтобы прекратить противоположную программу. Программа завершается, когда она пытается выполнить недопустимую инструкцию, которая является любой DAT
инструкцией.
Набор инструкций
Каждая программа состоит из серии низкоуровневых инструкций, каждое из которых занимает два поля, называемых полями A и B.
Этот набор инструкций в значительной степени опирается на оригинальную спецификацию. Основными изменениями являются: 1) уточнение при добавлении / вычитании команд и 2) изменение режима #
адресации, позволяющее использовать его где угодно. Большинство полных версий Core Wars имеют более 20 кодов операций, 8 режимов адресации и набор «модификаторов команд».
Opcodes
Каждая инструкция должна иметь один из семи различных кодов операций.
DAT A B
- (данные) - это просто содержит цифрыA
иB
. Важно отметить, что процесс умирает при попытке выполнить инструкцию DAT.MOV A B
- (переместить) - перемещает содержимое ячейки памятиA
в ячейку памятиB
. Вот демонстрация до и после:MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B
- (добавить) - это добавляет содержимое ячейки памятиA
в ячейку памятиB
. Два первых поля обоих добавляются, а вторые поля добавляются.ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B
- (вычитать) - вычитает содержимое ячейки памятиA
из (и сохраняет результат в ячейке памяти)B
.SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B
- (прыжок) - перейти к локацииA
, которая будет выполняться в следующем цикле.B
должно быть числом, но ничего не делает (хотя вы можете использовать его для хранения информации).JMP 2 1337 ADD 1 2 ADD 2 3
Прыжок означает, что
ADD 2 3
будет выполнен следующий цикл.JMZ A B
- (перейти, если ноль) - Если оба поля строкиB
равны 0, то программа переходит к местуA
.JMZ 2 1 SUB 0 @0 DAT 23 45
Поскольку два поля инструкции 1 равны 0, команда DAT будет выполнена на следующем ходу, что приведет к неизбежной смерти.
CMP A B
- (сравните и пропустите, если не равны) - Если поля в инструкцияхA
иB
не равны, пропустите следующую инструкцию.CMP #1 2 ADD 2 #3 SUB @2 3
Поскольку два поля команд 1 и 2 имеют одинаковое значение, команда ADD не пропускается и выполняется в следующий ход.
Когда две инструкции складываются / вычитаются, два поля (A и B) складываются / вычитаются попарно. Режим адресации и код операции не изменяются.
Режимы адресации
Существует три вида режимов адресации. Каждое из двух полей инструкции имеет один из этих трех режимов адресации.
Немедленно
#X
-X
это строка, которая будет использоваться непосредственно в вычислениях. Например,#0
это первая строка программы. Отрицательные строки относятся к строкам в ядре перед запуском программы.... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
Это добавит первую из двух строк DAT ко второй, так как они находятся в строках 3 и 4 соответственно. Однако вы не захотите использовать этот код, потому что DAT убьет вашего бота в следующем цикле.
Относительный
X
- числоX
представляет местоположение целевого адреса памяти относительно текущего адреса. Число в этом месте используется в вычислениях. Если строка#35
выполняется и содержит-5
, то#30
используется строка .... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
Это добавит вторую строку DAT к первой.
Косвенный
@X
- числоX
представляет относительный адрес. Содержимое в этом месте временно добавляется к номеру X, чтобы сформировать новый относительный адрес, из которого извлекается номер. Если строка#35
выполняется, и ее второе поле равно@4
, а второе поле строки#39
содержит число-7
, то#32
используется строка .... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
Это добавит первый DAT ко второму, но в более запутанном виде. Первое поле @ 1, которое получает данные с этого относительного адреса, который является первым полем первого DAT, 0. Это интерпретируется как второй относительный адрес из этого местоположения, поэтому 1 + 0 = 1 дает общее смещение от оригинальной инструкции. Для второго поля @ 1 получает значение из этого относительного адреса (1 во втором поле первого DAT) и таким же образом добавляет его к себе. Общее смещение составляет 1 + 1 = 2. Итак, эта инструкция выполняется аналогично
ADD 1 2
.
Каждая программа может содержать до 64 инструкций.
Когда начинается раунд, две программы размещаются случайным образом в банке памяти с 8192 местоположениями. Указатель инструкций для каждой программы начинается в начале программы и увеличивается после каждого цикла выполнения. Программа умирает, как только указатель ее команды пытается выполнить DAT
инструкцию.
Параметры ядра
Размер ядра составляет 8192 с таймаутом 8192 * 8 = 65536 тиков. Ядро циклическое, поэтому запись по адресу 8195 аналогична записи по адресу 3. Все неиспользуемые адреса инициализируются в DAT #0 #0
.
Каждый участник не должен быть длиннее 64 строк. Целые числа будут храниться как 32-разрядные целые числа со знаком.
анализ
Чтобы упростить программирование для конкурентов, я добавлю функцию разметки строк в анализатор. Любые слова, встречающиеся в строке перед кодом операции, будут интерпретироваться как метки строк. Например, tree mov 4 6
имеет метку строки tree
. Если где-либо в программе есть поле, содержащее tree
#tree
или @tree
, будет подставлено число. Кроме того, капитализация игнорируется.
Вот пример того, как подставляются метки строк:
labelA add labelB @labelC
labelB add #labelC labelC
labelC sub labelA @labelB
Здесь метки A, B и C находятся в строках 0, 1 и 2. Экземпляры #label
будут заменены номером строки метки. Экземпляры label
или @label
заменены относительным расположением метки. Режимы адресации сохранены.
ADD 1 @2
ADD #2 1
SUB -2 @-1
счет
Для каждой пары участников проводятся все возможные битвы. Поскольку исход битвы зависит от относительных смещений двух программ, пробуется каждое возможное смещение (около 8000 из них). Кроме того, у каждой программы есть шанс двигаться первым в каждом смещении. Программа, которая выигрывает большинство этих смещений, является победителем пары.
За каждую пару, которую выигрывает воин, ему присуждается 2 очка. За каждый галстук воин получает 1 очко.
Вам разрешено представить более одного воина. Применяются типичные правила для нескольких представлений, такие как отсутствие объединения тегов, отсутствие сотрудничества, создание королей и т. Д. В Core War в любом случае нет места для этого, так что это не должно иметь большого значения.
Контроллер
Код для контроллера, вместе с двумя простыми примерами ботов, находится здесь . Поскольку это соревнование (при запуске с использованием официальных настроек) является полностью детерминированным, созданная вами таблица лидеров будет точно такой же, как и официальная таблица лидеров.
Пример бота
Вот пример бота, который демонстрирует некоторые особенности языка.
main mov bomb #-1
add @main main
jmp #main 0
bomb dat 0 -1
Этот бот работает, медленно стирая всю остальную память в ядре, заменяя ее «бомбой». Поскольку бомба является DAT
инструкцией, любая программа, которая достигает бомбы, будет уничтожена.
Есть две строчные метки, «главная» и «бомба», которые служат для замены чисел. После предварительной обработки программа выглядит так:
MOV 3 #-1
ADD @-1 -1
JMP #0 0
DAT 0 -1
Первая строка копирует бомбу в строку непосредственно над программой. Следующая строка добавляет значение бомбы ( 0 -1
) к команде перемещения, а также демонстрирует использование режима @
адресации. Это дополнение заставляет команду перемещения указывать на новую цель. Следующая команда безоговорочно возвращается к началу программы.
Текущий список лидеров
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Параноик
10 - FirstTimer
10 - Дворник
10 - Развитый
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug
Попарные результаты:
Dwarf > Imp
CopyPasta > Imp
Evolved > Imp
FirstTimer > Imp
Imp > Janitor
Imp > ScanBomber
Slug > Imp
DwarvenEngineer > Imp
HanShotFirst > Imp
Turbo > Imp
EasterBunny > Imp
Paranoid > Imp
Dwarf > CopyPasta
Dwarf > Evolved
Dwarf > FirstTimer
Dwarf > Janitor
Dwarf > ScanBomber
Dwarf > Slug
DwarvenEngineer > Dwarf
HanShotFirst > Dwarf
Turbo > Dwarf
Dwarf > EasterBunny
Dwarf > Paranoid
Evolved > CopyPasta
FirstTimer > CopyPasta
Janitor > CopyPasta
ScanBomber > CopyPasta
CopyPasta > Slug
DwarvenEngineer > CopyPasta
HanShotFirst > CopyPasta
Turbo > CopyPasta
CopyPasta > EasterBunny
Paranoid > CopyPasta
Evolved > FirstTimer
Evolved > Janitor
ScanBomber > Evolved
Evolved > Slug
DwarvenEngineer > Evolved
HanShotFirst > Evolved
Turbo > Evolved
EasterBunny > Evolved
Paranoid > Evolved
Janitor > FirstTimer
ScanBomber > FirstTimer
FirstTimer > Slug
DwarvenEngineer > FirstTimer
HanShotFirst > FirstTimer
Turbo > FirstTimer
FirstTimer > EasterBunny
FirstTimer > Paranoid
ScanBomber > Janitor
Janitor > Slug
DwarvenEngineer > Janitor
HanShotFirst > Janitor
Turbo > Janitor
Janitor > EasterBunny
Janitor > Paranoid
ScanBomber > Slug
DwarvenEngineer > ScanBomber
HanShotFirst > ScanBomber
Turbo > ScanBomber
ScanBomber > EasterBunny
ScanBomber > Paranoid
DwarvenEngineer > Slug
HanShotFirst > Slug
Turbo > Slug
EasterBunny > Slug
Paranoid > Slug
DwarvenEngineer > HanShotFirst
Turbo > DwarvenEngineer
DwarvenEngineer > EasterBunny
DwarvenEngineer > Paranoid
Turbo > HanShotFirst
HanShotFirst > EasterBunny
HanShotFirst > Paranoid
Turbo > EasterBunny
Turbo > Paranoid
Paranoid > EasterBunny
Последнее обновление (новые версии Turbo и Paranoid) заняло около 5 минут для запуска на старом ноутбуке. Я хотел бы поблагодарить Ilmari Karonen за его улучшения в контроллере . Если у вас есть локальная копия контроллера, вы должны обновить ваши файлы.
источник
Ответы:
Дварфский Инженер
Новый и улучшенный гном. Побеждает все остальное, представленное до сих пор. Фантазии corestep -Оптимизация размер шага, вероятно , слишком здесь.
Примечательные особенности включают в себя быструю петлю бомбардировки, которая сбрасывает две бомбы за четыре цикла, для средней скорости бомбардировки 0,5 с на старом жаргоне Core War, и использование
JMZ
для определения, когда бомбардировка завершена, и пришло время переключиться на план B ( здесь, бес).Я играл в Core War еще в 90-х годах (некоторые из вас, возможно, видели основной справочник, который я написал в 97-м), поэтому я подумал, что было бы интересно узнать, какие старые стратегии из мира RedCode '88 / '94 могли бы быть полезным в этом варианте.
Мои первые мысли были:
Там нет
SPL
, следовательно, нет репликаторов (и нет колец / спиралей имп). Это должно сделать бомбардировщиков сильными. (Кроме того, все эти причудливые стратегии бомбардировок, предназначенные для борьбы с репликаторами и спиралями бесов? Здесь совершенно бесполезно и бесполезно. Просто бомбите любымиDAT
с.)С другой стороны,
CMP
сканирование все еще потенциально быстрее, чем бомбардировка, поэтому у быстрого сканера может быть шанс.Отсутствие в / декрементов делает очистку ядра очень медленной. На самом деле, ядро в этом варианте - просто бомбардировщик с (неоптимальным) размером шага ± 1. Опять же, это также вредит сканерам; Однако стратегия «один выстрел» → бомбардировщик может сработать.
Quickscanner / quickbombers (стратегия ранней игры, использующая развернутый цикл сканирования / бомбардировки, для тех, кто не очень знаком с жаргоном Core War), все еще потенциально полезны, но только против длинных программ (которыми они сами являются, так что есть своего рода обратная связь эффект здесь). Трудно сказать, действительно ли это стоит того.
Система начисления очков интересная. Связи набирают вдвое меньше очков, чем победа (а не 1/3, как в традиционной Core War), делая их более привлекательными. С другой стороны, единственная программа, которая может выиграть много связей по этим правилам, - бес. (Кроме того , отсутствие де / приросты делает имп ворота трудно, так что даже простые бесы на самом деле сделать есть шанс забил галстук , если они достигают своего противника в живых.)
Кроме того, поскольку итоговый рейтинг зависит только от того, какие программы вы избили, а не от того, насколько вы их побили, это способствует появлению универсальных записей. Лучше всего едва победить всех своих противников, чем полностью уничтожить половину из них и едва проиграть остальным.
Поскольку код является общедоступным, всегда можно найти программу, которая сможет побить любую заданную ранее отправку - возможно, даже несколько из них - независимо от того, насколько они хороши в целом. Такие трюки (например, настройка размера шага, чтобы поразить противника непосредственно перед тем, как он поразит вас) могут легко показаться дешевыми. И, конечно же, целевой игрок всегда может просто представить новую версию с разными константами.
Как бы то ни было, результатом всего этого стало то, что я решил, что мне следует написать либо быстрый бомбардировщик, либо очень быстрый сканер, и, возможно, прикрепить к нему быстродействующий сканер / бомбардировщик. Из этих вариантов быстрый бомбардировщик казался самым простым и наиболее вероятным.
В тот момент я потратил слишком много времени на настройку и оптимизацию кода интерпретатора PhiNotPi, потому что я решил, что, вероятно, проведу множество испытаний методом грубой силы для оптимизации констант. Так получилось, что мне никогда не приходилось этого делать - приведенный выше код в значительной степени является первой версией, которая действительно сработала (после пары неудачных попыток, которые просто покончили с собой из-за глупых ошибок).
Уловка, которая делает мой бомбардировщик быстрым, заключается в использовании косвенной адресации, чтобы бросать по две бомбы за каждую
ADD
. Вот как это работает:На первом цикле мы выполняем
MOV bomb @aim
. Это копируетbomb
инструкцию, куда бы ни лежало ядро B-поляaim
(изначально, ровно 6326 инструкций доaim
или 6328 инструкций доstep
; вы поймете, почему эти числа имеют значение позже).На следующем шаге мы выполняем
aim
саму инструкцию! На первом проходе, он выглядит следующим образом :MOV bomb @-6326
. Таким образом, он копируетbomb
в местоположение, на которое указывает B-поле инструкции в 6326 строк перед собой.Итак, что там на 6326 строк раньше
aim
? Да, это копия, которуюbomb
мы только что поместили туда на один цикл раньше! И мы просто так устроили, что B-полеbomb
имеет ненулевое значение, чтобы новая бомба не копировалась поверх старой, а на некотором расстоянии (фактически, здесь расстояние 3164, что составляет половину нашего номинального размера шага 6328, но другие смещения могут работать, возможно, даже лучше).В следующем цикле мы корректируем нашу цель с помощью
SUB step aim
, которая вычитает значенияstep
инструкции (которая также является прыжком, который мы собираемся выполнить в следующем, хотя это могло бы бытьDAT
где-то просто )aim
.(Одна деталь отметить здесь, что мы вроде хотим A-значение
step
равным нулю, так что мы по- прежнему бросать же бомбы на следующей итерации Даже то , что не является строго необходимым, хотя,. Только бомбы выброшен по первой инструкции нужно, чтобы их B-поле было равно 3164, остальные могут быть чем угодно.)Затем
JMZ
проверка того, что инструкция 6328 отходит от нее, по-прежнему равна нулю и, если так, возвращается к началу кода. Теперь 6328 - это размер шага нашего бомбардировщика, и он делится на 8 (но не на 16); таким образом, если бы мы просто продолжали бросать бомбы каждые 6328 шагов, мы в конечном итоге вернулись бы к тому, с чего начали, взорвав каждую восьмую инструкцию в ядре (и со смещением дополнительных бомб на 3163 = 6328/2 ≡ 4 (мод. 8) мы бы поразили каждую четвертую инструкцию).Но мы начали бомбардировку работать на 6328 инструкции перед
JMZ
, и отступили от -6328 на каждую итерации, так что мы будем бомбить местоположение 6328 шагов после того, как вJMZ
только одной итерации , прежде чем мы попали вJMZ
себя. Поэтому, когдаJMZ
обнаруживается бомба в 6328 инструкциях после нее, это признак того, что мы покрыли как можно большую часть ядра, не поразив себя, и должны перейти к стратегии резервного копирования, прежде чем убить себя.Что касается стратегии резервного копирования, то это просто старый
MOV 0 1
чертенок, потому что сейчас я не мог придумать ничего лучшего. На мой взгляд, если мы бомбили каждую четвертую локацию ядра и все еще не победили, мы, вероятно, сражаемся с чем-то очень маленьким или очень оборонительным, и могли бы просто попытаться выжить и согласиться на ничью. Это нормально, потому что такие маленькие или оборонительные программы, как правило, не очень хороши в убийстве чего-либо еще, и поэтому, даже если мы выиграем всего несколько боев случайно, мы, вероятно, все еще будем впереди.Ps.
На случай, если кто-то еще захочет, вот мой слегка улучшенный форк код турнира PhiNotPi . Это примерно в два раза быстрее, сохраняет старые результаты битвы, так что вам не нужно их повторно запускать, и исправляет, как я считаю, небольшую ошибку в подсчете результатов битвы.Изменения были объединены в основную версию PhiNotPi. Благодарность!источник
Графическое представление
Это можно использовать как инструмент отладки. Он отображает ядро и показывает местоположение игрока. Чтобы использовать его, вы должны вызвать его из кода. Я также предоставил модификацию,
Game.java
которая автоматически отображает GraphView.PhiNotPi и Ilmari Karonen недавно сменили контроллер. Илмари Каронен любезно предоставил обновленный GameView в этом месте .
Модифицированный Game.java:
источник
./Game.java:275: error: method toString in class Object cannot be applied to given types; System.out.println(Player.toString(line)); ^ required: no arguments found: int[]
printCore()
метод.Turbo
Моя вторая попытка CoreWar. Предназначен для победы над Гномами. Сканирует данные на 3, а затем ставит бомбу каждые 2. Каждый этап выполняется только по 3 инструкциям, в надежде, что бомбы Гнома пропустят это.
NEW Turbo ++ : теперь улучшено. Он сканирует назад, пока не находит данные, затем перемещается туда, а затем бомбит назад. Надежда состоит в том, что ход либо забивает противника, либо находится в месте, уже подвергнутом бомбардировке и, следовательно, в безопасности (иш).
... И редактирование, чтобы сделать его более скудным, заставляет всех биться!
источник
карликовый
Обычная и простая программа, которая представляет собой бросание камней гномами. Он размещает
DAT
инструкцию каждые четыре адреса.РЕДАКТИРОВАТЬ: Исправляет адресацию. Видимо, режимы адресации отличаются от спецификации, с которой связан OP.
источник
add 3 3
, но тогда он бы удваивал каждый цикл вместо добавления, и это было бы бесполезно.#4
является непосредственным, поэтому он добавляет число4
ко 2-му значению в адресе, который идет3
после текущего адреса.#
режим адресации в вызове. Как указано в спецификации, я внес изменение в#
режим адресации.Evolved
Честно говоря, я не понимаю, как это работает. Кажется, он создает свой исходный код, прежде чем что-то делать. Я был бы рад, если бы кто-нибудь объяснил мне, как это работает.Изучив его, я обнаружил, что это просто модифицированный гном с бесом. Вместо того, чтобы бомбить врагов
DAT
инструкциями, он тасует код врагов. Он также бомбит каждые два регистра вместо каждых четырех регистров. Если бы было достаточно времени, это, несомненно, разрушило бы себя.источник
FirstTimer
Если это работает, он должен попытаться занять позицию в начале ядра и создать защиту
источник
#0
относится к началу вашей программы (то есть так же, как#main
), а не к началу ядра (что в любом случае не является осмысленным понятием - ядро циркуляр, ваш код не может сказать, где он начинается или заканчивается). Что происходит, так это то, что ваша первая инструкция (main
) перезаписывает себя с помощьюMOV #data #100
, после чего ваш код фактически превращается в ядро с прямым переносом 0,25c (= одна инструкция на четыре цикла).#0
для запуска ядра. Первые 5 инструкций совершенно бесполезны.CopyPasta
Никогда не участвовавший в CoreWar, эта простая программа просто пытается копировать-вставить себя и затем выполнить копирование. Это может иметь неправильное поведение, пожалуйста, скажите мне, если это так.
Это слишком пацифистский и не может победить на самом деле.
источник
JMP loop 0
). Затем, когда он переходит туда, где должно быть начало копии, это просто пустое место, и он теряет.дворник
Он должен проверить, являются ли следующие адреса пустыми, а если нет, то очистить их (таким образом, мы надеемся, стереть бот противника).
Изменить: эта новая версия должна быть быстрее (теперь, когда я правильно понял
JMZ
команду и@
ссылку).источник
ADD 3 -2
, но вы правы, что он должен изменить его, я думаю.JMZ
и думал, чтоJMZ A B
проверялA
и перепрыгивал,B
если 0, по-видимому, наоборот. Спасибо за замечание, потому что я не сделал :)ScanBomber
Удалите мои комментарии перед компиляцией. Сканирует некоторое время, а затем бомбит, когда находит программу. Это, вероятно, все еще проиграет моему Гному.
источник
#
совершенно иначе, чем спецификация (прочитайте ссылку, на которую он ссылался), мне еще предстоит исправить эту программу для нее.#
перед каждой ссылкой наzero
? Да, я думаю, что мне нужно ...Хан выстрелил первым (v2)
Я подумал, что соревнования могут использовать больше разнообразия, так что вот моя вторая запись: один выстрел
CMP
сканер.Это версия 2 , с улучшенной защитой против Импа - теперь она может побить Беса, хотя бы только на одно очко. Он по-прежнему проигрывает Гномовому Инженеру, но пока превосходит все остальное, ставя его в настоящее время на первое место.
Он работает путем сравнения соседних основных местоположений на 5 шагов друг от друга, с 10-шаговыми интервалами, пока не обнаружит разницу. Когда это происходит, он начинает сбрасывать бомбы с двухступенчатым интервалом, пока не убьет своего противника или не облетит вокруг ядра, чтобы достичь самого себя.
Если сканирование не находит ничего другого, оно в конечном итоге будет зацикливаться, находить свой собственный код и атаковать его. Это было бы самоубийством, но по счастливому совпадению первая бомба приземлилась прямо на
aim
линию, в результате чего следующая бомба была выброшена на 12 позиций (а не обычные 2) вниз по ядру, что удобно пропускало код. (Это также происходит с вероятностью 50%, если при сканировании что-то обнаруживается, но не удается убить противника.) Поскольку размер ядра кратен двум, это также будет происходить, если запуск бомбардировки будет повторяться, устраняя необходимость дальнейшая стратегия резервного копирования.(Этот трюк самобомбировки изначально был чистым совпадением - я планировал совершенно другой способ перехода от режима сканирования к режиму бомбардировки, если ничего не было найдено, но когда я впервые протестировал код, константы просто оказались правильными, чтобы сделать его работать таким образом, и я решил придерживаться его.)
источник
чертенок
Просто дюймы через программу.
источник
слизень
Ползет по памяти назад. Изредка выбрасывает бомбу подальше.
источник
Пасхальный заяц
Он любит прыгать назад :)
источник
параноик
Вид копировально-макаронных изделий, но он проверит, был ли код изменен бомбардировкой. Если так, то скопируйте его за карлика и выполните. Если мне удастся снова создать GameView, я попытаюсь изменить некоторые из констант.
источник