#include <stdio.h>

#include <allegro.h>

#include <math.h>

#define border 30

#define GAME_DELAY 170 // Delay in milliseconds

#define SPEEDUP_DELAY 80000 // Speedup delay in milliseconds

#define DELAY_DECREMENT 1 // Amount to decrement speed by after speedup delay


BITMAP *bmp;
BITMAP *bmp2;
BITMAP *sideframe;
BITMAP *numbers;
BITMAP *gameover;
int block_array[20][10];
int current_block[4][4];
int rotated[4][4];
int next_block, next_sprite, current_sprite, bx, by, ox, oy, score;
volatile int timer_flag;
volatile int timer_flag2;
int possible_blocks[6][4][3]={ 0,1,0,  // 0
                               0,1,0,
                               0,1,0,
                               0,1,0,
                               0,1,0,  // 1
                               0,1,0,
                               0,1,1,
                               0,0,0,
                               0,0,0, // 2
                               1,1,1,
                               0,1,0,
                               0,0,0,
                               0,0,0, // 3
                               0,1,1,
                               0,1,1,
                               0,0,0,
                               0,1,0, // 4
                               0,1,1,
                               0,0,1,
                               0,0,0,
                               0,0,1, // 5
                               0,1,1,
                               0,1,0,
                               0,0,0 };
void setupscreen( void );
int return_random(int min, int max);
void draw_next(int next_block, int n);
void initialise_new_block();
void draw_current(int a, int b, int n);
void move_down();
int check_bottom(int hit);
int set_block();
void move(int lastkey);
void set_up_timer(int delay);
int check_lines();
void plot_score();

void interrupt_handler(void) { timer_flag = 1; };
void interrupt_handler2(void) { timer_flag2 = 1; };

int main( void )
{
int n, endgame, move_skip, hit, lastkey, mdelay, line;
int init_x, init_y;
char ch;
allegro_init();
install_timer();
set_gfx_mode(GFX_VGA, 320, 200, 0, 0);
do {
  mdelay = GAME_DELAY;
  srandom(time(0));
  score = 0;
  setupscreen();
  install_int(interrupt_handler, mdelay);
  install_int(interrupt_handler2, SPEEDUP_DELAY);
  set_up_timer(mdelay);
  next_block = return_random(0, 5);
  next_sprite = return_random(0, 5);
  initialise_new_block();
  draw_current(bx, by, current_sprite);
  endgame = 0; n=0; move_skip = 2; hit = 0; lastkey = 0; timer_flag2 = 0;
  for (init_x=0; init_x<10; init_x++)
    for (init_y=0; init_y<20; init_y++)
      block_array[init_y][init_x] = 0;
  do {
    lastkey = 0;
    do {
      lastkey = check_move(lastkey);
    } while (!timer_flag);
    if (lastkey == 5) move_skip = 1;
    timer_flag = 0;
    if (timer_flag2 == 1) {
      mdelay-=DELAY_DECREMENT;
      if (mdelay<1) mdelay = 1;
      set_up_timer(mdelay);
      timer_flag2 = 0;
    }
    move_skip --;
    if (lastkey>0) move(lastkey);
    if (move_skip == 0) {
      hit = check_bottom(hit);
      if (hit==1) {
        if (by<1) endgame = 1; // Reached top
        set_block();
        hit = -1;
        line = check_lines();
      }
      else move_down();
      move_skip = 2;
    }
    if (line==0) {
      draw_current(ox, oy, 6);
      draw_current(bx, by, current_sprite);
    }
    line = 0;
    if (hit==-1) initialise_new_block();
    hit = 0;
    ox = bx; oy = by;
    if (lastkey == 4) endgame = 1; // Quit Game (esc pressed)
  } while (!endgame);

  for (n=160; n>0; n--) {
    masked_blit(gameover, screen, 0, n, 150, n+20, 80, 160-n);
    delay(5);
  }
  while (!kbhit());
  ch = getch();
} while ((ch!='q') && (ch!='Q'));

//while (!key[KEY_ENTER]);
allegro_exit();
return( 0 );
}

