aksn818
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Engagement control
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Отчет по практике. Команда DeepIntoC ## `ссылка на документацию` ## `ссылка на багрепорт` ## Общие моменты выполнения практики ### Используемые ресурсы 1) https://repl.it/ - платформа для совместного написания кода; 2) discord - коммуникация в процессе обдумывания решения и совместного написания кода; 3) telegram - коммуникация, обсуждение мелких моментов; ### Временные рамки **Начало изучения заданий практики:** 8 мая 2020. **Начало реализации решений для задач:** 9 мая 2020. **Последний коммит (все задачи решены):** 18 мая 2020. **Сопровождение (отслеживание результатов соревнований и готовность совершенствовать реализованные решения):** до 3 июня 2020(конец практики). ### Достижения + 5 золотых медалей из 8 на момент написания отчёта (22:00, 3 июня 2020); + первыми реализовали решение для STRgame, которое всё ещё лидирует по скорости работы; + уверенное лидерство в TEEN48game, при том программа работает не в полную силу (подробнее в последующих разделах отчёта); + уверенное лидерство в дивизионе 3 на 3 для XOgame и 1-3 место, разделяемое с командами AlfErTeam и NICE в дивизионе 5 на 5 до появления "груш для битья" от других команд ближе к завершению практики (до 31 мая 2020; причины сдачи позиций освещены в последующих разделах отчёта) + все актуальные решения были готовы 18 мая 2020 (с учетом позднего старта 9 мая); + малое количество неудачных тестирований (большая часть проваленных job-ов связана с нашими эксперементами в TEEN48game и различиями в компиляции программы с обязательными ключами в нашей и тестовой системе). ### Особенности работы в течение практики + Вся работа с кодом осуществлялась на сторонней платформе (repl.it), с чем связанна активность в репозитории только с одного аккаунта и отсутствие промежуточных коммитов. + Благодаря использованию сторонней платформы (repl.it) отсутствовала необходимость строго разбивать решения на задачи для каждого члена команды, имелась возможность совместно в реальном времени работать над одной задачей, а использование discord упростило решение "конфликтных" ситуаций и обеспечило оперативность и согласованность в написании кода. + Объем совершённой работы примерно поровну разделился между обоими участниками. + Решения для NUM63RSgame, 7EQUEENCEgame и STRgame выполнены в виде единственной функции без декомпозиции на каждое задание. Причиной этому послужило то, что мы неправильно поняли пункты "сигнатура" и "Ожидаемый конечный вариант выполнения задания" из раздела "Теория и правила игры" на момент реализации решений для вышеперечисленных задач. + Решения для XOgame и TEEN48game содержат примитивный AI (подробнее в разделах, посвященных играм). ## NUM63RSgame ### Идея Необходимо найти наименьшее общее кратное для всех чисел из интервала `[min, max]`. Наиболее простое и неэффективное решение: перебор всех целых чисел начиная с 0 и до первого числа, которое является НОК. Оптимальное решение: следует из того факта, что `НОК(a, b, c) = НОК (НОК(a, b), c)`. Для поиска НОК двух чисел можно использовать алгоритм Евклида `(НОК(a, b) = a * b / НОД(a, b))`. ### Реализация Поиск НОК(НОК n-1, n), НОК 1 = НОК (min, min + 1). #### Первая версия Используется алгоритм Евклида через вычитание. Проблема - порядок арифметических действий приводит к переполнению при максимальных значениях в рамках игры. Скорость выполнения задачи оставляет желать лучшего. #### Вторая версия Изменен тип некоторых переменных (с int на long long). Программа действует корректно в рамках игры, но скорость всё еще плохая. #### Третья версия Алгоритм Евклида через вычитание проигрывает алгоритму Евклида через остаток от деления в скорости при работе с большими числами. Соответственно, Алгоритм Евклида внутри программы заменён на свою более эффективную версию. Также изменен порядок действий и присвоения переменных, возврат к переменным int. Достигнут желаемый результат. ## 7EQUEENCEgame ### Идея Решение в лоб - перебор 988 произведений в цикле, каждый раз элементы перемножаются заново. Оптимальное решение: использование решения наподобие 27 задания из ЕГЭ по информатике - "динамический" массив. Рассматриваем промежуток из 13 элементов, при сдвиге на одну позицию вперед произведение делится на "ушедший" из промежутка элемент и умножается на "вошедший" в промежуток элемент. Проблема данного решения - возможность встретить 0 в массиве `(n * 0 = 0; n / 0 = error)`. ### Реализация Используется динамический массив "ЕГЭ по информатике", что значительно сокращает время выполнения задачи. Необходимый момент - проверка на вхождение 0 в промежуток. Если программа встречает 0, она перепрыгивает на промежуток сразу за встреченным нулем до тех пор, пока не наткнется на промежуток, не содержащий нули. Данный элемент не только является защитой от ошибки вычисления, но и значительно сокращает время работы программы. (По крайней мере, данное утверждение верно для работы с массивом целых чисел, описанном в problem 8 (https://projecteuler.net/problem=8)). Было выяснено, что ежедневные соревнования проводятся при использовании массивов только с положительными числами (без 0), но проверка на встречу нуля не была исключена из программы, так как, по нашему мнению, сокращение времени обработки массива не стоит того, чтобы урезать функционал. ## STRgame - STRTOK ### Идея Было принято решение обратиться к проверенным ресурсам для получения имплементации функции strtok с целью написания идеально повторяющей поведение её поведение собственной реализации. Эта идея была обоснована тем фактом, что библиотека `<string.h>` может быть воспроизведена с помощью имеющихся инструментов библиотеки `<stdio.h>` (хоть это и оказалось не совсем так). (!!! Не для отчёта: https://stackoverflow.com/questions/28931379/implementation-of-strtok-function !!!) ### Реализация Имплементация показывает, что функция strtok включает в себя другие функции библиотеки `<string.h>`: `strspn` и `strpbrk`, а также `rawmemchr`. "Наблюдение" за первыми двумя функциями и точное воспроизведение их поведения не вызвало проблем, а третья была заменена более простой собственной реализацией, уступающей по скорости, но в данном случае это не было критично. ## STRgame - SPLIT ### Идея Проблемой реализации является различие входящих и возвращаемых аргументов в питоне и си. Необходимо было самостоятельно догадываться, чего требует задача практики. ### Реализация #### Первая версия Начальная реализация split была схожа с многократным вызовом функции strtok. Решение не являлось верным. #### Вторая версия Были проведены более детальные наблюдения за поведением split из python. Вывод: решение оказалось куда проще предыдущего, символы, не являющиеся разделителем, записываются в строку: символы, являющиеся разделителем, записывают в строку `'\0'` и переводят указатель массива на следующую строку. Добавлено исключение: `if (str == NULL): words = {""}` Реализация оказалась правильной. ## XOgame ### Идея Первым приходящим на ум решением является прописывание нужных ходов через ветвление условий (состояния поля на данный момент). К реализации этого варианта не приступали по нескольким причинам: 1) слишком большое дерево ходов (примерно 8 * 6 * 4 * 2 возможных расположений символов на поле при игре программы за X и 9 * 7 * 5 * 3 возможных расположений символов на поле при игре программы за O); 2) Стойкое ощущение, что изучение всех выигрышных комбинаций в игре крестики - нолики не является приоритетным решением в практике по программированию). Было проведено изучение основных алгоритмов в теории игр. Мы остановились на алгоритме minimax (правило принятия решений для минимизации возможных потерь из тех, которые лицу, принимающему решение, нельзя предотвратить при развитии событий по наихудшему для него сценарию). Главным его преимуществом мы посчитали возможность реализовать бота так, чтобы он не терпел поражений (принципы игры крестики - нолики 3 на 3 и тем более 5 на 5 позволяют это сделать). ### Начальная реализация Аглгоритм minimax (рекурсивная функция) заключается в том, чтобы выбирать наиболее выгодный для игрока ход при совершении противником наболее выгодного хода для себя. Производится оценка дерева исходов по следующему принципу (описание в теминах крестики - нолики для лучшего понимания): если текущим ветвлением являются ходы ПРОТИВНИКА, то узлу ветвления передается значение НАИМЕНЕЕ выгодного для игрока варианта (Пример: узел ветвления - ход X в угловую ячейку, ветвление: ход O в ячкейку 1 - игра не закончена, ход O в ячкейку 2 - игра не закончена,..., ход O в ячкейку n - победа O, наихудший исход; следовательно ходу X в угловую ячейку соответствует поражение игрока); если текущим ветвлением являются ходы ИГРОКА, то узлу ветвления передается значение НАИБОЛЕЕ выгодного для игрока варианта (Пример: узел ветвления - ход O в угловую ячейку, ветвление: ход X в ячкейку 1 - игра не закончена, ход X в ячкейку 2 - игра не закончена,..., ход X в ячкейку n - победа X, наилучший исход; следовательно ходу O в угловую ячейку соответствует победа игрока). Для оценки ситуации используется специальная check-функция и показатель depth, обозначающий то, при какой заполненности поля достигнут некоторый результат (победа, поражение, ничья, игра не закончена). Изначально check-функция писалась с учетом правила '4 в ряд' для игры на поле 5 на 5, но после изучения материалов проекта в открытом доступе был сделан вывод, что условием победы в игре 5 на 5 являются 5 символов в ряд, что позволило сделать check-функцию универсальной. Как только check-функция сообщает о завершении игры при конкретном исходе, ему присваивается определенное значение: результат(0 - ничья, 1 - победа, -1 - поражение) * depth(для простоты понимания - количество свободных клеток на поле, актуально для победы или поражения) (пример: ход X в угловую ячейку, победа, 4 свободных клетки на поле => результат = 4). В конечном итоге каждому из начальных разветвлений (т. е. текущему ходу программы) присваивается итоговый результат, и основная фунция возвращает координаты для наиболее выгодного хода. (!!! не для отчёта https://www.youtube.com/watch?v=l-hh51ncgDI&feature=youtu.be !!!) (!!!в самой программе коэф. depth может отличаться на 1 от его описания здесь, но сути это не меняет!!!) ### Проблемы начальной реализации На поле 3 на 3 программа показывает себя крайне эффективно, но размер дерева вероятности для игры 5 на 5 является слишком большим для обработки программой за адекватное время. Требовалось повышение эффективности алгоритма. ### Улучшения В ходе более глубокого изучения алгоритма minimax и методов его оптимизации было обнаружено подходящие нам решение: алгоритм alpha beta pruning (Описание сути на русском: https://ru.wikipedia.org/wiki/%D0%90%D0%BB%D1%8C%D1%84%D0%B0-%D0%B1%D0%B5%D1%82%D0%B0-%D0%BE%D1%82%D1%81%D0%B5%D1%87%D0%B5%D0%BD%D0%B8%D0%B5). Это решение является универсальным для minimax. Частным решением является отсечение дальнейшего поиска по ветвлению, если одно из разветвлений является завершением игры (не ничья) (пример: n возможных ходов, первый приводит к победе, а значит, нет смысла рассматривать остальные, так как мы уже имеем необходимый результат). Совокупность решений для оптимизации minimax всё равно не позволила программе обрабатывать полное дерево исходов для поля 5 на 5, потому было принято решение расширить применение параметра depth. В новой реализации параметр позволяет ограничить глубину/вложенность ветвления (например, чтобы убедиться, что текущий ход не подставит игрока и не приведет к поражению, но сделать это за адекватное время). Технический ресурс нашей команды не позволяет отследить максимально эффективное соотношение "свободные клетки(от этого зависит размер дерева исходов) : глубина изучения дерева исходов", потому глубина в зависимости от количества свободных клеток подбиралась по времени, затрачиваемому на ход. Ограничение глубины приводит к тому, что первые ходы в партии формируют "шахматное поле", что не выглядит эффективным решением. Из-за этого пришлось прописать начальные ходы через ветвление условий. ### Проблемы финальной версии Алгоритм игры на поле 5 на 5 всё ещё не совершенен, использование minimax приводит к тому, что бот играет от защиты, а не от нападения. Соответственно, вероятность поражения бота крайне мала (или равна нулю), но при этом страдает вероятность победы, так как наиболее вероятным исходом игры от защиты является ничья. Поставленные цели достигнуты - бот не проигрывает, но в соревнованиях часто случается ничья, что не очень благоприятно сказывается на очках в дивизионе. ## TEEN48 GAME ### Идея Алгоритм minimax продемонстрировал свою эффективность в предыдущей соревновательной игре, но в данном случае игра не предполагает наличие противника в классическом понимании. Присутствие элемента рандома также ставит под сомнение эффективность использования minimax. К тому же, было бы не очень интересно снова использовать тот же метод (больше экспериментов - больше знаний). Исследование секретных тактик тоже не является приоритетным вариантом реализации программы, так как это не является основной целью углубленного изучения языка программирования. В данном случае из множества алгоритмов теории игр нами был выбран monte carlo tree search (далее MC). Его модификация в нашем случае будет рассмотрена в разделе "Реализация" ### Начальная реализация Важным фактором при выборе метода для нас послужило начальное разветвление дерева исходов - всегда 4 возможных хода/действия. Алгоритм, основанный на MC предполагает проведение некоторого количества случайных игр для каждого из начальных ходов. Результаты игр суммируются для каждого начального направления, а затем сравниваются. (!!! в программе суммарный счет делится на 100 - это артефакт версии со статичным количеством ранов, роли не играет. не важно, что сравнивать : `x и y` или `x/100 и y/100`!!!) Основная функция возвращает символ, соответствующий направлению с наибольшим суммарным счётом. В дальнейшем тесты на поле 4 на 4 показали, что достаточно эффективным количеством случайных игр является 100 (это число и используется в финальной версии для поля 4 на 4). Наиболее тяжёлыми в плане реализации частями алгоритма являются рандомная генерация в Си и модификация копии поля (сдвиги, мерджи; используется копия поля, так как изменения на поле значительно труднее откатить, чем в игре крестики-нолики). Попытки самостоятельно реализовать рандом на Windows с помощью библиотеки time не увенчались успехом, но опытным путём удалось выяснить, что в тестовой системе определена встроенная функция random() (!!!У меня в qt на винде её нет, приходилось писать вслепую!!!). На ней и было решено остановиться. Функции для обработки игрового поля были позаимствованы из открытых источников в главном проекте, адаптированы сначала под синтаксис и функционал Си, а потом и под реализованный в программе рандом. (!!! например: в питоне фунция генерировала рандомную точку до тех пор, пока она не попадет в пустую клетку, я же считаю N = количество пустых клеток, генерирую число в промежутке от нуля до N - 1 включительно, потом по порядку перебираю пустые клетки и в нужном месте вставляю точку !!!) Рациональным решением является то, что все смещения и объединения производятся свайпом влево, но перед этим матрица поворачвается(транспонируется и/или отражается) так, чтобы привести любой ход к ходу влево. (!!!Пояснения к функциям, которые я "одолжил" https://git.iu7.bmstu.ru/IU7-Projects/iu7games/-/blob/master/games/teen48/teen48_runner.py !!!) ### Проблемы начальной реализации Программа не проходила тесты, так как время тестирование ограничено (1 час), исходя из информации в открытом доступе, условием успешного завершения тестирования является получение финального счёта обеих игр (4 на 4, 6 на 6) и демонстрация пользователю конечного состояния поля. Дело в том, что алгоритм оказался достаточно эффективным, чтобы после часа игры на поле 6 на 6 так и не встретить поражение. ### Улучшения Первым делом мы пердположили, что на совершение одного хода программа затрачивает слишком много времени (примерно 0.5 с для поля 6 на 6). Дальнейшие изменения касаются только версии 6 на 6. Время, затрачиваемое на ход, было сокращено примерно в 10 раз за счет уменьшения количества рандомных игр. Ситуация с тестами повторилась, а легкореализуемых возможностей уменьшить время на ход у нас не было. Одной из идей было обратиться к организаторам для решения спорной ситуации, однако после некоторых размышлений нам показалось интересным принять это дополнительное условие и выжать из отведённого нам часа всё возможное. Опытным путем мы приблизились к результату 330 тысяч (в виде условия, приводящего бота к нерациональному поведению и скорому завершению игры) за 50 минут (10 оставшихся минут - "пространство для манёвра"). ### Проблемы финальной версии Невозможность продемонстрировать реальную эффективность программы на игровом поле 6 на 6. # ВВОД задание 2 ``` int str_input(char *string) { int ch, i = 0; while ((ch = getchar()) != EOF && ch != '\n' && ch != '\r' && ch != '\0' && i <= MAX_STR_LEN) { string[i] = ch; i++; } if (i <= MAX_STR_LEN) string[i] = '\0'; else i = 0; return i; } ``` в main: ``` int str_len1 = str_input(string1), str_len2 = str_input(string2); if (str_len1 && str_len2) { ... } else { //printf("Error!"); r = 1; } ``` # Ввод 3 задание ``` int str_input(char *str) { int ch_check = 1, cycle_flag = 1; int i = 0; for (i = 0; i < MAX_STR_LEN + 1 && cycle_flag && ch_check && ch_check != EOF; i++) { ch_check = scanf("%c", str + i); if (ch_check && ch_check != EOF && (str[i] == '\0' || str[i] == '\n')) cycle_flag = 0; } if (i == MAX_STR_LEN + 1) ch_check = 0; *(str + --i) = '\0'; return i * ch_check; } ``` в main ``` int str_len = str_input(str); if (str_len > 0 && str_len < MAX_STR_LEN + 1) {.... ``` # Функции # XO ## check_result ### принимаемые параметры матрица игрового поля, размер игрового поля, наш символ, символ противника ### возвращаемые параметры field_size - игра не окончена -1 - поражение 0 - ничья 1 - победа (несколько точек выхода в функции, возвращаемые значения являются множителями для подсчета очков) ## minimax ### in поле, размер, свой символ, чужой символ, глубина(вложенность) (сомножитель при подсчете результата) alpha, beta - флаги a b pruning улучшения алгоритма, режим максимизации (1 - максимизация, выбор наиболее удачной ветки; 0 - минимизация - выбор наименее удачной ветки. ### out счёт (лучший или худший, по ситуации) # MAIN ``` #include <stdio.h> #include "my_string.h" int main() { int r = 0; char string1[MAX_STR_LEN + 1], string2[MAX_STR_LEN + 1]; int str_len1 = str_input(string1), str_len2 = str_input(string2); if (str_len1 && str_len2) { int words_count1 = 0, words_count2 = 0; int *p1, *p2; p1 = &words_count1; p2 = &words_count2; if (str_check(string1, str_len1, p1) && str_check(string2, str_len2, p2)) { char buffer1[WORDS_TOTAL][MAX_WORD_LEN + 1], buffer2[WORDS_TOTAL][MAX_WORD_LEN + 1]; char *words1[WORDS_TOTAL], *words2[WORDS_TOTAL]; transform(words1, *buffer1); transform(words2, *buffer2); get_words_from_str(words1, string1, p1); get_words_from_str(words2, string2, p2); mark_duplicates(words1, p1); find_duplicates_between_strs(words1, p1, words2, p2); } else { //printf("Error!"); r = 1; } } else { //printf("Error!"); r = 1; } return r; } ``` # func.h ``` #ifndef _MY_STRING_H_ #define _MY_STRING_H_ #define MAX_STR_LEN 256 #define MAX_WORD_LEN 16 #define WORDS_TOTAL 16 // MAX_STR_LEN / MAX_WORD_LEN int separation_char(char ch); int compare_words(char *word1, char *word2); int str_input(char *string); int str_check(char *string, int i, int *words_count); void transform(char **words, char *buffer); void get_words_from_str(char **words, char *string, int *p); void mark_duplicates(char **words, int *p); void find_duplicates_between_strs(char **words1, int *p1, char **words2, int *p2); #endif ``` # func.c ``` #include <stdio.h> #include "my_string.h" int separation_char(char c) { int sep_check = 0; if (c == ',' || c == ';' || c == ':' || c == '-' || c == '.' || c == '!' || c == '?' || c == ' ') sep_check = 1; return sep_check; } int compare_words(char *word1, char *word2) { int check = 1; for (int i = 0; i < MAX_WORD_LEN; i++) if (word1[i] != word2[i]) check = 0; return check; } int str_input(char *string) { int ch, i = 0; while ((ch = getchar()) != EOF && ch != '\n' && ch != '\r' && ch != '\0' && i <= MAX_STR_LEN) { string[i] = ch; i++; } if (i <= MAX_STR_LEN) string[i] = '\0'; else i = 0; return i; } int str_check(char *string, int i, int *words_count) { int word_check = 0, word_len_check = 1, total_check = 0; int word_len = 0; for (int j = 0; j < i; j++) { if (separation_char(string[j])) word_len = 0; else { if (word_len == 0) *words_count += 1; word_len += 1; if (word_len > MAX_WORD_LEN) word_len_check = 0; word_check = 1; } } if (word_check && word_len_check) total_check = 1; return total_check; } void transform(char **words, char *buffer) { for (int i = 0; i < WORDS_TOTAL; i++) words[i] = buffer + i * (MAX_WORD_LEN + 1); } void get_words_from_str(char **words, char *string, int *p) { int ch = 0; for (int i = 0; i < *p; i++) { while (separation_char(string[ch])) ch++; int j = 0; while (!separation_char(string[ch]) && string[ch] != '\0') { words[i][j] = string[ch]; ch++; j++; } while (j <= MAX_WORD_LEN) { words[i][j] = '\0'; j++; } } for (int i = *p; i < WORDS_TOTAL; i++) for (int j = 0; j < MAX_WORD_LEN; j++) words[i][j] = '\0'; } void mark_duplicates(char **words, int *p) { for (int i = 0; i < *p; i++) if (words[i][0] != '\0') for (int j = i + 1; j < *p; j++) if (compare_words(words[i], words[j])) words[j][0] = '\0'; } void find_duplicates_between_strs(char **words1, int *p1, char **words2, int *p2) { printf("Result:"); for (int i = 0; i < *p1; i++) if (words1[i][0] != '\0') { int current_d_check = 0; for (int j = 0; j < *p2; j++) if (!current_d_check && compare_words(words1[i], words2[j])) { current_d_check = 1; } printf("\n%s ", words1[i]); if (current_d_check) printf("yes"); else printf("no"); } } ```

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully