// src/game.mbt
// Core game logic for MoonTetris
pub struct Game {
pub width : Int
pub height : Int
pub board : Array[Array[Int]] // 0 empty, >0 index of color/kind
pub mut current_x : Int
pub mut current_y : Int
pub mut current_kind : Int
pub mut current_rotation : Int
pub mut next_kind : Int
pub mut hold_kind : Int // 0 if empty, 1..7 for kinds
pub mut can_hold : Bool
pub mut score : Int
pub mut lines : Int
pub mut level : Int
pub mut game_over : Bool
pub mut seed : Int
}
pub fn get_block_raw(kind : Int, row : Int, col : Int) -> Bool {
match kind {
1 => // I: size 4
row == 1
2 => // O: size 2
true
3 => // T: size 3
(row == 0 && col == 1) || (row == 1 && (col == 0 || col == 1 || col == 2))
4 => // S: size 3
(row == 0 && (col == 1 || col == 2)) || (row == 1 && (col == 0 || col == 1))
5 => // Z: size 3
(row == 0 && (col == 0 || col == 1)) || (row == 1 && (col == 1 || col == 2))
6 => // J: size 3
(row == 0 && col == 0) || (row == 1 && (col == 0 || col == 1 || col == 2))
7 => // L: size 3
(row == 0 && col == 2) || (row == 1 && (col == 0 || col == 1 || col == 2))
_ => false
}
}
pub fn get_block(kind : Int, rotation : Int, row : Int, col : Int) -> Bool {
let n = if kind == 1 { 4 } else if kind == 2 { 2 } else { 3 }
let (r, c) = match rotation % 4 {
0 => (row, col)
1 => (col, n - 1 - row)
2 => (n - 1 - row, n - 1 - col)
3 => (n - 1 - col, row)
_ => (row, col) // unreachable
}
get_block_raw(kind, r, c)
}
pub fn Game::new() -> Game {
let board = Array::make(20, Array::make(10, 0))
for r = 0; r < 20; r = r + 1 {
board[r] = Array::make(10, 0)
}
let mut seed = 12345
seed = (seed * 1103515245 + 12345) & 0x7fffffff
let current_kind = (seed % 7) + 1
seed = (seed * 1103515245 + 12345) & 0x7fffffff
let next_kind = (seed % 7) + 1
{
width: 10,
height: 20,
board,
current_x: 3,
current_y: -1,
current_kind,
current_rotation: 0,
next_kind,
hold_kind: 0,
can_hold: true,
score: 0,
lines: 0,
level: 1,
game_over: false,
seed,
}
}
pub fn Game::next_random_kind(self : Game) -> Int {
self.seed = (self.seed * 1103515245 + 12345) & 0x7fffffff
(self.seed % 7) + 1
}
pub fn Game::check_collision(self : Game, new_x : Int, new_y : Int, rotation : Int) -> Bool {
let kind = self.current_kind
let n = if kind == 1 { 4 } else if kind == 2 { 2 } else { 3 }
for r = 0; r < n; r = r + 1 {
for c = 0; c < n; c = c + 1 {
if get_block(kind, rotation, r, c) {
let board_x = new_x + c
let board_y = new_y + r
if board_x < 0 || board_x >= 10 || board_y >= 20 {
return true
}
if board_y >= 0 {
if self.board[board_y][board_x] != 0 {
return true
}
}
}
}
}
false
}
pub fn Game::spawn(self : Game) -> Unit {
self.current_kind = self.next_kind
self.next_kind = self.next_random_kind()
self.current_rotation = 0
self.current_x = 3
self.current_y = -1
self.can_hold = true
if self.check_collision(self.current_x, self.current_y, self.current_rotation) {
self.game_over = true
play_sound(3) // Game Over sound
}
}
pub fn Game::move_left(self : Game) -> Bool {
if self.game_over { return false }
if not(self.check_collision(self.current_x - 1, self.current_y, self.current_rotation)) {
self.current_x = self.current_x - 1
play_sound(1) // Move sound
true
} else {
false
}
}
pub fn Game::move_right(self : Game) -> Bool {
if self.game_over { return false }
if not(self.check_collision(self.current_x + 1, self.current_y, self.current_rotation)) {
self.current_x = self.current_x + 1
play_sound(1) // Move sound
true
} else {
false
}
}
pub fn Game::rotate(self : Game) -> Bool {
if self.game_over { return false }
let next_rot = (self.current_rotation + 1) % 4
// Try direct rotation
if not(self.check_collision(self.current_x, self.current_y, next_rot)) {
self.current_rotation = next_rot
play_sound(1) // Rotate sound
return true
}
// Basic Wall Kick
let kicks = [(-1, 0), (1, 0), (0, -1), (-2, 0), (2, 0)]
for i = 0; i < 5; i = i + 1 {
let (dx, dy) = kicks[i]
if not(self.check_collision(self.current_x + dx, self.current_y + dy, next_rot)) {
self.current_x = self.current_x + dx
self.current_y = self.current_y + dy
self.current_rotation = next_rot
play_sound(1) // Rotate sound
return true
}
}
false
}
pub fn Game::soft_drop(self : Game) -> Bool {
if self.game_over { return false }
if not(self.check_collision(self.current_x, self.current_y + 1, self.current_rotation)) {
self.current_y = self.current_y + 1
true
} else {
self.lock_piece()
false
}
}
pub fn Game::hard_drop(self : Game) -> Unit {
if self.game_over { return }
while not(self.check_collision(self.current_x, self.current_y + 1, self.current_rotation)) {
self.current_y = self.current_y + 1
}
play_sound(4) // Drop sound
self.lock_piece()
}
pub fn Game::hold(self : Game) -> Unit {
if self.game_over || not(self.can_hold) { return }
let temp = self.current_kind
if self.hold_kind == 0 {
self.hold_kind = temp
self.spawn()
} else {
self.current_kind = self.hold_kind
self.hold_kind = temp
self.current_rotation = 0
self.current_x = 3
self.current_y = -1
}
self.can_hold = false
play_sound(1)
}
pub fn Game::lock_piece(self : Game) -> Unit {
let kind = self.current_kind
let n = if kind == 1 { 4 } else if kind == 2 { 2 } else { 3 }
for r = 0; r < n; r = r + 1 {
for c = 0; c < n; c = c + 1 {
if get_block(kind, self.current_rotation, r, c) {
let board_y = self.current_y + r
let board_x = self.current_x + c
if board_y >= 0 && board_y < 20 && board_x >= 0 && board_x < 10 {
self.board[board_y][board_x] = kind
}
}
}
}
self.clear_lines()
self.spawn()
}
pub fn Game::clear_lines(self : Game) -> Unit {
let mut cleared = 0
for r = 19; r >= 0; r = r - 1 {
let mut full = true
for c = 0; c < 10; c = c + 1 {
if self.board[r + cleared][c] == 0 {
full = false
break
}
}
if full {
cleared = cleared + 1
} else if cleared > 0 {
for c = 0; c < 10; c = c + 1 {
self.board[r + cleared][c] = self.board[r][c]
}
}
}
if cleared > 0 {
for r = 0; r < cleared; r = r + 1 {
for c = 0; c < 10; c = c + 1 {
self.board[r][c] = 0
}
}
self.lines = self.lines + cleared
let score_add = match cleared {
1 => 100 * self.level
2 => 300 * self.level
3 => 500 * self.level
4 => 800 * self.level
_ => 0
}
self.score = self.score + score_add
self.level = (self.lines / 10) + 1
play_sound(2) // Line clear sound
}
}
pub fn Game::tick(self : Game) -> Unit {
if self.game_over { return }
let _ = self.soft_drop()
}
fn main { }