renamed project because it is to cool for stupid name

This commit is contained in:
tixiv 2008-12-03 05:40:16 +00:00
commit fe14b29d15
106 changed files with 16442 additions and 0 deletions

115
games/invader_draw.c Normal file
View file

@ -0,0 +1,115 @@
#include "invaders2.h"
/*----------------------getter/setter----------------------------*/
unsigned char getInvaderPixel(Invaders * iv, unsigned char x, unsigned char y)
{
if (((x - iv->pos.x) < MAX_INVADER_WIDTH) && ((x - iv->pos.x) >= 0) && ((y
- iv->pos.y) < MAX_INVADER_HEIGHT) && ((y - iv->pos.y) >= 0))
{
return iv->map[x - iv->pos.x][y - iv->pos.y];
}
return 0;
}
void setInvaderPixel(Invaders * iv, unsigned char x, unsigned char y,
unsigned char val)
{
if (((x - iv->pos.x) < MAX_INVADER_WIDTH) && ((x - iv->pos.x) >= 0) && ((y
- iv->pos.y) < MAX_INVADER_HEIGHT) && ((y - iv->pos.y) >= 0))
{
iv->map[x - iv->pos.x][y - iv->pos.y] = val;
}
}
unsigned char getGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
unsigned char y)
{
if (x < BORG_WIDTH && y == GUARD_LINE)
return guards[x];
return 0;
}
void setGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
unsigned char y, unsigned char val)
{
if (x < BORG_WIDTH && y == GUARD_LINE && val <= 3)
guards[x] = val;
}
/*----------------------drawing Method---------------------------*/
void draw(Invaders * iv, Spaceship * sc, Player * pl, Cannon * cn,
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot)
{
clearScreen ();
int x, y;
/*---SPACESHIP---*/
if (sc->pos < RIGHT_BORDER)
{
setPixel (sc->pos, SPACESHIP_LINE, sc->lives);
}
if (sc->pos - 1 < RIGHT_BORDER)
{
setPixel (sc->pos + 1, SPACESHIP_LINE, sc->lives);
}
/*---INVADERS--*/
for (y = 0; y < MAX_INVADER_HEIGHT; y++)
{
for (x = 0; x < MAX_INVADER_WIDTH; x++)
{
//mal in oder statement umwandeln ;-)
if (iv->map[x][y] == 0)
continue;
if (x + iv->pos.x > RIGHT_BORDER)
continue;
if (x + iv->pos.x < 0)
continue;
setPixel (x + iv->pos.x, y + iv->pos.y, iv->map[x][y]);
}
}
/*---GUARDS---*/
for (x = 0; x < BORG_WIDTH; ++x)
{
if (guards[x] != 0)
{
setPixel (x, GUARD_LINE, guards[x]);
}
}
/*---SHOTS--*/
int i;
for (i = 0; i < MAX_SHOTS; ++i)
{
if (st[i].x < BORG_WIDTH && st[i].y < BORG_HEIGHT)
{
setPixel (st[i].x, st[i].y, 3);
}
}
/*draw player shot */
if (!(cn->ready))
{
setPixel (shot->x, shot->y, 3);
}
/*-- CANNON --*/
if (cn->pos >= LEFT_BORDER + 1)
{
setPixel (cn->pos - 1, 15, pl->lives);
}
if (cn->pos < BORG_WIDTH)
{
setPixel (cn->pos, 15, pl->lives);
setPixel (cn->pos, 14, pl->lives);
}
if (cn->pos < RIGHT_BORDER)
{
setPixel (cn->pos + 1, 15, pl->lives);
}
}

153
games/invader_init.c Normal file
View file

@ -0,0 +1,153 @@
#include "invaders2.h"
unsigned char peter[8][11] =
{
{ 0, 0, P, 0, 0, 0, 0, 0, P, 0, 0 },
{ 0, 0, 0, P, 0, 0, 0, P, 0, 0, 0 },
{ 0, 0, P, P, P, P, P, P, P, 0, 0 },
{ 0, P, P, 0, P, P, P, 0, P, P, 0 },
{ P, P, P, P, P, P, P, P, P, P, P },
{ P, 0, P, P, P, P, P, P, P, 0, P },
{ P, 0, P, 0, 0, 0, 0, 0, P, 0, P },
{ 0, 0, 0, P, P, 0, P, P, 0, 0, 0 } };
unsigned char hans[8][11] =
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1 },
{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2 },
{ 1, 2, 1, 1, 2, 2, 2, 1, 2, 2, 1 },
{ 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2 },
{ 1, 2, 2, 1, 2, 1, 2, 1, 2, 2, 2 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } };
void initGuards(unsigned char guards[BORG_WIDTH])
{
int x;
for (x = 0; x < BORG_WIDTH; ++x)
{
guards[x] = 0;
}
guards[3] = 3;
guards[6] = 3;
guards[10] = 3;
guards[13] = 3;
}
void initInvaders(Invaders * iv, unsigned char lv)
{
unsigned char x, y;
// first zero out map!
for (x = 0; x < MAX_INVADER_WIDTH; ++x)
{
for (y = 0; y < MAX_INVADER_HEIGHT; ++y)
{
iv->map[x][y] = 0;
}
}
iv->speedinc = 0;
iv->isEdged = 0;
switch (lv)
{
case 0:
for (x = 0; x < 8; ++x)
{
iv->map[x][0] = 2;
iv->map[x][1] = 2;
iv->map[x][2] = 2;
iv->map[x][3] = 1;
// iv->map[x][4] = 1;
}
iv->pos.x = 4;
iv->pos.y = SPACESHIP_LINE + 1;
iv->speed = MIN_SPEED - 3;
iv->direction = 1;
break;
case 1:
for (x = 0; x < 8; ++x)
{
//for(y = 0; y < MAX_INVADER_HEIGHT; ++y) {
iv->map[x][0] = 3;
iv->map[x][1] = 3;
iv->map[x][2] = 2;
iv->map[x][3] = 2;
// iv->map[x][4] = 1;
//}
}
iv->pos.x = 4;
iv->pos.y = SPACESHIP_LINE + 1;
iv->speed = MIN_SPEED - 2;
iv->direction = 1;
break;
case 2:
for (x = 0; x < 8; ++x)
{
//for(y = 0; y < MAX_INVADER_HEIGHT; ++y) {
iv->map[x][0] = 3;
iv->map[x][1] = 3;
iv->map[x][2] = 2;
iv->map[x][3] = 2;
iv->map[x][4] = 1;
//}
}
iv->pos.x = 4;
iv->pos.y = SPACESHIP_LINE + 1;
iv->speed = MIN_SPEED - 1;
iv->direction = 1;
break;
case 3:
for (x = 0; x < 11; ++x)
{
for (y = 0; y < 8; ++y)
{
if (hans[y][x] != 0)
{
iv->map[x][y] = 2;
}
}
}
iv->pos.x = 3;
iv->pos.y = SPACESHIP_LINE + 1;
iv->speed = MIN_SPEED + 2;
iv->direction = 1;
break;
case 4:
for (x = 0; x < 11; ++x)
{
for (y = 0; y < 8; ++y)
{
if (peter[y][x] != 0)
{
iv->map[x][y] = 2;
}
}
}
iv->pos.x = 3;
iv->pos.y = SPACESHIP_LINE + 1;
iv->speed = MIN_SPEED + 3;
iv->direction = 1;
break;
}
}

304
games/invader_proc.c Normal file
View file

@ -0,0 +1,304 @@
#include <stdlib.h>
#include "prng.h"
#include "invaders2.h"
void procCannon(Cannon * cn, uPixel * shot)
{
static unsigned char mv = 0;
if (mv >= CANNON_SPEED)
{
mv = 0;
if (JOYISLEFT)
{
if (cn->pos != RIGHT_BORDER)
{
cn->pos++;
}
}
else if (JOYISRIGHT)
{
if (cn->pos != LEFT_BORDER)
{
cn->pos--;
}
}
else if (JOYISFIRE)
{
if (cn->ready)
{
shot->x = cn->pos;
shot->y = 14;
cn->ready = 0;
}
}
}
else
{
mv++;
}
}
unsigned char areAtBorder(Invaders * iv)
{
int y;
for (y = SPACESHIP_LINE + 1; y <= GUARD_LINE; ++y)
{
if (getInvaderPixel(iv, LEFT_BORDER, y) || getInvaderPixel(iv,
RIGHT_BORDER, y))
{
return 1;
}
}
return 0;
}
void procInvaders(Invaders * iv, uPixel st[MAX_SHOTS])
{
static unsigned char mv = 0;
if (mv >= iv->speed)
{
mv = 0;
if (areAtBorder(iv) && !(iv->isEdged))
{
iv->pos.y++;
iv->direction = -iv->direction;
iv->isEdged = 1;
}
else
{
iv->pos.x += iv->direction;
iv->isEdged = 0;
}
}
mv++;
unsigned char i, y;
unsigned char spos = random8() % 16;
if (spos >= BORG_WIDTH)
return;
unsigned char shoot = random8();
if (shoot < SHOOTING_RATE)
{
for (i = 0; i < MAX_SHOTS; ++i)
{
if (st[i].x > BORG_WIDTH || st[i].y > BORG_HEIGHT)
{
for (y = GUARD_LINE; y > SPACESHIP_LINE; --y)
{
if (getInvaderPixel(iv, spos, y) != 0)
{
st[i].x = spos;
st[i].y = y + 1;
return;
}
}
}
} //for SHOTS
}
}
void procShots(Invaders * iv, Player * pl, Cannon * cn, Spaceship * sc,
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot)
{
int i;
static unsigned char cmv = 0, imv = 0;
// shuß mit einen struct mit dem shuß!!
if (cmv >= CANNON_SHOOTING_SPEED)
{
cmv = 0;
if (!(cn->ready))
{
shot->y--;
}
if (shot->y > BORG_HEIGHT)
{
cn->ready = 1;
}
}
if (imv >= INVADER_SHOOTING_SPEED)
{
imv = 0;
for (i = 0; i < MAX_SHOTS; ++i)
{
if ( /*st[i].x < BORG_WIDTH && */st[i].y < BORG_HEIGHT)
{
st[i].y++;
}
}
}
cmv++;
imv++;
/****************************************************************/
/* TESTE OB GETROFFEN */
/****************************************************************/
// USER CANNON
unsigned char tmp;
if (!(cn->ready))
{
for (i = 0; i < MAX_SHOTS; ++i)
{
if (shot->x == st[i].x && shot->y == st[i].y)
{
st[i].x = 255;
st[i].y = 255;
cn->ready = 1;
}
}
//GUARDS
if ((tmp = getGuardPixel(guards, shot->x, shot->y)))
{
--tmp;
setGuardPixel(guards, shot->x, shot->y, tmp);
cn->ready = 1;
goto invader_shots;
}
//INVADER
if ((tmp = getInvaderPixel(iv, shot->x, shot->y)))
{
--tmp;
setInvaderPixel(iv, shot->x, shot->y, tmp);
if (tmp == 0)
{
iv->speedinc++;
if (iv->speedinc == SPEED_INC_RATE)
{
iv->speedinc = 0;
iv->speed -= SPEED_INC_VALUE;
}
pl->points += POINTS_FOR_KILL;
}
else
{
pl->points += POINTS_FOR_HIT;
}
cn->ready = 1;
goto invader_shots;
}
//SPACESHIP
if (shot->y == SPACESHIP_LINE)
{
if (shot->x == sc->pos || shot->x == sc->pos + 1)
{
sc->pos = 255;
pl->points += POINTS_FOR_SPACESHIP;
cn->ready = 1;
goto invader_shots;
}
}
} // !(cn->ready)
invader_shots: for (i = 0; i < MAX_SHOTS; ++i)
{
if ((tmp = getGuardPixel(guards, st[i].x, st[i].y)))
{
--tmp;
setGuardPixel(guards, st[i].x, st[i].y, tmp);
st[i].x = 255;
st[i].y = 255;
}
if (st[i].y == BORG_HEIGHT - 1)
{
if (st[i].x == cn->pos)
{
pl->lives--;
st[i].x = 255;
st[i].y = 255;
}
}
}
}
void procSpaceship(Spaceship * sc)
{
unsigned char rnd1 = random8();
unsigned char rnd2 = random8();
static unsigned char sct = 0;
if (sc->pos > RIGHT_BORDER)
{
if (rnd1 == 73)
{
if (rnd2 >= 200)
{
sc->pos = RIGHT_BORDER;
sct = 0;
}
}
}
else
{
if (sct == SPACESHIP_SPEED)
{
sct = 0;
if (sc->pos == 0)
{
sc->pos = 255;
}
else
{
sc->pos--;
}
}
}
sct++;
}
unsigned char getStatus(Invaders * iv)
{
//count Invader!
unsigned char x, y, inv = 0;
for (x = 0; x < MAX_INVADER_WIDTH; ++x)
{
for (y = 0; y < MAX_INVADER_HEIGHT; ++y)
{
if (iv->map[x][y] != 0)
inv++;
}
}
//LEVEL BEREINIGT
if (inv == 0)
return 1;
//INVADERS REACHED EARTH
for (x = 0; x < BORG_WIDTH; ++x)
{
if (getInvaderPixel(iv, x, GUARD_LINE + 1))
return 2;
}
return 0;
}

