Перейти к содержимому

Лидеры


Популярный контент

Показан контент с высокой репутацией 12.05.2020 во всех областях

  1. 3 балла
    Расширение для работы с Applied Energistics. Если вы ещё не прочитали вступительную статью - рекомендуется сделать это сейчас, так как описания данных предметов/жидкостей/сущностей здесь нет. Структуры "Отпечаток" (Fingerprint) предмета/жидкости, используется для описания объекта в ME сети id:string - идентификатор dmg:number - мета nbt_hash:string? - хэш-сумма nbt Адаптеры блоков Сеть - какая-либо часть ME сети, провод, интерфейс и т.п. Идентификатор: me_network getStoredPower():number - хранимая в сети энергия getMaxStoredPower():number - размер хранилища энергии getIdlePowerUsage():number - потребление сети в простое getAvgPowerUsage():number - среднестатистическое потребление сети getAvailableItems([details:string]):table[] - список предметов/жидкостей в сети. details - в каком виде выдавать информацию о предмете: all, proxy - fingerprint:table - отпечаток предмета/жидкости - size:number - количество предмета/жидкости - is_craftable:bool - есть ли автокрафт - is_fluid:bool - жидкость ли это - is_item:bool - предмет ли это - item:table? - информация о предмете, если указан details. Для all это таблица с данными, для proxy - прокси предмета. getItemDetail(item:table[, proxy:bool]):table - подробная информация о предмете, необходимо передать отпечаток getCraftingCPUs():table - получить список процессоров автокрафта - name:string - имя процессора - storage:number - размер хранилища в байтах - coprocessors:number - количество со-процессоров - busy:bool - занят ли сейчас процессор ME интерфейс Идентификатор: me_interface requestCrafting(fingerprint:table[, qty:number[, cpu:string]]) - запросить крафт предмета, необходимо передать отпечаток, количество, имя процессора canExport(direction:string):bool - может ли интерфейс экспортировать предмет в заданный инвентарь exportItem(fingerprint:table, direction:string[, maxAmount:number[, intoSlot:number]]) - экспортировать предмет в заданный инвентарь и слот (если есть в сети), maxAmount - сколько необходимо предметов - fingerprint:table - отпечаток предмета - size:number - сколько предметов было экспортировано - is_craftable:bool - есть ли автокрафт - is_fluid:bool - жидкость ли это - is_item:bool - предмет ли это - item:table - подробная информация о предмете ME накопитель Идентификатор: me_storage getCellCount():number - количество ячеек getCellStatus(slot:number):number, string - статус ячейки - номер статуса и наименование Провайдеры предметов ME предметная ячейка Ключ: me_cell Значение: table - preformatted:bool - настроенная ячейка - fuzzy:bool - ячейка с картой нечёткости - totalBytes:number - общий объем, в байтах - usedBytes:number - использовано байт - freeBytes:number - свободно байт - totalTypes:number - максимальное количество типов предметов - usedTypes:number - использовано типов - freeTypes:number - доступно типов для использования
  2. 3 балла
    Сейчас я покажу, как сделать это: На скрине выше — улучшенный debug.debug(). Он умеет: Бегать вверх-вниз по стэку вызовов независимо от того, где запущен. Показывать красивые стэктрейсы. Имитировать динамический скоуп: получать значения локальных переменных, редактировать их, не требуя возни с либой debug. При этом учитывает, на каком уровне в стэке вызовов он находится. Он не умеет: «Шагать» по коду, заходить внутрь функций, проскакивать над ними. Таким образом, это не совсем дебаггер. Но он может показать состояние всех доступных переменных. Чтобы заюзать в коде, нужно сделать так: require("dbg")() Впрочем, если в проге есть какой-то часто вызываемый сегмент, то безусловно падать в мини-дебаггер на каждой итерации очень печально. Поэтому можно задать условие, при котором его запускать. Например: require("dbg")(nonNegative < 0) У нас есть переменная nonNegative, которая семантически всегда неотрицательна. Если ж внезапно попалось что-то меньше нуля, есть смысл попросить программиста проверить, кто (и как) изобрёл свою алгебру. Команды: :bt — показать стэктрейс. :up — прыгнуть на уровень вверх. :down — спуститься на уровень вниз. :frame N — перейти на N-ый уровень. Выйти из интерпретатора можно, нажав Ctrl-D или Ctrl-C. Код: https://gist.github.com/Fingercomp/58388304f45bf6b2b8108e3b7a555315 (задумывался одноразовым, качество соответствующее). В обычной Lua надо просто кинуть содержимое куда-нибудь, откуда require тащит файлы. Чтобы это работало в OpenComputers, придётся пропатчить содержимое мода: Открываем jar-файл мода в архиваторе. Идём в /assets/opencomputers/lua. Открываем файл machine.lua и в районе 971 строки делаем как-то так: Сохраняемся и выходим. Если всё сделано правильно, в OpenComputers теперь доступна полная либа debug. Остаётся закинуть код мини-дебаггера, например, в /home/lib, дальше используем как обычно. Очевидно, что на серверах такое делать не надо. Ну, совсем не надо. Полной либой debug легко выудить нативную load. А это уже уязвимость. Но в сингле вещь незаменимая. Цитирую отзыв пользователя, пожелавшего остаться анонимным: Успехов вам в дезинсекции кода.
  3. 1 балл
    Расширение для работы с Forestry. Если вы ещё не прочитали вступительную статью - рекомендуется сделать это сейчас, так как описания данных предметов/жидкостей/сущностей здесь нет. Примечание: OpenComputers может перегружать некоторый функционал, например - getDrone/getQueen в пасеке вернёт другие результаты, но можно использовать информацию предмета. Условия посадки растений: cave - любое место, например - грибы crop - грядки desert - песок nether - песок душ plains - земля или трава water - вода, например - кувшинки Структуры Так как эти структуры часто используются в коде (или они просто слишком огромны), опишу их с самого начала. Вид name:string - название вида пчелы, зависит от языка сервера uid:string - идентификатор пчелы Полная информация о виде authority:string - создатель вида binomialName:string - имя вида name:string - название вида, зависит от языка сервера complexity:number - сложность генома humidity:string - влажность климата: arid, normal, damp temperature:string - температура: none, icy, cold, normal, warm, hot, hellish Мутация allele1:table - вид первой пчелы allele2:table - вид первой пчелы result:table - вид итоговой пчелы chance:number - шанс скрещивания, 0-100 specialConditions:string[] - список условий для скрещивания, текст зависит от языка сервера Гены пчёл species:table - полная информация о виде speed:number - множитель шанса производства lifespan:number - количество циклов, которое живет пчела fertility:number - плодовитость, количество дронов nocturnal:bool - работа ночью tolerantFlyer:bool - работа в дождь caveDwelling:bool - работа в пещерах flowerProvider:string - название используемых цветов, зависит от языка сервера humidityTolerance:string - допустимое отклонение влажности климата, зависит от языка сервера temperatureTolerance:string - допустимое отклонение температуры, зависит от языка сервера flowering:number - скорость опыления - шанс установки цветка или опыления дерева за цикл работы effect:string - название эффекта, зависит от языка сервера territory:table - размер области работы, массив: x, y, z Гены бабочек - возможна ошибка доступа к генам, при анализированной бабочке, но можно использовать методы OpenComputers species:table - полная информация о виде size:number - размер, 0.0-1.0 speed:number - скорость полёта бабочки lifespan:number - время жизни metabolism:number - множитель шанса выпадения лута fertility:number - плодовитость, количество гусениц nocturnal:bool - летает ночью tolerantFlyer:bool - летает в дождь fireResist:bool - имеет сопротивление огню flowerProvider:string - название используемых цветов, зависит от языка сервера humidityTolerance:string - допустимое отклонение влажности климата, зависит от языка сервера temperatureTolerance:string - допустимое отклонение температуры, зависит от языка сервера effect:string - название эффекта, зависит от языка сервера territory:table - размер области работы, массив: x, y, z Гены деревьев species:table - полная информация о виде growth:table - условия роста plant:table - пары {type=true} с возможными условиями посадки дерева girth:number - область посадки саженцев, например у тропического дерева - 2, т.е. необходимо поставить 4 саженца 2х2. height:number - относительная высота, 0.0-2.0 fertility:number - шанс дропа саженца, 0.0-1.0 sappiness:number - множитель шанса дропа саженца 0.0-1.0 fruits:string - тип фруктов yield:number - множитель шанса выпадения фруктов, а также шанса спавна бабочек maturation:number - количество случайных тиков для превращения саженца в дерево effect:string - название эффекта листьев, не используется, зависит от языка сервера territory:table - размер области эффект, массив: x, y, z, не используется Адаптеры блоков Пасека - показывает информацию о пчёлах в пасеке, и предоставляет доступ к базе данных пчёл (за исключением секретных, если не включено в конфиге). Идентификатор: forestry_bees canBreed():bool - возможна ли работа пчелы, зависит от потребностей пчелы. breedingProgress():number - прогресс скрещивания принцессы и дрона, 0-100 getDrone():table - получить гены дрона getQueen():table - получить гены принцессы или королевы(матки) listAllSpecies():table[] - список всех видов пчёл getBeeParents(childType:string):table[] - список всех мутаций, приводящих к данной пчеле, childType - идентификатор пчелы getBeeChildren(parentYpe:string):table[] - список всех мутаций, в которых участвует данная пчелы, parentYpe - идентификатор пчелы getBeeBreedingData():table[] - возвращает список всех возможных скрещиваний (Эксперементальная функция) - allele1:string - название первой пчелы, зависит от языка сервера - allele2:string - название второй пчелы, зависит от языка сервера - result:string - название итоговой пчелы, зависит от языка сервера - chance:number - шанс скрещивания, 0-100 - specialConditions:string[] - список условий для скрещивания, текст зависит от языка сервера Провайдеры предметов Вид - используется для описания пчёл, деревьев и бабочек. Ключ: individual Значение: table - displayName:string - название вида, зависит от языка сервера - ident:string - идентификатор вида - isAnalyzed:bool - были ли предмет анализирован - isSecret:bool - секретный ли это вид - hasEffect:bool - имеется ли эффект - health:number? - здоровье существа - maxHealth:number? - максимальное здоровье существа - isAlive:number? - живо ли существо - type:string - тип вида: bee, butterfly, tree - canSpawn:bool? - может ли пчела давать потомство - generation:number? - поколение пчелы - isNatural:number? - чистая (pristine) или низкая (ignoble) порода пчелы - size:number? - размер бабочки - plantType:string? - список возможных условий посадки дерева - active:table - активные гены - inactive:table - неактивные гены
  4. 1 балл
    @hohserg details это аргумент, all и proxy - значения аргумента.
  5. 0 баллов
    Здравствуй, брат автоматизатор! Желаешь ли ты услышать от меня несколько мыслей о красивом, понятном, и эффективном коде? На истину в последней инстанции я не претендую, и ты, возможно, знаешь решения получше моих. В этом случае оставь свои соображения в комментариях, и код на проекте станет немного чище. Небольшое отступление: Полгода назад я задумал написать серию статей о программировании для новичков, где я писал код и объяснял, почему он должен писаться так, а не иначе. Но такое объяснение оказалось для меня непосильным занятием, т. к. мне приходилось спорить с самим собой. Легко было только один раз, когда я обнаружил серьезный дефект, наверное, во всех копалках на проекте. Но потом я получил стрелу в колено... Вернувшись на проект, я неожиданно для себя понял, что демонстрировать эффективность удачного кода значительно проще, противопоставляя его коду неудачному. В прошлый раз я рассказал о том, как математика помогает писать более простые, понятные и при этом более эффективные программы. qwertyMAN заметил, что использование взятия остатка от деления – это не математическая проблема, и я с ним отчасти согласился. Решение о том, использовать или нет операцию %, мало влияет на общий алгоритм, но сильно влияет на то, как будет выглядеть готовый код. Но всё-таки об этой операции следует помнить уже на этапе проектирования алгоритма, а не только на этапе кодинга. А вот, о тонкостях написания кода я хочу рассказать сегодня, не особо вдаваясь в общие алгоритмы. Моим подопытным будет уже знакомый код из охранной системы турелей. Вот сам код: Первое, на что я обратил внимание – это использование pcall. Именно оно и побудило меня к разбору кода, но прежде я предлагаю обсудить другой нюанс. 1. Избегай повторения уже выполненных вычислений Имеются два фрагмента кода: if target and radar.getPlayerByName(target) then target=radar.getPlayerByName(target).all()...local mobif radar.getMobData(scan[i]).basic() then mob = radar.getMobData(scan[i]).basic() target = mob И в обоих фрагментах происходит дублирование вызовов. Первый раз вроде как для проверки. Но такой код всё равно не снимает проблему, т. к. при первом вызове функция может вернуть одно значение, а при втором вернет другое. Правильным решением будет сохранить результат в переменную. И если ее значение удовлетворяет условиям, использовать ее в дальнейшем. Во втором фрагменте следует заодно избавиться от переменной mob, которая больше нигде не используется. if target then target = radar.getPlayerByName(target) if target then target=target.all()...target = radar.getMobData(scan[i])if target then target = target.basic() Почему я проверяю результаты getPlayerByName и getMobData, а не других функций вроде all() или basic()? Потому что именно они могут вызвать ошибку. Ошибка возникает от того, что игрок или моб может покинуть зону действия сенсора, пока выполняется обработка внутри программы. К сожалению, указанной проверки недостаточно, т. к. мод OpenPeripheral вместо того чтобы вернуть nil или иным образом указать на проблему, тупо генерирует ошибку, не оставляя иного варианта кроме использования pcall. 2. Используй защищенный режим только там, где это необходимо Код программы выглядит примерно так: local function func() -- почти весь код программы помещен в эту функцию -- включая инициализацию переменных и определение функций ... -- этот бесконечный цикл прерывается из-за отсутствующей обработки ошибок while true do ... local target = false local scan=radar.getPlayers() ... target = ... if target and radar.getPlayerByName(target) then target=radar.getPlayerByName(target).all() ... end ... endend-- мегакостыль (комментарий самого автора)while true do local oop = pcall(func) print(oop)end Автор понимает, что использует костыль, но правильное решение использовать не хочет. Подобные городушки провоцируют появление новых: уже сейчас на ровном месте появилась дополнительная функция и вложение двух бесконечных циклов. Но главная проблема этого кода в том, что pcall скрывает любые ошибки, и дальнейшая отладка программы становится затруднительной. Даже печать результата, возвращаемого pcall, реализована неверно – ничего кроме false выведено не будет. Для решения проблемы следует использовать pcall точечно, только там, где это необходимо, а именно в вызове getPlayerByName и getMobData. А если есть возможность вообще обойтись без pcall, то в готовой программе без него следует обойтись. В нашем случае обойтись без pcall, похоже, нельзя. Поэтому убираем наш костыль и функцию func, оставив лишь ее содержимое, и дорабатываем проблемные участки таким образом: if target then local flag,res = pcall(radar.getPlayerByName,target) if flag then target=res.all()...local flag,res = pcall(radar.getMobData,scan[i])if flag then target = res.basic() Пришлось ввести дополнительные переменные, зато код избавился от костыля со всей его обвязкой, а ошибка, генерируемая модом OpenPeripheral, локализована, и новых проблем не создаёт. Программа работает стабильно и ее отладка не нарушена. На этом можно было бы и закончить, но я уже вошёл во вкус. Поэтому продолжу давать советы, иллюстрируя их фрагментами кода: 3. Выноси за цикл всё, что возможно Это правило подобно первому, но пренебрежение им не так бросается в глаза начинающего программиста, т.к. сам код не дублируется, зато дублируется его исполнение. Восстановим общий алгоритм работы программы, чуть подробнее разобрав этот кусок: while true do local target = false ... local scan=radar.getPlayers() if firePleayers and #scan>0 then if #White_Player_List>0 then добавление списка авторов в в белый список target = первый игрок на радаре вне белого списка elseif #Black_Player_List>0 then удаление авторов из черного списка target = первый игрок на радаре в черном списке else if #autors>0 then перенос авторов в белый список else target = первый игрок на радаре вычисление координат цели и выполнение выстрела сканирование мобов, вычисление их координат с последующим расстрелом. Как ты уже догадался по заголовку, в бесконечном цикле выполняется что-то явно лишнее. А именно, работа со списком авторов. Всё это могло бы прекрасно работать и до основного цикла, создавая при этом нужный эффект. Более того, вынос этого кода за цикл позволит исправить серьезнейшую ошибку: на каждой итерации бесконечного цикла происходит добавление авторов в белый список, при этом их наличие в белом списке никак не проверяется, но каждый раз происходит добавление новых. Даже с двумя планками памяти 3.5 уровня через сотню-другую тысяч итераций программа завершится с ошибкой «not enough memory». Вынося лишние действия за цикл, ты избавишь программу как от неконтролируемого расхода памяти, так и от лишних действий, замедляющих ее работу. Думаю, демонстрировать корректный код здесь излишне. Достаточно лишь вынести эти участки кода из цикла. В программе присутствуют и менее очевидные вычисления, которые можно вынести за цикл. -- этот код находится за цикломlocal correct = { x = 0, y = 4, z = 0 }...-- а этот внутриlocal x,y,z = target.position.x-0.5-correct.x, target.position.y+0.3-correct.y, target.position.z-0.5-correct.z...local x,y,z = target.position.x-0.5-correct.x, target.position.y-0.5-correct.y, target.position.z-0.5-correct.z Логичным будет переписать код таким образом: -- этот код за цикломlocal correct = { x = 0+0.5, y = 4+1, z = 0+0.5 }...-- а этот внутриlocal x,y,z = target.position.x-correct.x, target.position.y+1.3-correct.y, target.position.z-correct.z...local x,y,z = target.position.x-correct.x, target.position.y+0.5-correct.y, target.position.z-correct.z Так мы избавимся от лишних сложений и вычитаний в цикле. Лишние сложения при инициализации таблицы correct не сильно помещают, т. к. они выполняются всего один раз, а нужны они для наглядности кода. Поясню, что здесь происходит. Во-первых, турель и сенсор находятся в разных блоках, поэтому координаты игрока следует скорректировать на эту разницу. Координаты турели относительно сенсора хранятся в таблице correct. Во-вторых, сенсор определяет координаты игроков и мобов относительно своего северо-западного угла, а турель вращается вокруг центра блока. Поэтому приходится корректировать координаты (x,z) на половину блока по горизонтали. В-третьих, сама турель стреляет на один блок выше уровня ног игрока или моба. Автор корректирует высоту цели в зависимости от цели: для игрока прицел приподнимается на 0.3 блока, чтобы игрок не мог уходить от выстрела прыжком или прятаться за блоки, а для мобов прицел опускается на полблока, чтобы попадать, например, в кур или свиней. Но тут тоже не всё просто. Чтобы попадать в цыплят, прицел следует опустить еще ниже, но тогда турель промахивается мимо взрослых кур, стреляя им куда-то под ноги. Для эффективной стрельбы по любым мобам нужен алгоритм, определяющий вид моба, его возраст, и находящий по таблице его рост. Причем, нужен не только рост. Например, нет смысла целиться в нижнюю часть слизня, т. к. тот постоянно прыгает. В общем, это отдельная тема для поиска оптимального алгоритма, сейчас же я хочу продолжить рассказ о кодинге. В итоговом коде не только вынесены лишние вычисления из цикла. Кроме этого он стал более логичным: числа 1.3 и 0.5 по сути означают высоту цели относительно ног игрока или моба. 4. Прерывай циклы, когда они уже выполнили свою задачу Вот два подобных друг другу фрагмента кода: local swich = truefor j=1, #White_Player_List do if scan[i].name==White_Player_List[j] or not scan[i].name then swich = false endendif swich then...local swich = falsefor j=1, #Black_Player_List do if scan[i].name==Black_Player_List[j] then swich = true endendif swich then Задача циклов в том, чтобы при подходящем случае изменить значение переменной switch. Но как только оно изменилось, зачем продолжать работу? Далай break. Иначе выполнение твоей программы замедляется. Пока я писал этот текст, то напрягался при каждом наборе названия переменной swich. Нет такого слова, зато есть слово switch, и не глядя я набирал именно его. Поэтому буду писать switch. Кроме того, название переменной все равно не отражает ее сути. С тем же успехом можно было использовать однобуквенное название переменной. А лучше бы и вовсе избавиться от нее. 5. Избавляйся от лишних переменных Вот те же фрагменты в немного дополненном составе, внутри других циклов и с оператором break, как же теперь без него: local target = false...for i=1, #scan do local swich = true for j=1, #White_Player_List do if scan[i].name==White_Player_List[j] or not scan[i].name then swich = false break end end if swich then target = scan[i].name break endend...for i=1, #scan do local swich = false for j=1, #Black_Player_List do if scan[i].name==Black_Player_List[j] then swich = true break end end if swich then target = scan[i].name break endend Что выполняют оба фрагмента? Ищут подходящую цель по белому и черному спискам игроков. Где хранится цель? В переменной target. Что хранится в переменной switch? Флаг того, что переменная target должна быть изменена. А зачем нам этот флаг? Что мешает сразу изменить переменную? local target = false...for i=1, #scan do target = scan[i].name for j=1, #White_Player_List do if scan[i].name==White_Player_List[j] or not scan[i].name then target = false break end end if target then break endend...for i=1, #scan do for j=1, #Black_Player_List do if scan[i].name==Black_Player_List[j] then target = scan[i].name break end end if target then break endend Код стал немного короче и быстрее, но и это еще не предел. Еще непонятно, что делает, or not scan.name в условии. Повлиять это выражение может в том случае, если scan.name будет равно false, или nil. А разве такое возможно? Даже не знаю, как классифицировать этот недочет. Видимо, от старых экспериментов осталось. Посоветовать можно только одно: вычищать код перед публикацией. 6. Используй ассоциативные возможности таблиц Lua Таблицы в Lua – это больше чем массивы. Это и объекты, и списки, и словари. А может, и что-то еще, о чем я забыл или даже не знал. Таблицы в Lua – это наборы пар ключ-значение. В качестве ключей и значений таблицы может быть что угодно кроме nil. Пара ключ-значение в таблицах Lua присутствует даже если мы не используем ключи явным образом. Попробуй запустить такой код: PlayerList={"Ded", "Baba", "KurochkaRyaba"}for k,v in pairs(PlayerList)do print(k,v)endfor i=1,#PlayerList do print(PlayerList[i])endPlayerList={[1]="Ded", [2]="Baba", [3]="KurochkaRyaba"}for k,v in pairs(PlayerList)do print(k,v)endfor i=1,#PlayerList do print(PlayerList[i])endPlayerList={["Ded"]=1, ["Baba"]=2, ["KurochkaRyaba"]=3}for k,v in pairs(PlayerList)do print(k,v)endfor i=1,#PlayerList do print(PlayerList[i])endprint(#PlayerList)print(PlayerList["Ded"])print(PlayerList["Baba"])print(PlayerList["RedHatBaby"]) В первом случае мы не указываем ключи явно, но они создаются. Мы свободно перебираем как пары ключ-значение, так и значения по их индексу, который совпадает с ключом. Во втором случае мы явно указали ключи. Перебор пар ключ-значение показывает, что элементы таблицы хранятся в неведомом нам порядке, скорее всего, в соответствии с неким хешем, но на перебор по индексу это никак не влияет, порядок не нарушен, а это самое главное. В третьем случае мы поменяли ключи и значения местами. Естественно, ни о каком переборе по индексу теперь не идет и речи. Более того, определить длину таблицы теперь тоже не так просто. Зато появилась возможность, не выполняя перебор всех значений в цикле, одной командой определить, присутствует ли игрок в списке. Для игроков Дед и Баба есть значение, а игрок КраснаяШапочка в список не внесен, и имеет значение nil. Как это соотносится с нашим кодом? Попробуем заполнять списки игроков таким образом: local Black_Player_List = { ["qwertyMAN"]=1 } local White_Player_List = { ["Ded"]=1, ["Baba"]=1, ["KurochkaRyaba"]=1 } В качестве значения я использовал 1, и оно может быть любым, но 1 - записывается кратко. Главное, чтобы не nil. И не false, чтобы проще было выполнять проверку элемента. То, что qwertyMAN оказался в черном списке, ему никак не повредит, он же автор. Теперь все фрагменты кода изменятся таким образом: Добавление списка авторов в в белый список: -- былоfor i=1, #autors do White_Player_List[#White_Player_List+1] = autors[i]end-- сталоfor i=1, #autors do White_Player_List[autors[i]] = 1end Код немного укоротился, а главное – теперь переполнение памяти не грозит даже при работе в бесконечном цикле, т. к. элемент создается один раз, а в последующие – лишь изменяется его значение. Удаление авторов из черного списка: -- былоfor i=#Black_Player_List, 1, -1 do for j=1, #autors do if Black_Player_List[i] == autors[j] then table.remove(Black_Player_List,i) end endend-- сталоfor j=1, #autors do Black_Player_List[autors[i]] = nilend Код заметно укоротился. Сомневаешься, действительно ли элемент таблицы удаляется? Запусти этот код, и все станет понятным: PlayerList={["Ded"]=1, ["Baba"]=2, ["KurochkaRyaba"]=3}for k,v in pairs(PlayerList)do print(k,v)endPlayerList["Ded"]=nilfor k,v in pairs(PlayerList)do print(k,v)end И напоследок два уже разобранных перед этим фрагмента, которые еще более упростились: -- былоfor i=1, #scan do target = scan[i].name for j=1, #White_Player_List do if scan[i].name==White_Player_List[j] then target = false break end end if target then break endend...for i=1, #scan do for j=1, #Black_Player_List do if scan[i].name==Black_Player_List[j] then target = scan[i].name break end end if target then break endend-- сталоfor i=1, #scan do if not White_Player_List[scan[i].name] then target = scan[i].name break endend...for i=1, #scan do if Black_Player_List[scan[i].name] then target = scan[i].name break endend 7. Минимизируй идентичные участки кода При добавлении сходного функционала в программу можно скопировать рабочий участок кода и немного изменить его. Иногда до неузнаваемости. Но если фрагмент достаточно велик, а изменения малы, то правильнее будет вынести этот фрагмент в отдельную функцию. Во-первых, итоговой код будет более компактным. Во-вторых, при необходимости будет проще вносить изменения, не правя все участки кода. В программе имеется два таких похожих участка: local x,y,z = target.position.x-correct.x, target.position.y+1.3-correct.y, target.position.z-correct.zlocal vx,vy = pointer(x,y,z)turret.moveTo(vx,vy)if turret.isOnTarget() then turret.fire()end...local x,y,z = target.position.x-correct.x, target.position.y+0.5-correct.y, target.position.z-correct.zlocal vx,vy = pointer(x,y,z)turret.moveTo(vx,vy)if turret.isOnTarget() then turret.fire()end Ранее мы уже выяснили, что единственная изменяемая величина здесь – это коррекция высоты цели. Сейчас основной вопрос: что из этого следует вынести в отдельную функцию. С точки зрения минимизации кода следует выносить почти всё. Но чтобы сделать код более логичным, не следует всё мешать в одну кучу. Лучшим решением мне кажется вынос всего, что связано с вычислениями, в уже имеющуюся функцию pointer. Вот она: local function pointer(x,y,z) local distXY = math.sqrt(x^2+z^2) local distDY = math.sqrt(y^2+distXY^2) local outX = math.deg(math.acos(x/distXY))+90 local outY = 90-math.deg(math.acos(y/distDY)) if z<0 then outX = (180-outX)%360 end return outX,outYend Учитывая то, как я переписал эту функцию в прошлый раз, а также избавляясь от лишних переменных и перенося часть вычислений внутрь функции, перепишу код таким образом: local function pointer(pos,h) local x,y,z = pos.x-correct.x, pos.y-correct.y+h, pos.z-correct.z local azimuth=math.deg(math.atan2(x,-z))%360 local elevation=math.deg(math.atan2(y,math.sqrt(x*x+z*z))) return azimuth, elevationend...turret.moveTo(pointer(target.position,1.3))if turret.isOnTarget() then turret.fire()end...turret.moveTo(pointer(target.position,0.5))if turret.isOnTarget() then turret.fire()end Конечно, этот код можно ужать еще плотнее, но я увижу в этом смысл, если дальнейшая доработка программы вынудит меня продублировать эти блоки кода. На этом закончу. Возможно, я дал бы тебе еще пару советов, но за исключением описанных выше моментов код qwertyMAN'а вполне адекватен. Программируй красиво, брат!
Эта таблица лидеров рассчитана в Москва/GMT+03:00
×
×
  • Создать...