12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958 |
- /**
- Simple minigames for SAF.
- by drummyfish, released under CC0 1.0, public domain
- */
- //#define SAF_SETTING_FORCE_1BIT 1
- #define SAF_SETTING_BACKGROUND_COLOR 0xe0
- #define SAF_PROGRAM_NAME "Minigames"
- #define SAF_SETTING_FASTER_1BIT 2
- #define SAF_SETTING_ENABLE_SOUND 1
- #define SAF_SETTING_ENABLE_SAVES 1
- #define SAF_SETTING_BACKGROUND_COLOR 0
- //#define SAF_SETTING_FORCE_1BIT 1
- #include "../saf.h"
- #define GAMES 5 ///< number of minigames
- static const char *gameNames[GAMES] =
- {
- "SNEK",
- "MINE",
- "BLOQ",
- "2048",
- "RUNR"
- };
- #define MEMORY_SIZE 512
- #define MEMORY_VARIABLE_AREA ((32 * 29) / 2) // = 464
- #define BUTTON_HOLD_PERIOD 18
- void (*stepFunction) (void);
- void menuStep(void);
- void quitGame(void)
- {
- stepFunction = &menuStep;
- SAF_playSound(SAF_SOUND_CLICK);
- }
- /** Memory that's used by the games: the memory is shared between games and at
- any time at most one game is using the memory. This helps save RAM on
- platforms that don't have much of it. The memory has two parts:
- - array area (first MEMORY_VARIABLE_AREA bytes): used for 2D array data, the
- size e.g. allows to store 32 * 29 half-byte values.
- - variable area: game's global variables should be mapped into this area */
- uint8_t memory[MEMORY_SIZE];
- #define VAR(type, index) (*((type *) (memory + MEMORY_VARIABLE_AREA + index)))
- void clearMemory()
- {
- uint8_t *m = memory;
- for (uint16_t i = 0; i < MEMORY_SIZE; ++i, ++m)
- *m = 0;
- }
- void setMemoryHalfByte(uint16_t index, uint8_t data)
- {
- uint8_t *m = memory + index / 2;
- *m = (index % 2) ? ((*m & 0xf0) | data) : ((*m & 0x0f) | (data << 4));
- }
- uint8_t getMemoryHalfByte(uint16_t index)
- {
- return (index % 2) ? (memory[index / 2] & 0x0f) : (memory[index / 2] >> 4);
- }
- uint8_t buttonPressedOrHeld(uint8_t key)
- {
- uint8_t b = SAF_buttonPressed(key);
- return (b == 1) || (b >= BUTTON_HOLD_PERIOD);
- }
- void drawTextRightAlign(int8_t x, int8_t y, const char *text, uint8_t color,
- uint8_t size)
- {
- uint8_t l = 0;
- while (text[l] != 0)
- l++;
- x = x - l * 5 * size + 1;
- SAF_drawText(text,x,y,color,size);
- }
- void blinkHighScore()
- {
- if (SAF_frame() & 0x10)
- {
- SAF_drawRect(5,20,54,16,SAF_COLOR_GRAY_DARK,1);
- SAF_drawText("HISCORE!",11,25,SAF_COLOR_GREEN,1);
- }
- }
- void saveHiScore(uint8_t index, uint16_t score)
- {
- SAF_save(index * 2,score & 0x00ff);
- SAF_save(index * 2 + 1,score / 256);
- }
- uint16_t getHiScore(uint8_t index)
- {
- return SAF_load(index * 2) + (((uint16_t) SAF_load(index * 2 + 1)) * 256);
- }
- // SNAKE -----------------------------------------------------------------------
- #define SNAKE_BOARD_W 32
- #define SNAKE_BOARD_H 29
- #define SNAKE_START_LEN 6
- #define SNAKE_MOVE_SPEED 6
- #define SNAKE_COLOR1 SAF_COLOR_BLACK
- #define SNAKE_COLOR2 SAF_COLOR_RED
- #define SNAKE_SQUARE_EMPTY 0
- #define SNAKE_SQUARE_SU 1 // snake segment pointing upwards to next segment
- #define SNAKE_SQUARE_SR 2
- #define SNAKE_SQUARE_SD 3
- #define SNAKE_SQUARE_SL 4
- #define SNAKE_SAVE_SLOT 0
- // variables:
- #define SNAKE_TAIL_SQUARE VAR(int16_t,0)
- #define SNAKE_HEAD_SQUARE VAR(int16_t,2)
- #define SNAKE_FOOD_SQUARE VAR(int16_t,4)
- #define SNAKE_MOVE_COUNT VAR(uint8_t,6) // countdown to next snake move
- #define SNAKE_DIRECTION VAR(uint8_t,7)
- #define SNAKE_FOOD_STEPS VAR(uint8_t,8)
- #define SNAKE_SCORE VAR(uint16_t,9)
- #define SNAKE_GAME_STATE VAR(uint8_t,11) // 0: playing, 1: end, 2: end, hiscore
- void snakeSpawnFood()
- {
- int16_t square = ((((uint16_t) SAF_random()) << 8) | SAF_random()) %
- (SNAKE_BOARD_W * SNAKE_BOARD_H);
- while (getMemoryHalfByte(square) != SNAKE_SQUARE_EMPTY)
- square = ((square != 0) ? square : (SNAKE_BOARD_W * SNAKE_BOARD_H)) - 1;
- SNAKE_FOOD_SQUARE = square;
- }
- void snakeInit()
- {
- clearMemory();
- // TODO: init snake to random colors?
- SNAKE_TAIL_SQUARE = (SNAKE_BOARD_H / 2) * SNAKE_BOARD_W + SNAKE_BOARD_W / 2
- - SNAKE_START_LEN / 2;
- SNAKE_GAME_STATE = 0;
- SNAKE_HEAD_SQUARE = SNAKE_TAIL_SQUARE + SNAKE_START_LEN - 1;
- SNAKE_DIRECTION = SNAKE_SQUARE_SR;
- SNAKE_MOVE_COUNT = SNAKE_MOVE_SPEED;
- SNAKE_FOOD_STEPS = 0;
- SNAKE_SCORE = 0;
- for (uint8_t i = 0; i < SNAKE_START_LEN; ++i)
- setMemoryHalfByte(SNAKE_TAIL_SQUARE + i,SNAKE_SQUARE_SR);
- snakeSpawnFood();
- }
- int16_t snakeNextSquare(int16_t square)
- {
- switch (getMemoryHalfByte(square))
- {
- case SNAKE_SQUARE_SU: return square - 32; break;
- case SNAKE_SQUARE_SR: return square + 1; break;
- case SNAKE_SQUARE_SD: return square + 32; break;
- case SNAKE_SQUARE_SL: return square - 1; break;
- default: return square; break;
- }
- }
- void snakeDraw()
- {
- SAF_clearScreen(SAF_COLOR_WHITE);
- SAF_drawRect(0,SAF_SCREEN_HEIGHT,SAF_SCREEN_WIDTH,-6,SAF_COLOR_GRAY_DARK,1);
- char scoreText[6];
- SAF_drawText(SAF_intToStr(SNAKE_SCORE,scoreText),2,SAF_SCREEN_HEIGHT - 5,
- SAF_COLOR_WHITE,1);
- int16_t square = SNAKE_TAIL_SQUARE;
- uint8_t val = getMemoryHalfByte(square);
- #if SAF_PLATFORM_COLOR_COUNT > 2
- uint8_t pattern = 1; // pattern of next and prev square, for coloring
- #endif
- while (1) // draw from tail to head
- {
- uint8_t combinedPattern = 0;
- #if SAF_PLATFORM_COLOR_COUNT > 2
- switch (val)
- {
- case SNAKE_SQUARE_SU: combinedPattern = 1 | pattern; pattern = 4; break;
- case SNAKE_SQUARE_SR: combinedPattern = 2 | pattern; pattern = 8; break;
- case SNAKE_SQUARE_SD: combinedPattern = 4 | pattern; pattern = 1; break;
- case SNAKE_SQUARE_SL: combinedPattern = 8 | pattern; pattern = 2; break;
- default: break;
- }
- #endif
- uint8_t x = (square % 32) * 2;
- uint8_t y = (square / 32) * 2;
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_drawPixel(x,y,((combinedPattern & (1 | 8) ) || (combinedPattern == (2 | 4))) ? SNAKE_COLOR2 : SNAKE_COLOR1);
- SAF_drawPixel(x + 1,y,(combinedPattern & 2) ? SNAKE_COLOR2 : SNAKE_COLOR1);
- SAF_drawPixel(x,y + 1,(combinedPattern & 4) ? SNAKE_COLOR2 : SNAKE_COLOR1);
- SAF_drawPixel(x + 1,y + 1,SNAKE_COLOR1);
- #else
- SAF_drawPixel(x,y,SAF_COLOR_BLACK);
- SAF_drawPixel(x + 1,y,SAF_COLOR_BLACK);
- SAF_drawPixel(x,y + 1,SAF_COLOR_BLACK);
- SAF_drawPixel(x + 1,y + 1,SAF_COLOR_BLACK);
- #endif
- if (square == SNAKE_HEAD_SQUARE)
- break;
- square = snakeNextSquare(square);
- #if SAF_PLATFORM_COLOR_COUNT > 2
- val = getMemoryHalfByte(square);
- #endif
- }
- // draw food:
- uint8_t x = (SNAKE_FOOD_SQUARE % 32) * 2;
- uint8_t y = (SNAKE_FOOD_SQUARE / 32) * 2;
- SAF_drawPixel(x,y,SAF_COLOR_BLUE);
- SAF_drawPixel(x + 1,y,SAF_COLOR_BLUE);
- SAF_drawPixel(x,y + 1,SAF_COLOR_BLUE);
- SAF_drawPixel(x + 1,y + 1,SAF_COLOR_BLUE);
- #if SAF_PLATFORM_COLOR_COUNT <= 2
- SAF_drawRect(x - 1,y - 1,4,4,SAF_COLOR_WHITE,0);
- #endif
- if (SNAKE_GAME_STATE == 2)
- blinkHighScore();
- }
- void snakeStep()
- {
- if (SAF_buttonPressed(SAF_BUTTON_B) >= BUTTON_HOLD_PERIOD)
- quitGame();
- if (SNAKE_GAME_STATE != 0)
- {
- if (SAF_buttonJustPressed(SAF_BUTTON_A))
- snakeInit();
- snakeDraw();
- return;
- }
- SNAKE_MOVE_COUNT--;
- uint8_t timePressed = 0;
- // we register the button pressed for shortest time (allows best control):
- #define checkButton(b,d)\
- if (SAF_buttonPressed(b) && \
- (timePressed == 0 || SAF_buttonPressed(b) < timePressed)) {\
- timePressed = SAF_buttonPressed(b);\
- SNAKE_DIRECTION = d;}
- checkButton(SAF_BUTTON_UP,SNAKE_SQUARE_SU)
- checkButton(SAF_BUTTON_RIGHT,SNAKE_SQUARE_SR)
- checkButton(SAF_BUTTON_DOWN,SNAKE_SQUARE_SD)
- checkButton(SAF_BUTTON_LEFT,SNAKE_SQUARE_SL)
- if (SNAKE_MOVE_COUNT <=
- (SAF_buttonPressed(SAF_BUTTON_A) ? 0 : (SNAKE_MOVE_SPEED * 3) / 4))
- {
- if (SNAKE_FOOD_STEPS < 255)
- SNAKE_FOOD_STEPS++;
- uint8_t headVal = getMemoryHalfByte(SNAKE_HEAD_SQUARE);
- if (SNAKE_DIRECTION != headVal)
- {
- uint8_t opposite = 0;
- switch (headVal)
- {
- case SNAKE_SQUARE_SU: opposite = SNAKE_SQUARE_SD; break;
- case SNAKE_SQUARE_SR: opposite = SNAKE_SQUARE_SL; break;
- case SNAKE_SQUARE_SD: opposite = SNAKE_SQUARE_SU; break;
- case SNAKE_SQUARE_SL: opposite = SNAKE_SQUARE_SR; break;
- default: break;
- }
- // disallow turning 180 degrees (instadeath):
- if (SNAKE_DIRECTION != opposite)
- {
- headVal = SNAKE_DIRECTION;
- setMemoryHalfByte(SNAKE_HEAD_SQUARE,headVal);
- }
- }
- int16_t nextSquare = snakeNextSquare(SNAKE_HEAD_SQUARE);
- // check collision:
- uint8_t collides = 0;
- int8_t diff = (nextSquare % 32) - (SNAKE_HEAD_SQUARE % 32);
- if ( // collision with map borders
- (diff != 0 && diff != 1 && diff != -1) ||
- ((nextSquare < 0) || (nextSquare >= (SNAKE_BOARD_W * SNAKE_BOARD_H)))
- )
- collides = 1;
- if (!collides)
- {
- uint8_t squareVal = getMemoryHalfByte(nextSquare);
- if (squareVal != SNAKE_SQUARE_EMPTY)
- collides = 1; // collision with self
- }
- if (collides)
- {
- SAF_playSound(SAF_SOUND_BOOM);
- SNAKE_GAME_STATE = 1;
- if (SNAKE_SCORE >= getHiScore(SNAKE_SAVE_SLOT))
- {
- saveHiScore(SNAKE_SAVE_SLOT,SNAKE_SCORE);
- SNAKE_GAME_STATE = 2;
- }
- return;
- }
- setMemoryHalfByte(nextSquare,headVal);
- SNAKE_HEAD_SQUARE = nextSquare;
- if (nextSquare == SNAKE_FOOD_SQUARE) // takes food?
- {
- SNAKE_SCORE += 20 - SNAKE_FOOD_STEPS / 25;
- SNAKE_FOOD_STEPS = 0;
- SAF_playSound(SAF_SOUND_CLICK);
- snakeSpawnFood();
- }
- else
- {
- nextSquare = snakeNextSquare(SNAKE_TAIL_SQUARE);
- setMemoryHalfByte(SNAKE_TAIL_SQUARE,SNAKE_SQUARE_EMPTY);
- SNAKE_TAIL_SQUARE = nextSquare;
- }
- SNAKE_MOVE_COUNT = SNAKE_MOVE_SPEED;
- }
- snakeDraw();
- }
- // MINESWEEPER -----------------------------------------------------------------
- #define MINES_BOARD_W 12
- #define MINES_BOARD_H 11
- #define MINES_SQUARE_SIZE 5
- #define MINES_MINE_COUNT 22
- #define MINES_OFFSET_X 2
- #define MINES_OFFSET_Y 8
- #define MINES_SAVE_SLOT 1
- #define MINES_SQUARE_COVERED_MARKED 0x0f
- #define MINES_SQUARE_COVERED 0x0e
- #define MINES_SQUARE_MINE_MARKED 0x0d
- #define MINES_SQUARE_MINE 0x0c
- #define MINES_SELECTED_SQUARE VAR(uint16_t,0)
- #define MINES_GAME_STATE VAR(uint8_t,2) /* 0: start, 1: play, 2: lost, 3: won,
- 4: won, hiscore */
- #define MINES_SQUARES_LEFT VAR(uint8_t,3)
- #define MINES_MS_ELAPSED VAR(uint32_t,4)
- void minesInit()
- {
- MINES_GAME_STATE = 0;
- MINES_SELECTED_SQUARE = (MINES_BOARD_W * MINES_BOARD_H) / 2;
- for (uint16_t i = 0; i < MINES_BOARD_W * MINES_BOARD_H; ++i)
- setMemoryHalfByte(i,MINES_SQUARE_COVERED);
- MINES_SQUARES_LEFT = MINES_BOARD_W * MINES_BOARD_H - MINES_MINE_COUNT;
- MINES_MS_ELAPSED = 0;
- }
- void minesPlaceMines(uint16_t clickSquare)
- {
- for (uint8_t i = 0; i < MINES_MINE_COUNT; ++i)
- {
- uint16_t square = SAF_random() % (MINES_BOARD_W * MINES_BOARD_H);
- while (getMemoryHalfByte(square) == MINES_SQUARE_MINE ||
- square == clickSquare)
- square = ((square != 0) ? square : (MINES_BOARD_W * MINES_BOARD_H)) - 1;
- setMemoryHalfByte(square,MINES_SQUARE_MINE);
- }
- }
- void minesDraw()
- {
- uint8_t state = MINES_GAME_STATE;
- uint8_t color = SAF_COLOR_WHITE;
- #if SAF_PLATFORM_COLOR_COUNT > 2
- if (state == 2)
- color = SAF_COLOR_RED;
- else if (state == 3 || state == 4)
- color = SAF_COLOR_GREEN;
- #endif
- SAF_clearScreen(color);
- uint16_t square = 0;
- // bar:
- SAF_drawRect(0,0,SAF_SCREEN_WIDTH,MINES_OFFSET_Y - 2,SAF_COLOR_GRAY_DARK,1);
- char text[5];
- SAF_drawText(SAF_intToStr(MINES_MS_ELAPSED / 1000,text),26,1,
- SAF_COLOR_WHITE,1);
- SAF_drawText(SAF_intToStr(MINES_SQUARES_LEFT,text),3,1,SAF_COLOR_WHITE,1);
- SAF_drawText("E",58,1,SAF_COLOR_WHITE,1);
- // background rect:
- SAF_drawRect(MINES_OFFSET_X,MINES_OFFSET_Y,
- MINES_BOARD_W * MINES_SQUARE_SIZE,
- MINES_BOARD_H * MINES_SQUARE_SIZE,
- #if SAF_PLATFORM_COLOR_COUNT <= 2
- SAF_COLOR_BLACK,
- #else
- SAF_COLOR_BROWN,
- #endif
- 1);
- // squares:
- for (uint8_t y = MINES_OFFSET_Y;
- y < (MINES_BOARD_H * MINES_SQUARE_SIZE) + MINES_OFFSET_Y;
- y += MINES_SQUARE_SIZE)
- for (uint8_t x = MINES_OFFSET_X;
- x < (MINES_BOARD_W * MINES_SQUARE_SIZE) + MINES_OFFSET_X;
- x += MINES_SQUARE_SIZE)
- {
- uint8_t squareVal = getMemoryHalfByte(square);
- uint8_t marked = squareVal == MINES_SQUARE_COVERED_MARKED ||
- squareVal == MINES_SQUARE_MINE_MARKED;
- #if SAF_PLATFORM_COLOR_COUNT > 2
- color = marked ? SAF_COLOR_RED :
- ((squareVal < MINES_SQUARE_MINE) ? SAF_COLOR_WHITE : SAF_COLOR_GRAY);
- #else
- color = (squareVal >= MINES_SQUARE_MINE) ?
- SAF_COLOR_WHITE : SAF_COLOR_BLACK;
- #endif
- uint8_t s = squareVal < MINES_SQUARE_MINE ? MINES_SQUARE_SIZE : (MINES_SQUARE_SIZE - 1);
- SAF_drawRect(x,y,s,s,color,1);
- #if SAF_PLATFORM_COLOR_COUNT <= 2
- if (marked)
- SAF_drawText("*",x,y,SAF_COLOR_BLACK,1);
- #endif
- if (squareVal < MINES_SQUARE_MINE && squareVal > 0)
- {
- #if SAF_PLATFORM_COLOR_COUNT > 2
- switch (squareVal)
- {
- case 1: color = SAF_COLOR_BLUE; break;
- case 2: color = SAF_COLOR_GREEN; break;
- case 3: color = SAF_COLOR_RED; break;
- case 4: color = SAF_COLOR_BLUE_DARK; break;
- case 5: color = SAF_COLOR_GRAY_DARK; break;
- case 6: color = SAF_COLOR_RED_DARK; break;
- case 7: color = SAF_COLOR_YELLOW; break;
- case 8: color = SAF_COLOR_BROWN; break;
- default: break;
- }
- #else
- color = SAF_COLOR_WHITE;
- #endif
- char numText[2] = "0";
- numText[0] += squareVal;
- SAF_drawText(numText,x,y,color,1);
- }
- else if (state == 2 && squareVal == MINES_SQUARE_MINE)
- {
- char t[2] = "0";
- SAF_drawText(t,x,y,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_COLOR_RED,
- #else
- SAF_COLOR_BLACK,
- #endif
- 1);
- }
- square++;
- }
- uint16_t selectedSquare = MINES_SELECTED_SQUARE;
- uint8_t x = MINES_OFFSET_X + (selectedSquare % MINES_BOARD_W) * MINES_SQUARE_SIZE;
- uint8_t y = MINES_OFFSET_Y + (selectedSquare / MINES_BOARD_W) * MINES_SQUARE_SIZE;
- #if SAF_PLATFORM_COLOR_COUNT <= 2
- SAF_drawRect(x - 1,y - 1,MINES_SQUARE_SIZE + 1,MINES_SQUARE_SIZE + 1,SAF_COLOR_WHITE,0);
- SAF_drawRect(x - 2,y - 2,MINES_SQUARE_SIZE + 3,MINES_SQUARE_SIZE + 3,SAF_COLOR_BLACK,0);
- #else
- SAF_drawRect(x - 2,y - 2,MINES_SQUARE_SIZE + 3,MINES_SQUARE_SIZE + 3,SAF_COLOR_BLACK,0);
- #endif
- if (state == 4)
- blinkHighScore();
- }
- void minesReveal(uint16_t square)
- {
- uint8_t squareVal = getMemoryHalfByte(square);
- if (squareVal != MINES_SQUARE_COVERED &&
- squareVal != MINES_SQUARE_COVERED_MARKED)
- return;
- MINES_SQUARES_LEFT--;
- uint8_t mineCount = 0;
- int8_t offsets[8] = {
- -1 * MINES_BOARD_W - 1,
- -1 * MINES_BOARD_W,
- -1 * MINES_BOARD_W + 1,
- -1, 1,
- MINES_BOARD_W - 1,
- MINES_BOARD_W,
- MINES_BOARD_W + 1};
- uint8_t checkSquares = 0xff;
- uint8_t coordinate = square % MINES_BOARD_W; // x
- if (coordinate == 0)
- checkSquares &= 0x6b;
- else if (coordinate == MINES_BOARD_W - 1)
- checkSquares &= 0xd6;
- coordinate = square / MINES_BOARD_W; // y
- if (coordinate == 0)
- checkSquares &= 0x1f;
- else if (coordinate == MINES_BOARD_H - 1)
- checkSquares &= 0xf8;
- uint8_t checkSquaresbackup = checkSquares;
- for (uint8_t i = 0; i < 8; ++i)
- {
- if (checkSquares & 0x80)
- {
- squareVal = getMemoryHalfByte(square + offsets[i]);
-
- if (squareVal == MINES_SQUARE_MINE || squareVal == MINES_SQUARE_MINE_MARKED)
- mineCount++;
- }
- checkSquares <<= 1;
- }
- setMemoryHalfByte(square,mineCount);
- if (mineCount == 0)
- for (uint8_t i = 0; i < 8; ++i)
- {
- if (checkSquaresbackup & 0x80)
- minesReveal(square + offsets[i]);
- checkSquaresbackup <<= 1;
- }
- #undef checkSquare
- }
- void minesStep(void)
- {
- if (SAF_buttonPressed(SAF_BUTTON_B) >= BUTTON_HOLD_PERIOD)
- quitGame();
- if (MINES_GAME_STATE == 0 || MINES_GAME_STATE == 1)
- {
- if (MINES_SQUARES_LEFT == 0) // win
- {
- SAF_playSound(SAF_SOUND_BEEP);
- MINES_GAME_STATE = 3;
- uint16_t score = getHiScore(MINES_SAVE_SLOT);
- if (score == 0 || MINES_MS_ELAPSED / 1000 < score)
- {
- saveHiScore(MINES_SAVE_SLOT,MINES_MS_ELAPSED / 1000);
- MINES_GAME_STATE = 4;
- }
-
- return;
- }
-
- MINES_MS_ELAPSED += SAF_MS_PER_FRAME;
- uint8_t x = MINES_SELECTED_SQUARE % MINES_BOARD_W;
- uint8_t y = MINES_SELECTED_SQUARE / MINES_BOARD_W;
- if (buttonPressedOrHeld(SAF_BUTTON_RIGHT) && x < MINES_BOARD_W - 1)
- MINES_SELECTED_SQUARE++;
- else if (buttonPressedOrHeld(SAF_BUTTON_LEFT) && x > 0)
- MINES_SELECTED_SQUARE--;
- if (buttonPressedOrHeld(SAF_BUTTON_UP) && y > 0)
- MINES_SELECTED_SQUARE -= MINES_BOARD_W;
- else if (buttonPressedOrHeld(SAF_BUTTON_DOWN) && y < MINES_BOARD_H - 1)
- MINES_SELECTED_SQUARE += MINES_BOARD_W;
- uint8_t squareVal = getMemoryHalfByte(MINES_SELECTED_SQUARE);
- // mark/unmark:
- if (SAF_buttonJustPressed(SAF_BUTTON_B) && squareVal >= MINES_SQUARE_MINE)
- setMemoryHalfByte(MINES_SELECTED_SQUARE,squareVal ^ 0x01);
- // reveal:
- if (SAF_buttonJustPressed(SAF_BUTTON_A))
- {
- if (squareVal == MINES_SQUARE_COVERED)
- {
- if (MINES_GAME_STATE == 0)
- {
- minesPlaceMines(MINES_SELECTED_SQUARE);
- MINES_GAME_STATE = 1;
- }
- minesReveal(MINES_SELECTED_SQUARE);
- }
- else if (squareVal == MINES_SQUARE_MINE) // loss
- {
- MINES_GAME_STATE = 2;
- SAF_playSound(SAF_SOUND_BOOM);
- }
- }
- }
- else // won or lost, waiting for keypress
- if (SAF_buttonJustPressed(SAF_BUTTON_A) ||
- SAF_buttonJustPressed(SAF_BUTTON_B))
- minesInit();
- minesDraw();
- }
- // BLOCKS ----------------------------------------------------------------------
- /* square format is following:
- MSB 76543210 LSB
- 012: block type/color, 0 = empty square
- 3456: for rotation, says the position of the block within the tetromino
- 7: 1 for an active (falling) block, 0 otherwise
- the square with value 0xff is a flashing square to be removed */
- #define BLOCKS_BOARD_W 10
- #define BLOCKS_BOARD_H 15
- #define BLOCKS_BOARD_SQUARES (BLOCKS_BOARD_W * BLOCKS_BOARD_H)
- #define BLOCKS_SQUARE_SIZE 4
- #define BLOCKS_BLOCK_TYPES 8
- #define BLOCKS_LINE_SCORE 10
- #define BLOCKS_LAND_SCORE 1
- #define BLOCKS_LEVEL_DURATION (SAF_FPS * 60)
- #define BLOCKS_SPEED_INCREASE 3
- #define BLOCKS_START_SPEED 25
- #define BLOCKS_SAVE_SLOT 2
- #define BLOCKS_OFFSET_X \
- (SAF_SCREEN_WIDTH - BLOCKS_BOARD_W * BLOCKS_SQUARE_SIZE)
- #define BLOCKS_OFFSET_Y 2
- #define BLOCKS_STATE VAR(uint8_t,0)
- #define BLOCKS_SPEED VAR(uint8_t,1)
- #define BLOCKS_NEXT_MOVE VAR(uint8_t,2)
- #define BLOCKS_WAIT_TIMER VAR(uint8_t,3)
- #define BLOCKS_LEVEL VAR(uint8_t,4)
- #define BLOCKS_SCORE VAR(uint16_t,5)
- #define BLOCKS_NEXT_LEVEL_IN VAR(uint32_t,7)
- uint8_t blocksSquareIsSolid(uint8_t square)
- {
- uint8_t val = memory[square];
- return (val != 0) && (val & 0x80) == 0;
- }
- uint8_t blocksSpawnBlock(uint8_t type)
- {
- if (type == 0)
- type = 7;
- uint8_t s[4] = {4,5,14,15}; // start with square tetromino
- uint8_t v[4] = {6,6,6,6}; // all center squares, unrotatable
- /* the v array holds the position of the tetromino like this:
- 0
- 123
- 45678
- 9ab
- c */
- switch (type) // modify to a specific shape
- {
- case 1: // reverse L
- s[0] = 3; s[1] = 13;
- v[0] = 1; v[1] = 5; v[3] = 7;
- break;
- case 2: // L
- s[0] = 13;
- v[0] = 5; v[1] = 3; v[3] = 7;
- break;
- case 3: // S
- s[0] = 6;
- v[0] = 3; v[1] = 2; v[2] = 5; v[3] = 6;
- break;
- case 4: // Z
- s[2] = 16;
- v[0] = 1; v[1] = 2; v[2] = 7; v[3] = 6;
- break;
- case 5: // upside-down T
- s[0] = 16;
- v[0] = 7; v[1] = 2; v[2] = 5; v[3] = 6;
- break;
- case 6: // I
- s[2] = 3; s[3] = 6;
- v[1] = 7; v[2] = 5; v[3] = 8;
- break;
- default: break;
- }
- type |= 0x80;
- uint8_t result = 1;
- for (uint8_t i = 0; i < 4; ++i)
- {
- uint8_t square = s[i];
- if (memory[square] != 0)
- result = 0;
- memory[square] = type | ((v[i] << 3));
- }
- return result;
- }
- void blocksInit(void)
- {
- clearMemory();
- BLOCKS_SPEED = BLOCKS_START_SPEED;
- BLOCKS_NEXT_MOVE = BLOCKS_START_SPEED;
- BLOCKS_WAIT_TIMER = 0;
- BLOCKS_LEVEL = 0;
- BLOCKS_SCORE = 0;
- BLOCKS_NEXT_LEVEL_IN = 0;
- BLOCKS_STATE = 0;
- blocksSpawnBlock(SAF_random() % BLOCKS_BLOCK_TYPES);
- }
- void blocksRotate(uint8_t left)
- {
- const int8_t rotationMap[13 * 2] =
- /* old new square offset */
- {/* 0 */ 8, 2 * BLOCKS_BOARD_W + 2,
- /* 1 */ 3, 2,
- /* 2 */ 7, BLOCKS_BOARD_W + 1,
- /* 3 */ 11, 2 * BLOCKS_BOARD_W,
- /* 4 */ 0, -2 * BLOCKS_BOARD_W + 2,
- /* 5 */ 2, -1 * BLOCKS_BOARD_W + 1,
- /* 6 */ 6, 0,
- /* 7 */ 10, BLOCKS_BOARD_W - 1,
- /* 8 */ 12, 2 * BLOCKS_BOARD_W - 2,
- /* 9 */ 1, -2 * BLOCKS_BOARD_W,
- /* 10*/ 5, -1 * BLOCKS_BOARD_W - 1,
- /* 11*/ 9, -2,
- /* 12*/ 4, -2 * BLOCKS_BOARD_W - 2};
- uint8_t blocksProcessed = 0;
- uint8_t newPositions[4];
- uint8_t newValues[4];
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- {
- uint8_t square = memory[i];
- if (square & 0x80)
- {
- uint8_t index = 2 * ((square >> 3) & 0x0f);
- uint8_t newPos = i + rotationMap[index + 1];
- if (left)
- {
- // rotate two more times
- index = rotationMap[index] * 2;
- newPos += rotationMap[index + 1];
- index = rotationMap[index] * 2;
- newPos += rotationMap[index + 1];
- }
- int xDiff = (newPos % BLOCKS_BOARD_W) - (i % BLOCKS_BOARD_W);
- xDiff = xDiff >= 0 ? xDiff : (-1 * xDiff);
- if ((xDiff > 2) || // left/right outside?
- (newPos >= BLOCKS_BOARD_SQUARES) || // top/bottom outside?
- blocksSquareIsSolid(newPos))
- return; // can't rotate
- newValues[blocksProcessed] = (square & 0x87) | (rotationMap[index] << 3);
- newPositions[blocksProcessed] = newPos;
- blocksProcessed++;
- if (blocksProcessed >= 4)
- break;
- }
- }
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- if (memory[i] & 0x80)
- memory[i] = 0;
- for (uint8_t i = 0; i < 4; ++i)
- memory[newPositions[i]] = newValues[i];
- }
- void blocksDraw(void)
- {
- SAF_clearScreen(BLOCKS_STATE ? SAF_COLOR_RED_DARK : SAF_COLOR_GRAY_DARK);
- SAF_drawRect(
- #if SAF_PLATFORM_COLOR_COUNT > 2
- BLOCKS_OFFSET_X,BLOCKS_OFFSET_Y,
- BLOCKS_BOARD_W * BLOCKS_SQUARE_SIZE, BLOCKS_BOARD_H * BLOCKS_SQUARE_SIZE,
- #else
- BLOCKS_OFFSET_X - 1,BLOCKS_OFFSET_Y- 1,BLOCKS_BOARD_W * BLOCKS_SQUARE_SIZE
- + 2, BLOCKS_BOARD_H * BLOCKS_SQUARE_SIZE + 2,
- #endif
- SAF_COLOR_WHITE,1);
- char text[16] = "L ";
- SAF_intToStr(BLOCKS_LEVEL,text + 1);
- SAF_drawText(text,2,3,SAF_COLOR_WHITE,1);
- SAF_intToStr(BLOCKS_SCORE,text);
- SAF_drawText(text,2,10,SAF_COLOR_WHITE,1);
- uint8_t x = 0, y = 0;
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- {
- uint8_t square = memory[i];
- if (square)
- {
- if (square != 0xff)
- square &= 0x07;
- uint8_t color = 0;
- #if SAF_PLATFORM_COLOR_COUNT > 2
- switch (square)
- {
- case 1: color = SAF_COLOR_RED; break;
- case 2: color = SAF_COLOR_GREEN; break;
- case 3: color = SAF_COLOR_BROWN; break;
- case 4: color = SAF_COLOR_YELLOW; break;
- case 5: color = SAF_COLOR_ORANGE; break;
- case 6: color = SAF_COLOR_BLUE; break;
- case 7: color = SAF_COLOR_GREEN_DARK; break;
- case 0xff: color = ((SAF_frame() >> 2) & 0x01) ? SAF_COLOR_BLACK : SAF_COLOR_WHITE; break;
- default: break;
- }
- #else
- color = (square != 0xff) ? SAF_COLOR_BLACK :
- ((SAF_frame() >> 2) & 0x01) ? SAF_COLOR_BLACK : SAF_COLOR_WHITE;
- #endif
- uint8_t
- drawX = BLOCKS_OFFSET_X + x * BLOCKS_SQUARE_SIZE,
- drawY = BLOCKS_OFFSET_Y + y * BLOCKS_SQUARE_SIZE;
- SAF_drawRect(drawX,drawY,BLOCKS_SQUARE_SIZE,BLOCKS_SQUARE_SIZE,color,1);
- }
- x++;
- if (x >= 10)
- {
- x = 0;
- y++;
- }
- }
- if (BLOCKS_STATE && BLOCKS_SCORE == getHiScore(BLOCKS_SAVE_SLOT))
- blinkHighScore();
- }
- uint8_t blocksFallStep(void)
- {
- uint8_t canFall = 1;
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- if (memory[i] & 0x80)
- {
- if (i / BLOCKS_BOARD_W == BLOCKS_BOARD_H - 1)
- {
- canFall = 0;
- break;
- }
- if (blocksSquareIsSolid(i + BLOCKS_BOARD_W))
- {
- canFall = 0;
- break;
- }
- }
- if (canFall)
- {
- for (uint8_t i = BLOCKS_BOARD_W * (BLOCKS_BOARD_H - 1) - 1; i != 255; --i)
- if (memory[i] & 0x80)
- {
- memory[i + BLOCKS_BOARD_W] = memory[i];
- memory[i] = 0;
- }
- }
-
- return canFall;
- }
- void blocksMoveHorizontally(uint8_t left)
- {
- uint8_t limitCol = left ? 0 : (BLOCKS_BOARD_W - 1);
- int8_t increment = left ? -1 : 1;
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- if ((memory[i] & 0x80) &&
- (
- (i % BLOCKS_BOARD_W == limitCol) ||
- blocksSquareIsSolid(i + increment)
- ))
- return;
- uint8_t i0 = 0, i1 = BLOCKS_BOARD_SQUARES;
- if (!left)
- {
- i0 = BLOCKS_BOARD_SQUARES - 1;
- i1 = 255;
- }
- for (uint8_t i = i0; i != i1; i -= increment)
- if (memory[i] & 0x80)
- {
- memory[i + increment] = memory[i];
- memory[i] = 0;
- }
- }
- void blocksRemoveLines(void)
- {
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- if (memory[i] == 0xff)
- {
- BLOCKS_SCORE += BLOCKS_LINE_SCORE;
- for (uint8_t j = i + BLOCKS_BOARD_W - 1; j >= BLOCKS_BOARD_W; --j)
- memory[j] = memory[j - BLOCKS_BOARD_W];
- for (uint8_t j = 0; j < BLOCKS_BOARD_W; ++j)
- memory[j] = 0;
- }
- }
- void blocksStep(void)
- {
- if (SAF_buttonPressed(SAF_BUTTON_B) >= BUTTON_HOLD_PERIOD)
- quitGame();
- blocksDraw();
- if (BLOCKS_STATE == 1)
- {
- if (SAF_buttonJustPressed(SAF_BUTTON_A) ||
- SAF_buttonJustPressed(SAF_BUTTON_B))
- {
- blocksInit();
- BLOCKS_STATE = 0;
- }
- return;
- }
- if (BLOCKS_NEXT_LEVEL_IN == 0)
- {
- BLOCKS_LEVEL += 1;
- BLOCKS_NEXT_LEVEL_IN = BLOCKS_LEVEL_DURATION;
- if (BLOCKS_SPEED > BLOCKS_SPEED_INCREASE)
- BLOCKS_SPEED -= BLOCKS_SPEED_INCREASE;
- }
- BLOCKS_NEXT_LEVEL_IN -= 1;
- if (BLOCKS_WAIT_TIMER > 0)
- {
- BLOCKS_WAIT_TIMER -= 1;
- if (BLOCKS_WAIT_TIMER == 1)
- blocksRemoveLines();
- return;
- }
- BLOCKS_NEXT_MOVE = BLOCKS_NEXT_MOVE - 1;
- if (SAF_buttonJustPressed(SAF_BUTTON_A))
- {
- // drop the block:
- while (blocksFallStep());
- SAF_playSound(SAF_SOUND_BUMP);
- BLOCKS_NEXT_MOVE = 0;
- }
- if (buttonPressedOrHeld(SAF_BUTTON_LEFT))
- blocksMoveHorizontally(1);
- else if (buttonPressedOrHeld(SAF_BUTTON_RIGHT))
- blocksMoveHorizontally(0);
- if (SAF_buttonJustPressed(SAF_BUTTON_UP))
- blocksRotate(0);
- else if (SAF_buttonJustPressed(SAF_BUTTON_DOWN))
- blocksRotate(1);
- if (BLOCKS_NEXT_MOVE == 0)
- {
- if (!blocksFallStep())
- {
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- memory[i] &= 0x07;
- // scan for completed lines:
- uint8_t col = 0;
- uint8_t count = 0;
- uint8_t lineCompleted = 0;
- for (uint8_t i = 0; i < BLOCKS_BOARD_SQUARES; ++i)
- {
- if (memory[i] != 0)
- count++;
- col++;
- if (col >= BLOCKS_BOARD_W)
- {
- if (count >= BLOCKS_BOARD_W)
- {
- lineCompleted = 1;
- for (uint8_t j = i - BLOCKS_BOARD_W + 1; j <= i; ++j)
- memory[j] = 0xff;
- }
- col = 0;
- count = 0;
- }
- }
- if (lineCompleted)
- {
- BLOCKS_WAIT_TIMER = 20;
- SAF_playSound(SAF_SOUND_BEEP);
- }
- BLOCKS_SCORE += 1;
- if (!blocksSpawnBlock(SAF_random() % BLOCKS_BLOCK_TYPES))
- {
- BLOCKS_STATE = 1; // game over
- SAF_playSound(SAF_SOUND_BOOM);
- if (BLOCKS_SCORE >= getHiScore(BLOCKS_SAVE_SLOT))
- saveHiScore(BLOCKS_SAVE_SLOT,BLOCKS_SCORE);
- }
- }
- BLOCKS_NEXT_MOVE = BLOCKS_SPEED;
- }
- }
- // 2048 ------------------------------------------------------------------------
- /* The first 16 bytes of the board hold the power values of the squares, and
- another 16 byte block follows which contains info for animation: each square
- has the previous square value in the lower 4 bits and the travel distance in
- the upper 4 bits. */
- #define G2048_SQUARE_SIZE 11
- #define G2048_BOARD_SIZE ((G2048_SQUARE_SIZE + 1) * 4 + 1)
- #define G2048_SQUARES 16
- #define G2048_OFFSET_X ((SAF_SCREEN_WIDTH - G2048_BOARD_SIZE) / 2)
- #define G2048_OFFSET_Y 2
- #define G2048_SAVE_SLOT 3
- #define G2048_ANIMATION_FRAMES 8
- #define G2048_ANIMATION_COUNTDOWN VAR(uint8_t,0)
- #define G2048_ANIMATION_DIRECTION VAR(uint8_t,1)
- #define G2048_WON VAR(uint8_t,2)
- #define G2048_MS_ELAPSED VAR(uint32_t,3)
- #define G2048_HIGHSCORE_COUNTDOWN VAR(uint8_t,7)
- void g2048Spawn(void)
- {
- uint8_t square = SAF_random() % G2048_SQUARES;
- for (uint8_t i = 0; i < G2048_SQUARES; ++i)
- {
- uint8_t s = (square + i) % G2048_SQUARES;
- if (memory[s] == 0)
- {
- memory[s] = (SAF_random() < 200) ? 1 : 2;
- break;
- }
- }
- }
- void g2048Init(void)
- {
- G2048_ANIMATION_COUNTDOWN = 0;
- G2048_ANIMATION_DIRECTION = 0;
- G2048_WON = 0;
- G2048_MS_ELAPSED = 0;
- G2048_HIGHSCORE_COUNTDOWN = 0;
- for (uint8_t i = 0; i < G2048_SQUARES; ++i)
- memory[i] = 0;
- g2048Spawn();
- }
- void g2048Draw(void)
- {
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_clearScreen(SAF_COLOR_GRAY);
- #else
- SAF_clearScreen(SAF_COLOR_BLACK);
- #endif
- SAF_drawRect(G2048_OFFSET_X,G2048_OFFSET_Y,
- G2048_BOARD_SIZE,G2048_BOARD_SIZE,SAF_COLOR_WHITE,1);
- uint8_t square = 0;
- uint16_t animationNominator = G2048_SQUARE_SIZE *
- (G2048_ANIMATION_FRAMES - G2048_ANIMATION_COUNTDOWN + 1);
- for (uint8_t y = 0; y < 4; ++y)
- for (uint8_t x = 0; x < 4; ++x)
- {
- uint8_t v = memory[
- G2048_ANIMATION_COUNTDOWN == 0 ? square : (G2048_SQUARES + square)];
- if (v == 0)
- {
- square++;
- continue;
- }
-
- int8_t animX = 0, animY = 0;
- if (G2048_ANIMATION_COUNTDOWN != 0)
- {
- uint16_t offset = v >> 4;
- offset = (offset * animationNominator) / G2048_ANIMATION_FRAMES;
- switch (G2048_ANIMATION_DIRECTION)
- {
- case 0: animY = -1 * offset; break;
- case 1: animX = offset; break;
- case 2: animY = offset; break;
- case 3: animX = -1 * offset; break;
- default: break;
- }
- v &= 0x0f;
- }
- uint8_t drawX = G2048_OFFSET_X + 1 + x * (G2048_SQUARE_SIZE + 1) + animX,
- drawY = G2048_OFFSET_Y + 1 + y * (G2048_SQUARE_SIZE + 1) + animY;
- uint8_t color = SAF_COLOR_BLACK,
- color2 = SAF_COLOR_GRAY_DARK;
- #if SAF_PLATFORM_COLOR_COUNT == 2
- color2 = SAF_COLOR_WHITE;
- #else
- switch (v)
- {
- case 1: color = SAF_COLOR_RGB(31,222,79); break;
- case 2: color = SAF_COLOR_RGB(122,120,235); break;
- case 3: color = SAF_COLOR_RGB(235,123,89); break;
- case 4: color = SAF_COLOR_RGB(179,107,77); break;
- case 5: color = SAF_COLOR_RGB(237,162,36); break;
- case 6: color = SAF_COLOR_RGB(209,17,42); break;
- case 7: color = SAF_COLOR_RGB(13,189,148); break;
- case 8: color = SAF_COLOR_RGB(207,112,190); break;
- case 9: color = SAF_COLOR_RGB(156,62,115);
- color2 = SAF_COLOR_WHITE; break;
- case 10: color = SAF_COLOR_RGB(80,156,36);
- color2 = SAF_COLOR_WHITE; break;
- case 11: color = SAF_COLOR_RGB(224,126,20); break;
- default: color = SAF_COLOR_RGB(122,15,5);
- color2 = SAF_COLOR_GRAY; break;
- }
- #endif
- SAF_drawRect(drawX,drawY,
- G2048_SQUARE_SIZE,G2048_SQUARE_SIZE,color,1);
- char t[3] = " ";
- uint8_t plusX = 4;
- if (v < 10)
- t[0] = '0' + v;
- else
- {
- t[0] = '1';
- t[1] = '0' + v - 10;
- plusX = 0;
- }
- SAF_drawText(t,drawX + plusX,drawY + (G2048_SQUARE_SIZE - 4) / 2,
- color2,1);
- square++;
- }
- SAF_drawRect(
- 0,SAF_SCREEN_HEIGHT - 6,SAF_SCREEN_WIDTH,6,SAF_COLOR_GRAY_DARK,1);
- char timeText[8];
- SAF_drawText(SAF_intToStr(G2048_MS_ELAPSED / 1000,timeText),2,
- SAF_SCREEN_HEIGHT - 5,SAF_COLOR_WHITE,1);
- if (G2048_WON)
- SAF_drawText("WIN",SAF_SCREEN_WIDTH - 18,SAF_SCREEN_HEIGHT - 5,
- SAF_COLOR_YELLOW,1);
- if (G2048_HIGHSCORE_COUNTDOWN)
- blinkHighScore();
- }
- int8_t g2048NextSquare(int8_t square, uint8_t direction)
- {
- switch (direction)
- {
- case 0: return (square >= 4) ? square - 4 : -1; break; // up
- case 1: return (square % 4 != 3) ? square + 1 : -1; break; // right
- case 2: return (square < 12) ? square + 4 : -1; break; // down
- case 3: return (square % 4 != 0) ? square - 1 : -1; break; // left
- default: return -1;
- }
- }
- uint8_t g2048Shift(uint8_t direction)
- {
- uint8_t shifted = 0;
- int8_t square = (direction == 1 || direction == 2) ? G2048_SQUARES - 1 : 0;
- int8_t nextLineOffset = 0;
- uint8_t oppositeDirection = (direction + 2) % 4;
- // make a copy of the board that will be used for animation:
- for (uint8_t i = 0; i < G2048_SQUARES; ++i)
- memory[G2048_SQUARES + i] = memory[i];
- switch (direction)
- {
- case 0: nextLineOffset = -4 * 3 + 1; break;
- case 1: nextLineOffset = -1; break;
- case 2: nextLineOffset = 4 * 3 - 1; break;
- case 3: nextLineOffset = 1; break;
- default: break;
- }
- for (uint8_t i = 0; i < 4; ++i) // for each "line"
- {
- while (1) // for each square in the "line"
- {
- int8_t nextSquare = square;
- uint8_t *animSquare = memory + G2048_SQUARES + square;
- uint8_t distance = 0;
- uint8_t squareVal = memory[square];
- if (squareVal != 0)
- {
- while (1) // slide the square
- {
- int8_t previouSquare = nextSquare;
- nextSquare = g2048NextSquare(nextSquare,direction);
- if (nextSquare < 0)
- break; // end of board
- uint8_t squareVal2 = memory[nextSquare];
- if (squareVal2 == squareVal)
- {
- memory[nextSquare] = squareVal + 1;
- if (squareVal == 10)
- {
- G2048_WON = 1;
- uint16_t score = getHiScore(G2048_SAVE_SLOT);
- if (score == 0 || G2048_MS_ELAPSED / 1000 < score)
- {
- saveHiScore(G2048_SAVE_SLOT,G2048_MS_ELAPSED / 1000);
- G2048_HIGHSCORE_COUNTDOWN = 75;
- }
- }
- memory[previouSquare] = 0;
- shifted = 1;
- distance++;
- break;
- }
- if (squareVal2 == 0)
- {
- memory[nextSquare] = squareVal;
- memory[previouSquare] = 0;
- shifted = 1;
- distance++;
- }
- else
- break;
- }
- *animSquare |= (distance << 4);
- }
- nextSquare = g2048NextSquare(square,oppositeDirection);
- if (nextSquare < 0)
- {
- square += nextLineOffset;
- break;
- }
- square = nextSquare;
- }
- }
- if (shifted)
- {
- G2048_ANIMATION_COUNTDOWN = G2048_ANIMATION_FRAMES;
- G2048_ANIMATION_DIRECTION = direction;
- }
- return shifted;
- }
- void g2048Step(void)
- {
- if (SAF_buttonPressed(SAF_BUTTON_B) >= BUTTON_HOLD_PERIOD)
- quitGame();
- G2048_MS_ELAPSED += SAF_MS_PER_FRAME;
- if (G2048_HIGHSCORE_COUNTDOWN > 0)
- G2048_HIGHSCORE_COUNTDOWN -= 1;
- if (G2048_ANIMATION_COUNTDOWN == 0)
- {
- int8_t direction = -1;
- if (SAF_buttonJustPressed(SAF_BUTTON_UP))
- direction = 0;
- else if (SAF_buttonJustPressed(SAF_BUTTON_RIGHT))
- direction = 1;
- else if (SAF_buttonJustPressed(SAF_BUTTON_DOWN))
- direction = 2;
- else if (SAF_buttonJustPressed(SAF_BUTTON_LEFT))
- direction = 3;
- if (direction >= 0)
- if (g2048Shift(direction))
- g2048Spawn();
- }
- else
- {
- G2048_ANIMATION_COUNTDOWN -= 1;
- }
- g2048Draw();
- }
- // RUNNER ----------------------------------------------------------------------
- /* Memory is a list of obstacles, terminated by 0. Each obstacle takes two
- bytes: 1st is type, 2nd is x position. */
- #define RUNNER_OBSTACLE_NONE 0
- #define RUNNER_OBSTACLE_WALL 1
- #define RUNNER_OBSTACLE_CEILING 2
- #define RUNNER_OBSTACLE_WALL_TALL 3
- #define RUNNER_OBSTACLE_WALL_LONG 4
- #define RUNNER_OBSTACLE_MOVING 5
- #define RUNNER_REAL_OBSTACLES 5
- #define RUNNER_OBSTACLE_BONUS 6 // item: extra points
- #define RUNNER_OBSTACLE_SHIELD 7 // item: protection
- #define RUNNER_SCORE_OBSTACLE 2
- #define RUNNER_SCORE_BONUS 5
- #define RUNNER_PLAYER_WIDTH 5
- #define RUNNER_PLAYER_HEIGHT 8
- #define RUNNER_PLAYER_X_POSITION 16
- #define RUNNER_GROUND_POS 40
- #define RUNNER_RENDER_X_OFFSET 8 // so that obstacles go whole behind L border
- #define RUNNER_PLAYER_PHASE_INCREASE 7
- #define RUNNER_SAVE_SLOT 4
- #define RUNNER_POSITION VAR(uint16_t,0)
- #define RUNNER_PLAYER_PHASE VAR(uint8_t,2)
- #define RUNNER_NEXT_OBSTACLE_IN VAR(uint8_t,3)
- #define RUNNER_SHIELD_COUNTDOWN VAR(uint8_t,4)
- #define RUNNER_STATE VAR(uint8_t,5) // 0: play, 1: lost, 2: lost, high score
- #define RUNNER_SCORE VAR(uint16_t,6)
- uint8_t runnerLevel(void)
- {
- return (RUNNER_POSITION >> 8) & 0xff;
- }
- void runnerAddObstacle(uint8_t type)
- {
- uint8_t i = 0;
- while (memory[i] != RUNNER_OBSTACLE_NONE)
- i += 2;
- memory[i] = type;
- i++;
- memory[i] = SAF_SCREEN_WIDTH + RUNNER_RENDER_X_OFFSET;
- i++;
- memory[i] = RUNNER_OBSTACLE_NONE; // terminate
- uint8_t level = runnerLevel();
- if (level > 10)
- level = 10;
- int8_t minDistance = 25 - level;
- uint8_t additionalDistance = SAF_random() % (18 - level);
- RUNNER_NEXT_OBSTACLE_IN = minDistance + additionalDistance;
- }
- uint8_t runnerObstacleIsTakeable(uint8_t type)
- {
- return type == RUNNER_OBSTACLE_BONUS || type == RUNNER_OBSTACLE_SHIELD;
- }
- void runnerGetObstacleSize(uint8_t type, uint8_t position,
- uint8_t *width, uint8_t *height, uint8_t *elevation)
- {
- *width = RUNNER_PLAYER_WIDTH;
- *height = (RUNNER_PLAYER_HEIGHT * 3) / 4;
- *elevation = 0;
- switch (type)
- {
- case RUNNER_OBSTACLE_WALL_TALL:
- *width -= 1; *height = RUNNER_PLAYER_HEIGHT;
- break;
- case RUNNER_OBSTACLE_WALL_LONG:
- *width = (*width * 7) / 4; *height /= 2;
- break;
- case RUNNER_OBSTACLE_CEILING:
- *elevation = RUNNER_PLAYER_HEIGHT - 2;
- break;
- case RUNNER_OBSTACLE_MOVING:
- *width -= 2;
- *elevation =
- (SAF_cos((RUNNER_POSITION * 4 - position * 2) & 0xff) + 128) / 32;
- break;
- case RUNNER_OBSTACLE_BONUS:
- case RUNNER_OBSTACLE_SHIELD:
- *width = 4; *height = 4; *elevation = (RUNNER_PLAYER_HEIGHT * 10) / 4;
- break;
- default: break;
- }
- }
- void runnerInit(void)
- {
- RUNNER_POSITION = 0;
- RUNNER_PLAYER_PHASE = 0;
- RUNNER_NEXT_OBSTACLE_IN = 0;
- RUNNER_SHIELD_COUNTDOWN = 0;
- RUNNER_STATE = 0;
- RUNNER_SCORE = 0;
- memory[0] = RUNNER_OBSTACLE_NONE;
- }
- uint8_t runnerPlayerHeight(void)
- {
- return SAF_sin(RUNNER_PLAYER_PHASE) / 8;
- }
- uint8_t runnerPlayerTallness(void)
- {
- return RUNNER_PLAYER_PHASE != 255 ? RUNNER_PLAYER_HEIGHT :
- (RUNNER_PLAYER_HEIGHT / 2);
- }
- void runnerRemoveObstacle(uint8_t index)
- {
- while (memory[index] != RUNNER_OBSTACLE_NONE)
- {
- memory[index] = memory[index + 2];
- memory[index + 1] = memory[index + 3];
- index += 2;
- }
- }
- void runnerResolveCollisions(void)
- {
- uint8_t i = 0;
- uint8_t playerFrom = runnerPlayerHeight(),
- playerTo = playerFrom + runnerPlayerTallness();
- while (memory[i] != RUNNER_OBSTACLE_NONE)
- {
- uint8_t w, h, e;
- uint8_t pos = memory[i + 1];
- uint8_t obstacle = memory[i];
- runnerGetObstacleSize(obstacle,pos,&w,&h,&e);
- if (pos >= RUNNER_PLAYER_X_POSITION + RUNNER_PLAYER_WIDTH)
- break;
- uint8_t obstacleTo = e + h;
- if (pos + w > RUNNER_PLAYER_X_POSITION &&
- ((playerFrom >= e && playerFrom < obstacleTo) ||
- (playerTo >= e && playerTo < obstacleTo)))
- {
- if (runnerObstacleIsTakeable(memory[i]))
- runnerRemoveObstacle(i);
- if (obstacle == RUNNER_OBSTACLE_SHIELD)
- {
- RUNNER_SHIELD_COUNTDOWN = 255;
- SAF_playSound(SAF_SOUND_CLICK);
- }
- else if (obstacle == RUNNER_OBSTACLE_BONUS)
- {
- RUNNER_SCORE += RUNNER_SCORE_BONUS;
- SAF_playSound(SAF_SOUND_CLICK);
- }
- else
- {
- if (RUNNER_SHIELD_COUNTDOWN == 0)
- {
- RUNNER_STATE = 1;
-
- SAF_playSound(SAF_SOUND_BOOM);
- uint16_t score = getHiScore(RUNNER_SAVE_SLOT);
- if (RUNNER_SCORE > score)
- {
- saveHiScore(RUNNER_SAVE_SLOT,RUNNER_SCORE);
- RUNNER_STATE = 2;
- }
- }
- else
- {
- runnerRemoveObstacle(i);
- RUNNER_SHIELD_COUNTDOWN = 0;
- }
- }
- return;
- }
- i += 2;
- }
- }
- void runnerDraw(void)
- {
- SAF_drawRect(0,0,SAF_SCREEN_WIDTH,RUNNER_GROUND_POS,SAF_COLOR_WHITE,1);
- uint8_t groundPos = RUNNER_POSITION % 32;
- for (uint8_t i = 0; i < SAF_SCREEN_WIDTH; ++i) // draw ground
- {
- for (uint8_t j = RUNNER_GROUND_POS; j < SAF_SCREEN_HEIGHT; ++j)
- SAF_drawPixel(i,j,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- groundPos & 0x10 ? SAF_COLOR_GRAY : SAF_COLOR_GRAY_DARK
- #else
- (i % 2 == j % 2) ? SAF_COLOR_BLACK : SAF_COLOR_WHITE
- #endif
- );
- groundPos++;
- }
- uint8_t height = runnerPlayerTallness(),
- color = (RUNNER_STATE == 0) ? SAF_COLOR_BLACK :
- (SAF_frame() & 0x2 ? SAF_COLOR_BLACK : SAF_COLOR_WHITE),
- dx = RUNNER_PLAYER_X_POSITION - RUNNER_RENDER_X_OFFSET,
- dy = RUNNER_GROUND_POS - height - runnerPlayerHeight();
- // draw the player:
- SAF_drawRect(dx,dy,RUNNER_PLAYER_WIDTH,height,color,1);
- if (RUNNER_SHIELD_COUNTDOWN != 0)
- SAF_drawCircle(dx + RUNNER_PLAYER_WIDTH / 2,dy + RUNNER_PLAYER_HEIGHT / 2,7,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_COLOR_BLUE,
- #else
- SAF_COLOR_BLACK,
- #endif
- 0);
- uint8_t i = 0;
- while (1) // draw obstacles
- {
- uint8_t obstacle = memory[i];
- if (obstacle == RUNNER_OBSTACLE_NONE)
- break;
- uint8_t w, h, e;
- runnerGetObstacleSize(obstacle,memory[i + 1],&w,&h,&e);
- dx = memory[i + 1] - RUNNER_RENDER_X_OFFSET;
- dy = RUNNER_GROUND_POS - h - e;
- if (runnerObstacleIsTakeable(obstacle))
- SAF_drawCircle(dx,dy,w / 2,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- obstacle == RUNNER_OBSTACLE_BONUS ?
- SAF_COLOR_YELLOW : SAF_COLOR_BLUE,1
- #else
- SAF_COLOR_BLACK, obstacle == RUNNER_OBSTACLE_BONUS
- #endif
- );
- else
- SAF_drawRect(dx,dy,w,h,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_COLOR_RED_DARK,
- #else
- SAF_COLOR_BLACK,
- #endif
- 1);
-
- i += 2;
- }
- char text[12];
- SAF_intToStr(RUNNER_POSITION / 8,text);
- uint8_t p = 0;
- while (text[p] != 0)
- p++;
- text[p] = 'M';
- text[p + 1] = 0;
- SAF_drawText(text,2,2,
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_COLOR_GRAY
- #else
- SAF_COLOR_BLACK
- #endif
- ,1);
- drawTextRightAlign(SAF_SCREEN_WIDTH - 2,2,SAF_intToStr(RUNNER_SCORE,text),
- SAF_COLOR_BLACK,1);
- if (RUNNER_STATE == 2)
- blinkHighScore();
- }
- void runnerScroll(void)
- {
- RUNNER_POSITION += 1;
- uint8_t i = 0;
- while (memory[i] != RUNNER_OBSTACLE_NONE)
- {
- memory[i + 1] -= 1;
- i += 2;
- }
- uint8_t addScore = 0;
- if (memory[0] != RUNNER_OBSTACLE_NONE && memory[1] == 0)
- {
- addScore = memory[0] != RUNNER_OBSTACLE_SHIELD &&
- memory[0] != RUNNER_OBSTACLE_BONUS;
- // remove the obstacle once it's at position 0
- runnerRemoveObstacle(0);
- }
- if (addScore)
- RUNNER_SCORE += RUNNER_SCORE_OBSTACLE;
- }
- void runnerStep(void)
- {
- runnerDraw();
- if (SAF_buttonPressed(SAF_BUTTON_B) >= BUTTON_HOLD_PERIOD)
- quitGame();
- if (RUNNER_STATE != 0)
- {
- if (SAF_buttonJustPressed(SAF_BUTTON_A) ||
- SAF_buttonJustPressed(SAF_BUTTON_B))
- runnerInit();
- return;
- }
- uint8_t duck = SAF_buttonPressed(SAF_BUTTON_DOWN);
- if (RUNNER_PLAYER_PHASE == 0)
- {
- if (SAF_buttonJustPressed(SAF_BUTTON_A) || SAF_buttonPressed(SAF_BUTTON_UP))
- {
- // jump:
- RUNNER_PLAYER_PHASE = RUNNER_PLAYER_PHASE_INCREASE;
- SAF_playSound(SAF_SOUND_BUMP);
- }
- else if (duck)
- RUNNER_PLAYER_PHASE = 255;
- }
- else if (RUNNER_PLAYER_PHASE != 255)
- {
- RUNNER_PLAYER_PHASE += RUNNER_PLAYER_PHASE_INCREASE;
- if (RUNNER_PLAYER_PHASE > 128)
- RUNNER_PLAYER_PHASE = 0;
- }
- else
- RUNNER_PLAYER_PHASE = duck ? 255 : 0;
- if (RUNNER_SHIELD_COUNTDOWN != 0)
- RUNNER_SHIELD_COUNTDOWN -= 1;
- runnerScroll();
- runnerResolveCollisions();
- if (RUNNER_NEXT_OBSTACLE_IN == 0)
- {
- uint8_t level = runnerLevel() + 1;
-
- if (level > RUNNER_REAL_OBSTACLES)
- level = RUNNER_REAL_OBSTACLES;
-
- uint8_t r = SAF_random();
- if (r > 240)
- {
- runnerAddObstacle(r > 245 ? RUNNER_OBSTACLE_BONUS
- : RUNNER_OBSTACLE_SHIELD);
- }
- else
- runnerAddObstacle(SAF_random() % level + 1);
- }
- else
- RUNNER_NEXT_OBSTACLE_IN -= 1;
- }
- // -----------------------------------------------------------------------------
- uint8_t menuItem = 0;
- uint8_t firstClick = 0;
- void menuStep(void)
- {
- SAF_clearScreen(SAF_COLOR_WHITE);
- SAF_drawCircle(13,47,17,SAF_COLOR_GRAY_DARK,1);
- SAF_drawRect(0,29,SAF_SCREEN_WIDTH,6,SAF_COLOR_GREEN,1);
- #if SAF_PLATFORM_COLOR_COUNT > 2
- SAF_drawText(gameNames[menuItem],12,25,SAF_COLOR_GRAY,2);
- #endif
- SAF_drawText(gameNames[menuItem],11,24,SAF_COLOR_BLACK,2);
- uint16_t score = getHiScore(menuItem);
- char scoreText[6] = "XXX";
- if (score != 0)
- SAF_intToStr(score,scoreText);
- SAF_drawText(scoreText,score < 10000 ? 6 : 1,45,SAF_COLOR_WHITE,1);
- if (buttonPressedOrHeld(SAF_BUTTON_RIGHT))
- {
- menuItem = (menuItem + 1) % GAMES;
- SAF_playSound(SAF_SOUND_CLICK);
- }
- else if (buttonPressedOrHeld(SAF_BUTTON_LEFT))
- {
- menuItem = (menuItem > 0) ? menuItem - 1 : (GAMES - 1);
- SAF_playSound(SAF_SOUND_CLICK);
- }
- if (SAF_buttonPressed(SAF_BUTTON_A))
- {
- if (!firstClick)
- {
- SAF_randomSeed(SAF_frame()); // create a somewhat random initial seed
- firstClick = 1;
- }
- switch (menuItem)
- {
- case 0: snakeInit(); stepFunction = &snakeStep; break;
- case 1: minesInit(); stepFunction = &minesStep; break;
- case 2: blocksInit(); stepFunction = &blocksStep; break;
- case 3: g2048Init(); stepFunction = &g2048Step; break;
- case 4: runnerInit(); stepFunction = &runnerStep; break;
- default: break;
- }
- SAF_playSound(SAF_SOUND_CLICK);
- }
- }
- void SAF_init(void)
- {
- stepFunction = &menuStep;
- }
- uint8_t SAF_loop(void)
- {
- stepFunction();
- return 1;
- }
|