134
games/invaders2.c Normal file
View file

@ -0,0 +1,134 @@
#include <stdio.h>
#include "util.h"
#include "invaders2.h"
//#include <stdio.h>
#ifndef __AVR__
#define wait(_X) myWait(_X)
#endif
void borg_invaders()
{
// waitForFire = 0;
/****************************************************************/
/* INITIALIZE */
/****************************************************************/
Invaders iv;
Cannon cn;
Player pl;
Spaceship sc;
unsigned char guards[BORG_WIDTH];
unsigned char level = 0;
unsigned char ivStatus = 0;
uPixel st[MAX_SHOTS] =
{
{ 255, 255},
{ 255, 255},
{ 255, 255},
{ 255, 255},
{ 255, 255}
};
uPixel shot;
// = {0,0};
pl.points = 0;
pl.lives = 3;
//--------Init Cannon-------//
//cn.pos = 4;
//cn.ready = 1;
/****************************************************************/
/* INTRO */
/****************************************************************/
//drawIntroScreen(2000);
/****************************************************************/
/* GAME LOOP */
/****************************************************************/
do
{
//----- INITIALIZE LEVEL-----//
initGuards(guards);
initInvaders(&iv, level);
//Spaceship
sc.lives = 1;
sc.pos = 255;
//Cannon
cn.pos = 7;
cn.ready = 1;
draw(&iv, &sc, &pl, &cn, guards, st, &shot);
while (1)
{
procInvaders(&iv, st);
procSpaceship(&sc);
procShots(&iv, &pl, &cn, &sc, guards, st, &shot);
procCannon(&cn, &shot);
draw(&iv, &sc, &pl, &cn, guards, st, &shot);
ivStatus = getStatus(&iv);
if (ivStatus == 2) //LOST
{
//pl.lives--;
pl.lives = 0;
break;
}
else if (ivStatus == 1) //WON
{
unsigned int bonus= POINTS_FOR_LEVEL * (level + 1) * (12 - iv.pos.y);
pl.points += bonus;
//printf("cleared l: %d , y: %d bonus: %d \n",
// level, iv.pos.y, bonus);
if (level == MAX_LEVEL - 1)
{
level = 0;
}
else
{
level = (level + 1);
}
break;
}
if (pl.lives <= 0)
{
//scrolltext("GAME OVER",0,80);
break;
}
wait (WAIT_MS);
} //IN LEVEL LOOP
} while (pl.lives > 0); //GAME LOOP
clearScreen ();
//wait(5000);
char text[64];
snprintf(text, 64, "</#points: %u", pl.points);
scrolltext(text);
//printf("scores: %d\n", pl.points);
/****************************************************************/
/* PLAYER STAT */
/* HIGH SCORES */
/****************************************************************/
// waitForFire = 1;
}

182
games/invaders2.h Normal file
View file

@ -0,0 +1,182 @@
/* Space INVADERS V0.2
*
* by: Fabian Bremerich
* Thanx to: Peter F.
*
* date: Mi, 08.03.2006
*
*/
#ifndef INVADERS2_H
#define INVADERS2_H
#define USE_ORIGINAL_PIXEL_API
/*CONNECTION TO SIMULATOR*/
//extern char fkey;
/* TEST PARTS NEW API */
typedef struct
{
signed char x;
signed char y;
} sPixel;
typedef struct
{
unsigned char x;
unsigned char y;
} uPixel;
//for compatibility to pisel.h api!
#ifdef USE_ORIGINAL_PIXEL_API
#include "pixel.h"
#include "scrolltext.h"
#include "joystick.h"
//typedef uPixel pixel;
#define uPixel pixel
//#define getPixel(_X, _Y) get_pixel( (pixel){_X, _Y})
#define clearScreen() clear_screen(0)
//#define
//#ifdef SIMULATOR
#define setPixel(_X, _Y, _V) setpixel( (pixel){_X, _Y}, _V)
//#else //if defined (AVR)
//#define setPixel(_X, _Y, _V) reverseSetPixel( (pixel){_X, _Y}, _V)
//#endif
#endif
/****************************************************************/
/* GLOBALE VAR */
/****************************************************************/
#define P 3
extern unsigned char peter[8][11];
extern unsigned char hans[8][11];
/****************************************************************/
/* DEFINES */
/****************************************************************/
#define START_LIVES 3
#define SPACESHIP_LINE 1
//#define SPACESHIP_TRIGGER_POINTS 250
//#define SPACESHIP_TRIGGER_RATE 333
#define GUARD_LINE 13
#define BORG_WIDTH 16
#define BORG_HEIGHT 16
#ifdef SWITCHED_SIDE
#define RIGHT_BORDER 0
#define LEFT_BORDER (BORG_WIDTH -1 )
#else
#define RIGHT_BORDER (BORG_WIDTH -1 )
#define LEFT_BORDER 0
#endif
#define MAX_INVADER_HEIGHT 8
#define MAX_INVADER_WIDTH 12
#define MAX_INVADER_LIVES 3
#define POINTS_FOR_HIT 5
#define POINTS_FOR_KILL 25
#define POINTS_FOR_SPACESHIP 75
#define POINTS_FOR_LEVEL 100
#define MAX_SHOTS 7
#define MIN_SPEED 70
#define SPEED_INC_RATE 2
#define SPEED_INC_VALUE 3
#define MAX_LEVEL 5
#define SHOOTING_RATE 6
#define INVADER_SHOOTING_SPEED 10
#define CANNON_SHOOTING_SPEED 4
#define SPACESHIP_SPEED 30
#define CANNON_SPEED 2
#define WAIT_MS 15
//#define WAIT_MS 20
typedef struct
{
unsigned char map[MAX_INVADER_WIDTH][MAX_INVADER_HEIGHT];
sPixel pos;
unsigned char speed;
unsigned char speedinc;
signed char direction;
unsigned char isEdged;
} Invaders;
typedef struct
{
unsigned char pos;
unsigned char lives;
} Spaceship;
typedef struct
{
unsigned char pos;
unsigned char ready;
} Cannon;
//typedef struct {
// unsigned char guards[numGards];
//}
typedef struct
{
signed char lives;
unsigned int points;
} Player;
/****************************************************************/
/* FUNCTIONS */
/****************************************************************/
void borg_invaders();
/*----------------------main_level_funcs-------------------------*/
void procSpaceship(Spaceship * sp);
void procCannon(Cannon * cn, uPixel * shot);
void procInvaders(Invaders * iv, uPixel st[MAX_SHOTS]);
void procShots(Invaders * iv, Player * pl, Cannon * cn, Spaceship * sc,
unsigned char guards[BORG_WIDTH], uPixel st[MAX_SHOTS], uPixel * shot);
unsigned char getStatus(Invaders * iv);
/*----------------------Initialization---------------------------*/
void initGuards(unsigned char guards[BORG_WIDTH]);
void initInvaders(Invaders * iv, unsigned char lv);
//void initSpaceship(Spaceship* sc);
//void initPlayer(Player* pl);
/*----------------------getter/setter----------------------------*/
unsigned char getInvaderPixel(Invaders * iv, unsigned char x, unsigned char y);
void setInvaderPixel(Invaders * iv, unsigned char x, unsigned char y,
unsigned char val);
unsigned char getGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
unsigned char y);
void setGuardPixel(unsigned char guards[BORG_WIDTH], unsigned char x,
unsigned char y, unsigned char val);
/*----------------------drawing Method---------------------------*/
void draw(Invaders * iv, Spaceship * sc, Player * pl, Cannon * cn,
unsigned char guards[BORG_WIDTH], uPixel ishots[MAX_SHOTS],
uPixel * shot);
#endif

19
games/tetris/COPYING Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2007 Christian Kroll
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

19
games/tetris/LICENSE Normal file
View file

@ -0,0 +1,19 @@
Copyright (c) 2007 Christian Kroll
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

25
games/tetris/Makefile Normal file
View file

@ -0,0 +1,25 @@
LD = avr-ld
all: tetris.o
tetris.o: piece.o playfield.o view.o logic.o input.o
$(LD) -r piece.o playfield.o view.o logic.o input.o -o tetris.o
piece.o: piece.c piece.h
$(MCU_CC) $(CFLAGS) -c piece.c -o piece.o
playfield.o: playfield.c playfield.h piece.h
$(MCU_CC) $(CFLAGS) -c playfield.c -o playfield.o
view.o: view.c view.h logic.h piece.h playfield.h ../config.h ../pixel.h \
../util.h ../scrolltext.h
$(MCU_CC) $(CFLAGS) -c view.c -o view.o
logic.o: logic.c logic.h piece.h playfield.h input.h view.h
$(MCU_CC) $(CFLAGS) -c logic.c -o logic.o
input.o: input.c input.h ../joystick.h ../util.h
$(MCU_CC) $(CFLAGS) -c input.c -o input.o
clean:
rm -rf *.o *.d

400
games/tetris/input.c Normal file
View file

