Преамбула
К моему счастью, среди возможных вариантов времяпровождения с друзьями существует один совсем особенный вариант — это игра в преферанс. Еще лучше, когда компания состоит из сильных игроков, а колода радует всевозможными нетривиальными раскладами.
Во время игры игроки зачастую употребляют спиртное и к концу вечера возникает необходимость подсчета пули, что превращается в занятное дело из-за приподнятого настроения, царящего за столом. Дабы избежать ошибочных расчетов и легкого подрыва нервной системы, возникла идея автоматизации процесса. Именно поэтому было решено написать программу для мобильного телефона.
Фабула
Опыта написания программ для телефона у меня не было, как и вопроса «чем писать?» — уже давно хотелось попробовать в деле MIDLet Pascal. Сказано — сделано, но тут меня поджидало несколько подводных камней.
Версии MIDlet Pascal
На данный момент существует две версии IDE: оригинальная 2.02 и полностью свободная 3.2 (далее буду именовать как 2-я и 3-я ветки).
Для написания программ для меня удобнее оказалась 3-я ветка: нормальная настраиваемая подсветка синтаксиса и внешнего вида IDE, поддержка переводов интерфейса, человеческая история файлов и т.д. Но, если быть честным, писать код в Notepad++ ничуть не хуже, чем в обеих ветках, потому что ни там ни тут нет автодополнения.
Зато есть простейшие редакторы ресурсов. Причем во второй ветке простой и удобный редактор картинок (я все иконки для проекта нарисовал в нем), но нет HEX-редактора, а в 3-й ветке — неплохой HEX-редактор при отвратительном графическом.
От 3-й ветки мне всё равно пришлось отказаться по нескольким причинам. Первая и главная — абсолютно не работают сторонние библиотеки, которые работают во 2-й ветке. Вторая — компилятор 3-й ветки падает при сборке проекта, если в проекте используются строки на русском языке.
Сначала я пробовал писать программу в 3-й версии, а собирать во 2-й, но мне это быстро надоело и я полностью перебрался на 2-ю ветку.
Для сравнения приложу скриншоты обеих версий:


Написание программы
Так как я ни разу не брался за j2me и даже не интересовался что это и с чем едят, то и работа с MIDlet Pascal для меня началась с разбора азов. Примитивные примеры из серии «Hello, World!» быстро дали понять — будет не слишком просто. Для меня стало открытием, что любая программа так или иначе выполняется исключительно в циклах, внутри которых происходит обработка прерываний, вызванных событиями, нажатиями кнопок и т.д.
Простейший пример программы в цикле:
-
- program Test;
-
- { объявляем переменные}
- var
- k: integer;
- s: string;
-
- { основное тело программы }
- begin
- { задаем цвет в формате RGB }
- SetColor(255, 0, 0);
- s := 'Hello, Word!';
- { рисуем текст в середине экрана в буфере! }
- DrawText(s, GetWidth/2 - GetStringWidth(s)/2, GetHeight/2 - GetStringHeight(s)/2);
- { отрисовываем на экране }
- Repaint;
- repeat
- k := GetKeyClicked; // ловим нажатие клавиши
- Delay(100); // задержка
- until KeyToAction(k) = GA_FIRE; // ждем нажатия на ОК
- end.
-
Если заменить блок repeat на Delay (1000), то программа закроется сама через одну секунду работы.
Язык MIDlet Pascal напоминает усеченный pascal.
Поддерживаемые типы данных:
* boolean
* char
* integer
* real
* string
* image
* command
* recordStore
* http
* resource
Комплексные типы:
* records
* arrays
Объекты не поддерживаются. Можно писать свои процедуры и функции. Нет привычных по Delphi конструкций with, case и прочих. По спартански, но и этого достаточно. Как написал кто-то на форуме по мидлетпаскалю: «В мидлетпаскале базово реализовано всё что нужно для написания программ» и я с этим полностью согласен.
Принципы работы
Любая программа может работать в 2-х режимах — графическом и «режиме формы“. В первом случае мы всё рисуем сами в буфере телефона, а потом выводим на экран, во втором мы указываем компоненты формы, их последовательность, но внешний вид формы уже будет зависеть от конкретной модели телефона. У меня Fly MC300, а у друга — Nokia 3720. Приложения в режиме формы выглядят различно. Субъективно, у Nokia гораздо удобнее и качественнее.
Чтобы избежать непредсказуемости внешнего вида, надо использовать графический режим (который в MIDlet Pascal по умолчанию), но следует помнить, что простейшие вещи проще реализовать в режиме формы, чем в графическом режиме. В графическом режиме необходимо всё рисовать самому, а в режиме формы это делает за вас телефон.
Переключение между режимами производится командами:
ShowCanvas
ShowForm
Библиотеки
Для упрощения реализации различных типовых задач энтузиастами было написано много подключаемых библиотек. Со списком и инструкциями к применению можно ознакомиться тут.
В моем проекте были использованы библиотеки menu32 и font32. Первая служит для организации простого меню, а вторая — для вывода рисованного текста. Menu32 использует для своей работы font32 (и такое бывает). Font32 работает со шрифтом, описание которого содержится в 2-х файлах, один из которых представляет из себя картинку формата png со всеми символами, выведенными в строку, и dat файл со смещениями по оси Х, обозначающими границы букв. Подобную связку файлов можно получить с помощью специальной программы-генератора, которую можно взять в ветке форума, посвященного font32. Хорошо читаемые шрифты получаются только из растровых шрифтов, результат преобразования векторных шрифтов требует обязательной правки обоих файлов, потому что буквы наползают друг на друга, а dat файл содержит неверные смещения.
Мне было лень заниматься правкой, поэтому я использовал растровые шрифты: MS Sans Serif и Small Fonts.
Проблемы и их решения
Проблем было не много, но они были. Первой, поставившей в тупик, стала задача нахождения координат точки пересечения двух прямых. Задача крайне тривиальная и успешно решается еще в средней школе, но оказалось, что я полностью забыл элементарную геометрию — даже уравнение прямой вспомнилось с большим трудом. Пришлось немного погуглить и найти решение.
Вторая проблема возникла при обработке нажатий клавиш. Дело в том, что для определения нажатой клавиши есть две функции: GetKeyClicked и GetKeyPressed, которые возвращают код клавиши, а так же существует функция KeyToAction, возвращающая код действия клавиши. Я не знаю является ли это особенностью именно MIDlet Pascal или всей jme платформы в целом, но реализация работы с клавиатурой реализована следующим образом: первые две функции возвращают коды клавиш, соответствующие цифровой клавиатуре, а третья функция возвращает коды, соответствующие нажатию джойстика.
-
- program KeyBoardTest;
-
- var
- k, ka: integer;
-
- begin
- repeat
- k := GetKeyClicked;
- ka := KeyToAction(k);
- Delay(100);
- until ka = GA_FIRE;
- end.
-
Данная маленькая программа проверяет нажатие клавиатуры и если была нажата клавиша ОК (обычно это середина джойстика) — закрывается. Подводный камень кроется в том, что на различных телефонах нажатие клавиш может отрабатываться по разному. Например, на моем Fly нажатия на кнопки 2, 4, 6, 8 и 5 генерят еще и коды действия джойстика, чего не делает протестированная Nokia. А так как обработка нажатий происходит в цикле когда обрабатываются коды клавиш и действия, то может возникнуть вариант, когда произойдет лишняя обработка события, связанного с действием. Лечится cия беда двумя способами: при отлове срабатывания клавиши присваивать значение переменой k несуществующий код клавиши (KE_NONE) и/или обрабатывать нажатия клавиш по кодам. Например для телефонов Nokia и SonyEricsson нажатие джойстика соответсвует коду "-5», но есть и обратная сторона медали — коды, возвращаемые при нажатии, могут отличаться у телефонов различных марок. К счастью, у Nokia и SonyEricsson коды совпадают.
Приведенные выше функции возвращают коды, соответствующие псевдонимам клавиш/действий, но список этот неполный и не содержит софт клавиш, кнопки стирания и т.д. Коды подобных клавиш были получены энтузиастами опытным путем, но не для не всех производителей телефонов.
Программа
Программа BulletSolver предназначена для расчета преферансной пули. Возможен вариант пули на троих и на четверых (для четверых возможен расчет с призами). Результат расчета выводится в вистах и в деньгах, цена виста оговаривается.
Несколько скриншотов:




Особенности работы
Перемещение по пуле происходит последовательно, начиная с игрока запад, против часовой стрелки, путем нажатия джойстика вверх или вниз. Ввод значений происходит по нажатию клавиши ОК. Результаты начинают выводиться только тогда, когда у всех игроков введено хотя-бы по одному значению. Функцию левой софт клавиши так же выполняет звездочка.
Программа не поддерживает сенсорные экраны, потому что у меня нет возможности протестировать работу с сенсором, да и не представляю я как производить ввод цифр без кнопок.
Скачать исходники и саму программу
Исходники предназначены для изучения и их можно подвергать любой переработке, какая вам заблагорассудится. Так как писалось всё для себя, то код далеко не идеален. Наверняка можно всё подшерстить и кое-что переделать, оптимизировать и т.д., но мне лень.
Послесловие
В качестве эмулятора использовался Sun Java Wireless Toolkit 2.5.2 for CLDC.
На полное написание программы в свободное от работы-жены-ребенка время понадобилось три дня. Сам процесс написания крайне занимателен, потому что средств отладки нет, всё приходится лепить самому на коленке. Кривули IDE тоже ставят палки в колеса, но всё равно интересно. Программировал я в свое удовольствие, поставленную задачу благополучно решил, заработал +5 к опыту.
Очень хотелось бы увидеть в будущем MIDlet Pacal 3-й, а может уже и 4-й ветки, но с нормально работающим компилятором, поддержкой уже существующих библиотек и всех положительных моментов 2-й ветки. Долгой жизни проекту, если в общем, ведь где еще можно найти среду разработки jme программ объемом в 2 мегабайта?
Отдельная благодарность всем обитателям форума forum.boolean.name за примеры и информацию.
Благодарю за внимание!