~sircmpwn/tetrominoes

448076c0de93f9f6df3c39e025b6a888772922ed — Drew DeVault 2 years ago 0c8024b
Partially implement rotation

Still missing a test to make sure the next state will fit.
3 files changed, 80 insertions(+), 26 deletions(-)

M board.ha
M main.ha
M update.ha
M board.ha => board.ha +5 -4
@@ 22,7 22,8 @@ const color_map: [_](u8, u8, u8) = [

type board = struct {
	cells: [BOARD_WIDTH * BOARD_HEIGHT]color,
	active: [4 * 4]color,
	active: [][TETROMINO_LEN]color,
	active_i: int,
	active_x: int,
	active_y: int,
	offs_x: int,


@@ 48,9 49,9 @@ fn draw_board(state: *state, board: *board) (void | sdl2::error) = {
		})?;
	};
	const active = (board.active_x, board.active_y);
	for (let y = 0; y < 4; y += 1)
	for (let x = 0; x < 4; x += 1) {
		let cell = board.active[y * 4 + x];
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		let cell = board.active[board.active_i][y * TETROMINO_WIDTH + x];
		if (cell == color::EMPTY) {
			continue;
		};

M main.ha => main.ha +2 -0
@@ 96,6 96,8 @@ fn run() (void | sdl2::error) = {
		sdl2::game_controller_close(c);
	};

	do_spawn(&state);

	for (state.run) {
		update(&state)?;
		draw(&state)?;

M update.ha => update.ha +73 -22
@@ 2,20 2,55 @@ use math;
use math::random;
use sdl2::{event_type, controller_axis, controller_button};
use sdl2;
use fmt; // XXX: TEMP

def THRESHOLD: i16 = sdl2::JOYSTICK_AXIS_MAX / 2;
def TETROMINO_WIDTH: int = 4;
def TETROMINO_HEIGHT: int = 4;
def TETROMINO_LEN: size = 4 * 4;

type input = struct {
	axis_x: i16,
};

const tetrominoes: [_][4 * 4]color = [
	[1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], // L
	[0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], // Reverse L
	[1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // Square
	[1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], // |-
	[1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], // Zigzag
	[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], // Reverse zigzag
// TODO: These should be made to rotate more apparently around their centers
const tetrominoes: [_][][TETROMINO_LEN]color = [
	[
		// L
		[1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
		[0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// Reverse L
		[0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
		[1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// Square
		[1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// Zig-zag
		[1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
		[0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// Reverse zig-zag
		[0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
		[1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// |-
		[1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
		[1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
		[0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
		[0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
	[
		// |
		[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0],
		[1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
	],
];

fn update(state: *state) (void | sdl2::error) = {


@@ 46,6 81,9 @@ fn update(state: *state) (void | sdl2::error) = {
			move_left(state);
		case controller_button::DPAD_RIGHT =>
			move_right(state);
		case controller_button::A, controller_button::B,
				controller_button::X, controller_button::Y =>
			rotate(state);
		case controller_button::DPAD_DOWN =>
			state.faster = true;
		case => void;


@@ 87,10 125,15 @@ fn do_spawn(state: *state) void = {
		};
	};
	state.lastcolor = col;
	state.board.active[..] = tetrominoes[next][..];
	for (let i = 0z; i < len(state.board.active); i += 1) {
		if (state.board.active[i] != 0) {
			state.board.active[i] = col;
	state.board.active = tetrominoes[next];
	state.board.active_i = 0;
	let active = &state.board.active;
	for (let i = 0z; i < len(active); i += 1) {
		let active = &active[i];
		for (let i = 0z; i < len(active); i += 1) {
			if (active[i] != 0) {
				active[i] = col;
			};
		};
	};
	let width = 0, height = 0;


@@ 113,12 156,12 @@ fn do_fall(state: *state) void = {

fn canmove(state: *state, dx: int, dy: int) bool = {
	let board = &state.board.cells;
	let active = &state.board.active;
	let active = &state.board.active[state.board.active_i];
	const active_x = state.board.active_x;
	const active_y = state.board.active_y;
	for (let y = 0; y < 4; y += 1)
	for (let x = 0; x < 4; x += 1) {
		const active_ix = y * 4 + x;
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		const active_ix = y * TETROMINO_WIDTH + x;
		if (active[active_ix] == 0) {
			continue;
		};


@@ 136,13 179,13 @@ fn canmove(state: *state, dx: int, dy: int) bool = {

fn commit(state: *state) void = {
	let board = &state.board.cells;
	let active = &state.board.active;
	let active = &state.board.active[state.board.active_i];
	const active_x = state.board.active_x;
	const active_y = state.board.active_y;
	for (let y = 0; y < 4; y += 1)
	for (let x = 0; x < 4; x += 1) {
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		const board_ix = (y + active_y) * BOARD_WIDTH + (x + active_x);
		const active_ix = y * 4 + x;
		const active_ix = y * TETROMINO_WIDTH + x;
		if (board_ix: size >= len(board)) {
			continue;
		};


@@ 166,15 209,23 @@ fn move_right(state: *state) void = {
	};
};

fn rotate(state: *state) void = {
	// TODO: Make sure this rotation is allowed
	const board = &state.board;
	board.active_i += 1;
	board.active_i = board.active_i % len(board.active): int;
};

fn activesize(
	state: *state,
	width: nullable *int,
	height: nullable *int,
) void = {
	let max_x = 0, max_y = 0;
	for (let y = 0; y < 4; y += 1)
	for (let x = 0; x < 4; x += 1) {
		const active = state.board.active[y * 4 + x] != 0;
	const active = &state.board.active[state.board.active_i];
	for (let y = 0; y < TETROMINO_HEIGHT; y += 1)
	for (let x = 0; x < TETROMINO_WIDTH; x += 1) {
		const active = active[y * TETROMINO_WIDTH + x] != 0;
		if (active && x > max_x) {
			max_x = x;
		};