@ -0,0 +1,400 @@
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include "../joystick.h"
#include "../util.h"
#include "input.h"
/* - the API simulator and the real API have different named wait functions
* - the macro PM helps in reading values from PROGMEM on the AVR arch
*/
#ifdef __AVR__
#include <avr/pgmspace.h>
#define WAIT(ms) wait(ms)
#define PM(value) pgm_read_word(&value)
#else
#define PROGMEM
#define WAIT(ms) myWait(ms)
#define PM(value) (value)
#endif
/***********
* defines *
***********/
// amount of milliseconds that each loop cycle waits
#define TETRIS_INPUT_TICKS 5
// amount of milliseconds the input is ignored after the pause combo has been
// pressed, since it is difficult to release all buttons simultaneously
#define TETRIS_INPUT_PAUSE_TICKS 100
// amount of allowed loop cycles while in pause mode so that the game
// automatically continues after five minutes
#define TETRIS_INPUT_PAUSE_CYCLES 60000
// minimum of cycles in gliding mode
#define TETRIS_INPUT_GLIDE_CYCLES 75
// here you can adjust the delays (in loop cycles) for key repeat
#define TETRIS_INPUT_REPEAT_INITIALDELAY 40
#define TETRIS_INPUT_REPEAT_DELAY 10
// Here you can adjust the amount of loop cycles a command is ignored after
// its button has been released (to reduce joystick chatter)
#define TETRIS_INPUT_CHATTER_TICKS_ROT_CW 24
#define TETRIS_INPUT_CHATTER_TICKS_ROT_CCW 24
#define TETRIS_INPUT_CHATTER_TICKS_LEFT 12
#define TETRIS_INPUT_CHATTER_TICKS_RIGHT 12
#define TETRIS_INPUT_CHATTER_TICKS_DOWN 12
#define TETRIS_INPUT_CHATTER_TICKS_DROP 24
/***************************
* non-interface functions *
***************************/
/* Function: tetris_input_chatterProtect;
* Description: sets an ignore counter to a command specific value if it is 0
* Argument pIn: pointer to an input object
* Argument cmd: the command whose counter should be set
* Return value: void
*/
void tetris_input_chatterProtect (tetris_input_t *pIn,
tetris_input_command_t cmd)
{
// never exceed the index
assert(cmd < TETRIS_INCMD_NONE);
// amount of loop cycles a command is ignored after its button has been
// released (every command has its own counter)
const static uint8_t nInitialIgnoreValue[TETRIS_INCMD_NONE] PROGMEM =
{
TETRIS_INPUT_CHATTER_TICKS_ROT_CW,
TETRIS_INPUT_CHATTER_TICKS_ROT_CCW,
TETRIS_INPUT_CHATTER_TICKS_LEFT,
TETRIS_INPUT_CHATTER_TICKS_RIGHT,
TETRIS_INPUT_CHATTER_TICKS_DOWN,
TETRIS_INPUT_CHATTER_TICKS_DROP,
0, // TETRIS_INCMD_GRAVITY (irrelevant because it doesn't have a button)
0 // TETRIS_INCMD_PAUSE (is a combination of ROT_CW and DOWN)
};
// setting ignore counter according to the predefined array
if (pIn->nIgnoreCmdCounter[cmd] == 0)
{
// if the command isn't TETRIS_INCMD_PAUSE, setting the ignore counter
// is straight forward
if (cmd != TETRIS_INCMD_PAUSE)
{
pIn->nIgnoreCmdCounter[cmd] = PM(nInitialIgnoreValue[cmd]);
}
// TETRIS_INCMD_PAUSE is issued via a combination of the buttons for
// TETRIS_INCMD_ROT_CW and TETRIS_INCMD_DOWN, so we must set their
// ignore counters
else
{
pIn->nIgnoreCmdCounter[TETRIS_INCMD_ROT_CW] =
TETRIS_INPUT_CHATTER_TICKS_ROT_CW;
pIn->nIgnoreCmdCounter[TETRIS_INCMD_DOWN] =
TETRIS_INPUT_CHATTER_TICKS_DOWN;
}
}
// The ignore counter of TETRIS_INCMD_PAUSE is either set to the counter
// value of TETRIS_INCMD_ROT_CW or TETRIS_INCMD_DOWN (whichever is higher).
if ((cmd == TETRIS_INCMD_ROT_CW) || (cmd == TETRIS_INCMD_DOWN))
{
// helper variables (which the compiler hopefully optimizes away)
uint8_t nRotCw = pIn->nIgnoreCmdCounter[TETRIS_INCMD_ROT_CW];
uint8_t nDown = pIn->nIgnoreCmdCounter[TETRIS_INCMD_DOWN];
pIn->nIgnoreCmdCounter[TETRIS_INCMD_PAUSE] =
(nRotCw > nDown ? nRotCw : nDown);
}
}
/* Function: tetris_input_queryJoystick
* Description: translates joystick movements into tetris_input_command_t
* Argument pIn: pointer to an input object
* Return value: see definition of tetris_input_command_t
*/
tetris_input_command_t tetris_input_queryJoystick()
{
tetris_input_command_t cmdReturn;
if (JOYISFIRE)
{
cmdReturn = TETRIS_INCMD_DROP;
}
else if (JOYISLEFT)
{
cmdReturn = TETRIS_INCMD_LEFT;
}
else if (JOYISRIGHT)
{
cmdReturn = TETRIS_INCMD_RIGHT;
}
else if (JOYISUP && JOYISDOWN)
{
cmdReturn = TETRIS_INCMD_PAUSE;
WAIT(TETRIS_INPUT_PAUSE_TICKS);
}
else if (JOYISDOWN)
{
cmdReturn = TETRIS_INCMD_DOWN;
}
else if (JOYISUP)
{
cmdReturn = TETRIS_INCMD_ROT_CW;
}
else
{
cmdReturn = TETRIS_INCMD_NONE;
}
return cmdReturn;
}
/*****************************
* construction/destruction *
*****************************/
/* Function: tetris_input_construct
* Description: constructs an input object for André's borg
* Return value: pointer to a newly created input object
*/
tetris_input_t *tetris_input_construct()
{
tetris_input_t *pIn = (tetris_input_t *)malloc(sizeof(tetris_input_t));
assert(pIn != NULL);
pIn->cmdLast = TETRIS_INCMD_NONE;
pIn->nLevel = 0xFF;
tetris_input_setLevel(pIn, 0);
pIn->nLoopCycles = 0;
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
pIn->nPauseCount = 0;
memset(pIn->nIgnoreCmdCounter, 0, TETRIS_INCMD_NONE);
return pIn;
}
/* Function: tetris_input_destruct
* Description: destructs an input structure
* Argument pIn: pointer to the input object which should to be destructed
* Return value: void
*/
void tetris_input_destruct(tetris_input_t *pIn)
{
assert(pIn != NULL);
free(pIn);
}
/***************************
* input related functions *
***************************/
/* Function: retris_input_getCommand
* Description: retrieves commands from joystick or loop interval
* Argument pIn: pointer to an input object
* Argument nPace: falling pace (see definition of tetris_input_pace_t)
* Return value: see definition of tetris_input_command_t
*/
tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn,
tetris_input_pace_t nPace)
{
assert (pIn != NULL);
// holds the translated command value of the joystick
tetris_input_command_t cmdJoystick = TETRIS_INCMD_NONE;
// this variable both serves as the return value and as a flag for not
// leaving the function as long as its value is TETRIS_INCMD_NONE
tetris_input_command_t cmdReturn = TETRIS_INCMD_NONE;
uint8_t nMaxCycles;
// if the piece is gliding we grant the player a reasonable amount of time
// to make the game more controllable at higher falling speeds
if ((nPace == TETRIS_INPACE_GLIDING) &&
(pIn->nMaxCycles < TETRIS_INPUT_GLIDE_CYCLES))
{
nMaxCycles = TETRIS_INPUT_GLIDE_CYCLES;
}
else
{
nMaxCycles = pIn->nMaxCycles;
}
while (pIn->nLoopCycles < nMaxCycles)
{
cmdJoystick = tetris_input_queryJoystick();
// only obey current command if it is not considered as chattering
if (((cmdJoystick < TETRIS_INCMD_NONE) ?
pIn->nIgnoreCmdCounter[cmdJoystick] : 0) == 0)
{
switch (cmdJoystick)
{
case TETRIS_INCMD_LEFT:
case TETRIS_INCMD_RIGHT:
case TETRIS_INCMD_DOWN:
// only react if either the current command differs from the
// last one or enough loop cycles have been run on the same
// command (for key repeat)
if ((pIn->cmdLast != cmdJoystick)
|| ((pIn->cmdLast == cmdJoystick)
&& (pIn->nRepeatCount >= TETRIS_INPUT_REPEAT_DELAY)))
{
// reset repeat counter
if (pIn->cmdLast != cmdJoystick)
{
// different command: we set an extra initial delay
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
}
else
{
// same command: there's no extra initial delay
pIn->nRepeatCount = 0;
}
// update cmdLast and return value
pIn->cmdLast = cmdReturn = cmdJoystick;
}
else
{
// if not enough loop cycles have been run we increment the
// repeat counter, ensure that we continue the loop and
// keep the key repeat functioning
++pIn->nRepeatCount;
cmdReturn = TETRIS_INCMD_NONE;
}
break;
case TETRIS_INCMD_DROP:
case TETRIS_INCMD_ROT_CW:
case TETRIS_INCMD_ROT_CCW:
// no key repeat here
if (pIn->cmdLast != cmdJoystick)
{
pIn->cmdLast = cmdReturn = cmdJoystick;
}
else
{
// if we reach here the command is ignored
cmdReturn = TETRIS_INCMD_NONE;
}
break;
case TETRIS_INCMD_PAUSE:
// if this is an initial pause command, make sure that the logic
// module is informed about it
if (pIn->cmdLast != TETRIS_INCMD_PAUSE)
{
pIn->cmdLast = cmdReturn = cmdJoystick;
pIn->nPauseCount = 0;
}
// consecutive pause commands should not cause the loop to leave
else
{
cmdReturn = TETRIS_INCMD_NONE;
}
break;
case TETRIS_INCMD_NONE:
// chatter protection
if (pIn->cmdLast != TETRIS_INCMD_NONE)
{
tetris_input_chatterProtect(pIn, pIn->cmdLast);
}
// If the game is paused (last command was TETRIS_INCMD_PAUSE)
// we ensure that the variable which holds that last command
// isn't touched. We use this as a flag so that the loop cycle
// counter doesn't get incremented.
// We count the number of pause cycles, though. If enough pause
// cycles have been run, we enforce the continuation of the game.
if ((pIn->cmdLast != TETRIS_INCMD_PAUSE) ||
(++pIn->nPauseCount > TETRIS_INPUT_PAUSE_CYCLES))
{
pIn->cmdLast = TETRIS_INCMD_NONE;
}
// reset repeat counter
pIn->nRepeatCount = -TETRIS_INPUT_REPEAT_INITIALDELAY;
// using cmdReturn as a flag for not leaving the loop
cmdReturn = TETRIS_INCMD_NONE;
break;
default:
break;
}
}
// current command is considered as chattering
else
{
pIn->cmdLast = cmdReturn = TETRIS_INCMD_NONE;
}
// decrement all ignore counters
for (int nIgnoreIndex = 0; nIgnoreIndex < TETRIS_INCMD_NONE; ++nIgnoreIndex)
{
if (pIn->nIgnoreCmdCounter[nIgnoreIndex] != 0)
{
--pIn->nIgnoreCmdCounter[nIgnoreIndex];
}
}
// reset automatic falling if the player has dropped a piece
if ((cmdReturn == TETRIS_INCMD_DOWN)
|| (cmdReturn == TETRIS_INCMD_DROP))
{
pIn->nLoopCycles = 0;
}
// otherwise ensure automatic falling (unless the game is running)
else if ((cmdReturn != TETRIS_INCMD_PAUSE) &&
!((cmdReturn == TETRIS_INCMD_NONE) &&
(pIn->cmdLast == TETRIS_INCMD_PAUSE)))
{
++pIn->nLoopCycles;
}
WAIT(TETRIS_INPUT_TICKS);
if (cmdReturn != TETRIS_INCMD_NONE)
{
return cmdReturn;
}
}
// since we have left the loop we reset the cycle counter
pIn->nLoopCycles = 0;
return TETRIS_INCMD_GRAVITY;
}
/* Function: tetris_input_setLevel
* Description: modifies time interval of input events
* Argument pIn: pointer to an input object
* Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1)
* Return value: void
*/
void tetris_input_setLevel(tetris_input_t *pIn,
uint8_t nLvl)
{
assert(pIn != NULL);
assert(nLvl <= TETRIS_INPUT_LEVELS - 1);
if (pIn->nLevel != nLvl)
{
pIn->nLevel = nLvl;
pIn->nMaxCycles = 400 / (nLvl + 2);
}
}

129
games/tetris/input.h Normal file
View file