void set_up_timer(int delay)
{
  float percent;
  int height;
  remove_int(interrupt_handler);
  install_int(interrupt_handler, delay);
  percent = (float)delay / (float)GAME_DELAY;
  percent = 1 - percent;
  height = 140 - (int)(100.0 * percent);
  if (height<52) height = 52;
  rectfill(screen, 112, 140, 124, height, 50);
}

void move(int lastkey)
{
  int max_x, min_x, x, y, collide;
  collide = 0;
  // Move left
  if (lastkey == 1) {
    for (y=0; y<4; y++) {
      min_x = 5;
      for (x=3; x>=0; x--)
        if (current_block[y][x] == 1)
          min_x = x-1;
      if (min_x != 5)
        if (min_x+bx < 0)
          collide = 1;
        else if (block_array[y+by][min_x+bx] == 1)
          collide = 1;
    }
    if (collide == 0) bx--;
  }
  // Move right
  else if (lastkey == 2) {
    for (y=0; y<4; y++) {
      max_x = 5;
      for (x=0; x<4; x++)
        if (current_block[y][x] == 1)
          max_x = x+1;
      if (max_x != 5)
        if (max_x+bx > 9)
          collide = 1;
        else if (block_array[y+by][max_x+bx] == 1)
          collide = 1;
    }
    if (collide == 0) bx++;
  }
  // Rotate
  else if (lastkey == 3) {
    for (x=1; x<4; x++) {
      rotated[0][x] = current_block[3-x][0];
      rotated[3][3-x] = current_block[x][3];
      rotated[x][3] = current_block[0][x];
      rotated[3-x][0] = current_block[3][3-x];
    }
    rotated[1][1] = current_block[2][1];
    rotated[1][2] = current_block[1][1];
    rotated[2][2] = current_block[1][2];
    rotated[2][1] = current_block[2][2];
    for (y=0; y<4; y++)
      for (x=0; x<4; x++)
        if (rotated[y][x] == 1) {
          if (block_array[by+y][bx+x] == 1)
            collide = 1;
          if (bx+x < 0)
            collide = 1;
          if (bx+x > 9)
            collide = 1;
          if (by+y > 19)
            collide = 1;
        }
    if (collide == 0) {
      draw_current(bx, by, 6);
      for (y=0; y<4; y++)
        for (x=0; x<4; x++)
          current_block[y][x] = rotated[y][x];
    }
  }
}

int check_move(int lastkey)
{
  int c;
  if (kbhit()) {
     c = getch();
     if (c==0) c = getch();
     if (c==75) lastkey = 1; //Left
     if (c==62) lastkey = 1; //Left (Remote)
     if (c==77) lastkey = 2; //Right
     if (c==64) lastkey = 2; //Right (Remote)
     if (c==72) lastkey = 3; //Up
     if (c==27) lastkey = 4; //Esc
     if (c==80) lastkey = 5; //Down
  }
  return lastkey;
}

void draw_current(int a, int b, int n)
{
  int x, y;
  for (y=0; y<4; y++)
    for (x=0; x<4; x++)
      if (current_block[y][x] == 1)
        if (n<6)
          blit(bmp, screen, 0, n*8, 150+(x*8)+(a*8), 20+(y*8)+(b*8), 8, 8);
        else
          rectfill(screen, 150+(x*8)+(a*8), 20+(y*8)+(b*8), 157+(x*8)+(a*8), 27+(y*8)+(b*8), 0);
}

void move_down()
{
  by++;
}

int set_block()
{
  int x, y;
  for (x=0; x<4; x++)
    for (y=0; y<4; y++)
      if (current_block[y][x] == 1)
        block_array[y+by][x+bx] = 1;
}

void plot_score()
{
  int n, m, digit;
  //blit(numbers, screen, 6*8, 0, 5*8+10, 80, 8, 8);
  m = score;
  n=5;
  do {
    digit = (int)fmod((double)m, (double)10);
    blit(numbers, screen, digit*8, 0, n*9+3, 65, 8, 8);
    m=m/10;
    n--;
  } while (n>0);//while ((m > 0.1) && (n>0));
}

