Last active
March 8, 2022 08:00
-
-
Save Siguza/2ef468405d97c559fc88154585d63cf0 to your computer and use it in GitHub Desktop.
2048 for your calculator! :D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* t2048.c - 2048 for some TI calculators | |
* | |
* Copyright (c) 2014 Siguza | |
* | |
* Tested on TI-89 Titanium only. According to headers, it should work on TI-92 and Voyage 200 as well, but no promises. | |
* To be compiled with ti-gcc - as far as I remember, TI's own C compiler can't handle this. | |
* | |
* Licensed under MIT, i.e. feel free to use and redistribute at will, but I'd appreciate some credit. :) | |
*/ | |
#define USE_TI89 | |
#define USE_TI92PLUS | |
#define USE_V200 | |
#define OPTIMIZE_ROM_CALLS | |
#define MIN_AMS 100 | |
#define SAVE_SCREEN | |
#include <alloc.h> | |
#include <estack.h> | |
#include <graph.h> | |
#include <gray.h> | |
#include <kbd.h> | |
#include <limits.h> | |
#include <math.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <system.h> | |
const SCR_RECT *TILE_AREA = &(SCR_RECT){{0, 0, 103, 99}}; | |
void *screen; | |
unsigned char *field; | |
unsigned char *tmp; | |
unsigned char *tmp2; | |
unsigned char *fdir; | |
char *str; | |
unsigned long score; | |
unsigned long highscore; | |
unsigned char gameover; | |
unsigned char animate; | |
unsigned char gmode; | |
int round(float); | |
void init(); | |
void draw(); | |
void drawTile(unsigned int, unsigned int, unsigned int); | |
void drawBox(unsigned int, unsigned int); | |
void fillBox(unsigned int, unsigned int); | |
unsigned char load(); | |
void save(); | |
void move(signed int, signed int, signed int); | |
unsigned char nextTileIndex(); | |
void newTile(unsigned char); | |
void intToStr(unsigned long); | |
void _main() | |
{ | |
ClrScr(); | |
DrawStr(35, 1, "TI-2048, v1.1.1", A_NORMAL); | |
DrawStr(20, 11, "(Unofficial/Fanmade)", A_NORMAL); | |
DrawStr(44, 21, "by Siguza", A_NORMAL); | |
DrawStr(23, 36, "Arrows - Move tiles", A_NORMAL); | |
DrawStr(32, 46, "Clear - New game", A_NORMAL); | |
DrawStr(35, 56, "Home/Esc - Exit", A_NORMAL); | |
DrawStr(11, 66, "Mode - Toggle animation", A_NORMAL); | |
DrawStr(5, 76, "2nd Mode - Change shading", A_NORMAL); | |
DrawStr(5, 91, "Press any key to continue", A_NORMAL); | |
switch(ngetchx()) | |
{ | |
case 264: // Esc | |
case 277: // Home | |
return; | |
} | |
randomize(); | |
field = malloc(16); | |
tmp = malloc(16); | |
tmp2 = malloc(16); | |
fdir = malloc(16); | |
str = malloc(10); | |
screen = malloc(LCD_SIZE); | |
if(!screen) | |
{ | |
free(field); | |
free(tmp); | |
free(tmp2); | |
free(fdir); | |
free(str); | |
return; | |
} | |
if(load()) | |
{ | |
highscore = 0; | |
animate = 1; | |
gmode = 0; | |
init(); | |
} | |
if(gmode < 2) | |
{ | |
PortSet(screen, 239, 127); | |
} | |
else | |
{ | |
GrayOn(); | |
} | |
draw(); | |
while(1) | |
{ | |
idle(); | |
switch(ngetchx()) | |
{ | |
case 18: // 2nd + Mode | |
gmode = (gmode + 1) % 3; | |
if(gmode == 0) | |
{ | |
GrayOff(); | |
PortSet(screen, 239, 127); | |
} | |
else if(gmode == 2) | |
{ | |
PortRestore(); | |
GrayOn(); | |
} | |
draw(); | |
break; | |
case 263: // Clear | |
init(); | |
draw(); | |
break; | |
case 264: // Esc | |
case 277: // Home | |
save(); | |
if(GrayCheckRunning()) | |
{ | |
GrayOff(); | |
} | |
else | |
{ | |
PortRestore(); | |
} | |
free(field); | |
free(tmp); | |
free(tmp2); | |
free(fdir); | |
free(str); | |
free(screen); | |
return; | |
case 266: // Mode | |
animate = !animate; | |
break; | |
case 272: // F5 | |
PortRestore(); | |
if(gmode < 2) | |
{ | |
PortSet(screen, 239, 127); | |
} | |
draw(); | |
break; | |
case 337: // Up | |
move(1, 0, 4); | |
break; | |
case 340: // Down | |
move(1, 12, -4); | |
break; | |
case 338: // Left | |
move(4, 0, 1); | |
break; | |
case 344: // Right | |
move(4, 3, -1); | |
break; | |
} | |
} | |
} | |
int round(float x) | |
{ | |
return (int)(x + 0.5); | |
} | |
void init() | |
{ | |
unsigned char i, r; | |
for(i = 0; i < 16; i++) | |
{ | |
field[i] = 0; | |
} | |
i = random(16); | |
r = random(15); | |
if(r >= i) | |
{ | |
r++; | |
} | |
newTile(i); | |
newTile(r); | |
score = 0; | |
gameover = 0; | |
} | |
// Screen is 160x100 | |
void draw() | |
{ | |
unsigned int i, x, y; | |
if(gmode >= 2) | |
{ | |
GraySetAMSPlane(LIGHT_PLANE); | |
ClrScr(); | |
GraySetAMSPlane(DARK_PLANE); | |
} | |
ClrScr(); | |
DrawLine(104, 0, 104, 99, A_NORMAL); | |
DrawLine(104, 32, 159, 32, A_NORMAL); | |
DrawLine(104, 67, 159, 67, A_NORMAL); | |
DrawStr(120, 12, "2048", A_NORMAL); | |
DrawStr(117, 40, "Score", A_NORMAL); | |
DrawStr(105, 74, "Highscore", A_NORMAL); | |
intToStr(score); | |
DrawStr(132 - strlen(str) * 3, 52, str, A_NORMAL); | |
intToStr(highscore); | |
DrawStr(132 - strlen(str) * 3, 86, str, A_NORMAL); | |
for(y = 0; y < 4; y++) | |
{ | |
for(x = 0; x < 4; x++) | |
{ | |
i = field[4 * y + x]; | |
if(i > 0) | |
{ | |
drawTile(x * 26, y * 25, i); | |
} | |
} | |
} | |
if(gameover) | |
{ | |
if(gmode >= 2) | |
{ | |
GraySetAMSPlane(LIGHT_PLANE); | |
ScrRectFill(&(SCR_RECT){{24, 45, 79, 53}}, TILE_AREA, A_REVERSE); | |
GraySetAMSPlane(DARK_PLANE); | |
} | |
ScrRectFill(&(SCR_RECT){{24, 45, 79, 53}}, TILE_AREA, A_REVERSE); | |
DrawLine(23, 44, 80, 44, A_NORMAL); | |
DrawLine(23, 54, 80, 54, A_NORMAL); | |
DrawLine(23, 45, 23, 53, A_NORMAL); | |
DrawLine(80, 45, 80, 53, A_NORMAL); | |
DrawStr(25, 46, "Game Over", A_NORMAL); | |
} | |
if(gmode < 2) | |
{ | |
LCD_restore(screen); | |
} | |
} | |
void drawTile(unsigned int x, unsigned int y, unsigned int i) | |
{ | |
intToStr((long)pow(2, i)); | |
switch(gmode) | |
{ | |
case 2: | |
if(i <= 4) | |
{ | |
DrawStr(x + 12 - strlen(str) * 3, y + 8, str, A_NORMAL); | |
break; | |
} | |
if(i <= 8) | |
{ | |
GraySetAMSPlane(LIGHT_PLANE); | |
} | |
fillBox(x, y); | |
DrawStr(x + 12 - strlen(str) * 3, y + 8, str, A_REVERSE); | |
if(i <= 8) | |
{ | |
GraySetAMSPlane(DARK_PLANE); | |
} | |
break; | |
case 1: | |
if(i > 6) | |
{ | |
fillBox(x, y); | |
DrawStr(x + 12 - strlen(str) * 3, y + 8, str, A_REVERSE); | |
break; | |
} | |
case 0: | |
DrawStr(x + 12 - strlen(str) * 3, y + 8, str, A_NORMAL); | |
break; | |
} | |
drawBox(x, y); | |
} | |
void drawBox(unsigned int x, unsigned int y) | |
{ | |
DrawLine(x + 2, y, x + 22, y, A_NORMAL); | |
DrawLine(x + 2, y + 23, x + 22, y + 23, A_NORMAL); | |
DrawLine(x, y + 2, x, y + 21, A_NORMAL); | |
DrawLine(x + 24, y + 2, x + 24, y + 21, A_NORMAL); | |
DrawPix(x + 1, y + 1, A_NORMAL); | |
DrawPix(x + 1, y + 22, A_NORMAL); | |
DrawPix(x + 23, y + 1, A_NORMAL); | |
DrawPix(x + 23, y + 22, A_NORMAL); | |
} | |
void fillBox(unsigned int x, unsigned int y) | |
{ | |
ScrRectFill(&(SCR_RECT){{x + 1, y + 1, x + 23, y + 22}}, &(SCR_RECT){{0, 0, 103, 99}}, A_NORMAL); | |
} | |
unsigned char load() | |
{ | |
unsigned char c; | |
FILE *file = fopen("t2048dat", "rb"); | |
if(file == NULL) | |
{ | |
return 1; | |
} | |
if(fgetc(file) != 1) | |
{ | |
fclose(file); | |
return 1; | |
} | |
fread(field, 16, 1, file); | |
c = (unsigned char)fgetc(file); | |
gameover = c & 1; | |
animate = (c & 2) >> 1; | |
gmode = (c & 12) >> 2; | |
fread(&score, sizeof(unsigned long), 1, file); | |
fread(&highscore, sizeof(unsigned long), 1, file); | |
fclose(file); | |
return 0; | |
} | |
void save() | |
{ | |
FILE *file = fopen("t2048dat", "wb"); | |
if(file == NULL) | |
{ | |
return; | |
} | |
fputc(1, file); | |
fwrite(field, 16, 1, file); | |
fputc(gameover + (animate << 1) + (gmode << 2), file); | |
fwrite(&score, sizeof(unsigned long), 1, file); | |
fwrite(&highscore, sizeof(unsigned long), 1, file); | |
fputc(DATA_TAG, file); | |
fclose(file); | |
} | |
void move(signed int fu, signed int v0, signed int dv) | |
{ | |
signed int c, i, u, v; | |
float x, y; | |
unsigned char j, moved = 0; | |
for(c = 0; c < 16; c++) | |
{ | |
tmp[c] = field[c]; | |
fdir[c] = 4; | |
} | |
for(u = 0; u < 4 * fu; u += fu) | |
{ | |
c = 0; | |
for(v = 0; v < 4; v++) | |
{ | |
i = u + v0 + (v * dv); | |
if(field[i] > 0) | |
{ | |
if(v != c) | |
{ | |
field[u + v0 + (c * dv)] = field[i]; | |
fdir[i] += (v - c) * ((dv > 0) ? 1 : -1); | |
moved |= 1; | |
} | |
c++; | |
} | |
} | |
if(c > 0) | |
{ | |
for(; c < 4; c++) | |
{ | |
field[u + v0 + (c * dv)] = 0; | |
} | |
} | |
for(v = 0; v < 3; v++) | |
{ | |
i = u + v0 + (v * dv); | |
if((field[i] != 0) && (field[i] == field[i + dv])) | |
{ | |
score += pow(2, ++field[i]); | |
fdir[i + dv] += (dv > 0) ? 1 : -1; | |
for(c = v + 1; c < 3; c++) | |
{ | |
i = u + v0 + (c * dv); | |
field[i] = field[i + dv]; | |
fdir[i + dv] += (dv > 0) ? 1 : -1; | |
} | |
field[u + v0 + (3 * dv)] = 0; | |
moved |= 1; | |
} | |
} | |
} | |
if(!moved) | |
{ | |
return; | |
} | |
j = nextTileIndex(); | |
if(animate) | |
{ | |
OSFreeTimer(6); | |
OSRegisterTimer(6, 8); | |
for(c = 1; c < 8; c++) | |
{ | |
if(GrayCheckRunning()) | |
{ | |
GraySetAMSPlane(LIGHT_PLANE); | |
ScrRectFill(&(SCR_RECT){{0, 0, 103, 99}}, &(SCR_RECT){{0, 0, 103, 99}}, A_REVERSE); | |
GraySetAMSPlane(DARK_PLANE); | |
} | |
ScrRectFill(&(SCR_RECT){{0, 0, 103, 99}}, &(SCR_RECT){{0, 0, 103, 99}}, A_REVERSE); | |
v = (j % 4) * 26; | |
u = (j / 4) * 25; | |
i = round(1.5 * ((float)c)); | |
DrawClipRect(&(WIN_RECT){v + 12 - i, u + 12 - i, v + 13 + i, u + 12 + i}, TILE_AREA, A_NORMAL | B_NORMAL); | |
for(u = 0; u < 4; u++) | |
{ | |
for(v = 0; v < 4; v++) | |
{ | |
i = tmp[(4 * u) + v]; | |
if(i > 0) | |
{ | |
x = v; | |
y = u; | |
if(fu == 1) | |
{ | |
y -= ((float)(fdir[(4 * u) + v] - 4)) * (((float)c) / 8.0); | |
} | |
else | |
{ | |
x -= ((float)(fdir[(4 * u) + v] - 4)) * (((float)c) / 8.0); | |
} | |
drawTile(round(x * 26), round(y * 25), i); | |
} | |
} | |
} | |
i = 7 - OSTimerCurVal(6); | |
if(i > c) | |
{ | |
c = i + 1; | |
} | |
if(gmode < 2) | |
{ | |
LCD_restore(screen); | |
} | |
} | |
OSFreeTimer(6); | |
} | |
if(score > highscore) | |
{ | |
highscore = score; | |
} | |
newTile(j); | |
for(c = 0; c < 16; c++) | |
{ | |
if(field[c] == 0) | |
{ | |
goto okai; | |
} | |
} | |
for(c = 0; c < 16; c++) | |
{ | |
if(((c % 4 != 3) && (field[c] == field[c + 1])) || ((c < 12) && (field[c] == field[c + 4]))) | |
{ | |
goto okai; | |
} | |
} | |
gameover = 1; | |
okai: | |
draw(); | |
} | |
unsigned char nextTileIndex() | |
{ | |
unsigned char i; | |
unsigned char c = 0; | |
for(i = 0; i < 16; i++) | |
{ | |
if(field[i] == 0) | |
{ | |
tmp2[c++] = i; | |
} | |
} | |
return tmp2[random(c)]; | |
} | |
void newTile(unsigned char i) | |
{ | |
field[i] = random(10) == 0 ? 2 : 1; | |
} | |
void intToStr(unsigned long x) | |
{ | |
unsigned char c, i; | |
for(c = 1; c < 9; c++) | |
{ | |
if((x / ((long)pow(10, c))) == 0) | |
{ | |
break; | |
} | |
} | |
for(i = 0; i < c; i++) | |
{ | |
str[c - i - 1] = '0' + (x % 10); | |
x /= 10; | |
} | |
str[c] = '\0'; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment