Code:
/*
** board.c
**
** Written by Peter Sutton
**
** Board data is stored in an array of rowtype (which is wide enough
** to hold a bit for each column). The bits of the rowtype
** represent whether that square is occupied or not (a 1 indicates
** occupied). The least significant BOARD_WIDTH bits are used. The
** least significant bit is on the right.
*/
#include "board.h"
#include "pieces.h"
#include "timer.h"
#include "score.h"
#include "led_display.h"
/*
** Function prototypes.
** Board.h has the prototypes for functions in this module which
** are available externally, and because we include "board.h" above
** we do not need to repeat those prototypes.
*/
int8_t piece_overlap(piece_type* piece, int8_t row_num);
void check_for_completed_rows(void);
/*
** Global variables
**
*/
rowtype board[BOARD_ROWS];
piece_type current_piece; /* Current dropping piece */
int8_t piece_row_num; /* Current row number of the bottom of
** the current piece, -1 if have
** no current piece */
/*
** Initialise board - no pieces (i.e. set the row data to contain
** all zeroes.)
*/
void init_board(void) {
int8_t i;
for(i=0; i < BOARD_ROWS; i++) {
board[i] = 0;
}
/* -1 in piece_row_num indicates no current piece
*/
piece_row_num = -1;
}
/*
** Copy board to LED display. Note that difference in definitions of
** rows and columns for the board and the LED display. The Tetris board
** has 15 rows (numbered from the bottom), each 7 bits wide (with the
** 7 columns numbered as per the bits - i.e. least significant (0) on
** the right). The LED display has 7 rows (0 at the top, 6 at the bottom)
** with 15 columns (numbered from 0 at the bottom to 14 at the top).
*/
void copy_board_to_led_display(void) {
/* The board has BOARD_ROWS (e.g. 15), each of width BOARD_WIDTH.
** Board row 0 corresponds to LED display column bit 0 etc.
** The function updates our LED display to reflect the
** current state of the board.
*/
int8_t board_row_num;
int8_t board_col_num;
uint16_t led_display_row;
for(board_col_num = 0; board_col_num < BOARD_WIDTH; board_col_num++) {
led_display_row = 0;
for(board_row_num = BOARD_ROWS-1; board_row_num >= 0; board_row_num--) {
led_display_row <<=1;
led_display_row |= (board[board_row_num]>>board_col_num)&1;
/* If the current piece covers this row - add it in also. */
if(piece_row_num >= 0 && board_row_num >= piece_row_num &&
board_row_num < (piece_row_num + current_piece.y_dimension)) {
led_display_row |=
((current_piece.rowdata[board_row_num - piece_row_num]
>>board_col_num)&1);
}
}
/* Copy this row to the LED display. Lower LED display
** row numbers correspond to higher board column numbers
*/
display[6-board_col_num] = led_display_row;
}
}
/*
** Checks whether have current piece
*/
int8_t have_current_piece(void) {
return (piece_row_num != -1);
}
/*
** Add random piece, return false (0) if we can't add the piece - this
** means the game is over.
*/
int8_t add_random_piece(void) {
current_piece = generate_random_piece();
/* We add the piece at a position that ensures it will fit on
** the board, even if rotated (i.e. we check it's maximum
** dimension and come down that many rows).
** This allows rotation without worrying
** about whether the piece will end up off the top of the
** board or not.
*/
if(current_piece.x_dimension > current_piece.y_dimension) {
piece_row_num = BOARD_ROWS - current_piece.x_dimension;
} else {
piece_row_num = BOARD_ROWS - current_piece.y_dimension;
}
if(piece_overlap(¤t_piece, piece_row_num)) {
/* Game is over */
piece_row_num = -1; /* no current piece */
return 0;
} else {
return 1;
}
}
/*
** Attempt to move the current piece to the left or right.
** This succeeds if
** (1) the piece isn't all the way to the side, and
** (2) the board contains no pieces in that position.
** Returns 1 if move successful, 0 otherwise.
*/
int8_t attempt_move(int8_t direction) {
piece_type backup_piece;
/*
** Make a copy of our piece in its current position (in case
** we need to restore it)
*/
copy_piece(¤t_piece, &backup_piece);
/*
** Move the piece template left/right, if possible (will only
** fail if the piece is up against the side).
*/
if(direction == MOVE_LEFT) {
if(!move_piece_left(¤t_piece)) {
return 0;
}
} else {
if(!move_piece_right(¤t_piece)) {
return 0;
}
}
/*
** If we get here, piece is not at edge.
** Check that the board will allow a move (i.e. the pieces
** won't overlap).
*/
if(piece_overlap(¤t_piece, piece_row_num)) {
/*
** Current board position does not allow move.
** Restore original piece
*/
copy_piece(&backup_piece, ¤t_piece);
return 0;
}
/* Move has been made - return success */
return 1;
}
/*
** Attempt to drop the piece by one row. This succeeds unless there
** are squares blocked on the row below or we're at the bottom of
** the board. Returns 1 if drop succeeded,
** 0 otherwise. (If the drop fails, the caller should add the piece
** to the board.)
*/
int8_t attempt_drop_piece_one_row(void) {
/*
** Check if the piece has
** reached the bottom of the board. Nothing to do in this
** case - return false..
*/
if(piece_row_num == 0) {
return 0;
}
/*
** Check whether the piece would intersect with any board
** pieces if it dropped one position
*/
if(piece_overlap(¤t_piece, piece_row_num - 1)) {
return 0;
}
/*
** Only get here if piece drop would succeed - make it happen
*/
piece_row_num--;
return 1;
}
/*
** Attempt to rotate the piece clockwise 90 degrees. Returns 1 if the
** rotation is successful, 0 otherwise (e.g. a piece on the board
** blocks the rotation).
*/
int8_t attempt_rotation(void) {
/* We calculate what the rotated piece would look like,
** then compute if it would interect with any board pieces
*/
piece_type backup_piece;
/*
** Make a copy of our piece in its current orientation (in case
** we need to restore it)
*/
copy_piece(¤t_piece, &backup_piece);
/*
** Attempt rotation (will only fail if too close to right hand
** side)
*/
if(!rotate_piece(¤t_piece)) {
return 0;
}
/*
** Need to check if rotated piece will intersect with existing
** pieces. If yes, restore old piece and return failure
*/
if(piece_overlap(¤t_piece, piece_row_num)) {
/*
** Current board position does not allow move.
** Restore original piece
*/
copy_piece(&backup_piece, ¤t_piece);
return 0;
}
/* Move has been made - return success */
return 1;
}
/*
** Add piece to board at its current position. We do this using a
** bitwise OR for each row that contains the piece.
*/
void fix_piece_to_board(void) {
int8_t i;
for(i=0; i < current_piece.y_dimension; i++) {
board[piece_row_num + i] |= current_piece.rowdata[i];
}
/*
** Indicate that we no longer have a current piece
*/
piece_row_num = -1;
check_for_completed_rows();
}
#define COMPLETED_ROW ((1<<BOARD_WIDTH)-1)
void check_for_completed_rows(void) {
int8_t i, j;
for(i = 0; i < BOARD_ROWS; )
{
// check if row is completed
if(board[i]==COMPLETED_ROW)
{
// let j = the row, loop through j to board_rows,
// moving each row (j+1) down one (j), and insert a null row at the top
j = i;
for(; j < BOARD_ROWS; ++j)
{
board[j] = board[j+1];
}
// the top row = BOARD_ROWS-1 ( remember, arrays start at zero )
board[BOARD_ROWS-1] = 0; // if 0 means an empty row
// board[i] is actually a new row, so we don't increment i as this
// would mean that we wouldn't check the new "i" row.
}
else
++i; // increment i, that row isn't completed
}
}
/* Suggested approach is to iterate over all the rows (0 to
** BOARD_ROWS -1)in the board and check if the row is all ones
** i.e. matches ((1 << BOARD_WIDTH) - 1).
** If a row of all ones is found, the rows above the current
** one should all be moved down one position and a zero row
** inserted at the top.
** Repeat this process if more than one completed row is
** found.
**
** e.g. if rows 2 and 4 are completed (all ones), then
** rows 0 and 1 at the bottom will remain unchanged
** old row 3 becomes row 2
** old row 5 becomes row 3
** old row 6 becomes row 4
** ...
** old row BOARD_ROWS - 1 becomes row BOARD_ROWS - 3;
** row BOARD_ROWS - 2 (second top row) is set to 0
** row BOARD_ROWS - 1 (top row) is set to 0
*/
/*
** Check whether the given piece will intersect with pieces already on the
** board (assuming the piece is placed at the given row number).
*/
int8_t piece_overlap(piece_type* piece, int8_t row_num) {
int8_t row;
for(row=0; row < piece->y_dimension; row++) {
if(piece->rowdata[row] & board[row_num + row]) {
/* Got an intersection (AND is non-zero) */
return 1;
}
}
return 0;
}
/*
* FILE: project.c
*
* This is the main file.
*
* Written by Peter Sutton
*/
#include "board.h"
#include "pieces.h"
#include "led_display.h"
#include "score.h"
#include "serialio.h"
#include "terminalio.h"
#include "timer.h"
#include <stdio.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
/*
** Function prototypes - these are defined below main()
*/
void new_game(void);
void splash_screen(void);
void handle_game_over(void);
void time_increment(void);
volatile uint8_t time_passed_flag;
/*
* main -- Main program.
*/
int main(void) {
uint8_t board_updated = 1;
uint8_t chars_into_escape_sequence = 0;
uint8_t vara = 0;
uint8_t varb = 0;
char c;
/* Initialise our main clock */
init_timer_0();
/* Initialise serial I/O */
init_serial_stdio(19200, 0);
/* Make the display_row() function be called every 2ms */
start_sw_timer(0, 2, display_row, 0);
/* Make the time_increment() function be called every 0.5s.
** This function will just set the time_passed_flag to 1.
*/
start_sw_timer(1, 500, time_increment, 0);
/*
** Turn on interrupts (needed for timer to work)
*/
sei();
/*
** Display splash screen
*/
splash_screen();
/*
** Perform necessary initialisations for a new game.
*/
new_game();
/*
** Event loop - wait for a certain amount of time to pass (depending on how
** we've initialised the timer) or wait
** for a character to arrive from standard input. The time_passed_flag
** is set within the timer interrupt handler (whenever our time target
** is reached).
*/
for(;;) {
if(time_passed_flag) {
time_passed_flag = 0;
if(have_current_piece()) {
/*
** Attempt to drop current piece by one row
*/
board_updated = attempt_drop_piece_one_row();
if(!board_updated) {
/* Couldn't drop piece - add to board */
fix_piece_to_board();
board_updated = 1;
}
} else {
/*
** No current piece - add one
*/
if(add_random_piece()) {
/* Addition of piece succeeded */
board_updated = 1;
} else {
/* Addition failed - game over */
handle_game_over();
}
}
}
if(input_available()) {
/* Read the input from our terminal and handle it */
c = fgetc(stdin);
if(chars_into_escape_sequence == 0 && c == '\x1b') {
/*
** Received ESCAPE character - we're one character into
** an escape sequence
*/
chars_into_escape_sequence = 1;
} else if(chars_into_escape_sequence == 1 && c == '[') {
/*
** We're two characters into an escape sequence
*/
chars_into_escape_sequence = 2;
} else if (chars_into_escape_sequence == 2 && c >= 'A' && c <= 'D'
&& have_current_piece()) {
/*
** Have received a cursor key escape sequence - process it if
** we have a current piece - otherwise ignore it
*/
if(c == 'A') {
/* Cursor up key pressed - Rotate piece if possible */
board_updated = attempt_rotation();
} else if(c == 'B') {
/* Cursor down key pressed - Drop piece if possible */
board_updated = attempt_drop_piece_one_row();
if(!board_updated) {
/* Couldn't drop piece - add to board */
fix_piece_to_board();
board_updated = 1;
}
} else if(c == 'C') {
/* Cursor right key pressed - Move right if possible */
board_updated = attempt_move(MOVE_RIGHT);
} else { /* c == 'D' */
/* Presume that cursor left key pressed - move left if possible */
board_updated = attempt_move(MOVE_LEFT);
}
/* We're no longer part way through an escape sequence */
chars_into_escape_sequence = 0;
} else if(chars_into_escape_sequence) {
/*
** We started an escape sequence but didn't get a character
** we recognised - discard it and assume that we're not
** in an escape sequence.
*/
chars_into_escape_sequence = 0;
} else {
/*
** Some other character received. Handle it (or ignore it).
*/
if (c == 'N' || c == 'n'){
new_game() ;
main();} //}
else { if (c == ' ')
{
board_updated = 1;
while (board_updated == 1);
{
(vara = attempt_drop_piece_one_row() );
if(vara == 1 )
board_updated = 1;
else board_updated = 0;
} // end while loop
// fix_piece_to_board();} // end else if
} }
} // end if
/*
** YOUR CODE HERE TO CHECK FOR OTHER KEY PRESSES
** AND TAKE APPROPRIATE ACTION. You may need to
** add code in other locations also.
*/
}
if(board_updated) {
/*
** Update display of board since its appearance has changed.
*/
copy_board_to_led_display();
board_updated = 0;
}
}
}
void time_increment(void) {
/* Function that gets called when a certain amount of time passes */
time_passed_flag = 1;
}
void new_game(void) {
/*
** Initialise the board and the screen
*/
init_board();
init_display();
init_score();
}
/*
** Display some suitable message to the user that includes your name(s).
** You may need to use terminalio functions to position the cursor appropriately.
*/
/* Add software delay here. Use a software timer that counts to a certain value
** and wait for it to finish. Consider use of the wait_for_sw_timer_wraparound()
** function.
*/
void dummy_timer_func(void) {}
void splash_screen(void) {
/* Clear the terminal screen */
clear_terminal();
/* YOUR CODE HERE - replace the following */
move_cursor(30, 0);
printf_P(PSTR("Tetris \n")) ;
move_cursor(27,10);
printf_P(PSTR("Xavia Troeger \n")) ;
move_cursor(29,11);
printf_P(PSTR("41407563 \n")) ;
start_sw_timer(3, 2000, dummy_timer_func, 0);
wait_for_sw_timer_wraparound(2);
stop_sw_timer(3);
draw_horizontal_line(15, 0, 78) ;
move_cursor(0, 17);
printf_P(PSTR("Score:"));
}
void handle_game_over(void) {
/* Print "Game over" to the terminal */
printf_P(PSTR("Game over"));
/* Stop our timer (means we won't be attempting to drop pieces */
stop_sw_timer(1);
}
.