@ -0,0 +1,129 @@
#ifndef INPUT_H_
#define INPUT_H_
#include <inttypes.h>
/***********
* defines *
***********/
// number of levels
#define TETRIS_INPUT_LEVELS 20
/*********
* types *
*********/
typedef enum tetris_input_command_t
{
TETRIS_INCMD_ROT_CW, // rotate clockwise
TETRIS_INCMD_ROT_CCW, // rotate counter clockwise
TETRIS_INCMD_LEFT, // move piece left
TETRIS_INCMD_RIGHT, // move piece right
TETRIS_INCMD_DOWN, // lower piece by one row
TETRIS_INCMD_DROP, // move piece to the ground immediately
TETRIS_INCMD_GRAVITY, // piece gets pulled by gravity
TETRIS_INCMD_PAUSE, // pause the game
TETRIS_INCMD_NONE // idle (must alway be the last one)
}
tetris_input_command_t;
typedef enum tetris_input_pace_t
{
TETRIS_INPACE_HOVERING, // normal falling pace
TETRIS_INPACE_GLIDING /* guarantees a minimum docking time to avoid that
pieces are docked immediately if they hit something
in higher levels */
}
tetris_input_pace_t;
typedef struct tetris_input_t
{
// current level (determines falling speed)
uint8_t nLevel;
// Amount of loop cycles between forced piece movements. This value gets
// set via the tetris_input_setLevel() function.
uint8_t nMaxCycles;
// This counter keeps track of the number of loop cycles whoch have been
// done since the last forced piece movement. It gets reset if it either
// reaches a well defined value (causing a gravity command to be issued)
// or the player has moved down the piece herself/himself.
uint8_t nLoopCycles;
// Amount of loop cycles in which the same command has been issued
// consecutively. It gets reset if either the current command differs from
// the last one or a well-defined value has been reached (thereby
// regulating the pace of the key repeat as commands are only processed
// if that value is reached).
int8_t nRepeatCount;
// Keeps track of the number loop cycles which have been run while in
// pause mode. As soon as a well defined value is reached, the game
// continues (in case someone paused the game and forgot to resume it).
uint16_t nPauseCount;
// last command (important for key repeat)
tetris_input_command_t cmdLast;
// Every command has its own counter. A command is ignored as long as its
// counter is unequal to 0. A counter gets set to a specific value (or 0)
// if the button of the corresponding command has been released by the
// player. All counters get decremented by one every loop cycle until they
// are zero. This is used to work against joystick chatter. Look at the
// TETRIS_INPUT_CHATTER_TICKS_... constants in input.c for the initial
// values of these counters.
uint8_t nIgnoreCmdCounter[TETRIS_INCMD_NONE];
}
tetris_input_t;
/****************************
* construction/destruction *
****************************/
/* Function: tetris_input_construct
* Description: constructs an input object for André's borg
* Return value: pointer to a newly created input object
*/
tetris_input_t *tetris_input_construct();
/* Function: tetris_input_destruct
* Description: destructs an input object
* Argument pIn: pointer to the input object which should be destructed
* Return value: void
*/
void tetris_input_destruct(tetris_input_t *pIn);
/***************************
* input related functions *
***************************/
/* Function: retris_input_getCommand
* Description: retrieves commands from joystick or loop interval
* Argument pIn: pointer to an input object
* Argument nPace: falling pace (see definition of tetris_input_pace_t)
* Return value: see definition of tetris_input_command_t
*/
tetris_input_command_t tetris_input_getCommand(tetris_input_t *pIn,
tetris_input_pace_t nPace);
/* Function: tetris_input_setLevel
* Description: modifies time interval of input events
* Argument pIn: pointer to an input object
* Argument nLvl: desired level (0 <= nLvl <= TETRIS_INPUT_LEVELS - 1)
* Return value: void
*/
void tetris_input_setLevel(tetris_input_t *pIn,
uint8_t nLvl);
#endif /*INPUT_H_*/

467
games/tetris/logic.c Normal file
View file

@ -0,0 +1,467 @@
/* Borgtris
* by: Christian Kroll
* date: Tuesday, 2007/09/16
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#ifdef __AVR__
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#endif
#include "logic.h"
#include "piece.h"
#include "playfield.h"
#include "view.h"
#include "input.h"
#include "../prng.h"
#ifdef EEMEM
/***********************
* Highscore in EEPROM *
***********************/
uint16_t tetris_logic_nHighscore EEMEM;
#endif
/***************************
* non-interface functions *
***************************/
/* Function: tetris_logic_calculateLines
* Description: calculates number of lines for the given row mask
* Argument nRowMask: row mask from which the no. of lines will be calculated
* Return value: number of lines of the row mask
*/
uint8_t tetris_logic_calculateLines(uint8_t nRowMask)
{
uint8_t nMask = 0x0001;
uint8_t nLines = 0;
for (uint8_t i = 0; i < 4; ++i)
{
if ((nMask & nRowMask) != 0)
{
++nLines;
}
nMask <<= 1;
}
return nLines;
}
uint16_t tetris_logic_retrieveHighscore(void)
{
#ifdef EEMEM
uint16_t nHighscore = 0;
nHighscore = eeprom_read_word(&tetris_logic_nHighscore);
// a score of 65535 is most likely caused by uninitialized EEPROM addresses
if (nHighscore == 65535)
{
nHighscore = 0;
}
return nHighscore;
#else
return 0;
#endif
}
void tetris_logic_saveHighscore(uint16_t nHighscore)
{
#ifdef EEMEM
if (nHighscore > tetris_logic_retrieveHighscore())
{
eeprom_write_word(&tetris_logic_nHighscore, nHighscore);
}
#endif
}
/****************************
* construction/destruction *
****************************/
/* Function: tetris_logic_construct
* Description: constructs a logic object
* Return value: pointer to a newly created logic object
*/
tetris_logic_t *tetris_logic_construct()
{
tetris_logic_t *pLogic = (tetris_logic_t *)malloc(sizeof(tetris_logic_t));
assert(pLogic != NULL);
memset(pLogic, 0, sizeof(tetris_logic_t));
return pLogic;
}
/* Function: tetris_logic_destruct
* Description: destructs a logic object
* Argument pIn: pointer to the logic object to be destructed
* Return value: void
*/
void tetris_logic_destruct(tetris_logic_t *pLogic)
{
assert(pLogic != 0);
free(pLogic);
}
/***************************
* logic related functions *
***************************/
/* Function: tetris
* Description: runs the tetris game
* Return value: void
*/
void tetris ()
{
// get view dependent dimensions of the playfield
int8_t nWidth;
int8_t nHeight;
tetris_view_getDimensions(&nWidth, &nHeight);
// holds the current user command which should be processed
tetris_input_command_t inCmd;
// prepare data structures that drive the game...
tetris_logic_t *pLogic = tetris_logic_construct();
tetris_playfield_t *pPl = tetris_playfield_construct(nWidth, nHeight);
tetris_input_t *pIn = tetris_input_construct();
tetris_view_t *pView = tetris_view_construct(pLogic, pPl);
// runtime variable
int8_t nPieceRow;
// retrieve highscore
static uint16_t nHighscore = 0;
if (nHighscore == 0)
{
nHighscore = tetris_logic_retrieveHighscore();
}
// initialize current and next piece
tetris_piece_t *pPiece = NULL;
tetris_piece_t *pNextPiece = pPiece =
tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0);
// the view only monitors the logic and the playfield object for the game
// status so we must put information like the next piece or the current
// highscore to a place where the view can find it
tetris_logic_setHighscore(pLogic, nHighscore);
tetris_logic_setPreviewPiece(pLogic, pNextPiece);
// pace flag
tetris_input_pace_t inPace;
// game loop, runs as long as the game is not over
while (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
{
// what we do strongly depends on the status of the playfield
switch (tetris_playfield_getStatus(pPl))
{
// the playfield awaits a new piece
case TETRIS_PFS_READY:
// make preview piece the current piece and create new preview piece
pPiece = pNextPiece;
pNextPiece =
tetris_piece_construct(random8() % 7, TETRIS_PC_ANGLE_0);
tetris_logic_setPreviewPiece(pLogic, pNextPiece);
// insert new piece into playfield
tetris_piece_t *pOldPiece;
tetris_playfield_insertPiece(pPl, pPiece, &pOldPiece);
// destruct old piece (if it exists) since we don't need it anymore
if (pOldPiece != NULL)
{
tetris_piece_destruct(pOldPiece);
pOldPiece = NULL;
}
break;
// a piece is hovering and can be controlled by the player
case TETRIS_PFS_HOVERING:
case TETRIS_PFS_GLIDING:
// if the piece is gliding the input module has to grant us
// a minimum amount of time to move it
if (tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING)
{
inPace = TETRIS_INPACE_GLIDING;
}
else
{
inPace = TETRIS_INPACE_HOVERING;
}
// ensure correct view mode if the game isn't paused
if ((inCmd = tetris_input_getCommand(pIn, inPace))
!= TETRIS_INCMD_PAUSE)
{
tetris_view_setViewMode(pView, TETRIS_VIMO_RUNNING);
}
// what we do depends on what the input module tells us
switch (inCmd)
{
// game paused?
case TETRIS_INCMD_PAUSE:
// tell the view it should display the pause screen
tetris_view_setViewMode(pView, TETRIS_VIMO_PAUSED);
break;
// the piece was pulled down by the almighty gravity
case TETRIS_INCMD_GRAVITY:
tetris_playfield_advancePiece(pPl);
break;
// the player has pulled down the piece herself/himself
case TETRIS_INCMD_DOWN:
tetris_playfield_advancePiece(pPl);
// if the game still runs, reward the player with extra points
if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
{
tetris_logic_singleDrop(pLogic, 1);
}
break;
// player shifted the piece to the left
case TETRIS_INCMD_LEFT:
tetris_playfield_movePiece(pPl, TETRIS_PFD_LEFT);
break;
// player shifted the piece to the right
case TETRIS_INCMD_RIGHT:
tetris_playfield_movePiece(pPl, TETRIS_PFD_RIGHT);
break;
// player rotated the piece clockwise
case TETRIS_INCMD_ROT_CW:
tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CW);
break;
// player rotated the piece counter clockwise
case TETRIS_INCMD_ROT_CCW:
tetris_playfield_rotatePiece(pPl, TETRIS_PC_ROT_CCW);
break;
// the player decided to make an immediate drop
case TETRIS_INCMD_DROP:
nPieceRow = tetris_playfield_getRow(pPl);
// emulate immediate drop
while((tetris_playfield_getStatus(pPl) == TETRIS_PFS_GLIDING) ||
(tetris_playfield_getStatus(pPl) == TETRIS_PFS_HOVERING))
{
tetris_playfield_advancePiece(pPl);
}
// if the game still runs, reward the player with extra points
if (tetris_playfield_getStatus(pPl) != TETRIS_PFS_GAMEOVER)
{
tetris_logic_completeDrop(pLogic,
tetris_playfield_getRow(pPl) - nPieceRow);
}
break;
// avoid compiler warnings
default:
break;
}
break;
// the piece has irrevocably hit the ground
case TETRIS_PFS_DOCKED:
// remove complete lines (if any)
tetris_playfield_removeCompleteLines(pPl);
// let the logic object decide how many points the player gets
// and whether the level gets changed
tetris_logic_removedLines(pLogic, tetris_playfield_getRowMask(pPl));
tetris_input_setLevel(pIn, tetris_logic_getLevel(pLogic));
break;
// avoid compiler warnings
default:
break;
}
// the view updates it state every loop cycle to make changes visible
tetris_view_update(pView);
}
// game is over and we provide the player with her/his results
tetris_view_showResults(pView);
// update highscore if it has been beaten
uint16_t nScore = tetris_logic_getScore(pLogic);
if (nScore > nHighscore)
{
nHighscore = nScore;
tetris_logic_saveHighscore(nHighscore);
}
// clean up
tetris_view_destruct(pView);
tetris_input_destruct(pIn);
tetris_playfield_destruct(pPl);
tetris_logic_destruct(pLogic);
tetris_piece_destruct(pPiece);
tetris_piece_destruct(pNextPiece);
}
/* Function: tetris_logic_singleDrop
* Description: add points which result from single step dropping
* Argument pLogic: the logic object we want to modify
* Argument nLines: the number of rows involved
* Return value: void
*/
void tetris_logic_singleDrop(tetris_logic_t *pLogic,
uint8_t nLines)
{
assert(pLogic != 0);
pLogic->nScore += nLines;
}
/* Function: tetris_logic_completeDrop
* Description: add points which result from a complete drop
* Argument pLogic: the logic object we want to modify
* Argument nLines: the number of rows involved
* Return value: void
*/
void tetris_logic_completeDrop(tetris_logic_t *pLogic,
uint8_t nLines)
{
assert(pLogic != 0);
pLogic->nScore += nLines * 2;
}
/* Function: tetris_logic_removedLines
* Description: add points which result from removed rows
* Argument pLogic: the logic object we want to modify
* Argument nRowMask: see tetris_playfield_completeLines
* Return value: void
*/
void tetris_logic_removedLines(tetris_logic_t *pLogic,
uint8_t nRowMask)
{
assert(pLogic != 0);
uint8_t nLines = tetris_logic_calculateLines(nRowMask);
pLogic->nLines += nLines;
pLogic->nLevel = ((pLogic->nLines / 10) < TETRIS_INPUT_LEVELS) ?
(pLogic->nLines / 10) : (TETRIS_INPUT_LEVELS - 1);
switch (nLines)
{
case 1:
pLogic->nScore += 50;
break;
case 2:
pLogic->nScore += 150;
break;
case 3:
pLogic->nScore += 250;
break;
case 4:
pLogic->nScore += 400;
break;
}
}
/*****************
* get functions *
*****************/
/* Function: tetris_logic_getScore
* Description: returns the current score
* Argument pLogic: the logic object we want information from
* Return value: the score as uint16_t
*/
uint16_t tetris_logic_getScore(tetris_logic_t *pLogic)
{
assert(pLogic != NULL);
return pLogic->nScore;
}
/* Function: tetris_logic_getHighscore
* Description: returns the current highscore
* Argument pLogic: the logic object we want information from
* Return value: the highscore as uint16_t
*/
uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic)
{
assert(pLogic != NULL);
return pLogic->nHighscore;
}
/* Function: tetris_logic_setHighscore
* Description: set highscore
* Argument pLogic: the logic object we want to modify
* Argmument nHighscore: highscore
*/
void tetris_logic_setHighscore(tetris_logic_t *pLogic,
uint16_t nHighscore)
{
assert(pLogic != NULL);
pLogic->nHighscore = nHighscore;
}
/* Function: tetris_logic_getLevel
* Description: returns the current level
* Argument pLogic: the logic object we want information from
* Return value: the level as uint8_t
*/
uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic)
{
assert(pLogic != NULL);
return pLogic->nLevel;
}
/* Function: tetris_logic_getLines
* Description: returns the number of completed lines
* Argument pLogic: the logic object we want information from
* Return value: number of completed lines as uint16_t
*/
uint16_t tetris_logic_getLines(tetris_logic_t *pLogic)
{
assert(pLogic != NULL);
return pLogic->nLines;
}
/* Function: tetris_logic_setPreviewPiece
* Description: help for the view to determine the preview piece
* Argument pLogic: the logic object we want to modify
* Argument pPiece: pointer to piece intended to be the next one (may be NULL)
* Return value: void
*/
void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic,
tetris_piece_t *pPiece)
{
assert(pLogic != NULL);
pLogic->pPreviewPiece = pPiece;
}
/* Function: tetris_logic_getPreviewPiece
* Description: returns piece which was set via tetris_logic_setPreviewPiece
* Argument pLogic: the logic object we want information from
* Return value: the piece intended to be the next one (may be NULL)
*/
tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic)
{
assert(pLogic != NULL);
return pLogic->pPreviewPiece;
}