int check_lines()
{
  int x, y, line, lines, firstline, n;
  lines = 0; firstline = 1;
  for (y=by; y<by+4; y++) {
    line = 1;
    for (x=0; x<10; x++)
      if (block_array[y][x] == 0)
        line = 0;
    if (line == 1) {
      for (x=0; x<10; x++)
        for (n=y; n>0; n--)
          block_array[n][x] = block_array[n-1][x];
      lines=1;
      score++;
      plot_score();
      if (firstline == 1) {
        draw_current(ox, oy, 6);
        draw_current(bx, by, current_sprite);
        firstline = 0;
      }
      for (n=7; n>=0; n--) {
        hline(screen, 150, 20+(y*8)+n, 229, 0);
        delay(30);
      }
      bmp2 = create_bitmap(80, (y*8)+17);
      clear(bmp2);
      blit(screen, bmp2, 150, 10, 0, 0, 80, (y*8)+10);
      for (n=0; n<9; n++) {
        blit(bmp2, screen, 0, 0, 150, 10+n, 80, (y*8)+10);
        delay(30);
      }
    }
  }
  return lines;
}

int check_bottom(int hit)
{
  int max_y, x, y, collide;
  collide = 0;
  for (x=0; x<4; x++) {
    max_y = -1;
    for (y=0; y<4; y++)
      if (current_block[y][x] == 1)
        max_y = y+1;
    if (max_y != -1)
      if (max_y+by > 19)
        collide = 1;
      else if (block_array[max_y+by][x+bx] == 1)
        collide = 1;
  }
  if (collide == 1)
    return 1;
  else
    return hit;
}

void initialise_new_block()
{
  int x, y;
  bx = 3;
  by = 0;
  for (y=0; y<4; y++) {
    for (x=0; x<3; x++)
      current_block[y][x] = possible_blocks[next_block][y][x];
    current_block[y][3] = 0;
  }
  current_sprite = next_sprite;
  next_block = return_random(0, 5);
  next_sprite = return_random(0, 5);
  draw_next(next_block, next_sprite);
  draw_current(bx, by, current_sprite);
}

void draw_next(int next_block, int n)
{
  int a, b;
  for (b=0; b<4; b++)
    for (a=0; a<3; a++)
      if (possible_blocks[next_block][b][a] == 1)
        blit(bmp, screen, 0, n*8, 270+(a*8), 88+(b*8), 8, 8);
      else
        rectfill(screen, 270+(a*8), 88+(b*8), 277+(a*8), 95+(b*8), 0);
}

int return_random(int min, int max)
{
  int range, value;
  range = max - min + 1;
  value = (int)((float)random() / (float)RAND_MAX * range) + min;
  return value;
}

void setupscreen()
{

   PALETTE read_palette, def_palette;
   int x, y;
   bmp = create_bitmap(8, 48); // Create bitmap in memory
   sideframe = create_bitmap(135, 200);
   numbers = create_bitmap(80, 8);
   gameover = create_bitmap(80, 160);
   clear(bmp);
   clear(sideframe);
   clear(numbers);
   clear(gameover);
   bmp = load_bmp("blocks.bmp", read_palette);
   sideframe = load_bitmap("sframe.bmp", read_palette);
   numbers = load_bitmap("numbers.bmp", read_palette);
   gameover = load_bitmap("gameover.bmp", read_palette);
   //set_palette( read_palette );
   blit(sideframe, screen, 0, 0, 0, 0, 135, 200);
   plot_score();
   for (x=14; x>=0; x--) {
      //Main Area
      vline(screen, 149-x, 20, 180+x, border-x);
      vline(screen, 230+x, 20, 180+x, border-x);
      hline(screen, 149-x, 180+x, 230+x, border-x);
      for (y=14; y>=0; y--)
        if (border-y-x>15) {
          putpixel(screen, 149-x, 30-y, border-y-x);
          putpixel(screen, 230+x, 30-y, border-y-x);
        }
        else {
          putpixel(screen, 149-x, 30-y, 0);
          putpixel(screen, 230+x, 30-y, 0);
        };
      //Next Block Area
      vline(screen, 269-x, 87-x, 120+x, border-x);
      vline(screen, 294+x, 87-x, 120+x, border-x);
      hline(screen, 269-x, 120+x, 294+x, border-x);
      hline(screen, 269-x, 87-x, 294+x, border-x);
   }
   rectfill(screen, 150, 20, 229, 179, 0);

   //save_bmp("scdump.bmp", screen, def_palette);
}

syntax highlighted by Code2HTML, v. 0.8.11