init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
log.log
|
||||||
|
.zig-cache/
|
||||||
|
zig-out/
|
||||||
82
build.zig
Normal file
82
build.zig
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
const optimize = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
|
const mod = b.addModule("terimal_tetris", .{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "terimal_tetris",
|
||||||
|
.root_module = b.createModule(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.imports = &.{
|
||||||
|
.{ .name = "terimal_tetris", .module = mod },
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
exe.addIncludePath(b.path("include"));
|
||||||
|
switch (builtin.os.tag) {
|
||||||
|
.windows => {
|
||||||
|
exe.addCSourceFiles(.{
|
||||||
|
.files = &.{
|
||||||
|
"c-src/terminal_windows.c",
|
||||||
|
},
|
||||||
|
.flags = &.{
|
||||||
|
"-lc",
|
||||||
|
"-w",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
exe.linkLibC();
|
||||||
|
},
|
||||||
|
.linux, .macos => {
|
||||||
|
exe.addCSourceFiles(.{
|
||||||
|
.files = &.{
|
||||||
|
"c-src/terminal_linux.c",
|
||||||
|
},
|
||||||
|
.flags = &.{
|
||||||
|
"-lc",
|
||||||
|
"-w",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
exe.linkLibC();
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod_tests = b.addTest(.{
|
||||||
|
.root_module = mod,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_mod_tests = b.addRunArtifact(mod_tests);
|
||||||
|
|
||||||
|
const exe_tests = b.addTest(.{
|
||||||
|
.root_module = exe.root_module,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_exe_tests = b.addRunArtifact(exe_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run tests");
|
||||||
|
test_step.dependOn(&run_mod_tests.step);
|
||||||
|
test_step.dependOn(&run_exe_tests.step);
|
||||||
|
}
|
||||||
13
build.zig.zon
Normal file
13
build.zig.zon
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
.{
|
||||||
|
.name = .terimal_tetris,
|
||||||
|
.version = "0.0.0",
|
||||||
|
.fingerprint = 0xe0ee61f87efe174a,
|
||||||
|
.minimum_zig_version = "0.15.1",
|
||||||
|
.dependencies = .{
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
},
|
||||||
|
}
|
||||||
27
c-src/terminal_linux.c
Normal file
27
c-src/terminal_linux.c
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#include "../include/terminal.h"
|
||||||
|
|
||||||
|
#include <termios.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static struct termios orig_termios;
|
||||||
|
|
||||||
|
void exit_raw_mode() {
|
||||||
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
|
||||||
|
}
|
||||||
|
|
||||||
|
void enter_raw_mode() {
|
||||||
|
tcgetattr(STDIN_FILENO, &orig_termios);
|
||||||
|
// atexit(exit_raw_mode); // zig is handling this step
|
||||||
|
struct termios raw = orig_termios;
|
||||||
|
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
||||||
|
raw.c_oflag &= ~(OPOST);
|
||||||
|
raw.c_cflag |= (CS8);
|
||||||
|
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
|
||||||
|
|
||||||
|
raw.c_cc[VMIN] = 0;
|
||||||
|
// raw.c_cc[VTIME] = 1;
|
||||||
|
|
||||||
|
|
||||||
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
|
||||||
|
}
|
||||||
|
|
||||||
51
c-src/terminal_windows.c
Normal file
51
c-src/terminal_windows.c
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "../include/terminal.h"
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <consoleapi.h>
|
||||||
|
#include <minwindef.h>
|
||||||
|
#include <processenv.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
|
||||||
|
#define DEFAULT_CONSOLE_MODE (\
|
||||||
|
ENABLE_ECHO_INPUT |\
|
||||||
|
ENABLE_LINE_INPUT |\
|
||||||
|
ENABLE_MOUSE_INPUT |\
|
||||||
|
ENABLE_PROCESSED_INPUT |\
|
||||||
|
ENABLE_QUICK_EDIT_MODE |\
|
||||||
|
ENABLE_WINDOW_INPUT |\
|
||||||
|
ENABLE_PROCESSED_OUTPUT |\
|
||||||
|
ENABLE_WRAP_AT_EOL_OUTPUT |\
|
||||||
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING |\
|
||||||
|
DISABLE_NEWLINE_AUTO_RETURN |\
|
||||||
|
ENABLE_LVB_GRID_WORLDWIDE)
|
||||||
|
|
||||||
|
#define RAW_CONSOLE_MODE \
|
||||||
|
ENABLE_WINDOW_INPUT |\
|
||||||
|
ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||||
|
// ENABLE_PROCESSED_INPUT |\
|
||||||
|
|
||||||
|
static DWORD mode = 0;
|
||||||
|
|
||||||
|
void enter_raw_mode() {
|
||||||
|
|
||||||
|
GetConsoleMode(
|
||||||
|
GetStdHandle(STD_INPUT_HANDLE),
|
||||||
|
&mode
|
||||||
|
);
|
||||||
|
|
||||||
|
SetConsoleMode(
|
||||||
|
GetStdHandle(STD_INPUT_HANDLE),
|
||||||
|
RAW_CONSOLE_MODE
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void exit_raw_mode() {
|
||||||
|
|
||||||
|
SetConsoleMode(
|
||||||
|
GetStdHandle(STD_INPUT_HANDLE),
|
||||||
|
mode
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
10
include/terminal.h
Normal file
10
include/terminal.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _TERMINAL_H_
|
||||||
|
#define _TERMINAL_H_
|
||||||
|
|
||||||
|
|
||||||
|
void enter_raw_mode();
|
||||||
|
|
||||||
|
void exit_raw_mode();
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
199
src/colour.zig
Normal file
199
src/colour.zig
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
/// colour type
|
||||||
|
pub const Colour = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
r: u8,
|
||||||
|
g: u8,
|
||||||
|
b: u8,
|
||||||
|
|
||||||
|
/// inits the struct with a custom colour
|
||||||
|
pub fn custom(r: u8, g: u8, b: u8) Self {
|
||||||
|
return Self{ .r = r, .g = g, .b = b };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a red colour
|
||||||
|
pub fn red() Self {
|
||||||
|
return Self{ .r = 255, .g = 0, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a orange colour
|
||||||
|
pub fn orange() Self {
|
||||||
|
return Self{ .r = 255, .g = 128, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a yellow colour
|
||||||
|
pub fn yellow() Self {
|
||||||
|
return Self{ .r = 255, .g = 255, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a gold colour
|
||||||
|
pub fn gold() Self {
|
||||||
|
return Self{ .r = 255, .g = 200, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a lightGreen colour
|
||||||
|
pub fn lightGreen() Self {
|
||||||
|
return Self{ .r = 128, .g = 255, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a green colour
|
||||||
|
pub fn green() Self {
|
||||||
|
return Self{ .r = 0, .g = 255, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a cyan colour
|
||||||
|
pub fn cyan() Self {
|
||||||
|
return Self{ .r = 0, .g = 255, .b = 128 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a turquoise colour
|
||||||
|
pub fn turquoise() Self {
|
||||||
|
return Self{ .r = 0, .g = 255, .b = 190 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a lightBlue colour
|
||||||
|
pub fn lightBlue() Self {
|
||||||
|
return Self{ .r = 0, .g = 128, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a blue colour
|
||||||
|
pub fn blue() Self {
|
||||||
|
return Self{ .r = 0, .g = 0, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a darkPurple colour
|
||||||
|
pub fn darkPurple() Self {
|
||||||
|
return Self{ .r = 128, .g = 0, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a purple colour
|
||||||
|
pub fn purple() Self {
|
||||||
|
return Self{ .r = 170, .g = 0, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a lightPurple colour
|
||||||
|
pub fn lightPurple() Self {
|
||||||
|
return Self{ .r = 196, .g = 78, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a magenta colour
|
||||||
|
pub fn magenta() Self {
|
||||||
|
return Self{ .r = 255, .g = 0, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a pink colour
|
||||||
|
pub fn pink() Self {
|
||||||
|
return Self{ .r = 255, .g = 0, .b = 128 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a peach colour
|
||||||
|
pub fn peach() Self {
|
||||||
|
return Self{ .r = 255, .g = 158, .b = 158 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a black colour
|
||||||
|
pub fn black() Self {
|
||||||
|
return Self{ .r = 0, .g = 0, .b = 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a white colour
|
||||||
|
pub fn white() Self {
|
||||||
|
return Self{ .r = 255, .g = 255, .b = 255 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a grey colour
|
||||||
|
pub fn grey() Self {
|
||||||
|
return Self{ .r = 128, .g = 128, .b = 128 };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// inits the struct with a greyScale colour
|
||||||
|
pub fn greyScale(scale: u8) Self {
|
||||||
|
// 255 max
|
||||||
|
return Self{ .r = scale, .g = scale, .b = scale };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// this is the background colour of the defualt windows terminal
|
||||||
|
pub fn windowsTerminalBackground() Self {
|
||||||
|
return greyScale(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// this is the colour of the defualt windows terminal front
|
||||||
|
pub fn windowsTerminalFont() Self {
|
||||||
|
return greyScale(204);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// point must be an unsigned number type
|
||||||
|
pub fn pointToColour(point: anytype) Colour {
|
||||||
|
const normed_point_hue: u16 = @as(u16, @intFromFloat((@as(f32, @floatFromInt(point)) / @as(f32, @floatFromInt(std.math.maxInt(@TypeOf(point))))) * 255 * 5)) + 255;
|
||||||
|
const normed_point_rgb: u8 = @intCast((normed_point_hue + 1) % 256);
|
||||||
|
|
||||||
|
if (normed_point_hue <= 255 * 2) { //count up
|
||||||
|
return custom(255, normed_point_rgb, 0);
|
||||||
|
}
|
||||||
|
// 0, 255, 0, 255*3
|
||||||
|
if (normed_point_hue <= 255 * 3) { // count down
|
||||||
|
return custom(255 - normed_point_rgb, 255, 0);
|
||||||
|
}
|
||||||
|
// 0, 255, 255, 255*4
|
||||||
|
if (normed_point_hue <= 255 * 4) { // count up
|
||||||
|
return custom(0, 255, normed_point_rgb);
|
||||||
|
}
|
||||||
|
// 0, 0, 255, 255*5
|
||||||
|
if (normed_point_hue <= 255 * 5) { // count down
|
||||||
|
return custom(0, 255 - normed_point_rgb, 255);
|
||||||
|
}
|
||||||
|
// 255, 0, 255, 255*6
|
||||||
|
if (normed_point_hue <= 255 * 6) { // count up
|
||||||
|
return custom(normed_point_rgb, 0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Colour.white();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rangeToColour(start: usize, end: usize, point: usize) Colour {
|
||||||
|
const delta = end - start;
|
||||||
|
const shifted_point: usize = point - start;
|
||||||
|
// wrapping mul to insure that this will never overflow
|
||||||
|
const normed_point_pos: usize = (std.math.maxInt(usize) / delta) *% shifted_point;
|
||||||
|
return pointToColour(normed_point_pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// this will return a set colour based on the usize number
|
||||||
|
/// how ever at at point it will start to be random
|
||||||
|
pub fn usizeToColour(number: usize) Self {
|
||||||
|
return switch (number) {
|
||||||
|
0 => red(),
|
||||||
|
1 => orange(),
|
||||||
|
2 => yellow(),
|
||||||
|
3 => gold(),
|
||||||
|
4 => lightGreen(),
|
||||||
|
5 => green(),
|
||||||
|
6 => cyan(),
|
||||||
|
7 => turquoise(),
|
||||||
|
8 => lightBlue(),
|
||||||
|
9 => blue(),
|
||||||
|
10 => darkPurple(),
|
||||||
|
11 => purple(),
|
||||||
|
12 => lightPurple(),
|
||||||
|
13 => magenta(),
|
||||||
|
14 => pink(),
|
||||||
|
15 => peach(),
|
||||||
|
else => random(), // just make a random colour
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// returns a random colour
|
||||||
|
pub fn random() Self {
|
||||||
|
const static = struct {
|
||||||
|
var prng = std.rand.DefaultPrng.init(69420);
|
||||||
|
};
|
||||||
|
const rand = static.prng.random();
|
||||||
|
return custom(
|
||||||
|
rand.int(u8),
|
||||||
|
rand.int(u8),
|
||||||
|
rand.int(u8),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
546
src/keys.zig
Normal file
546
src/keys.zig
Normal file
@@ -0,0 +1,546 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("ctype.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const escape_char = 27;
|
||||||
|
|
||||||
|
pub fn isControlChar(char: u8) bool {
|
||||||
|
return c.iscntrl(@as(c_int, char)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Key = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
// home and end keys will be added
|
||||||
|
pub fn get(stdin: *std.Io.Reader) !?Key {
|
||||||
|
// if (self.poller.pollTimeout(100) catch false) {
|
||||||
|
// https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal%20input%20sequences
|
||||||
|
// <char> -> char
|
||||||
|
// <esc> <nochar> -> esc
|
||||||
|
// <esc> <esc> -> esc
|
||||||
|
// <esc> <char> -> Alt-keypress or keycode sequence
|
||||||
|
// <esc> '[' <nochar> -> Alt-[
|
||||||
|
// <esc> '[' (<modifier>) <char> -> keycode sequence, <modifier> is a decimal
|
||||||
|
// number and defaults to 1 (xterm)
|
||||||
|
// <esc> '[' 1 ; <modifier> <char> -> xterm
|
||||||
|
// <esc> '[' (<keycode>) (';'<modifier>) '~' -> keycode sequence, <keycode> and <modifier>
|
||||||
|
// are decimal numbers and default to 1 (vt)
|
||||||
|
|
||||||
|
const char = try stdin.takeByte();
|
||||||
|
var k: Key = Key{};
|
||||||
|
|
||||||
|
if (isControlChar(char)) {
|
||||||
|
k.escape = char;
|
||||||
|
} else {
|
||||||
|
k.keycode = char;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not part of an espace sequence
|
||||||
|
if (char != '\x1b') {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
k.escape = char;
|
||||||
|
|
||||||
|
const char_2 = try stdin.takeByte();
|
||||||
|
|
||||||
|
// not part of an Alt-[ sequence
|
||||||
|
if (char_2 != '[' and char_2 != 'O') {
|
||||||
|
k.keycode = char_2;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_3 = try stdin.takeByte();
|
||||||
|
k.keycode = char_3;
|
||||||
|
|
||||||
|
// bt function keys the strange one
|
||||||
|
if (char_2 == 'O' and char_3 >= 'P' and char_3 <= 'S') {
|
||||||
|
k.keycode = char_2;
|
||||||
|
k.keycode_2 = char_3;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// strange vt function keys we do the only convertion here
|
||||||
|
std.debug.print("char2 {} {c}\r\n", .{ char_2, char_2 });
|
||||||
|
std.debug.print("char3 {} {c}\r\n", .{ char_3, char_3 });
|
||||||
|
|
||||||
|
// xterm check
|
||||||
|
if (char_3 >= 'A' and char_3 <= 'O' or char_3 >= 'T' and char_3 <= 'Z') {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_4 = try stdin.takeByte();
|
||||||
|
std.debug.print("char_4 {}\r\n", .{char_4});
|
||||||
|
|
||||||
|
// modified xterm check
|
||||||
|
if (char_3 == '1' and char_4 == ';') {
|
||||||
|
const char_5 = try stdin.takeByte();
|
||||||
|
const char_6 = try stdin.takeByte();
|
||||||
|
k.modifier = char_5;
|
||||||
|
k.keycode = char_6;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// xterm sequence
|
||||||
|
if (char_3 == 1 and char_4 >= 'P' and char_4 <= 'S') {
|
||||||
|
k.keycode_2 = char_4;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char_4 == 0x1A) {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vt sequence with single keycode
|
||||||
|
if (char_4 == '~') {
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vt sequence with multi keycode
|
||||||
|
if (char_3 >= '1' and char_3 <= '3' and char_4 != ';') {
|
||||||
|
k.keycode_2 = char_4;
|
||||||
|
const tilde = try stdin.takeByte();
|
||||||
|
if (tilde != '~') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char_4 != ';') {
|
||||||
|
k.modifier = char_3;
|
||||||
|
k.keycode = char_4;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (char_4 == ';') {
|
||||||
|
const char_5 = try stdin.takeByte();
|
||||||
|
_ = try stdin.takeByte();
|
||||||
|
k.modifier = char_5;
|
||||||
|
k.keycode = char_3;
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
escape: u8 = 0,
|
||||||
|
keycode: u8 = 0,
|
||||||
|
// this fucker is only needed for 2 chars sequences
|
||||||
|
keycode_2: u8 = 0,
|
||||||
|
// Shift 1
|
||||||
|
// (Left) Alt 2
|
||||||
|
// ctrl 4
|
||||||
|
// meta 8
|
||||||
|
modifier: u8 = 0,
|
||||||
|
|
||||||
|
pub fn eq(a: Self, b: Self) bool {
|
||||||
|
return (a.escape == b.escape and
|
||||||
|
a.keycode == b.keycode and
|
||||||
|
a.modifier == b.modifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it 64 to make things nice
|
||||||
|
// this allows for switch cases to be used
|
||||||
|
pub fn keyToU64(self: Self) u64 {
|
||||||
|
return (@as(u64, self.escape) << 24) +
|
||||||
|
(@as(u64, self.modifier) << 16) +
|
||||||
|
(@as(u64, self.keycode_2) << 8) +
|
||||||
|
@as(u64, self.keycode);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn isRenderable(self: Self) bool {
|
||||||
|
return switch (self.escape) {
|
||||||
|
0 => true,
|
||||||
|
9 => true,
|
||||||
|
else => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toChar(self: Self) u8 {
|
||||||
|
return switch (self.escape) {
|
||||||
|
0 => self.keycode,
|
||||||
|
9 => '\t',
|
||||||
|
else => 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn format(
|
||||||
|
// self: Self,
|
||||||
|
// comptime fmt: []const u8,
|
||||||
|
// options: std.fmt.FormatOptions,
|
||||||
|
// writer: anytype,
|
||||||
|
// ) !void {
|
||||||
|
// _ = fmt;
|
||||||
|
// _ = options;
|
||||||
|
// // if (self.keycode_2 == 0) {
|
||||||
|
// // try writer.print(
|
||||||
|
// // "key{{esc={}, keycode={c}, mod={}}}",
|
||||||
|
// // .{
|
||||||
|
// // self.escape,
|
||||||
|
// // self.keycode,
|
||||||
|
// // self.modifier,
|
||||||
|
// // },
|
||||||
|
// // );
|
||||||
|
// // } else {
|
||||||
|
// try writer.print(
|
||||||
|
// "key{{esc={}, keycode=|{c}|{}|, mod={}, id={}}}",
|
||||||
|
// .{
|
||||||
|
// self.escape,
|
||||||
|
// self.keycode,
|
||||||
|
// self.keycode_2,
|
||||||
|
// self.modifier,
|
||||||
|
// self.keyToU64(),
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
|
||||||
|
// normal seqs
|
||||||
|
|
||||||
|
pub const Enter: Key = Key{
|
||||||
|
.escape = 13,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Backspace: Key = Key{
|
||||||
|
.escape = 127,
|
||||||
|
};
|
||||||
|
|
||||||
|
// vt sequences:
|
||||||
|
pub const Home_0: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Insert: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Delete: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const End_0: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '4',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PgUp: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '5',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PgDn: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '6',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Home_1: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '7',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const End_1: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '8',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F0: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '0',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F1: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F2: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F3: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '3',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F4: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '4',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F5: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '5',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F6: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '7',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F7: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '8',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F8: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '9',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F9: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '0',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F10: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F11: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '3',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F12: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '4',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F13: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '5',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F14: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '6',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F15: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '8',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F16: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '9',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F17: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '1',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F18: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F19: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '3',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const F20: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '4',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_9: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '9',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_16: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '1',
|
||||||
|
.keycode_2 = '6',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_22: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '2',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_27: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '2',
|
||||||
|
.keycode_2 = '7',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_30: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '0',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const vt_35: Key = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = '3',
|
||||||
|
.keycode_2 = '5',
|
||||||
|
};
|
||||||
|
|
||||||
|
// xterm sequences:
|
||||||
|
|
||||||
|
pub const Up = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'A',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Down = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'B',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Right = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'C',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Left = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'D',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_End = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'F',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Keypad_5 = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'G',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_Home = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'H',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_F1 = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.modifier = 1,
|
||||||
|
.keycode = 'P',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_F2 = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.modifier = 1,
|
||||||
|
.keycode = 'Q',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_F3 = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.modifier = 1,
|
||||||
|
.keycode = 'R',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_F4 = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.modifier = 1,
|
||||||
|
.keycode = 'S',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_E = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'E',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_I = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'I',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_J = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'J',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_K = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'K',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_L = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'L',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_M = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'M',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_N = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'N',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_O = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'O',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_T = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'T',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_U = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'U',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_V = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'V',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_W = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'W',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_X = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'X',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_Y = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'Y',
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const xterm_Z = Key{
|
||||||
|
.escape = escape_char,
|
||||||
|
.keycode = 'Z',
|
||||||
|
};
|
||||||
480
src/main.zig
Normal file
480
src/main.zig
Normal file
@@ -0,0 +1,480 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const terimal_tetris = @import("terimal_tetris");
|
||||||
|
const Colour = @import("colour.zig").Colour;
|
||||||
|
const keys = @import("keys.zig");
|
||||||
|
|
||||||
|
const c = @cImport({
|
||||||
|
@cInclude("terminal.h");
|
||||||
|
});
|
||||||
|
|
||||||
|
const colour_set_string_fg_fmt = "\x1B[38;2;{d};{d};{d}m";
|
||||||
|
const colour_set_string_bg_fmt = "\x1B[48;2;{d};{d};{d}m";
|
||||||
|
const end_mode = "\x1b[m";
|
||||||
|
|
||||||
|
const height = 20;
|
||||||
|
const width = 10;
|
||||||
|
|
||||||
|
const Cell = struct {
|
||||||
|
colour: Colour,
|
||||||
|
filled: bool = false, // determins if a give cell as something in it
|
||||||
|
};
|
||||||
|
|
||||||
|
const Tetromino = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
// this is needed for SRS rotations
|
||||||
|
// as j, l, s, z, piece can be rotated
|
||||||
|
// around a 3x3 grid but i need to be
|
||||||
|
// on a 4x4 grid and o should also be
|
||||||
|
// on this grid since the transform
|
||||||
|
// will be the same also i dont
|
||||||
|
is_4_piece: bool = false, // because it is 90% of the time
|
||||||
|
colour: Colour = .white(),
|
||||||
|
bitmap: [4][4]u1 = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
|
||||||
|
pub fn i() Self {
|
||||||
|
return Self{
|
||||||
|
.is_4_piece = true,
|
||||||
|
.colour = .cyan(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 1, 1, 1, 1 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn t() Self {
|
||||||
|
return Self{
|
||||||
|
.colour = .purple(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 1, 1, 1, 0 },
|
||||||
|
.{ 0, 1, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn j() Self {
|
||||||
|
return Self{
|
||||||
|
.colour = .blue(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 1, 1, 1, 0 },
|
||||||
|
.{ 0, 0, 1, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn l() Self {
|
||||||
|
return Self{
|
||||||
|
.colour = .orange(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 1, 1, 1, 0 },
|
||||||
|
.{ 1, 0, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn s() Self {
|
||||||
|
return Self{
|
||||||
|
.colour = .green(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 1, 1, 0 },
|
||||||
|
.{ 1, 1, 0, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn z() Self {
|
||||||
|
return Self{
|
||||||
|
.colour = .red(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 1, 1, 0, 0 },
|
||||||
|
.{ 0, 1, 1, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn o() Self {
|
||||||
|
return Self{
|
||||||
|
.is_4_piece = true,
|
||||||
|
.colour = .yellow(),
|
||||||
|
.bitmap = .{
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
.{ 0, 1, 1, 0 },
|
||||||
|
.{ 0, 1, 1, 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// uses SRS rotations
|
||||||
|
// index[r][c]
|
||||||
|
// 0 1 2 3 = c
|
||||||
|
// +-----------
|
||||||
|
// 0 | 1 2 3 4
|
||||||
|
// 1 | 5 6 7 8
|
||||||
|
// 2 | 9 10 11 12
|
||||||
|
// 3 | 13 14 15 16
|
||||||
|
// ║
|
||||||
|
// r
|
||||||
|
// 3x3
|
||||||
|
// 0 1 2
|
||||||
|
// +------
|
||||||
|
// 0 | 1 2 3
|
||||||
|
// 1 | 4 5 6
|
||||||
|
// 2 | 7 8 9
|
||||||
|
//
|
||||||
|
pub fn rotate(self: *Self, clockwise: bool) void {
|
||||||
|
const b = self.bitmap;
|
||||||
|
|
||||||
|
if (self.is_4_piece and clockwise) self.bitmap = .{
|
||||||
|
.{ b[3][0], b[2][0], b[1][0], b[0][0] },
|
||||||
|
.{ b[3][1], b[2][1], b[1][1], b[0][1] },
|
||||||
|
.{ b[3][2], b[2][2], b[1][2], b[0][2] },
|
||||||
|
.{ b[3][3], b[2][3], b[1][3], b[0][3] },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (self.is_4_piece and !clockwise) self.bitmap = .{
|
||||||
|
.{ b[0][3], b[1][3], b[2][3], b[3][3] },
|
||||||
|
.{ b[0][2], b[1][2], b[2][2], b[3][2] },
|
||||||
|
.{ b[0][1], b[1][1], b[2][1], b[3][1] },
|
||||||
|
.{ b[0][0], b[1][0], b[2][0], b[3][0] },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!self.is_4_piece and clockwise) self.bitmap = .{
|
||||||
|
.{ b[2][0], b[1][0], b[0][0], 0 },
|
||||||
|
.{ b[2][1], b[1][1], b[0][1], 0 },
|
||||||
|
.{ b[2][2], b[1][2], b[0][2], 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!self.is_4_piece and !clockwise) self.bitmap = .{
|
||||||
|
.{ b[0][2], b[1][2], b[2][2], 0 },
|
||||||
|
.{ b[0][1], b[1][1], b[2][1], 0 },
|
||||||
|
.{ b[0][0], b[1][0], b[2][0], 0 },
|
||||||
|
.{ 0, 0, 0, 0 },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Board = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
grid: [height][width]Cell,
|
||||||
|
active_tetromino: Tetromino,
|
||||||
|
tetromino_pos_x: usize = @divTrunc(width, 2) + 2,
|
||||||
|
tetromino_pos_y: usize = height - 1,
|
||||||
|
stop: bool = false,
|
||||||
|
random: std.Random,
|
||||||
|
|
||||||
|
pub fn init() Self {
|
||||||
|
var prng: std.Random.DefaultPrng = .init(@as(usize, @intCast(std.time.timestamp())));
|
||||||
|
const rand = prng.random();
|
||||||
|
|
||||||
|
var self = Self{
|
||||||
|
.random = rand,
|
||||||
|
.active_tetromino = undefined,
|
||||||
|
.grid = undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.clearGrid();
|
||||||
|
self.resetActiveTetromino();
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetActiveTetromino(self: *Self) void {
|
||||||
|
self.tetromino_pos_x = @divTrunc(width, 2) + 2;
|
||||||
|
self.tetromino_pos_x = 0;
|
||||||
|
self.tetromino_pos_y = height;
|
||||||
|
self.active_tetromino = self.newTetromino();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn newTetromino(self: Self) Tetromino {
|
||||||
|
const minos = [_]Tetromino{
|
||||||
|
Tetromino.i(),
|
||||||
|
Tetromino.t(),
|
||||||
|
Tetromino.j(),
|
||||||
|
Tetromino.l(),
|
||||||
|
Tetromino.s(),
|
||||||
|
Tetromino.z(),
|
||||||
|
Tetromino.o(),
|
||||||
|
};
|
||||||
|
_ = self;
|
||||||
|
// return minos[@mod(self.random.int(usize), 7)];
|
||||||
|
return minos[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearGrid(self: *Self) void {
|
||||||
|
for (0..height) |y| {
|
||||||
|
for (0..width) |x| {
|
||||||
|
self.grid[y][x] = Cell{
|
||||||
|
.colour = .black(),
|
||||||
|
.filled = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collided(self: *Self, mino: Tetromino) bool {
|
||||||
|
for (mino.bitmap, 0..) |mino_row, y| {
|
||||||
|
for (mino_row, 0..) |mino_cell, x| {
|
||||||
|
const mino_x = self.tetromino_pos_x + x;
|
||||||
|
const mino_y = height -| self.tetromino_pos_y + y;
|
||||||
|
if (mino_cell == 0) continue;
|
||||||
|
std.debug.print("mino : x {}, y {}\n", .{ mino_x, mino_y });
|
||||||
|
|
||||||
|
// make sure that the empty cells dont go out of bounds
|
||||||
|
if (mino_x >= width and mino_cell == 0) continue;
|
||||||
|
|
||||||
|
// hit the bottom
|
||||||
|
if (mino_y == height - 1) // because of indexing
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// debug check to see where mino is
|
||||||
|
if (mino_cell == 1) {
|
||||||
|
self.grid[mino_y][mino_x] = Cell{
|
||||||
|
.colour = .white(),
|
||||||
|
.filled = false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (self.grid[mino_y + 1][mino_x].filled)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setGridCells(self: *Self, mino: Tetromino) void {
|
||||||
|
std.debug.print("========== SETTING GRID CELLS ==========", .{});
|
||||||
|
for (mino.bitmap, 0..) |mino_row, y| {
|
||||||
|
for (mino_row, 0..) |mino_cell, x| {
|
||||||
|
const mino_x = self.tetromino_pos_x + x;
|
||||||
|
const mino_y = height -| self.tetromino_pos_y + y;
|
||||||
|
|
||||||
|
if (mino_cell == 1) {
|
||||||
|
self.grid[mino_y][mino_x] = Cell{
|
||||||
|
.colour = mino.colour,
|
||||||
|
.filled = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns if the given tetromino is out of bound from the current board
|
||||||
|
// the values are the amount x and amount y the mino is out of bounds
|
||||||
|
pub fn outOfBounds(self: Self, mino: Tetromino) struct { isize, isize } {}
|
||||||
|
|
||||||
|
pub fn processInputs(self: *Self, inputs: *std.Io.Reader) void {
|
||||||
|
const key = getKey(inputs) catch |err| switch (err) {
|
||||||
|
error.EndOfStream => return,
|
||||||
|
else => {
|
||||||
|
self.stop = true;
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (key == null) return;
|
||||||
|
|
||||||
|
std.debug.print("key : {?}\n", .{key});
|
||||||
|
|
||||||
|
const exit_key = comptime (keys.Key{ .escape = 17 }).keyToU64();
|
||||||
|
|
||||||
|
switch (key.?.keyToU64()) {
|
||||||
|
exit_key => self.stop = true,
|
||||||
|
keys.Left.keyToU64() => self.tetromino_pos_x -|= 1,
|
||||||
|
keys.Right.keyToU64() => {
|
||||||
|
self.tetromino_pos_x += 1;
|
||||||
|
// width - 4 since the minos are 4 wide
|
||||||
|
if (self.tetromino_pos_x + 1 > width - 4) {
|
||||||
|
self.tetromino_pos_x = width - 4;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
keys.Down.keyToU64() => self.tetromino_pos_y -|= 1,
|
||||||
|
(keys.Key{ .keycode = 'r' }).keyToU64() => self.active_tetromino.rotate(true),
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(self: *Self) void {
|
||||||
|
|
||||||
|
// if (self.tetromino_pos_y == 0) {}
|
||||||
|
const active_mino_collided = self.collided(self.active_tetromino);
|
||||||
|
if (active_mino_collided) {
|
||||||
|
self.setGridCells(self.active_tetromino);
|
||||||
|
self.resetActiveTetromino();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tetromino_pos_y -|= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn enterRawMode() void {
|
||||||
|
c.enter_raw_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exitRawMode() void {
|
||||||
|
c.exit_raw_mode();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getKey(stdin: *std.Io.Reader) !?keys.Key {
|
||||||
|
return keys.Key.get(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clearScreen(writer: *std.Io.Writer) !void {
|
||||||
|
_ = try writer.write("\x1b[2J");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resetCursor(writer: *std.Io.Writer) !void {
|
||||||
|
try moveCursorTo(writer, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn moveCursorTo(writer: *std.Io.Writer, x: usize, y: usize) !void {
|
||||||
|
try writer.print("\x1b[{};{}H", .{ y + 1, x + 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn drawTeromino(
|
||||||
|
mino: Tetromino,
|
||||||
|
x_offset: usize,
|
||||||
|
y_offset: usize,
|
||||||
|
writer: *std.Io.Writer,
|
||||||
|
) !void {
|
||||||
|
try moveCursorTo(
|
||||||
|
writer,
|
||||||
|
x_offset,
|
||||||
|
y_offset,
|
||||||
|
);
|
||||||
|
_ = try writer.write("@");
|
||||||
|
for (mino.bitmap, 0..) |row, y| {
|
||||||
|
for (row, 0..) |bit, x| {
|
||||||
|
if (bit == 1) {
|
||||||
|
try moveCursorTo(
|
||||||
|
writer,
|
||||||
|
((x_offset) + x) * 2 - 1, // mul 2 since the cell is 2 chars wide
|
||||||
|
y_offset + y,
|
||||||
|
);
|
||||||
|
|
||||||
|
try writer.print(
|
||||||
|
colour_set_string_fg_fmt ++ "██" ++ end_mode,
|
||||||
|
.{
|
||||||
|
mino.colour.r,
|
||||||
|
mino.colour.b,
|
||||||
|
mino.colour.g,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(self: Self, writer: *std.Io.Writer) !void {
|
||||||
|
try resetCursor(writer);
|
||||||
|
try clearScreen(writer);
|
||||||
|
|
||||||
|
// ======== draw top bar ========
|
||||||
|
_ = try writer.write("\n\r╔");
|
||||||
|
for (0..height) |_| {
|
||||||
|
_ = try writer.write("═");
|
||||||
|
}
|
||||||
|
_ = try writer.write("╗\n\r");
|
||||||
|
// ======== draw top bar ========
|
||||||
|
|
||||||
|
// ======== draw play space ========
|
||||||
|
|
||||||
|
for (self.grid) |row| {
|
||||||
|
_ = try writer.write("║");
|
||||||
|
// try writer.print("{} ║", .{i});
|
||||||
|
for (row) |cell| {
|
||||||
|
if (cell.filled) {
|
||||||
|
try writer.print(
|
||||||
|
colour_set_string_fg_fmt ++ "██" ++ end_mode,
|
||||||
|
.{
|
||||||
|
cell.colour.r, cell.colour.b, cell.colour.g,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
_ = try writer.write(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = try writer.write("║\n\r");
|
||||||
|
}
|
||||||
|
// ======== draw play space ========
|
||||||
|
|
||||||
|
// ======== draw bottom bar ========
|
||||||
|
_ = try writer.write("╚");
|
||||||
|
for (0..height) |_| {
|
||||||
|
_ = try writer.write("═");
|
||||||
|
}
|
||||||
|
_ = try writer.write("╝\n\r");
|
||||||
|
// ======== draw bottom bar ========
|
||||||
|
|
||||||
|
// ======== draw debug info ========
|
||||||
|
|
||||||
|
try writer.print("x : {}, y : {}\n\r", .{
|
||||||
|
self.tetromino_pos_x,
|
||||||
|
self.tetromino_pos_y,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ======== draw debug info ========
|
||||||
|
|
||||||
|
// ======== draw tetromino ========
|
||||||
|
try drawTeromino(
|
||||||
|
self.active_tetromino,
|
||||||
|
self.tetromino_pos_x + 1,
|
||||||
|
height -| self.tetromino_pos_y + 1,
|
||||||
|
writer,
|
||||||
|
);
|
||||||
|
// ======== draw tetromino ========
|
||||||
|
try resetCursor(writer);
|
||||||
|
|
||||||
|
try writer.flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// we wrap the game to make sure/hope we can exit out of rawmode if something occurs
|
||||||
|
pub fn gameMain(stdout: *std.Io.Writer, stdin: *std.Io.Reader) !void {
|
||||||
|
var b = Board.init();
|
||||||
|
Board.enterRawMode();
|
||||||
|
|
||||||
|
while (!b.stop) {
|
||||||
|
const current_time = std.time.microTimestamp() + 1 * 500000;
|
||||||
|
while (std.time.microTimestamp() < current_time) continue;
|
||||||
|
// b.active_tetromino.rotate(true);
|
||||||
|
b.processInputs(stdin);
|
||||||
|
b.step();
|
||||||
|
try b.draw(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
Board.exitRawMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var stdout_buffer: [1024]u8 = undefined;
|
||||||
|
var stdin_buffer: [1024]u8 = undefined;
|
||||||
|
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
||||||
|
var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);
|
||||||
|
|
||||||
|
const stdout = &stdout_writer.interface;
|
||||||
|
const stdin = &stdin_reader.interface;
|
||||||
|
|
||||||
|
gameMain(stdout, stdin) catch |e| {
|
||||||
|
Board.exitRawMode();
|
||||||
|
try Board.clearScreen(stdout);
|
||||||
|
try stdout.print("game exit got error {any}\n", .{e});
|
||||||
|
};
|
||||||
|
}
|
||||||
23
src/root.zig
Normal file
23
src/root.zig
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
//! By convention, root.zig is the root source file when making a library.
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn bufferedPrint() !void {
|
||||||
|
// Stdout is for the actual output of your application, for example if you
|
||||||
|
// are implementing gzip, then only the compressed bytes should be sent to
|
||||||
|
// stdout, not any debugging messages.
|
||||||
|
var stdout_buffer: [1024]u8 = undefined;
|
||||||
|
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
|
||||||
|
const stdout = &stdout_writer.interface;
|
||||||
|
|
||||||
|
try stdout.print("Run `zig build test` to run the tests.\n", .{});
|
||||||
|
|
||||||
|
try stdout.flush(); // Don't forget to flush!
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(a: i32, b: i32) i32 {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "basic add functionality" {
|
||||||
|
try std.testing.expect(add(3, 7) == 10);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user