143
games/tetris/logic.h Normal file
View file

@ -0,0 +1,143 @@
#ifndef TETRIS_LOGIC_H_
#define TETRIS_LOGIC_H_
#include <inttypes.h>
#include "piece.h"
/*********
* types *
*********/
typedef struct tetris_logic_t
{
uint16_t nScore; // score of the player
uint16_t nHighscore; // highscore
uint8_t nLevel; // current level
uint16_t nLines; // number of completed lines
tetris_piece_t *pPreviewPiece; // the piece intended to be the next one
}
tetris_logic_t;
/****************************
* construction/destruction *
****************************/
/* Function: tetris_logic_construct
* Description: constructs a logic object
* Return value: pointer to a newly created logic object
*/
tetris_logic_t *tetris_logic_construct();
/* Function: tetris_logic_destruct
* Description: destructs a logic object
* Argument pIn: pointer to the logic object to be destructed
* Return value: void
*/
void tetris_logic_destruct(tetris_logic_t *pLogic);
/***************************
* logic related functions *
***************************/
/* Function: tetris
* Description: runs the tetris game
* Return value: void
*/
void tetris();
/* Function: tetris_logic_singleDrop
* Description: add points which result from single step dropping
* Argument pLogic: the logic object we want to modify
* Argument nLines: the number of rows involved
* Return value: void
*/
void tetris_logic_singleDrop(tetris_logic_t *pLogic,
uint8_t nLines);
/* Function: tetris_logic_completeDrop
* Description: add points which result from a complete drop
* Argument pLogic: the logic object we want to modify
* Argument nLines: the number of rows involved
* Return value: void
*/
void tetris_logic_completeDrop(tetris_logic_t *pLogic,
uint8_t nLines);
/* Function: tetris_logic_removedLines
* Description: add points which result from removed rows
* Argument pLogic: the logic object we want to modify
* Argument nRowMask: see tetris_playfield_completeLines
* Return value: void
*/
void tetris_logic_removedLines(tetris_logic_t *pLogic,
uint8_t nRowMask);
/*********************
* get/set functions *
*********************/
/* Function: tetris_logic_getScore
* Description: returns the current score
* Argument pLogic: the logic object we want information from
* Return value: the score as uint16_t
*/
uint16_t tetris_logic_getScore(tetris_logic_t *pLogic);
/* Function: tetris_logic_getHighscore
* Description: returns the current highscore
* Argument pLogic: the logic object we want information from
* Return value: the highscore as uint16_t
*/
uint16_t tetris_logic_getHighscore(tetris_logic_t *pLogic);
/* Function: tetris_logic_setHighscore
* Description: set highscore
* Argument pLogic: the logic object we want to modify
* Argmument nHighscore: highscore
*/
void tetris_logic_setHighscore(tetris_logic_t *pLogic,
uint16_t nHighscore);
/* Function: tetris_logic_getLevel
* Description: returns the current level
* Argument pLogic: the logic object we want information from
* Return value: the level as uint8_t
*/
uint8_t tetris_logic_getLevel(tetris_logic_t *pLogic);
/* Function: tetris_logic_getLines
* Description: returns the number of completed lines
* Argument pLogic: the logic object we want information from
* Return value: number of completed lines as uint16_t
*/
uint16_t tetris_logic_getLines(tetris_logic_t *pLogic);
/* Function: tetris_logic_setPreviewPiece
* Description: help for the view to determine the preview piece
* Argument pLogic: the logic object we want to modify
* Argument pPiece: pointer to piece intended to be the next one
* Return value: void
*/
void tetris_logic_setPreviewPiece(tetris_logic_t *pLogic,
tetris_piece_t *pPiece);
/* Function: tetris_logic_getPreviewPiece
* Description: returns piece which was set via tetris_logic_setPreviewPiece
* Argument pLogic: the logic object we want information from
* Return value: the piece intended to be the next one
*/
tetris_piece_t* tetris_logic_getPreviewPiece(tetris_logic_t *pLogic);
#endif /*TETRIS_LOGIC_H_*/

119
games/tetris/piece.c Normal file
View file

@ -0,0 +1,119 @@
#include <stdlib.h>
#include <assert.h>
#include <inttypes.h>
#include "piece.h"
#ifdef __AVR__
#include <avr/pgmspace.h>
#else
#define PROGMEM
#endif
/*****************************
* construction/destruction *
*****************************/
/* Function: tetris_piece_construct
* Description: constructs a piece with the given attributes
* Argument s: shape of the piece (see tetris_piece_shape_t)
* Argument a: its angle (see tetris_piece_angel_t)
* Return value: pointer to a newly created piece
*/
tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s,
tetris_piece_angle_t a)
{
tetris_piece_t *p_piece = (tetris_piece_t*) malloc (sizeof(tetris_piece_t));
assert(p_piece != NULL);
p_piece->shape = s;
p_piece->angle = a;
return p_piece;
}
/* Function: tetris_piece_destruct
* Description: destructs a piece
* Argument pPc: pointer to the piece to be destructed
* Return value: void
*/
void tetris_piece_destruct(tetris_piece_t *pPc)
{
assert(pPc != NULL);
free(pPc);
}
/****************************
* piece related functions *
****************************/
/* Function: tetris_piece_getBitmap
* Description: returns bitfield representation of the piece
* Argument pPc: piece from which the bitfield shuld be retrieved
* Return value: bitfield representation of the piece
* - nth nibble is nth row of the piece (from upper left)
* - the LSB of a nibble represents the left side of a row
*/
uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc)
{
assert(pPc != NULL);
// Lookup table:
// A value in an array represents a piece in a specific angle (rotating
// clockwise from index 0).
const static uint16_t piece[][4] PROGMEM =
{{0x0F00, 0x2222, 0x0F00, 0x2222}, // LINE
{0x4E00, 0x4640, 0x0E40, 0x4C40}, // T
{0x0660, 0x0660, 0x0660, 0x0660}, // SQUARE
{0x2E00, 0x88C0, 0x0E80, 0xC440}, // L
{0x8E00, 0x6440, 0x0E20, 0x44C0}, // LBACK
{0x6C00, 0x4620, 0x6C00, 0x4620}, // S
{0xC600, 0x4C80, 0xC600, 0x4C80}}; // Z
#ifdef __AVR__
return pgm_read_word(&piece[pPc->shape][pPc->angle]);
#else
return piece[pPc->shape][pPc->angle];
#endif
}
/* Function: tetris_piece_rotate
* Description: rotates a piece
* Argument pPc: piece to rotate
* Argument r: type of rotation (see tetris_piece_rotation_t)
* Return value: void
*/
void tetris_piece_rotate(tetris_piece_t *pPc,
tetris_piece_rotation_t r)
{
assert(pPc != NULL);
// we just rotate through the available angles in the given direction and
// make wrap arounds where appropriate
switch (r)
{
case TETRIS_PC_ROT_CW:
if (pPc->angle == TETRIS_PC_ANGLE_270)
{
pPc->angle = TETRIS_PC_ANGLE_0;
}
else
{
++pPc->angle;
}
break;
case TETRIS_PC_ROT_CCW:
if (pPc->angle == TETRIS_PC_ANGLE_0)
{
pPc->angle = TETRIS_PC_ANGLE_270;
}
else
{
--pPc->angle;
}
break;
}
}

97
games/tetris/piece.h Normal file
View file

@ -0,0 +1,97 @@
#ifndef TETRIS_PIECE_H_
#define TETRIS_PIECE_H_
#include <inttypes.h>
/*********
* types *
*********/
typedef enum tetris_piece_shape_t
{
TETRIS_PC_LINE,
TETRIS_PC_T,
TETRIS_PC_SQUARE,
TETRIS_PC_L,
TETRIS_PC_LBACK,
TETRIS_PC_S,
TETRIS_PC_Z
}
tetris_piece_shape_t;
typedef enum tetris_piece_angle_t
{
TETRIS_PC_ANGLE_0,
TETRIS_PC_ANGLE_90,
TETRIS_PC_ANGLE_180,
TETRIS_PC_ANGLE_270
}
tetris_piece_angle_t;
typedef enum tetris_piece_rotation_t
{
TETRIS_PC_ROT_CW, // clockwise rotation
TETRIS_PC_ROT_CCW // counter clockwise rotation
}
tetris_piece_rotation_t;
typedef struct tetris_piece_t
{
tetris_piece_shape_t shape; // specifies the shape of the piece
tetris_piece_angle_t angle; // specifies one of 4 angels
}
tetris_piece_t;
/*****************************
* construction/destruction *
*****************************/
/* Function: tetris_piece_construct
* Description: constructs a piece with the given attributes
* Argument s: shape of the piece (see tetris_piece_shape_t)
* Argument a: its angle (see tetris_piece_angel_t)
* Return value: pointer to a newly created piece
*/
tetris_piece_t *tetris_piece_construct(tetris_piece_shape_t s,
tetris_piece_angle_t a);
/* Function: tetris_piece_destruct
* Description: destructs a piece
* Argument pPc: pointer to the piece to be destructed
* Return value: void
*/
void tetris_piece_destruct(tetris_piece_t *pPc);
/****************************
* piece related functions *
****************************/
/* Function: tetris_piece_getBitmap
* Description: returns bitfield representation of the piece
* Argument pPc: piece from which the bitfield shuld be retrieved
* Return value: bitfield representation of the piece
* - nth nibble is nth row of the piece (from upper left)
* - the LSB of a nibble represents the left side of a row
*/
uint16_t tetris_piece_getBitmap(tetris_piece_t *pPc);
/* Function: tetris_piece_rotate
* Description: rotates a piece
* Argument pPc: piece to rotate
* Argument r: type of rotation (see tetris_piece_rotation_t)
* Return value: void
*/
void tetris_piece_rotate(tetris_piece_t *pPc,
tetris_piece_rotation_t r);
#endif /*TETRIS_PIECE_H_*/

