Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
main.c
- Committer:
- HomineLudens
- Date:
- 2018-05-15
- Revision:
- 2:6d5ef2d31e52
- Parent:
- 0:b0c9d3b227f6
File content as of revision 2:6d5ef2d31e52:
#include "Pokitto.h"
#include <cstddef>
#include "gfx.h"
#include "math.h"
#define GRID_COLUMNS 4
#define GRID_ROWS 4
#define TILE_SIZE 40
#define SHUFFLE_MOVES 20
#define MAX_LEVEL 7
Pokitto::Core g;
uint8_t ShuffleCount=0;
uint16_t PlayerMoves=0;
int8_t LevelActive=0;
int8_t TextColor=7;
bool HighlightNumbers=false;
uint16_t FrameCount=0;
char *AuthorActive;
uint16_t Score=0;
typedef enum
{
Intro,
LoadAndShuffle,
Game,
Menu,
Victory,
Outro,
GameOver
} SSM;
SSM ssm=SSM::Intro;
struct Tile
{
int8_t val=0;
int8_t c=0;
float xa=0;
float ya=0;
float speed=0;
float dir=0;
};
Tile tiles[GRID_ROWS][GRID_COLUMNS];
const char * Menus[]= {"Back","Title screen","Select level","Shuffle"};
const uint8_t MenusSize = sizeof(Menus) / sizeof(Menus[0]);
typedef enum
{
Back,
TitleScreen,
SelectLevel,
Shuffle
} MenuItems;
MenuItems menuItem=MenuItems::Back;
const char * Info =
{
"Pok15 is the remake of the classic 15-puzzle.\r\n"
"Purpose of the game:slide all tiles in order.\r\n"
"Press B to highlight.\r\n"
"\r\n"
"Have fun."
};
void InitTiles()
{
int8_t i=0;
for (int8_t r=0; r<GRID_ROWS; r++)
{
for (int8_t c=0; c<GRID_COLUMNS; c++)
{
tiles[r][c].val=i;
tiles[r][c].c=i;
tiles[r][c].xa=0;
tiles[r][c].ya=0;
tiles[r][c].speed=random(10,20);
tiles[r][c].dir=random(0,2*PI);
i++;
}
}
}
//This kind of magic thanks to @Pherap the C++ Guru
template<std::size_t numberOfFrames, std::size_t frameSize>
void DrawTiles(const uint16_t * palette, const uint8_t (&images)[numberOfFrames][frameSize],bool drawBorder,bool drawNumbers,bool highlightNumbers, bool drawFull )
{
int x=0;
int y=0;
//Load/reload palette
g.display.load565Palette(palette);
//Center on screen
int xo = (g.display.width - (TILE_SIZE*GRID_COLUMNS))/2;
int yo = (g.display.height - (TILE_SIZE*GRID_ROWS))-4;
for (int8_t r=0; r<GRID_ROWS; r++)
{
for (int8_t c=0; c<GRID_COLUMNS; c++)
{
if(drawFull || tiles[r][c].val!=15 )
{
x=c*TILE_SIZE+tiles[r][c].xa;
y=r*TILE_SIZE+tiles[r][c].ya;
//Draw flat tile
g.display.setColor(tiles[r][c].c);
//g.display.fillRect(xo+x,yo+y,TILE_SIZE,TILE_SIZE);
//Draw image
g.display.drawBitmap(xo+x,yo+y,images[tiles[r][c].val]);
if(drawBorder)
{
//Draw border
g.display.setColor(7);
g.display.drawRect(xo+x,yo+y,TILE_SIZE,TILE_SIZE);
}
if(drawNumbers)
{
//Draw number of the tile1
if(highlightNumbers)
g.display.setInvisibleColor(255);
g.display.setColor(TextColor,0);
g.display.print(xo+x+1/2+ (g.display.fontWidth/2),
yo+y+1/2+(g.display.fontHeight/2),
tiles[r][c].val+1);//val + 1
g.display.setColor(0,0);
g.display.setInvisibleColor(0);
}
}
}
}
}
template <typename T> T
ClampVal(const T& value, const T& low, const T& high)
{
return value < low ? low : (value > high ? high : value);
}
bool SwapTiles(int8_t fx,int8_t fy,int8_t tx,int8_t ty)
{
//Verify if allowed
if(tx>=0 && ty>=0 && tx<GRID_COLUMNS && ty<GRID_ROWS)
{
Tile f=tiles[fx][fy];
Tile t=tiles[tx][ty];
tiles[tx][ty]=f;
tiles[fx][fy]=t;
return true;
}
return false;
}
bool MoveTiles(int8_t dx,int8_t dy)
{
//Find movable tile
int8_t re=0;
int8_t ce=0;
for (int8_t r=0; r<GRID_ROWS; r++)
{
for (int8_t c=0; c<GRID_COLUMNS; c++)
{
//found
if(tiles[r][c].val==15)
{
re=r;
ce=c;
break;
}
}
}
return SwapTiles(re,ce,re+dx,ce+dy);
}
void ShuffleTiles()
{
bool moved=false;
do
{
switch (random(0,4))
{
case 0:
moved=MoveTiles(1,0);
break;
case 1:
moved=MoveTiles(-1,0);
break;
case 2:
moved=MoveTiles(0,1);
break;
case 3:
moved=MoveTiles(0,-1);
break;
default:
moved=false;
break;
}
}
while(!moved);
ShuffleCount--;
}
bool IsWinning()
{
//During shuffle win is not valid
if(ShuffleCount>0) return false;
int8_t index=0;
for (int8_t r=0; r<GRID_ROWS; r++)
{
for (int8_t c=0; c<GRID_COLUMNS; c++)
{
//if reach last tile --> Wins!
if (index==15)
{
return true;
}
//If wrong index --> Not Win.
if(tiles[r][c].val!=index)
{
return false;
}
index++;
}
}
}
bool A_B_Pressed()
{
return g.buttons.pressed(BTN_A) || g.buttons.pressed(BTN_B);
}
void PlayerInput()
{
//Move on key pressed
bool moved=false;
if(g.buttons.pressed(BTN_UP))
{
moved=MoveTiles(1,0);
}
if(g.buttons.pressed(BTN_DOWN))
{
moved=MoveTiles(-1,0);
}
if(g.buttons.pressed(BTN_LEFT))
{
moved=MoveTiles(0,1);
}
if(g.buttons.pressed(BTN_RIGHT))
{
moved=MoveTiles(0,-1);
}
//Count valid moves
if(moved)
{
PlayerMoves++;
}
//Cheat to win
if(g.buttons.upBtn() && g.buttons.aBtn() )
{
ssm=SSM::Victory;
}
if(g.buttons.pressed(BTN_B))
{
HighlightNumbers=!HighlightNumbers;
}
//Enter Menu
if(g.buttons.cBtn())
{
menuItem=MenuItems::Back;
ssm=SSM::Menu;
}
}
void DrawLevel(int level,bool drawBorder,bool drawNumbers,bool highlightNumbers,bool drawFull)
{
switch(level)
{
case 0:
DrawTiles(jonne_pal,jonne,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="Art by @jonne";
break;
case 1:
DrawTiles(Vampirics_pal,Vampirics,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by Vampirics";
break;
case 2:
DrawTiles(adekto_pal,adekto,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by @adekto";
break;
case 3:
DrawTiles(JAP_pal,JAP,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by JAP";
break;
case 4:
DrawTiles(lancelot_gao_pal,lancelot_gao,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by lancelot_gao";
break;
case 5:
DrawTiles(jonne2_pal,jonne2,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by @jonne";
break;
case 6:
DrawTiles(buch_pal,buch,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by Buch";
break;
case 7:
DrawTiles(jonne3_pal,jonne3,drawBorder,drawNumbers,highlightNumbers,drawFull);
TextColor=7;
AuthorActive="by @jonne";
break;
default:
break;
}
}
void DrawGameGUI()
{
//Draw moves
g.display.color=TextColor;
g.display.setCursor(0,2);
g.display.print("Mov:");
g.display.print(PlayerMoves);
//Draw score
g.display.color=TextColor;
g.display.setCursor((g.display.width-(9*g.display.fontWidth))/2,2);
g.display.print("Score:");
g.display.print(Score);
//Draw Level
g.display.color=TextColor;
g.display.setCursor(g.display.width-(5*g.display.fontWidth),2);
g.display.print("Lvl:");
g.display.print(LevelActive+1);
}
void PrintCenter(uint8_t x,uint8_t y,char text[],int lenght)
{
g.display.print(x-((lenght*g.display.fontWidth)/2),
y-(g.display.fontHeight/2),
text);
}
void StateIntro()
{
g.display.load565Palette(Title_pal);
g.display.drawBitmap(0,20,Title);
g.display.color=(FrameCount/3)%15;
PrintCenter(g.display.width/2,10,"POK15",sizeof("POK15"));
g.display.color=(FrameCount/3)%15;
PrintCenter(g.display.width/2,g.display.height-10,"@HomineLudens",sizeof("@HomineLudens"));
LevelActive=0;
Score=0;
if(A_B_Pressed())
ssm=SSM::LoadAndShuffle;
}
void StateLoadAndShuffle()
{
InitTiles();
ShuffleCount=SHUFFLE_MOVES*(LevelActive+1);
PlayerMoves=0;
ssm=SSM::Game;
}
void StateGame()
{
if(ShuffleCount>0)
{
ShuffleTiles();
}
else
{
PlayerInput();
}
DrawLevel(LevelActive,true,true,HighlightNumbers,false);
DrawGameGUI();
if(IsWinning())
{
FrameCount=0;
ssm=SSM::Victory;
}
}
void StateMenu()
{
//Load palette for menu
g.display.loadRGBPalette(palettePico);
//User input
if(g.buttons.pressed(BTN_UP))
{
menuItem=(MenuItems)((int)(menuItem)-1);
}
if(g.buttons.pressed(BTN_DOWN))
{
menuItem=(MenuItems)((int)(menuItem)+1);
}
menuItem =(MenuItems) ClampVal((int)menuItem,0,MenusSize-1);
if(menuItem==MenuItems::SelectLevel)
{
if(g.buttons.pressed(BTN_LEFT))
{
LevelActive--;
}
if(g.buttons.pressed(BTN_RIGHT))
{
LevelActive++;
}
}
LevelActive = ClampVal((int)LevelActive,0,MAX_LEVEL);
g.display.color=8;
g.display.fillRect(10,8+(menuItem)*20,150,10);
//Draw menu
for(int8_t i=0; i<MenusSize; i++)
{
g.display.color=12;
g.display.print(10,10+(i)*20,Menus[i]);
if(i==MenuItems::SelectLevel)
{
g.display.print(120,10+(i)*20, LevelActive+1);
}
}
//Info
g.display.print(0,100,Info);
//Buttons
if(g.buttons.pressed(BTN_A))
{
switch(menuItem)
{
case MenuItems::Back:
ssm=SSM::Game;
break;
case MenuItems::TitleScreen:
ssm=SSM::Intro;
break;
case MenuItems::SelectLevel:
ssm=SSM::LoadAndShuffle;
break;
case MenuItems::Shuffle:
ssm=SSM::LoadAndShuffle;
break;
}
}
}
uint16_t StringLenght(char *s)
{
uint16_t l=0;
while(true)
{
if(s[l]==NULL)
return l;
l++;
}
}
void StateVictory()
{
g.display.clear();
DrawLevel(LevelActive,false,false,false,true);
//Draw author
g.display.color=random(15);
g.display.print((g.display.width - strlen(AuthorActive)*g.display.fontWidth)/2 ,0,AuthorActive);
g.display.color=random(15);
g.display.drawRect(g.display.width/2-80,10,160,160);
if(A_B_Pressed() || FrameCount>600)
{
Score+=(LevelActive+1)*1000/(PlayerMoves+1);
ssm=SSM::Outro;
}
}
void StateOutro()
{
g.display.persistence=true;
g.display.setInvisibleColor(255);
int xo = (g.display.width - (TILE_SIZE*GRID_COLUMNS))/2;
int yo = (g.display.height - (TILE_SIZE*GRID_ROWS))/2;
int x,y;
float dx,dy;
for (int8_t r=0; r<GRID_ROWS; r++)
{
for (int8_t c=0; c<GRID_COLUMNS; c++)
{
//Detect walls to bump
x=xo+c*TILE_SIZE+tiles[r][c].xa;
y=yo+r*TILE_SIZE+tiles[r][c].ya;
if(x<0 || x>g.display.width-TILE_SIZE)
{
tiles[r][c].dir=-tiles[r][c].dir;
}
else if(y<0 || y>g.display.height-TILE_SIZE)
{
tiles[r][c].dir=PI-tiles[r][c].dir;
}
//Friction
tiles[r][c].speed*=0.99f;
//Apply forces
dx=tiles[r][c].speed * sin(tiles[r][c].dir);
dy=(tiles[r][c].speed * cos(tiles[r][c].dir))+0.9f;
//Clamp on walls
if(x<0 && dx<0) dx=0;
if(x>g.display.width-TILE_SIZE && dx>0) dx=0;
if(y<0 && dy<0) dy=0;
if(y>g.display.height-TILE_SIZE && dy>0) dy=0;
//Apply movement
tiles[r][c].xa+=dx;
tiles[r][c].ya+=dy;
}
}
DrawLevel(LevelActive,true,false,false,true);
//Draw win sign
g.display.color=random(0);
g.display.fillRect((g.display.width-(g.display.fontWidth*10))/2,
(g.display.height-g.display.fontHeight*3)/2,
g.display.fontWidth*12,
g.display.fontHeight*4);
g.display.color=random(0,15);
g.display.drawRect((g.display.width-(g.display.fontWidth*10))/2,
(g.display.height-g.display.fontHeight*3)/2,
g.display.fontWidth*12,
g.display.fontHeight*4);
g.display.color=random(0,15);
g.display.print((g.display.width-(g.display.fontWidth*7))/2,
(g.display.height-g.display.fontHeight)/2,
"WELL DONE");
//g.display.color=random(0,15);
g.display.setCursor((g.display.width-(g.display.fontWidth*8))/2,
(g.display.height-g.display.fontHeight)/2+10);
g.display.print("Score:");
g.display.print(Score);
if(A_B_Pressed())
{
g.display.persistence=false;
g.display.setInvisibleColor(0);
LevelActive++;
if(LevelActive<=MAX_LEVEL)
ssm=SSM::LoadAndShuffle;
else
ssm=SSM::GameOver;
}
}
void StateGameOver()
{
PrintCenter(g.display.width/2,g.display.height/2,"GAME OVER",sizeof("GAME OVER"));
DrawGameGUI();
if(A_B_Pressed())
{
ssm=SSM::Intro;
}
if(g.buttons.cBtn())
{
menuItem=MenuItems::Back;
ssm=SSM::Menu;
}
}
int main ()
{
g.begin(); // start the application
g.setFrameRate(20);
g.display.font = fontC64;
g.display.setInvisibleColor(0);
while (g.isRunning())
{
if (g.update())
{
switch(ssm)
{
case SSM::Intro:
StateIntro();
break;
case SSM::Menu:
StateMenu();
break;
case SSM::LoadAndShuffle:
StateLoadAndShuffle();
break;
case SSM::Game:
StateGame();
break;
case SSM::Victory:
StateVictory();
break;
case SSM::Outro:
StateOutro();
break;
case SSM::GameOver:
StateGameOver();
break;
default:
break;
}
FrameCount++;
}
}
return 0; // this is "good programming manners". Program informs it ended without errors
}