Обнаружив в моей очередной контрольной энное количество вычислительных ошибок, учитель физики сказал несколько максимально вежливых фраз, которые в вольном переводе на живой великорусский язык звучали бы так:
Чтоб в следующий раз был нормальный графический калькулятор!
В результате набега на ближайший книжный магазин (никаких более релевантных в округе не было) был приобретен TI-84 Plus. После того как все контрольные были написаны, возник вопрос: а что, собственно говоря, этот зверь умеет? Путем гугления выяснилось, что TI-84 Plus — это не только ценный извлекатель квадратных корней но и:
- Легендарный Zilog Z80,
- 24 Кб RAM и 480 Кб Flash доступной пользователю,
- Куча полезных (и не очень) программ, написанных энтузиастами.
Под катом — небольшой обзор калькулятора и довольно-таки корявая игра «роботы» моего написания. Заранее прошу прощения у любителей обнаженки — разобрать калькулятор я не смог, да и побоялся.
TI-84+ — программируемый графический калькулятор с черно-белым экраном 96x64. «Говорит и понимает» © математическую нотацию (дроби вида а над бе, квадратные корни с аргументом под чертой и так далее):
Умеет кучу полезных математических штук, но интереснее с точки зрения Хабра его программируемость.
Программирование
Программировать TI-84+ «из коробки» можно либо на TI BASIC, либо в машинных кодах. TI BASIC — диалект BASICa. Это «структурный», а не «макаронный» диалект. Вместо ключевых слов — токены (по-моему что-то такое было в Спекртумах). Оптимизирован для математических операций, для игр и других приложений подходит так себе, хотя конечно, умеючи можно написать что угодно. Из минусов очень медленный, так как вдобавок к интерпретации, вся математика с плавающей точкой. Из плюсов — программы занимают очень мало места.
Другая возможность — машинные коды. Как правило, собранные ассемблером на компьютере. Соответственно, программы получаются на несколько порядков быстрее и раза в два больше чем соответствующая программа на бейсике. Еще они могут убить калькулятор.
Энтузиастами созданы и другие способы программировать TI-84+. Наиболее значимый, пожалуй, — AXE. Это аналог Си для калькуляторов: язык с Basic-подобным синтаксисом, транслирующийся в машинные коды. Не требует компьютера для написания и компиляции программы (хотя, писать большую программу на калькуляторе — удовольствие на любителя). Использует шестнадцатибитную арифметику, что конечно медленнее нативной восьмибитной.
Программы для TI-84+
Вот здесь есть архив с кучей программ — как полезных, ятак и игровых. Также, на оффициальном сайте TI есть много полезных программ. На калькуляторе из коробки память забита под завязку, но большая часть программ в стандартной поставке бесполезны, их можно смело удалить.
Пару слов об организации памяти: в калькуляторе есть RAM и FLASH (Archive в терминологии TI). И то и другое организованно в некое подобие файловой системы. Программы и данные можно хранить как в RAM, так и во FLASH. FLASH медленнее, но ее больше. Кроме «программ» и данных во FLASH хранятся «приложения» — от «программ» они отличаются, тем, что распределены по страницам FLASH-памяти и могут занимать больше памяти, чем процессор в состоянии адресовать.
Полезная вещь, которую стоит иметь на калькуляторе — это оболочка, приложение содержащее библиотеки, загрузчик бинарных программ, и графический интерфейс. Лично я пользуюсь Doors CS. Ее плюс в том, что она содержит библиотеки всех популярных оболочек, а минус — в большом размере. Создатели Doors CS попытались сделать интерфейс похожим на Windows, для чего TI-84+ подходит, на мой взгляд, плохо. Впрочем, использовать GUI Door CS необязательно, она автоматический интегрируется в ОС.
Некоторые интересные программы для TI-84+:
«Роботы» на AXE
Погоняв птичку и попрыгав через порталы, я решил сам что-нибудь написать. В качестве языка я выбрал AXE, а в качестве задачи — BSDшных роботов. Получилось коряво: большую часть памяти занимают массивы. Но как сделать компактнее, я так и не придумал.
Программа состоит из пяти «частей»: инициализации, отрисовки, создания нового уровня, «интеллекта» роботов и главного цикла. Отличия от оригинала: игровое поле 23x15 (каждый спрайт 4x4 + бордюр), меньше роботов из-за уменьшения игрового поля, нет подсчета очков.
Инициализация
.ROBOTS
.IMAGES
.EMPTY
.ROBOT
.JUNK
.PLAYER
.STONE
.STONE
[000000000000000020702000000000007070700000000000702070000000000020707000000000002070700000000000]->Pic1
.FIELD
Buff(23*15)->Str1
Buff(23*15)->Str2
Buff(23*15)->Str3
1->L
Здесь все просто. Сначала идет заголовок с именем программы, потом шестнадцатиричная строка со спрайтами. AXE поддерживает спрайты 8x8, а у меня — 4x4, так что большая часть этогй стоки — нули. Также, я использовал «пустой» спрайт и повторил спрайт с могилой два раза для упрощения отрисовки.
Дальше инициализируются массивы с игровым полем. Str1 — главное игровое поле, Str2 — вспомогательное, Str3 — копия старого игрового поля для предотвращения ошибок игрока. Переменная L — уровень.
Обратите внимание на особенность синтаксиса AXE (и TI-BASICа): присваивание происходит справа налево.
Создание нового уровня
Lbl NEWLVL
Fill(Str1,15*23,0)
L*5->R
For(R)
rand^15->I
rand^23->J
I*23+J+Str1->P
1->{P}
End
rand^23->X
rand^15->Y
3->{Y*23+X+Str1}
Return
Сперва игровое поле обнуляется, затем на нем размещаются роботы (спрайт за номером 1). В оригинале на каждом уровне появлялось по десять роботов, у меня игровое поле меньше, поэтому я выбрал пять. Роботов может быть меньше, чем L*5 из-за наложения, я решил считать это фитчей. Затем выбирается позиция игрока (спрайт номер 3), опять же: в отличии от оригинала, где помереть можно на нулевом ходу, у меня игрок «убивает» робота, если приземлится на него в начале игры.
Отрисовка
Lbl DRAW
ClrDraw
VLine(0,0,62)
VLine(94,0,62)
HLine(0,0,94)
HLine(62,0,94)
For(I,0,22)
For(J,0,14)
Pt-On(I*4+1,J*4+2,{J*23+I+Str1}*8+Pic1)
End:End
DispGraph
Return
Здесь, опять же, все просто. Пожалуй, стоит сказать, что двоеточие — разделитель операторов, а Pt-On — отрисовка спрайта. Lbl — метка, от LaBeL. Фигурные скобки в AXE — значение байта по заданному адресу.
Передвижение роботов
Lbl STEP
0->R
Fill(Str2,23*15,0)
For(I,0,22):For(J,0,14)
J*23+I->A
{A+Str1}->B
If B=2
2->{A+Str2}:End
If B=1:
R+1->R
(J+SIGN(J,Y))*23+I+SIGN(I,X)+Str2->A
{A}+({A}<2)->{A}
End
End:End
Copy(Str2,Str1,23*15)
Y*23+X+Str1->A
{A}+3->{A}
Return
Lbl SIGN
([r1]<[r2])-([r1]>[r2])
Return
Здесь начинаются грязные хаки. Программа сканирует игровое поле, ища роботов, и передвигает их в сторону игрока. Операции сравнения в AXE возвращают единицу, поэтому {A}+({A}<2)->{A} инкременирует A если и только если А меньше двух. Затем отмечается позиция игрока.
Главный цикл
While 1
0->G
NEWLVL()
DRAW()
While R>0
X->M:Y->N
Copy(Str1,Str3,23*15)
If G<200
getKey^^r->G
ReturnIf G=9
G^100->K
End
If K=47
rand^23->X
rand^15->Y
STEP()
DRAW()
If {A}!=3
getKey^^r:Return
End
Else
(K=51)+(K=48)+(K=45)-(K=49)-(K=46)-(K=43)->I
((K>=43) and (K<=45))-((K>=49) and (K<=51))->J
If ((J+Y)<15) and ((J+Y)>=0) and ((I+X)<23) and ((I+X)>=0)
X+I->X:Y+J->Y
Else:0->G
End
STEP()
If {A}!=3
M->X:N->Y
Copy(Str3,Str1,23*15)
0->G
End
End
Собственно, можете наблюдать ухудшение качества кода: главный цикл мне пришлось переписывать три раза (заповедь «делай бекапы» для TI-84+ как никогда актуальна), и под конец я устал. В результате получилось то, что получилось.
Пояснения:
R — количество роботов на игровом поле. Если роботов больше не осталось — значит уровень пройден.
getKey^^r ждет нажатия клавиши, затем возвращает скан-код. Реализуя свой преступный замысел, я использовал тот факт, что модификатор 2nd прибавляет к скан-кодам цифровых клавиш сотню. Таким образом, новая клавиша не считывается если 2nd была нажата — таким образом реализуется повторение. Истинный скан-код сохраняется в переменную G, и если G больше 200, новая команда не считывается.
Скан-код 147 — клавиша «5». В игре это команда телепортации. Телепортация — единственный ход, на котором игра может закончится, поэтому он обрабатывается отдельно.
Скан-коды 43…45, 49…51, 46 и 48 — это цифровые клавиши, по ним игрок двигается. Убить игрока во время движения нельзя, так что состояние игры сохраняется до хода и восстанавливается если игрок с кем-то столкнулся. При этом, G обнуляется.
Как это все выглядит в собранном виде:
.ROBOTS
.IMAGES
.EMPTY
.ROBOT
.JUNK
.PLAYER
.STONE
.STONE
[000000000000000020702000000000007070700000000000702070000000000020707000000000002070700000000000]->Pic1
.FIELD
Buff(23*15)->Str1
Buff(23*15)->Str2
Buff(23*15)->Str3
1->L
While 1
0->G
NEWLVL()
DRAW()
While R>0
X->M:Y->N
Copy(Str1,Str3,23*15)
If G<200
getKey^^r->G
ReturnIf G=9
G^100->K
End
If K=47
rand^23->X
rand^15->Y
STEP()
DRAW()
If {A}!=3
getKey^^r:Return
End
Else
(K=51)+(K=48)+(K=45)-(K=49)-(K=46)-(K=43)->I
((K>=43) and (K<=45))-((K>=49) and (K<=51))->J
If ((J+Y)<15) and ((J+Y)>=0) and ((I+X)<23) and ((I+X)>=0)
X+I->X:Y+J->Y
Else:0->G
End
STEP()
If {A}!=3
M->X:N->Y
Copy(Str3,Str1,23*15)
0->G
End
End
DRAW()
End
L+1->L
End
Return
Lbl DRAW
ClrDraw
VLine(0,0,62)
VLine(94,0,62)
HLine(0,0,94)
HLine(62,0,94)
For(I,0,22)
For(J,0,14)
Pt-On(I*4+1,J*4+2,{J*23+I+Str1}*8+Pic1)
End:End
DispGraph
Return
Lbl NEWLVL
Fill(Str1,15*23,0)
L*5->R
For(R)
rand^15->I
rand^23->J
I*23+J+Str1->P
1->{P}
End
rand^23->X
rand^15->Y
3->{Y*23+X+Str1}
Return
Lbl STEP
0->R
Fill(Str2,23*15,0)
For(I,0,22):For(J,0,14)
J*23+I->A
{A+Str1}->B
If B=2
2->{A+Str2}:End
If B=1:
R+1->R
(J+SIGN(J,Y))*23+I+SIGN(I,X)+Str2->A
{A}+({A}<2)->{A}
End
End:End
Copy(Str2,Str1,23*15)
Y*23+X+Str1->A
{A}+3->{A}
Return
Lbl SIGN
([r1]<[r2])-([r1]>[r2])
Return
Спасибо за внимание!
Ссылки:
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.
Комментариев нет:
Отправить комментарий