KAZIK/tools/generators/generate_fortune_mind.py
Artvasil09 ce06659ab2 feat: implement offline neural fortune teller and native tarot divination
- Offline neural worker based on llama.cpp (fortune-engine.exe) with local Qwen3-4B model support

- Native assembly Tarot 3-card spread divination and teacher grade divination mode

- Text input dialog in MODE_FORTUNE communicating over anonymous pipes

- Clean process lifecycle management, WM_CHAR dispatch and fallback handling

- Test suites and packaging tools for offline release
2026-10-21 13:53:06 +03:00

1033 lines
55 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- coding: utf-8 -*-
"""
Generator for Fortune Mind system (CP1251):
- src_graphics/fortune_mind_data.inc
- src_graphics/fortune_mind.inc
Generates the complete native algorithmic Tarot Mind and Teacher Grade Divination system.
"""
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
CARDS = [
("Шут", "0",
"Начало нового пути, чистый лист, смелый шаг в неизвестность.",
"Безрассудство, легкомыслие, риск без подготовки и плана.",
"Неожиданный поворот судьбы или наивный подход к делу.",
"Страх сделать первый шаг, нежелание учиться на ошибках.",
"Доверься интуиции и начни с чистого сердца; дерзай.",
"Не торопись прыгать в омут с головой; взвесь последствия.",
"Студент полон энтузиазма, легко схватывает новые идеи на лету.",
"Сдавал лабы наугад, надеясь на великую удачу и чудо в 23:59.",
1),
("Маг", "I",
"Полный контроль над ресурсами, мастерство, воля и лидерство.",
"Манипуляции, распыление сил, неиспользованный потенциал.",
"Необходимость проявить решительность и взять инициативу.",
"Сомнения в собственных силах или пустая трата энергии.",
"У тебя есть все инструменты для триумфа; действуй смело.",
"Сконцентрируйся на главном, не пытайся объять необъятное.",
"Самостоятельно писал код, задавал умные вопросы преподавателю.",
"Пытался заговорить зубы на коллоквиуме и переложить вину на баг.",
1),
("Верховная Жрица", "II",
"Глубокая интуиция, тайные знания, мудрое наблюдение.",
"Скрытые мотивы, игнорирование знаков, тайное станет явным.",
"Не все карты раскрыты; ситуация требует терпеливого изучения.",
"Заблуждения и поверхностные суждения мешают увидеть суть.",
"Прислушайся к внутреннему голосу; тайное скоро прояснится.",
"Не доверяй слухам; перепроверяй факты и сохраняй ясность.",
"Тихий студент, но на зачёте раскрыл глубину понимания предмета.",
"Молчал на парах как партизан, скрывая отсутствие конспекта.",
1),
("Императрица", "III",
"Плодородие, расцвет, гармония, забота и рост результатов.",
"Творческий застой, пресыщение, излишняя опека или лень.",
"Почва готова для прекрасных всходов; время развивать дело.",
"Лень и праздность тормозят воплощение задуманного.",
"Всё задуманное принесет обильные плоды; доверься созиданию.",
"Удели внимание практическим деталям, не витай в облаках.",
"Конспекты написаны аккуратно, лабораторные оформлены образцово.",
"Слишком расслабился в середине семестра, уповая на автомат.",
1),
("Император", "IV",
"Твёрдый порядок, авторитет, железная дисциплина и стабильность.",
"Тирания, упрямство, закостенелость или потеря контроля.",
"Требуется навести строгий порядок и выстроить четкие границы.",
"Хаос в делах и нехватка самодисциплины создают заторы.",
"Держи ситуацию под контролем; твой авторитет непоколебим.",
"Будь гибче: излишнее упрямство может разрушить договоренности.",
"Дисциплинированный студент, ни одного пропуска без справки.",
"Спорил с методичкой и пытался доказать, что преподаватель неправ.",
1),
("Иерофант", "V",
"Мудрость традиций, наставничество, духовность и верность слову.",
"Догматизм, лицемерие, следование устаревшим догмам.",
"Ситуация требует уважения к правилам и совету наставника.",
"Нежелание признавать чужой авторитет и полезный опыт.",
"Следуй проверенным традициям и заветам старших учителей.",
"Думай своей головой, не слепо верь всему, что проповедуют.",
"Почитает авторитет кафедры, внимательно слушал каждую лекцию.",
"Списывал по старым лекциям пятилетней давности, которые устарели.",
1),
("Влюблённые", "VI",
"Гармоничный выбор, зов сердца, доверие и союз двух начал.",
"Внутренний раскол, нерешительность, конфликт интересов.",
"Перед тобой судьбоносный перекресток; слушай свое сердце.",
"Колебания между долгом и сиюминутным искушением.",
"Искренний выбор приведет к гармонии и взаимному счастью.",
"Не откладывай решение; неопределенность вытягивает силы.",
"Искренне любит предмет, учился по призванию и велению души.",
"Выбирал между сном, личной жизнью и домашкой — и домашка проиграла.",
0),
("Колесница", "VII",
"Стремительный прорыв, победа над обстоятельствами, триумф.",
"Потеря управления, срыв планов, агрессивный напор.",
"Энергия бьет ключом; время набрать максимальную скорость.",
"Спешка и внутренние противоречия грозят сбить с колеи.",
"Ты добьешься триумфа благодаря воле и концентрации.",
"Притормози на поворотах; не пытайся пробить стену лбом.",
"Сдал все долги за рекордные три дня на бешеной скорости.",
"Летел по предмету без тормозов, наделав кучу глупых ошибок.",
1),
("Сила", "VIII",
"Внутреннее благородство, укрощение страстей, стойкость духа.",
"Сомнения в себе, слабость перед искушением, упадок сил.",
"Мягкая настойчивость одолеет любые внешние препятствия.",
"Страх неудачи парализует; не хватает веры в свои силы.",
"Ты сильнее любых бурь; победитель выйдет с улыбкой.",
"Не дави силой; прояви дипломатию, терпение и великодушие.",
"Стойко выдержал все каверзные вопросы на коллоквиуме.",
"Пасовал перед сложными задачами и опускал руки раньше времени.",
1),
("Отшельник", "IX",
"Поиск истины, глубинная концентрация, мудрое уединение.",
"Замкнутость, одиночество, упрямый отказ от помощи мира.",
"Время поставить дела на паузу и разобраться в себе.",
"Чрезмерная изоляция мешает получить поддержку коллег.",
"Свет твоего разума укажет верную тропу сквозь тьму.",
"Выйди в мир: ответы лежат во взаимодействии с людьми.",
"Самостоятельно изучил сложнейшие разделы курса в тишине ночи.",
"Ушел в глубокий академический астрал, проигнорировав дедлайны.",
0),
("Колесо Фортуны", "X",
"Счастливый шанс, поворот колеса судьбы, кармический взлет.",
"Временный спад, сопротивление неизбежным переменам.",
"Судьба делает виток; грядут великие перемены.",
"Попытка держаться за прошлое, когда колесо уже повернулось.",
"Удача на твоей стороне; лови момент, пока фортуна улыбается.",
"Всё циклично: за полосой испытаний неизбежно придет рассвет.",
"Вытянул именно тот единственный счастливый билет, который учил.",
"Надеялся исключительно на слепую удачу, не открыв учебник.",
1),
("Справедливость", "XI",
"Закон кармы, абсолютная честность, объективный вердикт.",
"Предвзятость, несправедливый упрек, уклонение от ответа.",
"Время подвести честный итог и взвесить все 'за' и 'против'.",
"Попытка обойти правила обернется строгим уроком судьбы.",
"Справедливость восторжествует; каждый получит по заслугам.",
"Будь честен перед собой: признай ошибки и исправь их.",
"Работал стабильно, честно заслужил каждую крупицу своих баллов.",
"Пытался списать со смартфона прямо перед глазами комиссии.",
1),
("Повешенный", "XII",
"Новый взгляд на вещи, добровольная жертва ради высшей цели.",
"Бесполезное упрямство, застревание, позиция жертвы.",
"Ситуация требует взглянуть на всё под совершенно иным углом.",
"Застой и нежелание пожертвовать малым ради победы.",
"Пауза пойдет на пользу; озарение придет в момент покоя.",
"Хватит тянуть резину; начни действовать прямо сейчас.",
"Необычный подход к решению задач, оригинальное мышление.",
"Завис в долгах до самой сессии, придумывая нелепые оправдания.",
0),
("Смерть", "XIII",
"Глубинная трансформация, завершение старого цикла, перерождение.",
"Цепляние за отжившее, страх перемен, затянувшаяся агония.",
"Старый этап подошел к финалу; расчисти место новому рассвету.",
"Нежелание отпустить то, что уже исчерпало себя.",
"Сбрось старую оболочку; впереди ждет обновленная судьба.",
"Не бойся закрыть старую главу; перемены несут благо.",
"Полностью переписал проект с нуля за ночь и блестяще защитил.",
"До последнего защищал неработающий багованный код из сети.",
0),
("Умеренность", "XIV",
"Золотая середина, баланс, гармония противоположностей.",
"Дисбаланс, крайности, поспешность и чрезмерный аппетит.",
"Сохраняй внутреннее равновесие и спокойный шаг.",
"Нетерпение и эмоциональные качели расшатывают лодку.",
"Тише едешь — дальше будешь; время работает на тебя.",
"Избегай крайностей: чрезмерный азарт приведет к потерям.",
"Равномерно распределял нагрузку в течение всего учебного года.",
"Всю ночь зубрил, пришел на экзамен с тремором и чашкой кофе.",
1),
("Дьявол", "XV",
"Магнетизм, земные искушения, материальная хватка, азарт.",
"Освобождение от иллюзий, падение оков, разоблачение страстей.",
"Сильное искушение или зависимость от сиюминутных страстей.",
"Оковы вредных привычек и самообман держат в плену.",
"Осознай свои истинные мотивы; не продавай душу за мелочи.",
"Сбрось оковы сомнений: никто не властен над твоей волей.",
"Хитрый, пробивной ум, найдет выход из любой безнадеги.",
"Использовал нейросеть для генерации диплома, даже не читая текст.",
-1),
("Башня", "XVI",
"Крах иллюзий, очистительная буря, внезапное освобождение.",
"Катастрофа, упорное отрицание фактов, попытка склеить руины.",
"Ненадежные основания рушатся, чтобы ты построил твердыню.",
"Внезапный удар судьбы застигнет врасплох, если медлить.",
"Прими бурю как очищение: на обломках вырастет величие.",
"Не пытайся спасти карточный домик; строй заново на скале.",
"После жесткого разноса на зачете собрался и всё пересдал.",
"Уронил продакшн, удалил репозиторий перед самой сдачей проекта.",
-1),
("Звезда", "XVII",
"Светлая надежда, вдохновение, путеводная звезда и благодать.",
"Разочарование, пессимизм, утрата веры в счастливое завтра.",
"Перед тобой ясный ориентир; верь в свою путеводную мечту.",
"Сомнения в завтрашнем дне туманят истинный свет.",
"Твоя звезда ведет тебя к триумфу; не сворачивай с тропы.",
"Не витай в мечтах без дела: воплощай надежду в поступках.",
"Светлая голова, вдохновлял всю группу своими ответами.",
"Мечтал о красном дипломе, лежа на диване с чипсами.",
1),
("Луна", "XVIII",
"Тайны подсознания, интуиция, путешествие сквозь сумрак.",
"Иллюзии, страхи, обман, самообман и блуждание в потемках.",
"Ситуация окутана туманом; берегись скрытых ловушек.",
"Страхи и тревоги рисуют мнимые угрозы на пустом месте.",
"Включи фонарь разума; не верь теням, ищи проверенные факты.",
"Пройди сквозь сумрак не дрогнув; утро рассеет морок.",
"Нестандартно мыслит, умеет находить обходные элегантные пути.",
"Блуждал в трех соснах, запутавшись в простейших формулах.",
0),
("Солнце", "XIX",
"Ясный триумф, истина, радость, благословение и успех.",
"Эгоизм, временные облака на горизонте, ослепление гордыней.",
"Всё прозрачно и ясно; судьба озарена лучами славы.",
"Чрезмерная самоуверенность может ослепить перед финишем.",
"Тебя ждет безоговорочная победа и признание заслуг.",
"Поделись радостью с окружающими; гордыня губит триумф.",
"Звезда курса, безупречные знания, гордость преподавателя.",
"Слишком задирал нос перед одногруппниками, схлопотал допвопрос.",
1),
("Суд", "XX",
"Пробуждение, духовный подъем, избавление от прошлых долгов.",
"Сомнения, страх перед воздаянием, упущенный второй шанс.",
"Зов трубы возвещает час истины: пора расправить крылья.",
"Груз старых ошибок тянет вниз; пора рассчитаться со спорами.",
"Час воздаяния настал: ты готов перейти на высший уровень.",
"Не бойся суда: твоя совесть чиста, если ты действовал честно.",
"Смог блестяще реабилитироваться на комиссии и доказать знания.",
"До последнего надеялся, что ведомость закроют без его участия.",
1),
("Мир", "XXI",
"Абсолютный триумф, совершенство, гармоничное завершение пути.",
"Недоделанные штрихи, страх перед новым горизонтом.",
"Все пазлы сложились в шедевр; ты на вершине успеха.",
"Остался всего один шаг до финала; не бросай дело у цели.",
"Цель достигнута, впереди открываются врата нового мира.",
"Заверши начатое до конца; не оставляй хвостов за спиной.",
"Идеальный студент, курс освоен на все 100%, эталонная работа.",
"Не хватило ровно одной подписи в обходном листе для полного триумфа.",
1)
]
TOPICS = [
("казино_деньги", ["покер", "деньг", "банк", "казино", "ставк", "выигр", "куш", "фишк", "рулетк", "джекпот", "богат", "золот", "выигра"]),
("любовь", ["любов", "девушк", "парен", "чувств", "отношен", "брак", "сердц", "любит", "пара", "женщин", "мужчин"]),
("учеба", ["учеб", "экзамен", "сесси", "зачет", "препод", "универ", "балл", "оценк", "институт", "диплом", "лаб", "семестр"]),
("судьба", ["будущ", "судьб", "выбор", "что ждет", "стоит ли", "как поступить", "жизн", "завтра", "путь", "дорог"])
]
def generate():
# 1. Generate fortune_mind_data.inc
d_lines = [
'; ==============================================================================',
'; FORTUNE MIND DATA - MASM32 / CP1251',
'; Algorithmic Tarot Oracle & Teacher Grade Divination Database',
'; ==============================================================================',
'',
'FORTUNE_SUBMODE_MENU equ 0',
'FORTUNE_SUBMODE_TAROT equ 1',
'FORTUNE_SUBMODE_STUDENT equ 2',
'',
'.data',
'ALIGN 4',
'fortuneSubMode3D dd 0',
'fortuneCardsDrawn3D dd 0',
'fortuneDrawCards3D dd 0,0,0',
'',
'studentName3D db 64 dup(0)',
'studentNameLen3D dd 0',
'studentYear3D db 16 dup(0)',
'studentYearLen3D dd 0',
'studentChoice3D dd 0 ; 0=9-10, 1=8-9, 2=7-8, 3=4-5, 4=5-6',
'studentField3D dd 0 ; 0=Name, 1=Year, 2=Choice',
'studentDone3D dd 0',
'studentKarmaSum3D dd 0',
'',
'; UI Static Strings (CP1251)',
'fortuneMenuTitle3D db "ТАИНСТВЕННЫЙ ОРАКУЛ КАЗИНО",0',
'fortuneMenuGreeting1_3D db "«Приветствую тебя, путник. Карты видят то, что скрыто от глаз.",0',
'fortuneMenuGreeting2_3D db "С чем ты предстал перед священным оракулом?»",0',
'fortuneMenuOpt1_3D db "[1] Обычное гадание на картах Таро (вопрос судьбе)",0',
'fortuneMenuOpt2_3D db "[2] Кабинет преподавателя: решить спорную оценку студента",0',
'fortuneMenuOpt0_3D db "[0] Покинуть обитель гадалки",0',
'',
'strTarotRole1 db "1. СИТУАЦИЯ",0',
'strTarotRole2 db "2. ПРЕПЯТСТВИЕ",0',
'strTarotRole3 db "3. ИСХОД",0',
'',
'strStudRole1 db "1. КАРМА УЧЕБЫ",0',
'strStudRole2 db "2. ТАЙНЫЙ ГРЕХ",0',
'strStudRole3 db "3. СУД ВСЕЛЕННОЙ",0',
'',
'strCardUpright db "[ПРЯМАЯ]",0',
'strCardReversed db "[ПЕРЕВЕРНУТА]",0',
'',
'strAskPrompt db "Задай вопрос картам (Enter - спросить):",0',
'strStudentTitle db "КАБИНЕТ ПРЕПОДАВАТЕЛЯ: СУДЬБА СТУДЕНТА",0',
'strStudentPromptName db "ФИО студента : ",0',
'strStudentPromptYear db "Год рождения : ",0',
'strStudentPromptChoice db "Спорный балл : [1] 9-10 [2] 8-9 [3] 7-8 [4] 4-5 [5] 5-6",0',
'strStudentAction db "Enter / Клик: Рассудить студента | Tab: Сменить поле | Esc: Меню",0',
'strBtnNextStudent db "[СЛЕДУЮЩИЙ СТУДЕНТ]",0',
'strBtnMenu db "[В МЕНЮ]",0',
'strBtnAsk db "[СПРОСИТЬ]",0',
'strBtnExit db "[ВЫЙТИ]",0',
'strDefaultStudentName db "Иванов Иван",0',
'strDefaultStudentYear db "2005",0',
'',
'; Grade Choice Display Names',
'strGradeChoice0 db "9 или 10",0',
'strGradeChoice1 db "8 или 9",0',
'strGradeChoice2 db "7 или 8",0',
'strGradeChoice3 db "4 или 5",0',
'strGradeChoice4 db "5 или 6",0',
'',
'gradeChoicesTable dd OFFSET strGradeChoice0, OFFSET strGradeChoice1, OFFSET strGradeChoice2, OFFSET strGradeChoice3, OFFSET strGradeChoice4',
'',
'; Card Data Structures',
]
for i, c in enumerate(CARDS):
name, rom, sit_u, sit_r, obs_u, obs_r, out_u, out_r, st_p, st_n, k = c
d_lines.append(f'cardName_{i} db "{rom} {name}",0')
d_lines.append(f'cardSitU_{i} db "{sit_u}",0')
d_lines.append(f'cardSitR_{i} db "{sit_r}",0')
d_lines.append(f'cardObsU_{i} db "{obs_u}",0')
d_lines.append(f'cardObsR_{i} db "{obs_r}",0')
d_lines.append(f'cardOutU_{i} db "{out_u}",0')
d_lines.append(f'cardOutR_{i} db "{out_r}",0')
d_lines.append(f'cardStudP_{i} db "{st_p}",0')
d_lines.append(f'cardStudN_{i} db "{st_n}",0')
d_lines.append('')
d_lines.append('ALIGN 4')
d_lines.append('cardNamesTable dd ' + ', '.join(f'OFFSET cardName_{i}' for i in range(len(CARDS))))
d_lines.append('cardSitUTable dd ' + ', '.join(f'OFFSET cardSitU_{i}' for i in range(len(CARDS))))
d_lines.append('cardSitRTable dd ' + ', '.join(f'OFFSET cardSitR_{i}' for i in range(len(CARDS))))
d_lines.append('cardObsUTable dd ' + ', '.join(f'OFFSET cardObsU_{i}' for i in range(len(CARDS))))
d_lines.append('cardObsRTable dd ' + ', '.join(f'OFFSET cardObsR_{i}' for i in range(len(CARDS))))
d_lines.append('cardOutUTable dd ' + ', '.join(f'OFFSET cardOutU_{i}' for i in range(len(CARDS))))
d_lines.append('cardOutRTable dd ' + ', '.join(f'OFFSET cardOutR_{i}' for i in range(len(CARDS))))
d_lines.append('cardStudPTable dd ' + ', '.join(f'OFFSET cardStudP_{i}' for i in range(len(CARDS))))
d_lines.append('cardStudNTable dd ' + ', '.join(f'OFFSET cardStudN_{i}' for i in range(len(CARDS))))
d_lines.append('cardKarmaTable dd ' + ', '.join(str(c[10]) for c in CARDS))
d_lines.append('')
# Helper strings for synthesis
d_lines.append('strHeadingStudent db "=== СУДЕБНЫЙ РАСКЛАД ТАРО: СТУДЕНТ ===",10,0')
d_lines.append('strLabelStudent db "Студент: ",0')
d_lines.append('strLabelBorn db " (",0')
d_lines.append('strLabelBornEnd db " г.р.) | Спорная отметка: ",0')
d_lines.append('strCard1Prefix db "1. Карма учебы: ",0')
d_lines.append('strCard2Prefix db "2. Тайный грех: ",0')
d_lines.append('strCard3Prefix db "3. Суд Вселенной: ",0')
d_lines.append('strVerdictHighPrefix db 10,"*** ВЕРДИКТ СУДЬБЫ: СТАВИТЬ БОЛЬШИЙ БАЛЛ (",0')
d_lines.append('strVerdictLowPrefix db 10,"*** ВЕРДИКТ СУДЬБЫ: СТАВИТЬ МЕНЬШИЙ БАЛЛ (",0')
d_lines.append('strVerdictSuffix db ")! ***",10,0')
d_lines.append('strAdviceHigh db "Совет преподавателю: Студент полностью искупил ночные грехи и проявил искру таланта. Поставьте высший балл — это окрылит его!",0')
d_lines.append('strAdviceLow db "Совет преподавателю: Карты суровы, но справедливы. Меньший балл станет целебным уроком и научит не сдавать лабы за 5 минут до дедлайна!",0')
d_lines.append('strReadingHeading db "=== РАСКЛАД ТАРО ИЗ 3 КАРТ ===",10,0')
d_lines.append('strReadingSit db "1. Ситуация (Исток): ",0')
d_lines.append('strReadingObs db "2. Препятствие (Вызов): ",0')
d_lines.append('strReadingOut db "3. Исход (Совет): ",0')
d_lines.append('strReadingSynth db 10,"Синтез оракула: Карты указали путь. Твоя воля определяет исход — действуй разумно и помни знаки судьбы.",0')
d_lines.append('strNewLine db 10,0')
d_lines.append('strSpace db " ",0')
d_lines.append('strDashSpace db " — ",0')
d_lines.append('strColonSpace db ": ",0')
d_lines.append('strTopicCasino db 10,"(Оракул чувствует дыхание казино и азарта: Фортуна любит смелых, но карает жадных!)",0')
d_lines.append('strTopicLove db 10,"(Оракул слышит биение твоего сердца: в любви важны искренность и доверие, а не гордыня.)",0')
d_lines.append('strTopicStudy db 10,"(Оракул видит свитки знаний и дедлайны: упорство и труд сломят гранит науки!)",0')
d_lines.append('strTopicFuture db 10,"(Оракул вглядывается в туман грядущего: будущее не высечено в камне, ты кузнец своего пути!)",0')
d_lines.append('')
# Keyword strings for topic detection
d_lines.append('kwPoker db "покер",0')
d_lines.append('kwMoney db "деньг",0')
d_lines.append('kwBank db "банк",0')
d_lines.append('kwBet db "ставк",0')
d_lines.append('kwCasino db "казино",0')
d_lines.append('kwWin db "выигр",0')
d_lines.append('kwRoulette db "рулетк",0')
d_lines.append('kwLove db "любов",0')
d_lines.append('kwGirl db "девушк",0')
d_lines.append('kwBoy db "парен",0')
d_lines.append('kwRel db "отношен",0')
d_lines.append('kwStudy db "учеб",0')
d_lines.append('kwExam db "экзамен",0')
d_lines.append('kwSession db "сесси",0')
d_lines.append('kwScore db "балл",0')
d_lines.append('kwGrade db "оценк",0')
d_lines.append('kwFuture db "будущ",0')
d_lines.append('kwFate db "судьб",0')
d_lines.append('kwChoice db "выбор",0')
d_lines.append('')
d_lines.append('strScore10 db "10",0')
d_lines.append('strScore9 db "9",0')
d_lines.append('strScore8 db "8",0')
d_lines.append('strScore7 db "7",0')
d_lines.append('strScore6 db "6",0')
d_lines.append('strScore5 db "5",0')
d_lines.append('strScore4 db "4",0')
d_lines.append('')
d_lines.append('SubStrI3D PROTO :DWORD,:DWORD')
d_lines.append('')
data_content = '\n'.join(d_lines) + '\n'
# 2. Generate fortune_mind.inc
c_lines = [
'; ==============================================================================',
'; FORTUNE MIND CODE - MASM32 / CP1251',
'; Algorithmic Oracle Logic, String Assembly & Student Grade Decision Engine',
'; ==============================================================================',
'',
'.code',
'',
'; StrLen: Returns string length in EAX',
'FortuneStrLen PROC uses esi strPtr:DWORD',
' mov esi, strPtr',
' xor eax, eax',
' test esi, esi',
' jz @F',
'fortune_strlen_loop:',
' cmp BYTE PTR [esi+eax], 0',
' je @F',
' inc eax',
' jmp fortune_strlen_loop',
'@@:',
' ret',
'FortuneStrLen ENDP',
'',
'; StrCat: Appends src to dst, returns new end pointer in EAX',
'FortuneStrCat PROC uses esi edi dst:DWORD, src:DWORD',
' mov edi, dst',
' test edi, edi',
' jz fortune_strcat_done',
' xor eax, eax',
' mov ecx, -1',
' repne scasb',
' dec edi',
' mov esi, src',
' test esi, esi',
' jz fortune_strcat_ret',
'fortune_strcat_copy:',
' lodsb',
' stosb',
' test al, al',
' jnz fortune_strcat_copy',
' dec edi',
'fortune_strcat_ret:',
' mov eax, edi',
'fortune_strcat_done:',
' ret',
'FortuneStrCat ENDP',
'',
'; SubStrI3D: Case-insensitive substring search for CP1251',
'SubStrI3D PROC uses ebx esi edi hHaystack:DWORD, hNeedle:DWORD',
' mov esi, hHaystack',
' mov edi, hNeedle',
' test esi, esi',
' jz substr_fail',
' test edi, edi',
' jz substr_fail',
'substr_outer:',
' mov al, [esi]',
' test al, al',
' jz substr_fail',
' push esi',
' push edi',
'substr_inner:',
' mov al, [esi]',
' mov bl, [edi]',
' test bl, bl',
' jz substr_found',
' test al, al',
' jz substr_next_outer',
' ; Lowercase compare',
' cmp al, "A"',
' jb @F',
' cmp al, "Z"',
' ja @F',
' add al, 32',
'@@:',
' cmp al, 192 ; CP1251 А..Я',
' jb @F',
' cmp al, 223',
' ja @F',
' add al, 32',
'@@:',
' cmp bl, "A"',
' jb @F',
' cmp bl, "Z"',
' ja @F',
' add bl, 32',
'@@:',
' cmp bl, 192',
' jb @F',
' cmp bl, 223',
' ja @F',
' add bl, 32',
'@@:',
' cmp al, bl',
' jne substr_next_outer',
' inc esi',
' inc edi',
' jmp substr_inner',
'substr_next_outer:',
' pop edi',
' pop esi',
' inc esi',
' jmp substr_outer',
'substr_found:',
' pop edi',
' pop esi',
' mov eax, 1',
' ret',
'substr_fail:',
' xor eax, eax',
' ret',
'SubStrI3D ENDP',
'',
'; InitFortuneMindState3D: Clears buffers and sets up defaults',
'InitFortuneMindState3D PROC uses esi edi',
' mov fortuneSubMode3D, FORTUNE_SUBMODE_MENU',
' mov fortuneCardsDrawn3D, 0',
' mov studentDone3D, 0',
' mov studentField3D, 0',
' mov studentChoice3D, 0',
' ; Setup default student name if empty',
' cmp studentName3D[0], 0',
' jne @F',
' lea edi, studentName3D',
' lea esi, strDefaultStudentName',
'fortune_copy_def_name:',
' lodsb',
' stosb',
' test al, al',
' jnz fortune_copy_def_name',
' invoke FortuneStrLen, OFFSET studentName3D',
' mov studentNameLen3D, eax',
'@@:',
' cmp studentYear3D[0], 0',
' jne @F',
' lea edi, studentYear3D',
' lea esi, strDefaultStudentYear',
'fortune_copy_def_year:',
' lodsb',
' stosb',
' test al, al',
' jnz fortune_copy_def_year',
' invoke FortuneStrLen, OFFSET studentYear3D',
' mov studentYearLen3D, eax',
'@@:',
' ret',
'InitFortuneMindState3D ENDP',
'',
'; DetectTopic3D: Scans question for keywords',
'; Returns: 0=General, 1=Casino, 2=Love, 3=Study, 4=Future',
'DetectTopic3D PROC uses ebx esi edi qStr:DWORD',
' mov esi, qStr',
' test esi, esi',
' jz topic_general',
' ; Simple substring check for Russian CP1251 keywords',
' ; Check Casino: "покер", "деньг", "банк", "ставк", "казино"',
' invoke SubStrI3D, esi, OFFSET kwPoker',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwMoney',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwBank',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwBet',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwCasino',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwWin',
' test eax, eax',
' jnz topic_casino',
' invoke SubStrI3D, esi, OFFSET kwRoulette',
' test eax, eax',
' jnz topic_casino',
' ; Check Love: "любов", "девушк", "парен", "чувств", "отношен"',
' invoke SubStrI3D, esi, OFFSET kwLove',
' test eax, eax',
' jnz topic_love',
' invoke SubStrI3D, esi, OFFSET kwGirl',
' test eax, eax',
' jnz topic_love',
' invoke SubStrI3D, esi, OFFSET kwBoy',
' test eax, eax',
' jnz topic_love',
' invoke SubStrI3D, esi, OFFSET kwRel',
' test eax, eax',
' jnz topic_love',
' ; Check Study: "учеб", "экзамен", "сесси", "балл", "оценк", "лаб"',
' invoke SubStrI3D, esi, OFFSET kwStudy',
' test eax, eax',
' jnz topic_study',
' invoke SubStrI3D, esi, OFFSET kwExam',
' test eax, eax',
' jnz topic_study',
' invoke SubStrI3D, esi, OFFSET kwSession',
' test eax, eax',
' jnz topic_study',
' invoke SubStrI3D, esi, OFFSET kwScore',
' test eax, eax',
' jnz topic_study',
' invoke SubStrI3D, esi, OFFSET kwGrade',
' test eax, eax',
' jnz topic_study',
' ; Check Future: "будущ", "судьб", "выбор", "стоит ли", "что ждет"',
' invoke SubStrI3D, esi, OFFSET kwFuture',
' test eax, eax',
' jnz topic_future',
' invoke SubStrI3D, esi, OFFSET kwFate',
' test eax, eax',
' jnz topic_future',
' invoke SubStrI3D, esi, OFFSET kwChoice',
' test eax, eax',
' jnz topic_future',
'topic_general:',
' xor eax, eax',
' ret',
'topic_casino:',
' mov eax, 1',
' ret',
'topic_love:',
' mov eax, 2',
' ret',
'topic_study:',
' mov eax, 3',
' ret',
'topic_future:',
' mov eax, 4',
' ret',
'DetectTopic3D ENDP',
'',
'; GenerateTarotReadingMind3D: Builds full 3-card reading in fortuneAnswer3D',
'GenerateTarotReadingMind3D PROC uses ebx esi edi',
' local c1:DWORD, c2:DWORD, c3:DWORD',
' local r1:DWORD, r2:DWORD, r3:DWORD',
' local topic:DWORD',
'',
' mov eax, fortuneDrawCards3D[0]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c1, edx',
' cmp fortuneDrawCards3D[0], 22',
' setae al',
' movzx eax, al',
' mov r1, eax ; 1 if reversed, 0 if upright',
'',
' mov eax, fortuneDrawCards3D[4]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c2, edx',
' cmp fortuneDrawCards3D[4], 22',
' setae al',
' movzx eax, al',
' mov r2, eax',
'',
' mov eax, fortuneDrawCards3D[8]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c3, edx',
' cmp fortuneDrawCards3D[8], 22',
' setae al',
' movzx eax, al',
' mov r3, eax',
'',
' invoke DetectTopic3D, OFFSET fortuneQuestion3D',
' mov topic, eax',
'',
' ; Reset answer buffer',
' mov fortuneAnswer3D[0], 0',
'',
' ; Header',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strReadingHeading',
'',
' ; Card 1: Situation',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strReadingSit',
' mov eax, c1',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r1, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' jmp fortune_c1_interp',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
'fortune_c1_interp:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c1',
' cmp r1, 0',
' je @F',
' mov eax, cardSitRTable[eax*4]',
' jmp fortune_c1_add',
'@@:',
' mov eax, cardSitUTable[eax*4]',
'fortune_c1_add:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
'',
' ; Card 2: Obstacle',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strReadingObs',
' mov eax, c2',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r2, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' jmp fortune_c2_interp',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
'fortune_c2_interp:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c2',
' cmp r2, 0',
' je @F',
' mov eax, cardObsRTable[eax*4]',
' jmp fortune_c2_add',
'@@:',
' mov eax, cardObsUTable[eax*4]',
'fortune_c2_add:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
'',
' ; Card 3: Outcome',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strReadingOut',
' mov eax, c3',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r3, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' jmp fortune_c3_interp',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
'fortune_c3_interp:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c3',
' cmp r3, 0',
' je @F',
' mov eax, cardOutRTable[eax*4]',
' jmp fortune_c3_add',
'@@:',
' mov eax, cardOutUTable[eax*4]',
'fortune_c3_add:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
'',
' ; Synthesis & Topic',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strReadingSynth',
' cmp topic, 1',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strTopicCasino',
'@@:',
' cmp topic, 2',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strTopicLove',
'@@:',
' cmp topic, 3',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strTopicStudy',
'@@:',
' cmp topic, 4',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strTopicFuture',
'@@:',
' ret',
'GenerateTarotReadingMind3D ENDP',
'',
'; GenerateStudentGradeMind3D: Evaluates borderline student grade',
'GenerateStudentGradeMind3D PROC uses ebx esi edi',
' local c1:DWORD, c2:DWORD, c3:DWORD',
' local r1:DWORD, r2:DWORD, r3:DWORD',
' local karma:DWORD',
' local pickHigh:DWORD',
'',
' mov eax, fortuneDrawCards3D[0]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c1, edx',
' cmp fortuneDrawCards3D[0], 22',
' setae al',
' movzx eax, al',
' mov r1, eax',
'',
' mov eax, fortuneDrawCards3D[4]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c2, edx',
' cmp fortuneDrawCards3D[4], 22',
' setae al',
' movzx eax, al',
' mov r2, eax',
'',
' mov eax, fortuneDrawCards3D[8]',
' xor edx, edx',
' mov ecx, 22',
' div ecx',
' mov c3, edx',
' cmp fortuneDrawCards3D[8], 22',
' setae al',
' movzx eax, al',
' mov r3, eax',
'',
' ; Calculate karma points from cards + name letters',
' xor ebx, ebx',
' ; Card 1: upright = +2, rev = -1',
' cmp r1, 0',
' jne @F',
' add ebx, 2',
' jmp fortune_karma_c2',
'@@:',
' dec ebx',
'fortune_karma_c2:',
' ; Card 2: obstacle reversed = inner struggle (-2), upright = challenge (+1)',
' cmp r2, 0',
' jne @F',
' inc ebx',
' jmp fortune_karma_c3',
'@@:',
' sub ebx, 2',
'fortune_karma_c3:',
' ; Card 3: final outcome card karma influence',
' mov eax, c3',
' mov eax, cardKarmaTable[eax*4]',
' cmp r3, 0',
' je @F',
' neg eax',
'@@:',
' add ebx, eax',
'',
' ; Name letters influence (hash sum)',
' xor ecx, ecx',
' lea esi, studentName3D',
'fortune_name_hash:',
' lodsb',
' test al, al',
' jz fortune_name_hash_done',
' movzx eax, al',
' add ecx, eax',
' jmp fortune_name_hash',
'fortune_name_hash_done:',
' test ecx, 1',
' jz @F',
' inc ebx ; Odd letter hash gives +1 karma boost',
'@@:',
' mov karma, ebx',
' mov studentKarmaSum3D, ebx',
'',
' ; Determine if high or low grade wins',
' ; If karma >= 1 -> higher grade, else lower grade',
' cmp ebx, 1',
' jge fortune_pick_high',
' mov pickHigh, 0',
' jmp fortune_format_student',
'fortune_pick_high:',
' mov pickHigh, 1',
'',
'fortune_format_student:',
' mov fortuneAnswer3D[0], 0',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strHeadingStudent',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strLabelStudent',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET studentName3D',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strLabelBorn',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET studentYear3D',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strLabelBornEnd',
' mov eax, studentChoice3D',
' cmp eax, 5',
' jb @F',
' xor eax, eax',
'@@:',
' mov eax, gradeChoicesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
'',
' ; Card 1: Study Habits',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCard1Prefix',
' mov eax, c1',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r1, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c1',
' mov eax, cardStudNTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' jmp fortune_stud_c2',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c1',
' mov eax, cardStudPTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
'',
'fortune_stud_c2:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCard2Prefix',
' mov eax, c2',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r2, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c2',
' mov eax, cardStudNTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' jmp fortune_stud_c3',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c2',
' mov eax, cardStudPTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
'',
'fortune_stud_c3:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strNewLine',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCard3Prefix',
' mov eax, c3',
' mov eax, cardNamesTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strSpace',
' cmp r3, 0',
' je @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardReversed',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c3',
' mov eax, cardOutRTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
' jmp fortune_stud_verdict',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strCardUpright',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strDashSpace',
' mov eax, c3',
' mov eax, cardOutUTable[eax*4]',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, eax',
'',
'fortune_stud_verdict:',
' cmp pickHigh, 1',
' jne fortune_verdict_low',
'',
' ; High Verdict (e.g. 10 or 9 or 8 or 5 or 6)',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strVerdictHighPrefix',
' mov eax, studentChoice3D',
' cmp eax, 0',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore10',
' jmp fortune_verdict_high_done',
'@@:',
' cmp eax, 1',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore9',
' jmp fortune_verdict_high_done',
'@@:',
' cmp eax, 2',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore8',
' jmp fortune_verdict_high_done',
'@@:',
' cmp eax, 3',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore5',
' jmp fortune_verdict_high_done',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore6',
'fortune_verdict_high_done:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strVerdictSuffix',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strAdviceHigh',
' jmp fortune_stud_end',
'',
'fortune_verdict_low:',
' ; Low Verdict (e.g. 9 or 8 or 7 or 4 or 5)',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strVerdictLowPrefix',
' mov eax, studentChoice3D',
' cmp eax, 0',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore9',
' jmp fortune_verdict_low_done',
'@@:',
' cmp eax, 1',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore8',
' jmp fortune_verdict_low_done',
'@@:',
' cmp eax, 2',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore7',
' jmp fortune_verdict_low_done',
'@@:',
' cmp eax, 3',
' jne @F',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore4',
' jmp fortune_verdict_low_done',
'@@:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strScore5',
'fortune_verdict_low_done:',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strVerdictSuffix',
' invoke FortuneStrCat, OFFSET fortuneAnswer3D, OFFSET strAdviceLow',
'',
'fortune_stud_end:',
' ret',
'GenerateStudentGradeMind3D ENDP',
''
]
code_content = '\n'.join(c_lines) + '\n'
# Save files in CP1251
data_path = ROOT / 'src_graphics/fortune_mind_data.inc'
code_path = ROOT / 'src_graphics/fortune_mind.inc'
data_path.write_bytes(data_content.encode('cp1251'))
print(f'[OK] Wrote {data_path} ({len(data_content)} chars, CP1251)')
code_path.write_bytes(code_content.encode('cp1251'))
print(f'[OK] Wrote {code_path} ({len(code_content)} chars, CP1251)')
if __name__ == '__main__':
generate()