pvjc26/final/README.md
2026-05-13 18:36:00 +02:00

8.8 KiB
Raw Blame History

Tetris Game

A fully functional Tetris game implemented in C using the ncurses library through the world library wrapper. The game features all 7 standard tetrominoes, a polished text-based graphical interface with colors and Unicode characters, and complete game mechanics including scoring, levels, and gravity-based piece falling.

Building and Running

Prerequisites

  • GCC compiler
  • ncurses development library (libncurses5-dev or equivalent)
  • Unix-like terminal environment (Linux, macOS, WSL, etc.)

Build Commands

To build the game:

cd final
make

To run the game:

./tetris

To clean build artifacts:

make clean

Testing on Sigma Server

This game has been developed to compile on the sigma server using standard ncurses libraries. To compile on sigma:

ssh your-username@sigma.cs.uchicago.edu
cd final
make
./tetris

The game requires a terminal window at least 50 characters wide and 24 lines tall for optimal display.

How to Play

Game Overview

Tetris is a tile-matching puzzle game where players must arrange falling blocks (tetrominoes) to create complete horizontal lines. When a line is complete, it is cleared and everything above it falls down. The game becomes progressively harder as the player clears more lines.

Controls

Key Action
Arrow Left Move piece left
Arrow Right Move piece right
Arrow Down Soft drop (accelerate falling)
Space Hard drop (instant drop to bottom)
Z Rotate piece clockwise
P Toggle pause
Q Quit game

During game over screen:

  • R - Restart the game
  • Q - Quit

Scoring System

Points are awarded based on the number of lines cleared at once:

Lines Cleared Base Points
1 line 100
2 lines 300
3 lines 500
4 lines (Tetris!) 800

Score multiplier: All points are multiplied by the current level. For example, clearing 4 lines at level 2 = 800 × 2 = 1,600 points.

Levels and Difficulty

  • Levels increase every 10 lines cleared
  • Starting level is 1
  • Exponential difficulty scaling: Gravity speed = 30 / level (frames until next drop)
  • Level 1: 30 frames per drop
  • Level 2: 15 frames per drop (50% speed increase)
  • Level 3: 10 frames per drop
  • Level 5: 6 frames per drop
  • Level 10: 3 frames per drop
  • Level 30: 1 frame per drop (instant drop - maximum difficulty)
  • Minimum gravity speed is capped at 1 frame (instant drop)

Game End Condition

The game ends when a new tetromino cannot be spawned at the top of the board because the board is full. This usually happens after the player cannot clear lines quickly enough and the stack reaches the top.

Visual Features

  • Next Piece Preview: Shows the next tetromino that will spawn in the right panel
  • Ghost Piece: A semi-transparent outline shows where the current piece will land if dropped
  • Color-Coded Pieces: Each tetromino type has a unique color:
    • I-piece: Cyan
    • O-piece: Yellow
    • T-piece: Magenta
    • S-piece: Green
    • Z-piece: Red
    • J-piece: Blue
    • L-piece: Orange/Red

Code Description

Main Components

GameState Structure

The core game state is maintained in the GameState struct defined in main.c:

typedef struct {
    int board[BOARD_HEIGHT][BOARD_WIDTH];  /* Game board grid */
    int piece_type;                         /* Current tetromino type */
    int piece_x, piece_y;                   /* Current piece position */
    int piece_rotation;                     /* Current rotation state (0-3) */
    int next_piece_type;                    /* Preview of next piece */
    int score;                              /* Current score */
    int level;                              /* Current level */
    int lines_cleared;                      /* Lines cleared this frame */
    int total_lines;                        /* Total lines cleared */
    int game_over;                          /* Game over flag */
    int paused;                             /* Pause flag */
    int gravity_counter;                    /* Gravity timing counter */
    int gravity_speed;                      /* Frames until next gravity tick */
    int frames_since_last_gravity;          /* Current frame counter */
    int screen_width, screen_height;        /* Terminal dimensions */
} GameState;

Key Functions

Game Initialization & Cleanup

  • init_game() - Allocates and initializes the game state
  • destroy_game() - Frees game memory

Piece Movement & Rotation (state-modifying functions)

  • move_piece_left() - Moves current piece left if possible
  • move_piece_right() - Moves current piece right if possible
  • move_piece_down() - Moves current piece down, returns 1 if successful
  • hard_drop_piece() - Instantly drops piece to bottom
  • rotate_piece() - Rotates current piece clockwise with collision detection

State Modifications (3+ required functions)

  1. lock_piece() - Locks the current piece onto the board and spawns the next piece. Places the piece blocks into the board grid and calls spawn_piece().

  2. check_and_clear_lines() - Identifies and clears complete rows, updates score and level. Compacts the board so blocks fall down to fill gaps.

  3. spawn_piece() - Creates a new tetromino at the top of the board. Generates random piece type and checks for game-over condition.

Game Logic

  • can_place_piece() - Checks if a piece can be placed at a given position without collision
  • apply_gravity() - Applies gravity each frame, moving pieces down automatically and speeding up with level
  • render_game() - Redraws the entire screen with all game elements

Game Loop

  • world_event() - Main event handler that processes input, updates state, and triggers rendering
  • main() - Entry point that starts the game loop

Tetromino System

The game implements all 7 standard tetrominoes with 4 rotation states each. Pieces are defined as arrays of (x, y) offsets from a center point:

static const int PIECE_SHAPES[NUM_PIECES][4][4][2]

Each piece (I, O, T, S, Z, J, L) has 4 rotation states with 4 blocks per piece (3 × 3 grid with one empty space or similar).

Rendering System

The render_game() function:

  1. Clears the screen
  2. Draws the main game board with Unicode borders (┌─┐│└┘)
  3. Renders filled board cells using the block character (█) with piece-specific colors
  4. Renders the ghost piece (showing landing position) using light shade (░)
  5. Renders the current falling piece with its color
  6. Draws the right panel with:
    • Score display
    • Current level
    • Total lines cleared
    • Next piece preview
  7. Draws a status bar at the bottom with game state information

Display Architecture

  • Board Area: 20 rows × 10 columns (rendered with double-width cells for squareness)
  • Left Panel: Main game board with border (26 characters wide, 24 lines tall)
  • Right Panel: Stats sidebar starting at column 26 (scores, level, next piece)
  • Status Bar: Bottom line showing current game state (paused, game over, controls hint)

Timing System

  • Frame rate: 100ms per frame (10 FPS)
  • Gravity speeds from level 1 (50 frames) to level 16 (5 frames minimum)
  • Pieces fall automatically based on gravity speed
  • Input is non-blocking (game runs even with no input)

World Library Modifications

The world library (world.c and world.h) is a wrapper around ncurses that provides a simplified interface for terminal-based graphics. No modifications were made to the world library for this Tetris implementation.

Why No Modifications?

The existing world library already provides all necessary features:

  1. Color Support: The set_color_cell() function already supports full ncurses color pairs with customizable foreground and background colors
  2. Unicode Characters: The library handles Unicode characters natively through ncurses by accepting any integer character code, including Unicode values (e.g., 0x2588 for █, 0x2502 for │)
  3. Screen Management: clear_screen(), refresh(), and event handling are fully implemented
  4. Game Loop Integration: The start_world() callback mechanism allows complete control over rendering and game logic

World Library Usage in Tetris

The Tetris implementation uses the following world library functions:

  • set_color_cell(char, x, y, fg_color, bg_color) - Draw a colored character
  • set_message(string, x, y) - Draw a text message (draws with white on black)
  • clear_screen() - Clear entire screen
  • game_speed(ms) - Set frame timing (100ms = 10 FPS)
  • start_world(init, event, destroy) - Main game loop callback system

Color Constants (defined in world.h)

#define COLOR_BLACK   0
#define COLOR_RED     1
#define COLOR_GREEN   2
#define COLOR_YELLOW  3
#define COLOR_BLUE    4
#define COLOR_MAGENTA 5
#define COLOR_CYAN    6
#define COLOR_WHITE   7