595
games/tetris/playfield.c Normal file
View file

@ -0,0 +1,595 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
#include "playfield.h"
#include "piece.h"
/***************************
* non-interface functions *
***************************/
/* Function: tetris_playfield_hoverStatus;
* Description: determines if piece is either hovering or gliding
* Argument pPl: playfield perform action on
* Return value: TETRIS_PFS_HOVERING or TETRIS_PFS_GLIDING
*/
tetris_playfield_status_t tetris_playfield_hoverStatus(tetris_playfield_t* pPl)
{
// if the piece touches the dump we ensure that the status is "gliding"
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1))
{
return TETRIS_PFS_GLIDING;
}
// otherwise the status must be "hovering"
else
{
return TETRIS_PFS_HOVERING;
}
}
/****************************
* construction/destruction *
****************************/
/* Function: tetris_playfield_construct
* Description: constructs a playfield with the given diemensions
* Argument nWidth: width of playfield (4 <= n <= 16)
* Argument nHeight: height of playfield (4 <= n <= 124)
* Return value: pointer to a newly created playfield
*/
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth,
int8_t nHeight)
{
assert((nWidth >= 4) && (nWidth <= 16));
assert((nHeight >= 4) && (nHeight <= 124));
tetris_playfield_t *pPlayfield =
(tetris_playfield_t*) malloc(sizeof(tetris_playfield_t));
if (pPlayfield != NULL)
{
// allocating mem for dump array
pPlayfield->dump = (uint16_t*) calloc(nHeight, sizeof(uint16_t));
if (pPlayfield->dump != NULL)
{
// setting desired attributes
pPlayfield->nWidth = nWidth;
pPlayfield->nHeight = nHeight;
tetris_playfield_reset(pPlayfield);
return pPlayfield;
}
else
{
free(pPlayfield);
pPlayfield = NULL;
}
}
return NULL;
}
/* Function: tetris_playfield_destruct
* Description: destructs a playfield
* Argument pPl: pointer to the playfield to be destructed
* Return value: void
*/
void tetris_playfield_destruct(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
// if memory for the dump array has been allocated, free it
if (pPl->dump != NULL)
{
free(pPl->dump);
}
free(pPl);
}
/*******************************
* playfield related functions *
*******************************/
/* Function: tetris_playfield_reset
* Description: resets playfield to begin a new game
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_reset(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
pPl->pPiece = NULL;
pPl->nColumn = 0;
pPl->nRow = 0;
pPl->nRowMask = 0;
// clear dump if it has been allocated in memory
if (pPl->dump != NULL)
{
memset(pPl->dump, 0, pPl->nHeight);
}
pPl->status = TETRIS_PFS_READY;
}
/* Function: tetris_playfield_insertPiece
* Description: inserts a new piece
* Argument pPl: playfield to perform action on
* Argument pPiece: piece to be inserted
* Argument ppOldPiece: [out] indirect pointer to former piece for deallocation
* Return value: void
*/
void tetris_playfield_insertPiece(tetris_playfield_t *pPl,
tetris_piece_t *pPiece,
tetris_piece_t** ppOldPiece)
{
assert((pPl != NULL) && (pPiece != NULL) && (ppOldPiece != NULL));
// a piece can only be inserted in state TETRIS_PFS_READY
assert(pPl->status == TETRIS_PFS_READY);
// row mask is now meaningless
pPl->nRowMask = 0;
// replace old piece
*ppOldPiece = pPl->pPiece;
pPl->pPiece = pPiece;
// set horizontal start position (in the middle of the top line)
pPl->nColumn = (pPl->nWidth - 2) / 2;
// set vertical start position (first piece row with matter at pos. 1)
uint16_t nPieceMap = tetris_piece_getBitmap(pPl->pPiece);
uint16_t nElementMask = 0xF000;
pPl->nRow = -3;
while ((nPieceMap & nElementMask) == 0)
{
++pPl->nRow;
nElementMask >>= 4;
}
if (pPl->nRow < 0)
{
++pPl->nRow;
}
// did we already collide with something?
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) == 1)
{
// game over man, game over!!
pPl->status = TETRIS_PFS_GAMEOVER;
}
else
{
// bring it on!
pPl->status = tetris_playfield_hoverStatus(pPl);
}
}
/* Function: tetris_playfield_collision
* Description: detects if piece collides with s.th. at a given position
* Argument pPl: playfield to perform action on
* Argument nColumn: column where the piece should be moved
* Argument nRow: row where the piece should be moved
* Return value: 1 for collision, 0 otherwise
*/
uint8_t tetris_playfield_collision(tetris_playfield_t *pPl,
int8_t nColumn,
int8_t nRow)
{
assert(pPl != NULL);
// only allow coordinates which are within sane ranges
assert((nColumn >= -4) && (nColumn < pPl->nWidth));
assert((nRow >= -4) && (nRow < pPl->nHeight));
// The rows of a piece get compared with the background one by one
// until either a collision occures or all rows are compared. Both the
// piece row and the part of the playfield it covers are represented in
// 4 bits which were singled out from their corresponding uint16_t
// values and are aligned to LSB. In case where a piece overlaps with
// either the left or the right border we "enhance" the playfield part
// via bit shifting and set all bits representing the border to 1.
//
// NOTE: LSB represents the left most position.
uint16_t nPieceMap = tetris_piece_getBitmap(pPl->pPiece);
uint16_t nPlayfieldPart;
uint16_t nPieceRowMap;
// negative nRow values indicate that the piece hasn't fully entered the
// playfield yet which requires special treatment if the piece overlaps
// with either the left or the right border
if (nRow < 0)
{
uint16_t nBorderMask = 0x0000;
// piece overlaps with left border
if (nColumn < 0)
{
nBorderMask = 0x1111 << (-nColumn - 1);
}
// piece overlaps with right border
else if ((nColumn + 3) >= pPl->nWidth)
{
nBorderMask = 0x8888 >> ((nColumn + 3) - pPl->nWidth);
}
// return if piece collides with border
if ((nPieceMap & nBorderMask) != 0)
{
return 1;
}
}
// here we check the part which has already entered the playfield
for (int8_t y = (nRow < 0) ? -nRow : 0; y < 4; ++y)
{
// current piece row overlaps with lower border
if ((y + nRow) >= pPl->nHeight)
{
// all 4 bits represent the lower border
nPlayfieldPart = 0x000F;
}
// piece overlaps with left border
else if (nColumn < 0)
{
// clear all bits we are not interested in
nPlayfieldPart = (pPl->dump[y + nRow] & (0x000F >> -nColumn));
// add zeros to the left (the bits "behind" the left border)
nPlayfieldPart <<= -nColumn;
// set bits beyond left border to 1
nPlayfieldPart |= 0x000F >> (4 + nColumn);
}
// piece overlaps with right border
else if ((nColumn + 3) >= pPl->nWidth)
{
// align the bits we are interested in to LSB
// (thereby clearing the rest)
nPlayfieldPart = pPl->dump[y + nRow] >> nColumn;
// set bits beyond right border to 1
nPlayfieldPart |= 0xFFF8 >> (nColumn + 3 - pPl->nWidth);
}
// current row neither overlaps with left, right nor lower border
else
{
// clear all bits we are not interested in and align the
// remaing row to LSB
nPlayfieldPart =
(pPl->dump[y + nRow] & (0x000F << nColumn)) >> nColumn;
}
// clear all bits of the piece we are not interested in and
// align the remaing row to LSB
nPieceRowMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2);
// finally check for a collision
if ((nPlayfieldPart & nPieceRowMap) != 0)
{
return 1;
}
}
// if we reach here, no collision was detected
return 0;
}
/* Function: tetris_playfield_advancePiece
* Description: lowers piece by one row or finally docks it
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_advancePiece(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
// a piece can only be lowered if it is hovering or gliding
assert ((pPl->status == TETRIS_PFS_HOVERING) ||
(pPl->status == TETRIS_PFS_GLIDING));
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow + 1))
{
uint16_t nPiece = tetris_piece_getBitmap(pPl->pPiece);
// Is the playfield filled up?
if ((pPl->nRow < 0) && (nPiece & (0x0FFF >> ((3 + pPl->nRow) << 2))) != 0)
{
pPl->status = TETRIS_PFS_GAMEOVER;
}
else
{
// determine valid start point for dump index
int8_t nStartRow = ((pPl->nRow + 3) < pPl->nHeight) ?
(pPl->nRow + 3) : pPl->nHeight - 1;
for (int8_t i = nStartRow; i >= pPl->nRow; --i)
{
int8_t y = i - pPl->nRow;
// clear all bits of the piece we are not interested in and
// align the rest to LSB
uint16_t nPieceMap = (nPiece & (0x000F << (y << 2))) >> (y << 2);
// shift the remaining content to the current column
if (pPl->nColumn >= 0)
{
nPieceMap <<= pPl->nColumn;
}
else
{
nPieceMap >>= -pPl->nColumn;
}
// embed piece in playfield
pPl->dump[i] |= nPieceMap;
}
// the piece has finally been docked
pPl->status = TETRIS_PFS_DOCKED;
}
}
else
{
// since there is no collision the piece may continue its travel
// to the ground...
pPl->nRow++;
// are we gliding?
pPl->status = tetris_playfield_hoverStatus(pPl);
}
}
/* Function: tetris_playfield_movePiece
* Description: moves piece to the given direction
* Argument pPl: playfield to perform action on
* Argument direction: direction (see tetris_playfield_direction_t)
* Return value: 1 if piece could be moved, 0 otherwise
*/
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl,
tetris_playfield_direction_t direction)
{
assert(pPl != NULL);
// a piece can only be moved if it is still hovering or gliding
assert((pPl->status == TETRIS_PFS_HOVERING) ||
(pPl->status == TETRIS_PFS_GLIDING));
int8_t nOffset = (direction == TETRIS_PFD_LEFT) ? -1 : 1;
if (tetris_playfield_collision(pPl, pPl->nColumn + nOffset, pPl->nRow) == 0)
{
pPl->nColumn += nOffset;
// are we gliding?
pPl->status = tetris_playfield_hoverStatus(pPl);
return 1;
}
return 0;
}
/* Function: tetris_playfield_rotatePiece
* Description: rotates piece to the given direction
* Argument pPl: playfield to perform action on
* Argument r: type of rotation (see tetris_piece_rotation_t)
* Return value: 1 if piece could be rotated, 0 otherwise
*/
uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl,
tetris_piece_rotation_t rotation)
{
assert(pPl != NULL);
// a piece can only be rotation if it is still hovering or gliding
assert((pPl->status == TETRIS_PFS_HOVERING) ||
(pPl->status == TETRIS_PFS_GLIDING));
tetris_piece_rotate(pPl->pPiece, rotation);
// does the rotated piece cause a collision?
if (tetris_playfield_collision(pPl, pPl->nColumn, pPl->nRow) != 0)
{
// in that case we revert the rotation
if (rotation == TETRIS_PC_ROT_CW)
{
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CCW);
}
else
{
tetris_piece_rotate(pPl->pPiece, TETRIS_PC_ROT_CW);
}
return 0;
}
// are we gliding?
pPl->status = tetris_playfield_hoverStatus(pPl);
return 1;
}
/* Function: tetris_playfield_removeCompletedLines
* Description: removes completed lines (if any) and lowers the dump
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
// rows can only be removed if we are in state TETRIS_PFS_DOCKED
assert(pPl->status == TETRIS_PFS_DOCKED);
// bit mask of a full row
uint16_t nFullRow = 0xFFFF >> (16 - pPl->nWidth);
// bit mask (only 4 bits) that tells us if the n-th row after the
// current nRow is complete (n-th bit set to 1, LSB represents nRow itself)
uint8_t nRowMask = 0;
// determine sane start and stop values for the dump' index
int8_t nStartRow =
((pPl->nRow + 3) >= pPl->nHeight) ? pPl->nHeight - 1 : pPl->nRow + 3;
int8_t nStopRow = (pPl->nRow < 0) ? 0 : pPl->nRow;
// dump index variables
// for incomplete rows, both variables will be decremented
// for complete rows, only i gets decremented
int8_t nLowestRow = nStartRow;
// this loop only considers rows which are affected by the piece
for (int8_t i = nStartRow; i >= nStopRow; --i)
{
// is current row a full row?
if ((nFullRow & pPl->dump[i]) == nFullRow)
{
// set corresponding bit for the row mask
// nRowMask |= 0x08 >> (nStartRow - i);
nRowMask |= 0x01 << (i - pPl->nRow);
}
else
{
// if nLowestRow and i differ, the dump has to be shifted
if (i < nLowestRow)
{
pPl->dump[nLowestRow] = pPl->dump[i];
}
--nLowestRow;
}
}
// if rows have been removed, this loop shifts the rest of the dump
uint8_t nComplete = nLowestRow - nStopRow + 1;
if (nComplete > 0)
{
for (int8_t i = nStopRow - 1; nLowestRow >= 0; --i)
{
// is the row we are copying from below the upper border?
if (i >= 0)
{
// just copy from that row
pPl->dump[nLowestRow] = pPl->dump[i];
}
else
{
// rows above the upper border are always empty
pPl->dump[nLowestRow] = 0;
}
--nLowestRow;
}
}
// ready to get the next piece
pPl->status = TETRIS_PFS_READY;
pPl->nRowMask = nRowMask;
}
/*****************
* get functions *
*****************/
/* Function: tetris_playfield_getWidth
* Description: returns the width of the playfield
* Argument pPl: the playfield we want information from
* Return value: width of the playfield
*/
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nWidth;
}
/* Function: tetris_playfield_getHeight
* Description: returns the height of the playfield
* Argument pPl: the playfield we want information from
* Return value: height of the playfield
*/
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nHeight;
}
/* Function: tetris_playfield_getPiece
* Description: returns the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: pointer to the currently falling piece
*/
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->pPiece;
}
/* Function: tetris_playfield_getColumn
* Description: returns the column of the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: column of the currently falling piece
*/
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nColumn;
}
/* Function: tetris_playfield_getRow
* Description: returns the row of the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: row of the currently falling piece
*/
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nRow;
}
/* Function: tetris_playfield_getRowMask
* Description: returns the row mask relative to nRow
* Argument pPl: the playfield we want information from
* Return value: the first 4 bits indicate which lines (relative to nRow)
* have been removed if we are in status TETRIS_PFS_READY
* LSB is the highest line
*/
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->nRowMask;
}
/* Function: tetris_playfield_getStatus
* Description: returns the status of the playfield
* Argument pPl: the playfield we want information from
* Return value: status of the playfield (see tetris_playfield_status_t)
*/
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl)
{
assert(pPl != NULL);
return pPl->status;
}
/* Function: tetris_playfield_getDumpRow
* Description: returns the given row of the dump (as bitmap)
* Argument pPl: the playfield we want information from
* Argument nRow: the number of the row (0 <= nRow < height of playfield)
* Return value: bitmap of the requested row (LSB is leftmost column)
*/
uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl,
int8_t nRow)
{
assert(pPl != NULL);
assert((0 <= nRow) && (nRow < pPl->nHeight));
return pPl->dump[nRow];
}

