···23This is a card solitaire game for the TI-84 Plus CE and TI-83 Premium CE.
45-## How to Play
6-7-This is calculation. There are four foundation piles, each of which start with one card at the beginning of the game: the first pile starts with an ace, the second with a two, and so on. Each foundation is built on in multiples of the initial card (i.e. its position). So the first pile is built A, 2, 3, 4...; the second 2, 4, 6, 8...; the third 3, 6, 9, Q...; and the fourth 4, 8, Q, 3.... The card after K is A, though once a foundation has a king played to it, that is its thirteenth card and it is now complete, and no more cards may be played to it.
8-9-Additionally, there are four tableau piles beneath the foundation piles. A card can always be placed on the top of a tableau pile, but it can only be removed from the pile by playing it to a foundation. Therefore, the clever placement of cards in the tableau is key to victory.
10-11-Cards may be drawn one at a time from the deck by pressing `alpha` or picked up from the selected tableau pile with `2nd`. The card held in the player's hand may be played to the selected pile with `2nd` or returned to its origin pile in the tableau with `clear`. When interacting with tableau and foundation piles, the selected pile is marked with the cursor, which can be moved with the arrow keys. Moving the cursor all the way to the left or right causes it to wrap around from the tableau to the foundations or vice versa.
12-13-Once all four foundations are completed, the game is over. The game can be reset at any point by pressing `del`.
14-15## Roadmap
1617-As more and more of the core game logic is made generic, the goal is to spin out the rules of the game to an interpreted script, and then allow the play of multiple solitaire games with one program and set of graphical assets.
1819## Dependencies
20
···23This is a card solitaire game for the TI-84 Plus CE and TI-83 Premium CE.
400000000005## Roadmap
67+This branch is still heavily under development! Check back later for more information.
89## Dependencies
10
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#include "drawing.h"
18-19-#include <graphx.h>
20-#include <time.h>
21-#include "gfx/gfx.h"
22-#include "variables.h"
23-#include "ops.h"
24-25-// these are used for placing pips on cards
26-const unsigned char segments[] = {
27- 0x01, 0x24, 0x25, 0x48, 0x49, 0x4a, 0xa8, 0x90, 0x91, 0xb4
28-};
29-const unsigned char pipCode[] = {
30- 0x04, 07, 02, 15, 02, 07, 12, 15, 12,
31- 0x02, 07, 07, 15, 07,
32- 0x01, 11, 07,
33- 0x84, 12, 29, 20, 29, 12, 39, 20, 39,
34- 0x82, 12, 34, 20, 34,
35- 0x81, 16, 34,
36- 0x02, 07, 17, 15, 17,
37- 0x01, 11, 17
38-};
39-40-void drawMask(const unsigned char *data, unsigned char rows, unsigned int x, unsigned char y)
41-{
42- unsigned char yy = y;
43-44- for (unsigned char row = 0; row < rows; row++)
45- {
46- unsigned char row_data = data[row];
47-48- for (unsigned int xx = x; xx < x + 8; xx++)
49- {
50- if (row_data & 0x80)
51- {
52- gfx_SetPixel(xx, yy);
53- }
54-55- row_data <<= 1;
56- }
57-58- yy++;
59- }
60-}
61-62-void drawMaskInverted(const unsigned char *data, unsigned char rows, unsigned int x, unsigned char y)
63-{
64- unsigned char yy = y - rows;
65-66- for (unsigned char row = 1; row <= rows; row++)
67- {
68- unsigned char row_data = data[rows - row];
69-70- for (unsigned int xx = x - 8; xx < x; xx++)
71- {
72- if (row_data & 0x01)
73- {
74- gfx_SetPixel(xx, yy);
75- }
76-77- row_data >>= 1;
78- }
79-80- yy++;
81- }
82-}
83-84-unsigned int getCursorX()
85-{
86- if (cursorStack < NUM_FREECELLS) return FC_HPOS + cursorStack * (CARD_WIDTH + CARD_SPACING);
87- else return TABL_HPOS + (cursorStack - NUM_FREECELLS) * (CARD_WIDTH + CARD_SPACING);
88-}
89-90-unsigned char getCursorY()
91-{
92- if (cursorStack < NUM_FREECELLS) return FC_VPOS;
93- else return TABL_VPOS + cursorIndex * CARD_VOFFSET;
94-}
95-96-void drawCursor()
97-{
98- const unsigned int X = getCursorX();
99- const unsigned char Y = getCursorY();
100-101- gfx_SetColor(cursorMode == SELECT ? BLACK_COLOR : RED_COLOR);
102-103- drawMask(selcorner_tile_0_data, 6, X - 2, Y - 2);
104- drawMask(selcorner_tile_1_data, 6, X + CARD_WIDTH - 4, Y - 2);
105- drawMaskInverted(selcorner_tile_1_data, 6, X + 4, Y + CARD_HEIGHT + 2);
106- drawMaskInverted(selcorner_tile_0_data, 6, X + CARD_WIDTH + 2, Y + CARD_HEIGHT + 2);
107-}
108-109-#include <debug.h>
110-111-void drawCardBlank(unsigned int x, unsigned char y)
112-{
113- gfx_SetColor(CARD_COLOR);
114- gfx_HorizLine(x + 1, y, CARD_WIDTH - 2);
115- gfx_HorizLine(x + 1, y + CARD_HEIGHT - 1, CARD_WIDTH - 2);
116- gfx_FillRectangle(x, y + 1, CARD_WIDTH, CARD_HEIGHT - 2);
117-118- gfx_SetColor(BLACK_COLOR);
119- gfx_SetPixel(x, y + CARD_HEIGHT - 1);
120- gfx_SetPixel(x + CARD_WIDTH - 1, y + CARD_HEIGHT - 1);
121- gfx_HorizLine(x + 1, y + CARD_HEIGHT, CARD_WIDTH - 2);
122-}
123-124-void drawCard(card_t toDraw, unsigned int x, unsigned char y, bool useCutoff)
125-{
126- if (!(toDraw & CARD_EXISTS)) return;
127-128- const unsigned char cardNumber = toDraw & CARD_NUMBER;
129- const unsigned char cardSuit = (toDraw & CARD_SUIT) >> 4;
130-131- drawCardBlank(x, y);
132-133- if (cardNumber >= 10)
134- {
135- // this is a face card
136- const unsigned char tileIndex = cardSuit * 3 + cardNumber - 10;
137- const gfx_sprite_t *faceSprite = faces_tiles[tileIndex];
138-139- gfx_TempSprite(bottomSprite, CARD_FACE_WIDTH, CARD_FACE_HEIGHT);
140- gfx_RotateSpriteHalf(faceSprite, bottomSprite);
141-142- gfx_Sprite(faceSprite, x + CARD_FACE_HOFFSET, y + CARD_FACE_VOFFSET);
143- gfx_Sprite(bottomSprite, x + CARD_FACE_HOFFSET, y + CARD_HEIGHT / 2);
144-145- gfx_SetColor(BLACK_COLOR);
146- gfx_VertLine(x + 3, y + 12, 26);
147- gfx_VertLine(x + 23, y + 3, 26);
148- gfx_HorizLine(x + 6, y + 3, 17);
149- gfx_HorizLine(x + 4, y + 37, 17);
150-151- if (toDraw & CARD_RED) gfx_SetColor(RED_COLOR);
152- }
153- else
154- {
155- // this is a number card (A-10) and should have pips
156- dbg_printf("drawing pips for card with value %u at (%u, %u)...\n", cardNumber, x, y);
157- gfx_SetColor((toDraw & CARD_RED) ? RED_COLOR : BLACK_COLOR);
158- const unsigned char *pipMask = medium_suits_tiles_data[cardSuit];
159- dbg_printf("located pip mask at %p...\n", pipMask);
160- const unsigned char *codePtr = pipCode;
161- for (unsigned char pipMap = segments[cardNumber]; pipMap != 0x00; pipMap <<= 1)
162- {
163- dbg_printf("current pip map is %x...\n", pipMap);
164- unsigned char counter = *codePtr & 0x0f;
165- if (pipMap & 0x80)
166- {
167- dbg_printf("executing instruction at %p...\n", codePtr);
168- const bool isNegative = *codePtr & 0x80;
169- if (isNegative) dbg_printf("pip is inverted...\n");
170- codePtr++;
171-172- while (counter-- > 0)
173- {
174- if (isNegative) drawMaskInverted(pipMask, 6, x + codePtr[0], y + codePtr[1]);
175- else drawMask(pipMask, 6, x + codePtr[0], y + codePtr[1]);
176-177- codePtr += 2;
178- }
179- }
180- else
181- {
182- codePtr += counter * 2 + 1;
183- }
184- }
185- }
186-187- // no matter what, we need numerals and suit icons
188- drawMask(numerals_tiles_data[cardNumber], 5, x + CARD_NUMERAL_HOFFSET, y + CARD_NUMERAL_VOFFSET);
189- drawMask(small_suits_tiles_data[cardSuit], 4, x + CARD_FSUIT_HOFFSET, y + CARD_FSUIT_VOFFSET);
190-191- if (!useCutoff)
192- {
193- drawMaskInverted(numerals_tiles_data[cardNumber], 5, x + CARD_WIDTH - CARD_NUMERAL_HOFFSET, y + CARD_HEIGHT - CARD_NUMERAL_VOFFSET);
194- drawMaskInverted(small_suits_tiles_data[cardSuit], 4, x + CARD_WIDTH - CARD_FSUIT_HOFFSET, y + CARD_HEIGHT - CARD_FSUIT_VOFFSET);
195- }
196-}
197-198-void drawCardBack(unsigned int x, unsigned char y)
199-{
200- drawCardBlank(x, y);
201-202- gfx_SetColor(BLACK_COLOR);
203- for (unsigned char i = 0; i < 3; i++)
204- {
205- drawMask(card_back_tiles_data[i], 20, x + 8 * i + 1, y + 1);
206- drawMaskInverted(card_back_tiles_data[i], 20, x - 8 * i + 26, y + 40);
207- }
208-209- gfx_VertLine(x + 1, y + 21, 19);
210- gfx_VertLine(x + 25, y + 1, 19);
211-}
212-213-void drawDeck()
214-{
215- if (deckCards == 0) return;
216- unsigned char y = DECK_VPOS;
217- for (unsigned char x = 0; x <= deckCards; x += DECK_CARDS_PER_HEIGHT)
218- {
219- drawCardBack(DECK_HPOS, y);
220- y -= 2;
221- }
222-}
223-224-void drawStack(unsigned char stackIndex)
225-{
226- for (unsigned char j = 0; j < TABL_STACK_SIZE; j++)
227- {
228- unsigned char cardX = TABL_HPOS + stackIndex * (CARD_WIDTH + CARD_SPACING);
229- unsigned char cardY = TABL_VPOS + j * CARD_VOFFSET;
230-231- drawCard(tableau[stackIndex][j], cardX, cardY, tableau[stackIndex][j + 1] & CARD_EXISTS);
232- }
233-}
234-235-void drawBar()
236-{
237- gfx_SetColor(BORDER_COLOR);
238- gfx_FillRectangle(0, 0, GFX_LCD_WIDTH, TOP_BORDER);
239-240- gfx_SetTextFGColor(BLACK_COLOR);
241-242- if (progress < PROGRESS_COMPLETE)
243- {
244- if (cursorMode == SELECT) gfx_PrintStringXY("SELECT", GFX_LCD_WIDTH / 2 - 3 * TEXT_CHAR_WIDTH, SELCARD_DISP_Y);
245- else gfx_PrintStringXY("DROP", GFX_LCD_WIDTH / 2 - 2 * TEXT_CHAR_WIDTH, SELCARD_DISP_Y);
246- }
247-248- else gfx_PrintStringXY("COMPLETE", GFX_LCD_WIDTH / 2 - 4 * TEXT_CHAR_WIDTH, SELCARD_DISP_Y);
249-250- gfx_SetTextXY(NUMWINS_DISP_X, SELCARD_DISP_Y);
251- gfx_PrintUInt(*numWins, 3);
252-}
253-254-void drawFrame(bool drawSelected)
255-{
256- gfx_FillScreen(BKGND_COLOR);
257-258- for (unsigned char i = 0; i < NUM_FREECELLS; i++) drawCard(freeCells[i], FC_HPOS + i * (CARD_WIDTH + CARD_SPACING), FC_VPOS, false);
259- for (unsigned char i = 0; i < NUM_TABLSLOTS; i++) drawStack(i);
260- if (drawSelected && (selectedCard & CARD_EXISTS) && cursorMode == DROP) drawCard(selectedCard, SELCARD_XPOS, SELCARD_YPOS, false);
261-262- drawDeck();
263- drawBar();
264- drawCursor();
265-266- gfx_BlitBuffer();
267-}
268-269-void animateMoveInternal(bool flipX, bool flipY, unsigned int x0, unsigned char y0, unsigned int x1, unsigned char y1, bool faceDown)
270-{
271- gfx_TempSprite(spriteBuffer, CARD_WIDTH, CARD_HEIGHT + 1);
272-273- const unsigned int Dx = x1 - x0;
274- const unsigned char Dy = y1 - y0;
275- const clock_t duration = MOVE_ANIM_LENGTH * (Dy + Dx);
276-277- const clock_t startTime = clock();
278-279- while (true)
280- {
281- const clock_t nowTime = clock();
282- const clock_t elapsed = nowTime - startTime;
283-284- const bool lastIteration = nowTime - startTime > duration;
285-286- const unsigned int dx = lastIteration ? Dx : Dx * elapsed / duration;
287- const unsigned char dy = lastIteration ? Dy : Dy * elapsed / duration;
288-289- const unsigned int cardX = flipX ? x1 - dx : x0 + dx;
290- const unsigned char cardY = flipY ? y1 - dy : y0 + dy;
291-292- gfx_GetSprite(spriteBuffer, cardX, cardY);
293- if (faceDown)
294- {
295- drawCardBack(cardX, cardY);
296- }
297- else
298- {
299- drawCard(selectedCard, cardX, cardY, false);
300- }
301- gfx_BlitBuffer();
302- gfx_Sprite(spriteBuffer, cardX, cardY);
303-304- if (lastIteration) break;
305- }
306-}
307-308-void animateMove(unsigned int x0, unsigned char y0, unsigned int x1, unsigned char y1, bool faceDown)
309-{
310- const bool flipX = x0 > x1;
311- const bool flipY = y0 > y1;
312-313- if (flipX)
314- {
315- x0 ^= x1;
316- x1 ^= x0;
317- x0 ^= x1;
318- }
319- if (flipY)
320- {
321- y0 ^= y1;
322- y1 ^= y0;
323- y0 ^= y1;
324- }
325-326- drawFrame(false);
327- animateMoveInternal(flipX, flipY, x0, y0, x1, y1, faceDown);
328-}
329-330-void animateDeal()
331-{
332- gfx_FillScreen(BKGND_COLOR);
333- drawDeck();
334- drawBar();
335- gfx_BlitBuffer();
336-337- for (unsigned char i = NUM_FREECELLS - 1; i < NUM_FREECELLS; i--)
338- {
339- selectedCard = freeCells[i];
340-341- const unsigned int targetX = FC_HPOS + i * (CARD_WIDTH + CARD_SPACING);
342-343- animateMoveInternal(false, false, DECK_HPOS, DECK_VPOS, targetX, FC_VPOS, false);
344- drawCard(selectedCard, targetX, FC_VPOS, false);
345- }
346-347- selectedCard = CARD_EMPTY;
348- drawFrame(false);
349-}
···1+#include "drawing.h"
2+#include "gfx/gfx.h"
3+#include "card.h"
4+#include <graphx.h>
5+6+#define CARD_WIDTH 27
7+#define CARD_HEIGHT 41
8+#define CARD_NUMERAL_HOFFSET 1
9+#define CARD_NUMERAL_VOFFSET 1
10+#define CARD_FSUIT_HOFFSET 1
11+#define CARD_FSUIT_VOFFSET 7
12+#define CARD_FACE_HOFFSET 4
13+#define CARD_FACE_VOFFSET 4
14+#define CARD_FACE_WIDTH 19
15+#define CARD_FACE_HEIGHT 17
16+17+#define CARD_COLOR 0
18+#define BLACK_COLOR 1
19+#define RED_COLOR 2
20+21+// these are used for placing pips on cards
22+const uint8_t segments[] = {
23+ 0x01, 0x24, 0x25, 0x48, 0x49, 0x4a, 0xa8, 0x90, 0x91, 0xb4
24+};
25+const uint8_t pip_code[] = {
26+ 0x04, 07, 02, 15, 02, 07, 12, 15, 12,
27+ 0x02, 07, 07, 15, 07,
28+ 0x01, 11, 07,
29+ 0x84, 12, 29, 20, 29, 12, 39, 20, 39,
30+ 0x82, 12, 34, 20, 34,
31+ 0x81, 16, 34,
32+ 0x02, 07, 17, 15, 17,
33+ 0x01, 11, 17
34+};
35+36+void draw_mask(const uint8_t *data, uint8_t rows, uint24_t x, uint8_t y)
37+{
38+ uint8_t yy = y;
39+40+ for (uint8_t row = 0; row < rows; row++)
41+ {
42+ uint8_t row_data = data[row];
43+44+ for (uint24_t xx = x; xx < x + 8; xx++)
45+ {
46+ if (row_data & 0x80)
47+ {
48+ gfx_SetPixel(xx, yy);
49+ }
50+51+ row_data <<= 1;
52+ }
53+54+ yy++;
55+ }
56+}
57+58+void draw_mask_inverted(const uint8_t *data, uint8_t rows, uint24_t x, uint8_t y)
59+{
60+ uint8_t yy = y - rows;
61+62+ for (uint8_t row = 1; row <= rows; row++)
63+ {
64+ uint8_t row_data = data[rows - row];
65+66+ for (uint24_t xx = x - 8; xx < x; xx++)
67+ {
68+ if (row_data & 0x01)
69+ {
70+ gfx_SetPixel(xx, yy);
71+ }
72+73+ row_data >>= 1;
74+ }
75+76+ yy++;
77+ }
78+}
79+80+void draw_card_blank(uint24_t x, uint8_t y)
81+{
82+ gfx_SetColor(CARD_COLOR);
83+ gfx_HorizLine(x + 1, y, CARD_WIDTH - 2);
84+ gfx_HorizLine(x + 1, y + CARD_HEIGHT - 1, CARD_WIDTH - 2);
85+ gfx_FillRectangle(x, y + 1, CARD_WIDTH, CARD_HEIGHT - 2);
86+87+ gfx_SetColor(BLACK_COLOR);
88+ gfx_SetPixel(x, y + CARD_HEIGHT - 1);
89+ gfx_SetPixel(x + CARD_WIDTH - 1, y + CARD_HEIGHT - 1);
90+ gfx_HorizLine(x + 1, y + CARD_HEIGHT, CARD_WIDTH - 2);
91+}
92+93+void draw_card(uint24_t x, uint8_t y, card_t card)
94+{
95+ if (!EXISTS(card)) return;
96+97+ const uint8_t card_num = NUMBER(card);
98+ const uint24_t card_suit = SUIT(card);
99+100+ draw_card_blank(x, y);
101+102+ if (card_num >= 10)
103+ {
104+ // this is a face card
105+ const uint8_t tile_index = card_suit * 3 + card_num - 10;
106+ const gfx_sprite_t *face_sprite = faces_tiles[tile_index];
107+108+ gfx_TempSprite(bottom_sprite, CARD_FACE_WIDTH, CARD_FACE_HEIGHT);
109+ gfx_RotateSpriteHalf(face_sprite, bottom_sprite);
110+111+ gfx_Sprite(face_sprite, x + CARD_FACE_HOFFSET, y + CARD_FACE_VOFFSET);
112+ gfx_Sprite(bottom_sprite, x + CARD_FACE_HOFFSET, y + CARD_HEIGHT / 2);
113+114+ gfx_SetColor(BLACK_COLOR);
115+ gfx_VertLine(x + 3, y + 12, 26);
116+ gfx_VertLine(x + 23, y + 3, 26);
117+ gfx_HorizLine(x + 6, y + 3, 17);
118+ gfx_HorizLine(x + 4, y + 37, 17);
119+120+ if (IS_RED(card)) gfx_SetColor(RED_COLOR);
121+ }
122+ else
123+ {
124+ // this is a number card (A-10) and should have pips
125+ gfx_SetColor(IS_RED(card) ? RED_COLOR : BLACK_COLOR);
126+ const uint8_t *pip_mask = medium_suits_tiles_data[card_suit];
127+ const uint8_t *code_ptr = pip_code;
128+ for (uint8_t pip_map = segments[card_num]; pip_map != 0x00; pip_map <<= 1)
129+ {
130+ uint8_t counter = *code_ptr & 0x0f;
131+ if (pip_map & 0x80)
132+ {
133+ const uint8_t is_negative = *code_ptr & 0x80;
134+ code_ptr++;
135+136+ while (counter-- > 0)
137+ {
138+ if (is_negative) draw_mask_inverted(pip_mask, 6, x + code_ptr[0], y + code_ptr[1]);
139+ else draw_mask(pip_mask, 6, x + code_ptr[0], y + code_ptr[1]);
140+141+ code_ptr += 2;
142+ }
143+ }
144+ else
145+ {
146+ code_ptr += counter * 2 + 1;
147+ }
148+ }
149+ }
150+151+ draw_mask(numerals_tiles_data[card_num], 5, x + CARD_NUMERAL_HOFFSET, y + CARD_NUMERAL_VOFFSET);
152+ draw_mask(small_suits_tiles_data[card_suit], 4, x + CARD_FSUIT_HOFFSET, y + CARD_FSUIT_VOFFSET);
153+ draw_mask_inverted(numerals_tiles_data[card_num], 5, x + CARD_WIDTH - CARD_NUMERAL_HOFFSET, y + CARD_HEIGHT - CARD_NUMERAL_VOFFSET);
154+ draw_mask_inverted(small_suits_tiles_data[card_suit], 4, x + CARD_WIDTH - CARD_FSUIT_HOFFSET, y + CARD_HEIGHT - CARD_FSUIT_VOFFSET);
155+}
156+157+void draw_stack(stack_t *stack)
158+{
159+ uint24_t x = stack->x;
160+ uint8_t y = stack->y;
161+162+ for (uint8_t i = 0; i < stack->max_cards; i++)
163+ {
164+ if (!EXISTS(stack->cards[i])) return;
165+166+ draw_card(x, y, stack->cards[i]);
167+168+ x += stack->dx;
169+ y += stack->dy;
170+ }
171+}
172+173+void draw_stacks()
174+{
175+ for (uint8_t i = 0; i < num_stacks; i++)
176+ {
177+ draw_stack(&stacks[i]);
178+ }
179+}
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+10-86
src/drawing.h
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#ifndef drawing_include_file
18-#define drawing_include_file
19-20-#include <graphx.h>
21-#include <gfx/gfx.h>
22-#include "variables.h"
23-24-#define CARD_HEIGHT 41
25-#define CARD_WIDTH 27
26-#define CARD_SPACING 5
27-#define CARD_VOFFSET 7
28-29-#define CARD_NUMERAL_HOFFSET 1
30-#define CARD_NUMERAL_VOFFSET 1
31-#define CARD_FACE_HOFFSET 4
32-#define CARD_FACE_VOFFSET 4
33-#define CARD_FACE_WIDTH 19
34-#define CARD_FACE_HEIGHT 17
35-#define CARD_FSUIT_HOFFSET 1
36-#define CARD_FSUIT_VOFFSET 7
37-#define LOCK_ICON_VOFFSET 17
38-#define LOCK_ICON_HOFFSET 9
39-#define FC_VPOS 50
40-#define TABL_VPOS 100
41-#define FC_HPOS 98
42-#define TABL_HPOS 98
43-44-#define TEXT_CHAR_WIDTH 8
45-#define SELCARD_DISP_X 5
46-#define SELCARD_DISP_Y 9
47-#define NUMWINS_DISP_X (GFX_LCD_WIDTH - 5 - 3 * TEXT_CHAR_WIDTH)
48-49-#define BKGND_COLOR 3
50-#define BORDER_COLOR 0
51-#define CARD_COLOR 0
52-#define BLACK_COLOR 1
53-#define RED_COLOR 2
54-55-#define TOP_BORDER 25
56-57-#define DECK_VPOS FC_VPOS
58-#define DECK_HPOS (FC_HPOS - CARD_WIDTH - CARD_SPACING)
59-#define DECK_CARDS_PER_HEIGHT 9
60-61-#define SELCARD_XPOS 147
62-#define SELCARD_YPOS 210
63-64-#define MOVE_ANIM_LENGTH 40
65-66-extern gfx_sprite_t* cardSprite[11];
67-68-unsigned int getCursorX();
69-unsigned char getCursorY();
70-void drawCursor();
71-void drawCard(card_t toDraw, unsigned int x, unsigned char y, bool useCutoff);
72-void drawStack(unsigned char stackIndex);
73-void drawBar();
74-void drawFrame(bool drawSelected);
75-void animateMove(unsigned int x0, unsigned char y0, unsigned int x1, unsigned char y1, bool faceDown);
76-void animateDeal();
77-78-#define getOrgX() (TABL_HPOS + (orgStack - NUM_FREECELLS) * (CARD_WIDTH + CARD_SPACING))
79-#define getOrgY() (TABL_VPOS + orgIndex * CARD_VOFFSET)
80-81-#define animateGrab() animateMove(getCursorX(), getCursorY(), SELCARD_XPOS, SELCARD_YPOS, false)
82-#define animateDrop() animateMove(SELCARD_XPOS, SELCARD_YPOS, getCursorX(), getCursorY(), false)
83-#define animateDraw() animateMove(DECK_HPOS, DECK_VPOS, SELCARD_XPOS, SELCARD_YPOS, true)
84-#define animateClear() animateMove(SELCARD_XPOS, SELCARD_YPOS, getOrgX(), getOrgY(), false)
85-86-#endif
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#include "input.h"
18-19-#include <stdlib.h>
20-#include <keypadc.h>
21-#include "variables.h"
22-#include "ops.h"
23-#include "save.h"
24-25-unsigned char down, left, right, up;
26-27-bool doInput()
28-{
29- const bool prevSecond = kb_IsDown(kb_Key2nd);
30- const bool prevAlpha = kb_IsDown(kb_KeyAlpha);
31- const bool prevClear = kb_IsDown(kb_KeyClear);
32-33- kb_Scan();
34-35- up = kb_IsDown(kb_KeyUp) ? up + 1 : 0;
36- down = kb_IsDown(kb_KeyDown) ? down + 1 : 0;
37- left = kb_IsDown(kb_KeyLeft) ? left + 1 : 0;
38- right = kb_IsDown(kb_KeyRight) ? right + 1 : 0;
39-40- const bool select = (kb_IsDown(kb_Key2nd) && !prevSecond);
41- const bool draw = (kb_IsDown(kb_KeyAlpha) && !prevAlpha);
42- const bool clear = (kb_IsDown(kb_KeyClear) && !prevClear);
43-44- if (select)
45- {
46- if (cursorMode == SELECT)
47- {
48- if (canGrabCard())
49- {
50- grabCard();
51- cursorMode = DROP;
52- }
53- }
54- else if (canDropCard())
55- {
56- dropCard();
57- cursorMode = SELECT;
58- }
59- }
60- else if (clear)
61- {
62- if (canClearCard())
63- {
64- clearCard();
65- cursorMode = SELECT;
66- }
67- }
68- else if (draw)
69- {
70- if (cursorMode == SELECT && deckCards > 0)
71- {
72- getNewCard();
73- orgStack = DECK_ORG;
74- cursorMode = DROP;
75- }
76- }
77-78- unsigned char prevCursorStack = cursorStack;
79-80- if (down == 1 || down > HOLD_TIME)
81- {
82- if (cursorStack < NUM_FREECELLS || !(tableau[cursorStack][cursorIndex + 1] & CARD_EXISTS)) cursorIndex++;
83- }
84- else if (left == 1 || left > HOLD_TIME)
85- {
86- if (cursorStack == 0) cursorStack = NUM_FREECELLS + NUM_TABLSLOTS - 1; // wrap
87- else cursorStack--;
88- }
89- else if (right == 1 || right > HOLD_TIME)
90- {
91- if (cursorStack > NUM_FREECELLS + NUM_TABLSLOTS - 2) cursorStack = 0; // wrap
92- else cursorStack++;
93- }
94- else if (up == 1 || up > HOLD_TIME)
95- {
96- if (cursorIndex > 0) cursorIndex--;
97- }
98-99- if (cursorStack < NUM_FREECELLS)
100- {
101- cursorIndex = 0;
102- }
103- else if (cursorMode == DROP)
104- {
105- maxCursorIndex();
106- if (cursorStack >= NUM_FREECELLS && (tableau[cursorStack - NUM_FREECELLS][0] & CARD_EXISTS)) cursorIndex++;
107- }
108- else if (cursorStack != prevCursorStack)
109- {
110- maxCursorIndex();
111- }
112-113- if (kb_IsDown(kb_KeyDel))
114- {
115- deleteSave();
116- start();
117- }
118-119- return !kb_On && progress < 10;
120-}
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#ifndef input_include_file
18-#define input_include_file
19-20-#include <stdbool.h>
21-22-bool doInput();
23-24-#endif
···000000000000000000000000
+30-92
src/main.c
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#include <fileioc.h>
18-#include <stdlib.h>
19-#include <math.h>
20-#include <time.h>
21-#include <sys/rtc.h>
22-#include <keypadc.h>
23-24-#include "variables.h"
25-#include "input.h"
26-#include "drawing.h"
27-#include "save.h"
28-#include "ops.h"
29-30-card_t tableau[NUM_TABLSLOTS][TABL_STACK_SIZE];
31-card_t freeCells[NUM_FREECELLS];
32-unsigned char progress;
33-34-bool run()
35-{
36- start();
37-38- while (doInput())
39- {
40- clock_t frameTimer = clock();
41-42- drawFrame(true);
43-44- while (clock() - frameTimer < FRAME_TIME);
45- }
46-47- if (progress == PROGRESS_COMPLETE)
48- {
49- // this way we know not to try to resume
50- deleteSave();
51-52- drawFrame(true);
53-54- while (kb_AnyKey());
55-56- while (true)
57- {
58- kb_Scan();
59- if (kb_On) return false;
60- if (kb_IsDown(kb_Key2nd) || kb_IsDown(kb_KeyEnter)) return true;
61- }
62- }
63- else
64- {
65- // user exited mid-game
66- save();
67- return false;
68- }
69-}
70-71-int main(void)
72-{
73- srand(rtc_Time());
74-75- gfx_Begin();
76- gfx_SetDrawBuffer();
77- gfx_SetPalette(global_palette, sizeof_global_palette, 0);
78- gfx_SetTransparentColor(3);
79-80- kb_EnableOnLatch();
81- kb_ClearOnLatch();
82-83- loadWins();
84-85- while (run());
86-87- gfx_End();
88-89- kb_ClearOnLatch();
90-91- return 0;
92-}
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#ifndef ops_include_file
18-#define ops_include_file
19-20-#include <stdbool.h>
21-#include "variables.h"
22-23-#define canPress2nd() (cursorMode == SELECT ? canGrabCard() : canDropCard())
24-#define canPressAlpha() (!(selectedCard & CARD_EXISTS))
25-#define canPressClear() (canClearCard())
26-#define canPressDel() (true)
27-28-void start();
29-30-bool canGrabCard();
31-bool canDropCard();
32-bool canClearCard();
33-34-void getNewCard();
35-bool removeFromDeck(card_t toRemove); // returns true iff card not in deck
36-void grabCard();
37-void dropCard();
38-void clearCard();
39-void maxCursorIndex();
40-41-#endif
···00000000000000000000000000000000000000000
-152
src/save.c
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#include "save.h"
18-19-#include <sys/rtc.h>
20-#include <fileioc.h>
21-#include <stdlib.h>
22-23-#include "variables.h"
24-#include "drawing.h"
25-#include "ops.h"
26-27-unsigned char deck[7];
28-unsigned char deckCards;
29-30-unsigned int *numWins;
31-unsigned char *saveData;
32-33-void deal()
34-{
35- progress = 0;
36- deckCards = 52;
37-38- for (unsigned char i = 0; i < 7; i++)
39- {
40- deck[i] = 0x00;
41- }
42-43- for (unsigned char i = 0; i < NUM_TABLSLOTS; i++)
44- {
45- for (unsigned char j = 0; j < TABL_STACK_SIZE; j++)
46- {
47- tableau[i][j] = CARD_EMPTY;
48- }
49- }
50-51- for (unsigned char i = 0; i < NUM_FREECELLS; i++)
52- {
53- freeCells[i] = CARD_EXISTS | i | (unsigned char)(rand() & CARD_SUIT);
54- removeFromDeck(freeCells[i]);
55- }
56-57- animateDeal();
58-}
59-60-void loadWins()
61-{
62- unsigned char winsHandle = ti_Open(WINS_VAR_NAME, "r+");
63-64- if (winsHandle == 0)
65- {
66- // create a new blank file
67- winsHandle = ti_Open(WINS_VAR_NAME, "w");
68- ti_PutC(0, winsHandle);
69- ti_Seek(0, SEEK_SET, winsHandle);
70- }
71-72- // is there already an entry for this game?
73- unsigned char *length = ti_GetDataPtr(winsHandle);
74- highscore_t *entries = (highscore_t *)length + sizeof(char);
75- for (unsigned char i = 0; i < *length; i++)
76- {
77- if (entries[i].gameId == GAME_ID)
78- {
79- numWins = &(entries[i].score);
80- goto found_wins;
81- }
82- }
83-84- // add a new blank entry
85- (*length)++;
86- ti_Seek(sizeof(char) + *length * sizeof(highscore_t), SEEK_SET, winsHandle);
87- const highscore_t newHighScore = { GAME_ID, 0 };
88- highscore_t *entry = (highscore_t *)ti_GetDataPtr(winsHandle);
89- ti_Write(&newHighScore, sizeof(highscore_t), 1, winsHandle);
90- numWins = &(entry->score);
91-92-found_wins:
93- ti_Close(winsHandle);
94-}
95-96-void load()
97-{
98- unsigned char saveHandle = ti_Open(SAVE_VAR_NAME, "r");
99-100- if (saveHandle == 0)
101- {
102- // no save present
103- deal();
104- }
105- else
106- {
107- unsigned char magicNumber = ti_GetC(saveHandle);
108- if (magicNumber != GAME_ID)
109- {
110- // this is from a different solitaire game
111- deal();
112- }
113- else
114- {
115- // load save
116- ti_Read(freeCells, 1, NUM_FREECELLS, saveHandle);
117- ti_Read(tableau, 1, NUM_TABLSLOTS * TABL_STACK_SIZE, saveHandle);
118-119- // how far along does that make us?
120- progress = 0;
121- for (unsigned char i = 0; i < NUM_FREECELLS; i++)
122- {
123- if ((freeCells[i] & CARD_NUMBER) == CARD_KING) progress++;
124- }
125-126- ti_Read(&deckCards, 1, 1, saveHandle);
127- ti_Read(deck, 1, 7, saveHandle);
128- ti_Read(&selectedCard, 1, 1, saveHandle);
129- }
130- }
131-132- ti_Close(saveHandle);
133-}
134-135-void save()
136-{
137- unsigned char saveHandle = ti_Open(SAVE_VAR_NAME, "w");
138-139- ti_PutC(GAME_ID, saveHandle);
140- ti_Write(freeCells, 1, NUM_FREECELLS, saveHandle);
141- ti_Write(tableau, 1, NUM_TABLSLOTS * TABL_STACK_SIZE, saveHandle);
142- ti_Write(&deckCards, 1, 1, saveHandle);
143- ti_Write(deck, 1, 7, saveHandle);
144- ti_Write(&selectedCard, 1, 1, saveHandle);
145-146- ti_Close(saveHandle);
147-}
148-149-void deleteSave()
150-{
151- ti_Delete(SAVE_VAR_NAME);
152-}
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#ifndef save_include_file
18-#define save_include_file
19-20-typedef struct {
21- unsigned char gameId;
22- unsigned int score;
23-} highscore_t;
24-25-void deal();
26-void load();
27-void save();
28-void deleteSave();
29-void loadWins();
30-31-#endif
···0000000000000000000000000000000
-59
src/variables.h
···1-// Calculation Solitaire / CALCSLTR for the TI-84 Plus CE
2-// Copyright (C) 2025 euphory
3-//
4-// This program is free software: you can redistribute it and/or modify
5-// it under the terms of the GNU General Public License as published by
6-// the Free Software Foundation, either version 3 of the License, or
7-// (at your option) any later version.
8-//
9-// This program is distributed in the hope that it will be useful,
10-// but WITHOUT ANY WARRANTY; without even the implied warranty of
11-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-// GNU General Public License for more details.
13-//
14-// You should have received a copy of the GNU General Public License
15-// along with this program. If not, see <https://www.gnu.org/licenses/>.
16-17-#ifndef variables_include_file
18-#define variables_include_file
19-20-#define WINS_VAR_NAME "SLTRWINS"
21-#define SAVE_VAR_NAME "SLTRSAVE"
22-#define GAME_ID 0x02
23-24-#define FRAME_TIME 3277
25-#define HOLD_TIME 2
26-27-#define NUM_FREECELLS 4
28-#define NUM_TABLSLOTS 4
29-#define TABL_STACK_SIZE 12
30-31-#define PROGRESS_COMPLETE 4
32-#define DECK_ORG 0xff
33-34-#define CARD_RED 0x20
35-#define CARD_SUIT 0x30
36-#define CARD_NUMBER 0x0f
37-#define CARD_KING 12
38-#define CARD_EMPTY 0x00
39-#define CARD_EXISTS 0x80
40-41-typedef unsigned char card_t;
42-43-extern card_t tableau[NUM_TABLSLOTS][TABL_STACK_SIZE];
44-extern card_t freeCells[NUM_FREECELLS];
45-46-extern unsigned char cursorStack;
47-extern unsigned char cursorIndex;
48-extern enum cursorMode_t { SELECT, DROP } cursorMode;
49-extern card_t selectedCard;
50-extern unsigned char orgStack;
51-extern unsigned char orgIndex;
52-53-extern unsigned char progress;
54-extern unsigned int *numWins;
55-56-extern unsigned char deck[7];
57-extern unsigned char deckCards;
58-59-#endif