/*
* Copyright (c) 2015, Alex Zen <me@alexzen.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of poseur-blocks nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <LiquidCrystal.h>
#define LEFT 1
#define RIGHT 2
#define DOWN 3
#define ROTATE 4
#define N_ROWS 32
#define N_COLS 6
#define N_LCD_CHAR 8
#define SOUND_NOTE 262
#define SOUND_DURATION 100
unsigned long curr_time;
unsigned long prev_time;
int score;
int base_pointer;
typedef char game_matrix[N_ROWS][N_COLS];
struct lcd_map
{
int max;
int lcd_char_map[N_LCD_CHAR];
};
/* Matrix of the game */
game_matrix matrix;
//LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
/*
* Check if the char in input is "empty" ( == ' ' )
*/
int is_empty(char p)
{
if (p == ' ')
return 0;
else
return 1;
}
/* Point in 2-dimensional matrix */
struct point
{
int x;
int y;
};
/* Tetromino piece */
struct tetromino
{
char type;
struct point pos;
int dir;
struct point blocks[4];
} tetr;
int max_line = N_ROWS + 1;
struct tetromino start_pos[7];
/*
* Draw the tetromino on the matrix of the game
*/
void draw_tetromino(struct tetromino *tetr, char val)
{
struct point bl;
int i;
for (i=0; i<4; i++)
{
bl = tetr->blocks[i];
matrix[bl.x][bl.y] = val;
}
}
/*
* Move the tetromino.
* If collision occurs, return 1
*/
int move(int mov, struct tetromino *tetr)
{
int i;
struct point temp[4]; /* New position of blocks */
struct point pos; /* New position of tetromino */
/* Compute new blocks' positions */
switch(mov)
{
case LEFT:
for (i=0; i<4; i++)
{
temp[i].x = tetr->blocks[i].x;
temp[i].y = tetr->blocks[i].y - 1;
}
pos.x = tetr->pos.x;
pos.y = tetr->pos.y - 1;
break;
case RIGHT:
for (i=0; i<4; i++)
{
temp[i].x = tetr->blocks[i].x;
temp[i].y = tetr->blocks[i].y + 1;
}
pos.x = tetr->pos.x;
pos.y = tetr->pos.y + 1;
break;
case DOWN:
for (i=0; i<4; i++)
{
temp[i].x = tetr->blocks[i].x + 1;
temp[i].y = tetr->blocks[i].y;
}
pos.x = tetr->pos.x + 1;
pos.y = tetr->pos.y;
break;
case ROTATE:
for (i=0; i<4; i++)
{
temp[i].x = -(tetr->blocks[i].y - tetr->pos.y) + tetr->pos.x;
temp[i].y = tetr->blocks[i].x - tetr->pos.x + tetr->pos.y;
}
pos.x = tetr->pos.x;
pos.y = tetr->pos.y;
break;
}
for (i=0; i<4; i++)
{
if (temp[i].x < 0 ||
temp[i].x > base_pointer-1 ||
temp[i].y < 0 ||
temp[i].y > N_COLS-1)
return 1;
}
/* Move the tetromino */
draw_tetromino(tetr, ' ');
/* Check for collisions */
for (i=0; i<4; i++)
{
if (matrix[temp[i].x][temp[i].y] != ' ')
{
draw_tetromino(tetr, tetr->type);
return 1;
}
}
for (i=0; i<4; i++)
tetr->blocks[i] = temp[i];
tetr->pos = pos;
if (mov == ROTATE)
tetr->dir = (tetr->dir + 1) % 4;
draw_tetromino(tetr, tetr->type);
return 0;
}
/*
* Search in the mapping table if the key is present
*/
int find_mapping(struct lcd_map *mapping, byte key)
{
int i;
for (i=0; i < (mapping->max); i++)
if (mapping->lcd_char_map[i] == key)
return i;
return -1;
}
/*
* Insert the key into the mapping table
*/
int insert_mapping(struct lcd_map *mapping, byte key)
{
int pos = mapping->max;
if (pos > N_LCD_CHAR-1) // Upper limit of array
return -1;
mapping->lcd_char_map[pos] = key;
mapping->max = pos + 1;
return pos;
}
/*
* Compose the key for the mapping table
*/
byte compose_key(int i, int j)
{
byte key = 0;
key |= is_empty(matrix[i ] [j+2]) << 5;
key |= is_empty(matrix[i+1] [j+2]) << 4;
key |= is_empty(matrix[i ] [j+1]) << 3;
key |= is_empty(matrix[i+1] [j+1]) << 2;
key |= is_empty(matrix[i ] [j] ) << 1;
key |= is_empty(matrix[i+1] [j] );
return key;
}
/*
* Create a new char for the LCD screen
*/
void create_new_char(int i, int j, int index)
{
byte bt_1 = B00000;
byte bt_2 = B00000;
byte bt_3 = B00000;
bt_1 |= is_empty(matrix[i ] [j+2]) << 4;
bt_1 |= is_empty(matrix[i ] [j+2]) << 3;
bt_1 |= is_empty(matrix[i+1] [j+2]) << 1;
bt_1 |= is_empty(matrix[i+1] [j+2]);
bt_2 |= is_empty(matrix[i ] [j+1]) << 4;
bt_2 |= is_empty(matrix[i ] [j+1]) << 3;
bt_2 |= is_empty(matrix[i+1] [j+1]) << 1;
bt_2 |= is_empty(matrix[i+1] [j+1]);
bt_3 |= is_empty(matrix[i ] [j] ) << 4;
bt_3 |= is_empty(matrix[i ] [j] ) << 3;
bt_3 |= is_empty(matrix[i+1] [j] ) << 1;
bt_3 |= is_empty(matrix[i+1] [j] );
byte new_char[8] =
{
bt_1,
bt_1,
B00000,
bt_2,
bt_2,
B00000,
bt_3,
bt_3
};
lcd.createChar(index, new_char);
return;
}
/*
* Draw the matrix on Arduino's LCD
*/
void print_to_lcd()
{
struct lcd_map lcd_char_map;
lcd_char_map.max = 0;
byte key = 0;
int index, i, j;
//lcd.clear();
// Create custom LCD's characters
for (i=0; i<base_pointer; i+=2)
for (j=0; j<N_COLS; j+=3)
{
key = compose_key(i, j);
if (key == 0)
continue;
index = find_mapping(&lcd_char_map, key);
if (index == -1)
{
index = insert_mapping(&lcd_char_map, key);
create_new_char(i, j, index);
}
}
lcd.begin(16, 2);
// Print the characters on LCD screen
for (j=N_COLS-3; j>=0; j-=3)
{
for (i=0; i<N_ROWS; i+=2)
{
if (i >= base_pointer)
{
lcd.write("$");
continue;
}
key = compose_key(i, j);
if (key == 0)
{
lcd.write(" ");
continue;
}
index = find_mapping(&lcd_char_map, key);
lcd.write(byte(index));
}
lcd.setCursor(0, 1);
}
}
/*
* Add a new tetromino on the game
*/
int add_new_tetromino(int foo)
{
tetr.blocks[0].x = start_pos[foo].blocks[0].x;
tetr.blocks[0].y = start_pos[foo].blocks[0].y;
tetr.blocks[1].x = start_pos[foo].blocks[1].x;
tetr.blocks[1].y = start_pos[foo].blocks[1].y;
tetr.blocks[2].x = start_pos[foo].blocks[2].x;
tetr.blocks[2].y = start_pos[foo].blocks[2].y;
tetr.blocks[3].x = start_pos[foo].blocks[3].x;
tetr.blocks[3].y = start_pos[foo].blocks[3].y;
tetr.dir = start_pos[foo].dir;
tetr.type = start_pos[foo].type;
tetr.pos.x = start_pos[foo].pos.x;
tetr.pos.y = start_pos[foo].pos.y;
// Check space
if (matrix[tetr.blocks[0].x][tetr.blocks[0].y] != ' ' ||
matrix[tetr.blocks[1].x][tetr.blocks[1].y] != ' ' ||
matrix[tetr.blocks[2].x][tetr.blocks[2].y] != ' ' ||
matrix[tetr.blocks[3].x][tetr.blocks[3].y] != ' ')
return 1;
else
return 0;
}
/*
* Arduino's setup() function. It is called when the sketch starts
*/
void setup()
{
int i, j, mvm, foo;
char cmd;
score = 0;
base_pointer = N_ROWS;
randomSeed(millis());
/* initial positions of the tetrominoes */
pinMode(3, OUTPUT);
/* I-block */
start_pos[0].type = 'I';
start_pos[0].pos.x = 1;
start_pos[0].pos.y = 2;
start_pos[0].dir = 0;
start_pos[0].blocks[0].x = 1;
start_pos[0].blocks[0].y = 1;
start_pos[0].blocks[1].x = 1;
start_pos[0].blocks[1].y = 2;
start_pos[0].blocks[2].x = 1;
start_pos[0].blocks[2].y = 3;
start_pos[0].blocks[3].x = 1;
start_pos[0].blocks[3].y = 4;
/* J-block */
start_pos[1].type = 'J';
start_pos[1].pos.x = 1;
start_pos[1].pos.y = 3;
start_pos[1].dir = 0;
start_pos[1].blocks[0].x = 0;
start_pos[1].blocks[0].y = 2;
start_pos[1].blocks[1].x = 1;
start_pos[1].blocks[1].y = 2;
start_pos[1].blocks[2].x = 1;
start_pos[1].blocks[2].y = 3;
start_pos[1].blocks[3].x = 1;
start_pos[1].blocks[3].y = 4;
/* L-block */
start_pos[2].type = 'L';
start_pos[2].pos.x = 1;
start_pos[2].pos.y = 3;
start_pos[2].dir = 0;
start_pos[2].blocks[0].x = 1;
start_pos[2].blocks[0].y = 2;
start_pos[2].blocks[1].x = 1;
start_pos[2].blocks[1].y = 3;
start_pos[2].blocks[2].x = 1;
start_pos[2].blocks[2].y = 4;
start_pos[2].blocks[3].x = 0;
start_pos[2].blocks[3].y = 4;
/* O-block */
start_pos[3].type = 'O';
start_pos[3].pos.x = 1;
start_pos[3].pos.y = 3;
start_pos[3].dir = 0;
start_pos[3].blocks[0].x = 0;
start_pos[3].blocks[0].y = 2;
start_pos[3].blocks[1].x = 0;
start_pos[3].blocks[1].y = 3;
start_pos[3].blocks[2].x = 1;
start_pos[3].blocks[2].y = 2;
start_pos[3].blocks[3].x = 1;
start_pos[3].blocks[3].y = 3;
/* S-block */
start_pos[4].type = 'S';
start_pos[4].pos.x = 1;
start_pos[4].pos.y = 3;
start_pos[4].dir = 0;
start_pos[4].blocks[0].x = 1;
start_pos[4].blocks[0].y = 2;
start_pos[4].blocks[1].x = 1;
start_pos[4].blocks[1].y = 3;
start_pos[4].blocks[2].x = 0;
start_pos[4].blocks[2].y = 3;
start_pos[4].blocks[3].x = 0;
start_pos[4].blocks[3].y = 4;
/* T-block */
start_pos[5].type = 'T';
start_pos[5].pos.x = 1;
start_pos[5].pos.y = 3;
start_pos[5].dir = 0;
start_pos[5].blocks[0].x = 0;
start_pos[5].blocks[0].y = 3;
start_pos[5].blocks[1].x = 1;
start_pos[5].blocks[1].y = 2;
start_pos[5].blocks[2].x = 1;
start_pos[5].blocks[2].y = 3;
start_pos[5].blocks[3].x = 1;
start_pos[5].blocks[3].y = 4;
/* Z-block */
start_pos[6].type = 'Z';
start_pos[6].pos.x = 1;
start_pos[6].pos.y = 3;
start_pos[6].dir = 0;
start_pos[6].blocks[0].x = 0;
start_pos[6].blocks[0].y = 2;
start_pos[6].blocks[1].x = 0;
start_pos[6].blocks[1].y = 3;
start_pos[6].blocks[2].x = 1;
start_pos[6].blocks[2].y = 3;
start_pos[6].blocks[3].x = 1;
start_pos[6].blocks[3].y = 4;
for (i=0; i<N_ROWS; i++)
for (j=0; j<N_COLS; j++)
matrix[i][j] = ' ';
prev_time = millis();
add_new_tetromino(random(7));
draw_tetromino(&tetr, tetr.type);
print_to_lcd();
}
/*
* Return the first full row found
*/
int find_full_rows()
{
int i, j;
int flag;
for (i = base_pointer-1; i>=0; i--)
{
flag = 0;
for (j=0; j<N_COLS; j++)
if (matrix[i][j] == ' ')
flag = 1;
if (flag == 0)
return i;
}
return -1;
}
/*
* Erase a row
*/
void mode_down_rows(int row)
{
int i, j;
for (i=row; i>0; i--)
for (j=0; j<N_COLS; j++)
matrix[i][j] = matrix[i-1][j];
matrix[0][0] = ' ';
matrix[0][1] = ' ';
matrix[0][2] = ' ';
matrix[0][3] = ' ';
}
/*
* Arduino's loop() function
*/
void loop()
{
int foo, ret, ret_mov;
int mov = 0;
int row_index;
int i, j;
int flag = 0;
curr_time = millis();
//int key_val = analogRead(A0);
//Serial.println(key_val);
// Check input
/*if (key_val == 1023) // left
mov = LEFT;
else if (key_val >= 990 && key_val <= 1010) // right
mov = RIGHT;
else if (key_val >= 505 && key_val <= 515) // rotate
mov = ROTATE;*/
int b = analogRead(A0);
if (b > 1000) {}
else if (b < 50) {}
else if (b < 180) mov = RIGHT;
else if (b < 330) mov = LEFT;
else if (b < 520) mov = ROTATE;
else if (b < 700) {}
// Move/rotate the piece
if (mov != 0)
{
ret = move(mov, &tetr);
if (ret == 0)
{
draw_tetromino(&tetr, tetr.type);
print_to_lcd();
tone(3, SOUND_NOTE, SOUND_DURATION);
delay(100);
return;
}
}
// Move down the piece
curr_time = millis();
if (curr_time - prev_time > 500)
{
prev_time = curr_time;
if ((ret_mov = move(DOWN, &tetr)) == 1) // piece has reached the bottom
{
while ((row_index = find_full_rows()) != -1)
{
mode_down_rows(row_index);
score += 10;
}
// Apply the rule
for (i=0; i<base_pointer;i++)
{
for (j=0; j<N_COLS; j++)
if (matrix[i][j] != ' ')
{
flag = 1;
if (base_pointer - i > 4)
{
base_pointer -= (base_pointer - i - 4);
if (base_pointer % 2 != 0)
base_pointer -= 1;
}
break;
}
if (flag == 1)
break;
}
ret = add_new_tetromino(random(7));
if (ret != 0)
{
// print score
lcd.clear();
lcd.print("Score: " );
lcd.print(score);
while(1)
;
}
draw_tetromino(&tetr, tetr.type);
}
print_to_lcd();
if (ret_mov == 1)
tone(3, SOUND_NOTE, SOUND_DURATION);
}
}
Intuitive Machines™ Biotronics™ Zoikrons™ Autognorics™ ELFS™ IM™
are original trademarks and logos
solely distributed by
L.A.W.S.I.N.

Comments
Post a Comment