213
games/tetris/playfield.h Normal file
View file

@ -0,0 +1,213 @@
#ifndef TETRIS_PLAYFIELD_H_
#define TETRIS_PLAYFIELD_H_
#include <inttypes.h>
#include "piece.h"
/*********
* types *
*********/
// directions to which a piece can be moved
typedef enum tetris_playfield_direction_t
{
TETRIS_PFD_LEFT,
TETRIS_PFD_RIGHT
}
tetris_playfield_direction_t;
// status of the playfield
typedef enum tetris_playfield_status_t
{
TETRIS_PFS_READY, // ready to get next piece
TETRIS_PFS_HOVERING, // piece is still hovering
TETRIS_PFS_GLIDING, // piece is gliding on the dump
TETRIS_PFS_DOCKED, // piece has been docked
TETRIS_PFS_GAMEOVER // playfield is filled up
}
tetris_playfield_status_t;
// tetris_playfield_t
typedef struct tetris_playfield_t
{
int8_t nWidth; // width of playfield
int8_t nHeight; // height of playfield
tetris_piece_t *pPiece; // currently falling piece
int8_t nColumn; // horz. piece pos. (0 is left)
int8_t nRow; // vert. piece pos. (0 is top)
uint8_t nRowMask; // removed lines relative to nRow (bitmap)
tetris_playfield_status_t status; // status
uint16_t *dump; // playfield itself
}
tetris_playfield_t;
/****************************
* construction/destruction *
****************************/
/* Function: tetris_playfield_construct
* Description: constructs a playfield with the given diemensions
* Argument nWidth: width of playfield (4 <= n <= 16)
* Argument nHeight: height of playfield (4 <= n <= 124)
* Return value: pointer to a newly created playfield
*/
tetris_playfield_t *tetris_playfield_construct(int8_t nWidth, int8_t nHeight);
/* Function: tetris_playfield_destruct
* Description: destructs a playfield
* Argument pPl: pointer to the playfield to be destructed
* Return value: void
*/
void tetris_playfield_destruct(tetris_playfield_t *pPl);
/*******************************
* playfield related functions *
*******************************/
/* Function: tetris_playfield_reset
* Description: resets playfield to begin a new game
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_reset(tetris_playfield_t *pPl);
/* Function: tetris_playfield_insertPiece
* Description: inserts a new piece
* Argument pPl: playfield to perform action on
* Argument pPiece: piece to be inserted
* Argument ppOldPiece: [out] indirect pointer to former piece for deallocation
* Return value: void
*/
void tetris_playfield_insertPiece(tetris_playfield_t *pPl,
tetris_piece_t *pPiece,
tetris_piece_t** ppOldPiece);
/* Function: tetris_playfield_collision
* Description: detects if piece collides with s.th. at a given position
* Argument pPl: playfield to perform action on
* Argument nColumn: column where the piece should be moved
* Argument nRow: row where the piece should be moved
* Return value: 1 for collision, 0 otherwise
*/
uint8_t tetris_playfield_collision(tetris_playfield_t *pPl,
int8_t nColumn,
int8_t nRow);
/* Function: tetris_playfield_advancePiece
* Description: lowers piece by one row or finally docks it
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_advancePiece(tetris_playfield_t *pPl);
/* Function: tetris_playfield_movePiece
* Description: moves piece to the given direction
* Argument pPl: playfield to perform action on
* Argument direction: direction (see tetris_playfield_direction_t)
* Return value: 1 if piece could be moved, 0 otherwise
*/
uint8_t tetris_playfield_movePiece(tetris_playfield_t *pPl,
tetris_playfield_direction_t direction);
/* Function: tetris_playfield_rotatePiece
* Description: rotates piece to the given direction
* Argument pPl: playfield to perform action on
* Argument r: type of rotation (see tetris_piece_rotation_t)
* Return value: 1 if piece could be rotated, 0 otherwise
*/
uint8_t tetris_playfield_rotatePiece(tetris_playfield_t *pPl,
tetris_piece_rotation_t rotation);
/* Function: tetris_playfield_removeCompletedLines
* Description: removes completed lines (if any) and lowers the dump
* Argument pPl: playfield to perform action on
* Return value: void
*/
void tetris_playfield_removeCompleteLines(tetris_playfield_t *pPl);
/*****************
* get functions *
*****************/
/* Function: tetris_playfield_getWidth
* Description: returns the width of the playfield
* Argument pPl: the playfield we want information from
* Return value: width of the playfield
*/
int8_t tetris_playfield_getWidth(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getHeight
* Description: returns the height of the playfield
* Argument pPl: the playfield we want information from
* Return value: height of the playfield
*/
int8_t tetris_playfield_getHeight(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getPiece
* Description: returns the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: pointer to the currently falling piece
*/
tetris_piece_t *tetris_playfield_getPiece(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getColumn
* Description: returns the column of the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: column of the currently falling piece
*/
int8_t tetris_playfield_getColumn(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getRow
* Description: returns the row of the currently falling piece
* Argument pPl: the playfield we want information from
* Return value: row of the currently falling piece
*/
int8_t tetris_playfield_getRow(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getRowMask
* Description: returns the row mask relative to nRow
* Argument pPl: the playfield we want information from
* Return value: the first 4 bits indicate which lines (relative to nRow)
* have been removed if we are in status TETRIS_PFS_READY
*/
uint8_t tetris_playfield_getRowMask(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getStatus
* Description: returns the status of the playfield
* Argument pPl: the playfield we want information from
* Return value: status of the playfield (see tetris_playfield_status_t)
*/
tetris_playfield_status_t tetris_playfield_getStatus(tetris_playfield_t *pPl);
/* Function: tetris_playfield_getDumpRow
* Description: returns the given row of the dump (as bitmap)
* Argument pPl: the playfield we want information from
* Argument nRow: the number of the row (0 <= nRow <= 124)
* Return value: bitmap of the requested row (LSB is leftmost column)
*/
uint16_t tetris_playfield_getDumpRow(tetris_playfield_t *pPl,
int8_t nRow);
#endif /*TETRIS_PLAYFIELD_H_*/

421
games/tetris/view.c Normal file
View file

@ -0,0 +1,421 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <inttypes.h>
#include "../config.h"
#include "../pixel.h"
#include "../util.h"
#include "../scrolltext.h"
#include "logic.h"
#include "piece.h"
#include "playfield.h"
#include "view.h"
/* the API simulator and the real API have different named wait functions */
#ifdef __AVR__
#define WAIT(ms) wait(ms)
#else
#define WAIT(ms) myWait(ms)
#endif
/***********
* defines *
***********/
// how often should the border blink (to indicate level up)
#define TETRIS_VIEW_BORDER_BLINK_COUNT 2
// amount of time (in ms) between border color changes
#define TETRIS_VIEW_BORDER_BLINK_DELAY 100
// how often should the lines blink when they get removed
#define TETRIS_VIEW_LINE_BLINK_COUNT 3
// amount of time (in ms) between line color changes
#define TETRIS_VIEW_LINE_BLINK_DELAY 75
// colors of game elements
#define TETRIS_VIEW_COLORSPACE 0
#define TETRIS_VIEW_COLORBORDER 1
#define TETRIS_VIEW_COLORFADE 2
#define TETRIS_VIEW_COLORPIECE 3
/***************************
* non-interface functions *
***************************/
/* Function: tetris_view_getPieceColor
* Description: helper function to dim the piece color if game is paused
* Argument pV: pointer to the view whose pause status is of interest
* Return value: void
*/
uint8_t tetris_view_getPieceColor (tetris_view_t *pV)
{
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
{
return TETRIS_VIEW_COLORPIECE;
}
else
{
return TETRIS_VIEW_COLORBORDER;
}
}
/* Function: tetris_view_drawDump
* Description: redraws the dump and the falling piece (if necessary)
* Argument pV: pointer to the view on which the dump should be drawn
* Return value: void
*/
void tetris_view_drawDump(tetris_view_t *pV)
{
assert(pV->pPl != NULL);
if (tetris_playfield_getRow(pV->pPl) <= -4)
{
return;
}
int8_t nPieceRow = tetris_playfield_getRow(pV->pPl);
// only redraw dump completely if the view mode has been changed
int8_t nStartRow;
if (pV->modeCurrent == pV->modeOld)
{
nStartRow = ((nPieceRow + 3) < 16) ? (nPieceRow + 3) : 15;
}
else
{
nStartRow = 15;
}
uint16_t nRowMap;
uint16_t nElementMask;
tetris_playfield_status_t status = tetris_playfield_getStatus(pV->pPl);
for (int8_t nRow = nStartRow; nRow >= 0; --nRow)
{
nRowMap = tetris_playfield_getDumpRow(pV->pPl, nRow);
// if a piece is hovering or gliding it needs to be drawn
if ((status == TETRIS_PFS_HOVERING) || (status == TETRIS_PFS_GLIDING) ||
(status == TETRIS_PFS_GAMEOVER))
{
if ((nRow >= nPieceRow) && (nRow <= nPieceRow + 3))
{
int8_t y = nRow - nPieceRow;
int8_t nColumn = tetris_playfield_getColumn(pV->pPl);
uint16_t nPieceMap =
tetris_piece_getBitmap(tetris_playfield_getPiece(pV->pPl));
// clear all bits of the piece we are not interested in and
// align the remaining row to LSB
nPieceMap = (nPieceMap & (0x000F << (y << 2))) >> (y << 2);
// shift remaining part to current column
if (nColumn >= 0)
{
nPieceMap <<= nColumn;
}
else
{
nPieceMap >>= -nColumn;
}
// cut off unwanted stuff
nPieceMap &= 0x03ff;
// finally embed piece into the view
nRowMap |= nPieceMap;
}
}
nElementMask = 0x0001;
for (int8_t x = 0; x < 10; ++x)
{
unsigned char nColor;
if ((nRowMap & nElementMask) != 0)
{
nColor = tetris_view_getPieceColor(pV);
}
else
{
nColor = TETRIS_VIEW_COLORSPACE;
}
setpixel((pixel){14-x,nRow}, nColor);
nElementMask <<= 1;
}
}
}
/* Function: tetris_view_drawPreviewPiece
* Description: redraws the preview window
* Argument pV: pointer to the view on which the piece should be drawn
* Argmument pPc: pointer to the piece for the preview window (may be NULL)
* Return value: void
*/
void tetris_view_drawPreviewPiece(tetris_view_t *pV, tetris_piece_t *pPc)
{
if (pPc != NULL)
{
uint8_t nColor;
uint16_t nElementMask = 0x0001;
uint16_t nPieceMap;
if (pV->modeCurrent == TETRIS_VIMO_RUNNING)
{
nPieceMap = tetris_piece_getBitmap(pPc);
}
else
{
nPieceMap = 0x26a6;
}
for (uint8_t y = 0; y < 4; ++y)
{
for (uint8_t x = 0; x < 4; ++x)
{
if ((nPieceMap & nElementMask) != 0)
{
nColor = TETRIS_VIEW_COLORPIECE;
}
else
{
nColor = TETRIS_VIEW_COLORSPACE;
}
setpixel((pixel) {3 - x, y + 6}, nColor);
nElementMask <<= 1;
}
}
}
else
{
for (uint8_t y = 0; y < 4; ++y)
{
for (uint8_t x = 0; x < 4; ++x)
{
setpixel((pixel) {3 - x, y + 6}, TETRIS_VIEW_COLORSPACE);
}
}
}
}
/* Function: tetris_view_drawBorders
* Description: draws borders in the given color
* Argument nColor: the color for the border
* Return value: void
*/
void tetris_view_drawBorders(uint8_t nColor)
{
// drawing playfield
uint8_t x, y;
for (y = 0; y < 16; ++y)
{
setpixel((pixel){4, y}, nColor);
setpixel((pixel){15, y}, nColor);
}
for (y = 0; y < 5; ++y)
{
for (x = 0; x <= 3; ++x){
setpixel((pixel){x, y}, nColor);
setpixel((pixel){x, y + 11}, nColor);
}
}
}
/* Function: tetris_view_blinkBorders
* Description: lets the borders blink to notify player of a level change
* Return value: void
*/
void tetris_view_blinkBorders()
{
for (uint8_t i = 0; i < TETRIS_VIEW_BORDER_BLINK_COUNT; ++i)
{
tetris_view_drawBorders(TETRIS_VIEW_COLORPIECE);
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER);
WAIT(TETRIS_VIEW_BORDER_BLINK_DELAY);
}
}
/* Function: tetris_view_blinkLines
* Description: lets complete lines blink to emphasize their removal
* Argmument pPl: pointer to the playfield whose complete lines should blink
* Return value: void
*/
void tetris_view_blinkLines(tetris_playfield_t *pPl)
{
// reduce necessity of pointer arithmetic
int8_t nRow = tetris_playfield_getRow(pPl);
uint8_t nRowMask = tetris_playfield_getRowMask(pPl);
// don't try to draw below the border
int8_t nDeepestRowOffset = ((nRow + 3) < tetris_playfield_getHeight(pPl) ?
3 : tetris_playfield_getHeight(pPl) - (nRow + 1));
// this loop controls how often the lines should blink
for (uint8_t i = 0; i < TETRIS_VIEW_LINE_BLINK_COUNT; ++i)
{
// this loop determines the color of the line to be drawn
for (uint8_t nColIdx = 0; nColIdx < 2; ++nColIdx)
{
// iterate through the possibly complete lines
for (uint8_t j = 0; j <= nDeepestRowOffset; ++j)
{
// is current line a complete line?
if ((nRowMask & (0x01 << j)) != 0)
{
// draw line in current color
uint8_t y = nRow + j;
for (uint8_t x = 0; x < 10; ++x)
{
uint8_t nColor = (nColIdx == 0 ? TETRIS_VIEW_COLORFADE
: TETRIS_VIEW_COLORPIECE);
setpixel((pixel){14 - x, y}, nColor);
}
}
}
// wait a few ms to make the blink effect visible
WAIT(TETRIS_VIEW_LINE_BLINK_DELAY);
}
}
}
/****************************
* construction/destruction *
****************************/
/* Function: tetris_view_construct
* Description: constructs a view for André's borg
* Argument pPl: pointer to logic object which should be observed
* Argument pPl: pointer to playfield which should be observed
* Return value: pointer to a newly created view
*/
tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic,
tetris_playfield_t *pPl)
{
// memory allocation
assert((pLogic != NULL) && (pPl != NULL));
tetris_view_t *pView =
(tetris_view_t *) malloc(sizeof(tetris_view_t));
assert(pView != NULL);
// init
memset(pView, 0, sizeof(tetris_view_t));
pView->pLogic = pLogic;
pView->pPl = pPl;
pView->modeCurrent = TETRIS_VIMO_RUNNING;
pView->modeOld = TETRIS_VIMO_RUNNING;
// drawing some first stuff
clear_screen(0);
tetris_view_drawBorders(TETRIS_VIEW_COLORBORDER);
return pView;
}
/* Function: tetris_view_destruct
* Description: destructs a view
* Argument pView: pointer to the view which should be destructed
* Return value: void
*/
void tetris_view_destruct(tetris_view_t *pView)
{
assert(pView != NULL);
free(pView);
}
/***************************
* view related functions *
***************************/
/* Function: tetris_view_getDimensions
* Description: destructs a view
* Argument w: [out] pointer to an int8_t to store the playfield width
* Argument h: [out] pointer to an int8_t to store the playfield height
* Return value: void
*/
void tetris_view_getDimensions(int8_t *w,
int8_t *h)
{
assert((w != NULL) && (h != NULL));
*w = 10;
*h = 16;
}
/* Function: tetris_view_setViewMode
* Description: sets the view mode (pause or running)
* Argument pV: pointer to the view whose mode should be set
* Argument vm: see definition of tetris_view_mode_t
* Return value: void
*/
void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm)
{
pV->modeOld = pV->modeCurrent;
pV->modeCurrent = vm;
}
/* Function: tetris_view_update
* Description: informs a view about changes in the game
* Argument pV: pointer to the view which should be updated
* Return value: void
*/
void tetris_view_update(tetris_view_t *pV)
{
assert(pV != NULL);
// let complete lines blink (if there are any)
if (tetris_playfield_getRowMask(pV->pPl) != 0)
{
tetris_view_blinkLines(pV->pPl);
}
// draw preview piece
tetris_view_drawPreviewPiece(pV, tetris_logic_getPreviewPiece(pV->pLogic));
// draw dump
tetris_view_drawDump(pV);
// visual feedback to inform about a level change
uint8_t nLevel = tetris_logic_getLevel(pV->pLogic);
if (nLevel != pV->nOldLevel)
{
tetris_view_blinkBorders();
pV->nOldLevel = nLevel;
}
}
/* Function: tetris_view_showResults
* Description: shows results after game
* Argument pV: pointer to the view which should show the reults
* Return value: void
*/
void tetris_view_showResults(tetris_view_t *pV)
{
char pszResults[48];
uint16_t nScore = tetris_logic_getScore(pV->pLogic);
uint16_t nHighscore = tetris_logic_getHighscore(pV->pLogic);
uint16_t nLines = tetris_logic_getLines(pV->pLogic);
if (nScore <= nHighscore)
{
snprintf(pszResults, 48 * sizeof(char),
"</#Lines %u Score %u Highscore %u",
nLines, nScore, nHighscore);
}
else
{
snprintf(pszResults, 48 * sizeof(char),
"</#Lines %u New Highscore %u", nLines, nScore);
}
scrolltext(pszResults);
}

95
games/tetris/view.h Normal file
View file

@ -0,0 +1,95 @@
#ifndef TETRIS_VIEW_H_
#define TETRIS_VIEW_H_
#include <inttypes.h>
#include "logic.h"
#include "piece.h"
#include "playfield.h"
/*********
* types *
*********/
// presentation modes
typedef enum tetris_view_mode_t
{
TETRIS_VIMO_PAUSED,
TETRIS_VIMO_RUNNING
}
tetris_view_mode_t;
typedef struct tetris_view_t
{
tetris_logic_t *pLogic; // associated logic object
tetris_playfield_t *pPl; // associated playfield
tetris_view_mode_t modeCurrent; // current presentation mode
tetris_view_mode_t modeOld; // old presentation mode
uint8_t nOldLevel; // helper variable to recognize level changes
}
tetris_view_t;
/*****************************
* construction/destruction *
*****************************/
/* Function: tetris_view_construct
* Description: constructs a view for André's borg
* Argument pPl: pointer to logic object which should be observed
* Argument pPl: pointer to playfield which should be observed
* Return value: pointer to a newly created view
*/
tetris_view_t *tetris_view_construct(tetris_logic_t *pLogic,
tetris_playfield_t *pPl);
/* Function: tetris_view_destruct
* Description: destructs a view
* Argument pView: pointer to the view to be destructed
* Return value: void
*/
void tetris_view_destruct(tetris_view_t *pView);
/***************************
* view related functions *
***************************/
/* Function: tetris_view_getDimensions
* Description: destructs a view
* Argument w: [out] pointer to an int8_t to store the playfield width
* Argument h: [out] pointer to an int8_t to store the playfield height
* Return value: void
*/
void tetris_view_getDimensions(int8_t *w,
int8_t *h);
/* Function: tetris_view_setViewMode
* Description: sets the view mode (pause or running)
* Argument pV: pointer to the view whose mode should be set
* Argument vm: see definition of tetris_view_mode_t
* Return value: void
*/
void tetris_view_setViewMode(tetris_view_t *pV, tetris_view_mode_t vm);
/* Function: tetris_view_update
* Description: informs a view about changes in the game
* Argument pV: pointer to the view which should be updated
* Return value: void
*/
void tetris_view_update(tetris_view_t *pV);
/* Function: tetris_view_showResults
* Description: shows results after game
* Argument pV: pointer to the view which should show the reults
* Return value: void
*/
void tetris_view_showResults(tetris_view_t *pV);
#endif /*TETRIS_VIEW_H_*/