📄 tetrislib.c
字号:
//
// Title : tetrislib - part of scopetris
// Author : Lars Pontoppidan
// Date : Aug. 2007
// Version : 0.1
// Target MCU : AtMega32 at 8 MHz
//
//
// TETRIS LIBRARY
//
// with vector rendering and animation effects
//
// with naive or real gravity
//
// for the AVR
//
//
// DESCRIPTION:
// Tetrislib contains game logic, game state and graphics rendering using
// drawlib for a tetris game.
//
// CREDITS:
// Tetrislib was created by Lars Pontoppidan in August 2007.
//
// COMPILER INFO:
// This code compiles with avr-gcc v.4.1.2
//
// DISCLAIMER:
// The author is in no way responsible for any problems or damage caused by
// using this code. Use at your own risk.
//
// LICENSE:
// This code is distributed under the GNU Public License
// which can be found at http://www.gnu.org/licenses/gpl.txt
//
#include <string.h>
#include <stdlib.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include "drawlib.h"
#include "vectorfont.h"
#include "tetrislib.h"
////////////////////////////////////////////////////////////////////////////////
// GAME DEFINITIONS AND DECLARATIONS
////////////////////////////////////////////////////////////////////////////////
//
// PLAYGROUND MATRIX is 10 cubes wide, 20 cubes high
// NOTE: The game might break if these defines are changed...
//
#define MATRIX_WIDTH 10
#define MATRIX_HEIGHT 20
//
// Coordinate definition (x,y)
//
// (0,20) (10,20)
// +----------+
// |x x|
// | |
// . .
// . .
// |x x|
// +----------+
// (0,0) (10,0)
//
//
// CUBE OCCUPATION is stored as bits in a 16 bit var for each row
//
// NOTE: LSB is leftmost cube!
unsigned short occupancy[MATRIX_HEIGHT];
// NOTE: sentinel cubes on each side!
// NOTE: MSB in occupancy is used temporarily for animation
#define OCCUPANCY_FREE (1|(1<<(MATRIX_WIDTH+1)))
#define OCCUPANCY_NONE 0xFFF
// BRICKS
//
// The actual state of the game is stored as an array of bricks.
//
// The cubes of a brick are described in 4 bytes:
//
// 4 bytes: brick[3]: LSB X X X X...
// brick[2]: LSB X X X X...
// brick[1]: LSB X X X X...
// brick[0]: LSB X X X X...
//
// For instance: . . . . => brick[3] = 0
// x . . . => brick[2] = 1
// x x . . => brick[1] = 3
// x . . . => brick[0] = 1
//
// The coordinates of the brick indicate position of lower left cube
struct Brick_Type {
unsigned char brick[4];
unsigned char x; // high nibble of x is used for y offset (pixels)
signed char y;
};
//
// BRICKS IN GAME
//
// The player controlled falling brick:
struct Brick_Type falling_brick;
// The pending brick
struct Brick_Type pending_brick;
// The array of bricks currently active in the game:
#define MAX_BRICKS 100
struct Brick_Type active_brick[MAX_BRICKS];
unsigned char active_bricks;
//
// BRICK TEMPLATES
//
// When a new brick is entering, it is randomly selected from one of these:
//
#define BRICK_TEMPLATES 7
const unsigned char brick_template[BRICK_TEMPLATES][2] PROGMEM = {
{15,0},
{7,1},
{7,4},
{3,3},
{3,6},
{7,2},
{6,3}};
//
// GRAPHICS
//
// The graphics rendering reacts to graphics_status
//
#define SHOW_FALLING_BRICK 1
#define SHOW_ZAPPED_ROWS 2
#define SHOW_ZAP_COUNT 4
unsigned char graphics_status;
//
// Graphics definitions
//
#define BRICK_SIZE 10
#define X_OFFSET 50
#define Y_OFFSET 5
#define BRICK_INSET 2
// | | |
// | | |
// 2 | x | x x |
// | | |
// 0 +---------+---------+
// 0 2 10
// |
// brick inset
// Tetris game statemachine.
//
#define TETRIS_IDLE 0
#define TETRIS_NEW_BRICK 1
#define TETRIS_BRICK_FALLING 2
#define TETRIS_TRY_ZAP 3
#define TETRIS_DO_GRAVITY 4
#define TETRIS_SHOW_ZAPPED 5
#define TETRIS_GAME_OVER 255
//
unsigned char tetris_state;
// Tetris mode is an external bitflag variable and specifies:
//
// TETRIS_MODE_SPEEDUP 1 // falling brick speedup
// TETRIS_MODE_GRAVITY 2 // for real gravity. If not set, naive gravity
//
unsigned char tetris_mode;
//
// Game and animation timing
//
// tetris_draw_frame is called with 100 Hz (tf = 10ms)
//
// tetris_speed defines how fast the brick is falling and is defined:
//
// each frame brick moves pixels: = tetris_speed / 64
// => each frame brick moves grids: = tetris_speed / (64 * 10)
// => each sec brick moves grids: = tetris_speed / (64 * 10 * tf)
// => brick moves one grid each (sec): = (64 * 10 * tf) / tetris_speed
// = 6.4(s) / tetris_speed
// for example: tetris_speed = 255 => tbg ~ 0.025 sec (max speed)
// for example: tetris_speed = 128 => tbg = 0.050 sec
// for example: tetris_speed = 64 => tbg = 0.100 sec
// for example: tetris_speed = 32 => tbg = 0.2 sec
// for example: tetris_speed = 16 => tbg = 0.4 sec
// for example: tetris_speed = 8 => tbg = 0.8 sec
// for example: tetris_speed = 4 => tbg = 1.6 sec
//
unsigned char tetris_speed;
#define SPEEDUP_SPEED 255 // The speed of speedup mode
//
// Falling brick pixel offset
//
signed short falling_pixels; // this is stored in /256 fixpoint
// thus 2560 means 10 pixel offset
unsigned char animation_frame;
unsigned char lines_zapped;
//
// Score keeping
//
// Total number of lines zapped
unsigned short tetris_stats_lines;
//
// Number of zap-rounds
unsigned short tetris_stats_zapcount;
////////////////////////////////////////////////////////////////////////////////
// BRICK MANAGEMENT IMPLEMENTATION
////////////////////////////////////////////////////////////////////////////////
void inline copy_brick(struct Brick_Type *dest, struct Brick_Type *src)
{
memcpy(dest, src, sizeof(struct Brick_Type));
}
// Expand array of bricks
void new_brick(struct Brick_Type *brick)
{
if (active_bricks < MAX_BRICKS) {
copy_brick(&(active_brick[active_bricks]), brick);
active_bricks++;
}
}
// Delete element from array of bricks
void delete_brick(unsigned char index)
{
if (index < (active_bricks-1)) {
copy_brick(&(active_brick[index]), &(active_brick[active_bricks-1]));
}
active_bricks--;
}
// New brick from template
void init_brick_template(struct Brick_Type *dest, unsigned char tno)
{
}
// Rotate brick
void rotate_brick(struct Brick_Type *dest, struct Brick_Type *src, unsigned char ccw)
{
unsigned char c,b;
if (ccw) {
b=1;
for (c=0; c<4; c++) {
dest->brick[c] = ((src->brick[3] & b)?1:0) | ((src->brick[2] & b)?2:0) |
((src->brick[1] & b)?4:0) | ((src->brick[0] & b)?8:0);
b = b<<1;
}
}
else {
b=8;
for (c=0; c<4; c++) {
dest->brick[c] = ((src->brick[0] & b)?1:0) | ((src->brick[1] & b)?2:0) |
((src->brick[2] & b)?4:0) | ((src->brick[3] & b)?8:0);
b = b>>1;
}
}
dest->x = src->x;
dest->y = src->y;
// left adjust
while (((dest->brick[0] & 1) | (dest->brick[1] & 1) |
(dest->brick[2] & 1) | (dest->brick[3] & 1)) == 0) {
dest->brick[0] >>= 1;
dest->brick[1] >>= 1;
dest->brick[2] >>= 1;
dest->brick[3] >>= 1;
}
// down adjust
while (dest->brick[0] == 0) {
dest->brick[0] = dest->brick[1];
dest->brick[1] = dest->brick[2];
dest->brick[2] = dest->brick[3];
dest->brick[3] = 0;
}
}
// Split brick in two. Splitrow refers to either 0,1,2,3 of source brick
void split_brick(struct Brick_Type *lower, struct Brick_Type *upper,
struct Brick_Type *src, unsigned char splitrow)
{
unsigned char c;
copy_brick(upper, src);
copy_brick(lower, src);
for (c=0; c<=splitrow; c++) {
upper->brick[c] = 0;
}
for (c=splitrow; c<4; c++) {
lower->brick[c] = 0;
}
}
// This function fixes a brick to have row 0 occupied, returns false if brick
// is empty
unsigned char fix_brick(struct Brick_Type *brick)
{
// check emptiness
if ((brick->brick[0]|brick->brick[1]|brick->brick[2]|brick->brick[3])==0)
return 0;
// fix brick
while (brick->brick[0] == 0) {
brick->brick[0] = brick->brick[1];
brick->brick[1] = brick->brick[2];
brick->brick[2] = brick->brick[3];
brick->brick[3] = 0;
brick->y++;
}
return 1;
}
// Calculate occupancy grid according to active_brick array
void calc_occupancy_grid(void)
{
unsigned char i;
unsigned char c,y;
signed char s;
// clear occupancy grid (only sentinel cubes)
for (i=0; i<MATRIX_HEIGHT; i++) {
occupancy[i] = OCCUPANCY_FREE;
}
// or each brick into grid
for (i=0; i<active_bricks; i++) {
s = active_brick[i].x+1;
y = active_brick[i].y;
for (c=0; c<4; c++) {
occupancy[c+y] |= (unsigned short)active_brick[i].brick[c] << s;
}
}
}
// Xors the brick's cubes in occupancy grid
void occupancy_xor_brick(struct Brick_Type *brick)
{
unsigned char c,y;
signed char s;
s = brick->x+1;
y = brick->y;
for (c=0; c<4; c++) {
occupancy[c+y] ^= (unsigned short)brick->brick[c] << s;
}
}
// Test if brick fits in occupancy grid
unsigned char test_if_brick_fits(struct Brick_Type *brick)
{
unsigned char c,y;
signed char s;
unsigned short ret=0;
s = brick->x+1;
y = brick->y;
for (c=0; c<4; c++) {
ret |= occupancy[c+y] & ((unsigned short)brick->brick[c] << s);
}
return (ret==0);
}
// Changes brick positions according to naive gravity, as in traditional tetris
// Returns false if no changes
// Assumes up-to-date occupancy grid, and updates occupancy also.
//
unsigned char apply_naive_gravity(void)
{
// To emulate naive gravity, the occupancy grid is checked from down to up
// for any completely cleared rows. If found, all bricks above the cleared
// row are moved down with animation.
unsigned char i,y;
unsigned char moving = 0;
// First, check if bricks are currently moving in animation
for (i=0; i<active_bricks; i++) {
if (active_brick[i].x & 0xF0) {
active_brick[i].x -= 1 << 4;
moving = 1;
}
}
if (!moving) {
// Find first free row in occupancy grid
y = 0;
while (occupancy[y] != OCCUPANCY_FREE) {
if (++y == MATRIX_HEIGHT)
return 0;
}
// Find all bricks above this row
for (i=0; i<active_bricks; i++) {
if (active_brick[i].y > y) {
// Remove brick from occupancy grid
occupancy_xor_brick(&active_brick[i]);
// Move down and start animation
active_brick[i].y--;
// Add brick to occupancy grid in new position
occupancy_xor_brick(&active_brick[i]);
// Start animation
active_brick[i].x |= 10 << 4 ;
moving = 1;
}
}
}
return moving;
}
// Changes brick positions according to gravity, returns false if no changes
// Assumes up-to-date occupancy grid, and updates occupancy also.
//
unsigned char apply_gravity(void)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -