Skip to content

Instantly share code, notes, and snippets.

@xqm32
Created April 16, 2026 09:44
Show Gist options
  • Select an option

  • Save xqm32/87591aed0884cf3709f7ebbc20ab3f82 to your computer and use it in GitHub Desktop.

Select an option

Save xqm32/87591aed0884cf3709f7ebbc20ab3f82 to your computer and use it in GitHub Desktop.
const std = @import("std");
const B = u64;
const D = enum(u2) { l, r, u, d };
const K = union(enum) { m: D, u, r, q, n };
const M = struct { b: B, s: u32 };
const R = packed struct(u64) { r: u16, s: u32, _: u16 = 0 };
const T = mk();
const S = sk();
const G = struct {
b: B = 0,
p: B = 0,
s: u32 = 0,
ps: u32 = 0,
m: u32 = 0,
r: u64,
h: i8 = -1,
u: bool = false,
fn new(z: u64, m: u32) G {
var g: G = .{ .r = z ^ 0x9e3779b97f4a7c15, .m = m };
g.sp();
g.sp();
return g;
}
fn x(self: *G) u64 {
var y = self.r;
y ^= y << 7;
y ^= y >> 9;
y ^= y << 8;
self.r = y;
return y *% 0x2545F4914F6CDD1D;
}
fn sp(self: *G) void {
var a: [16]u8 = undefined;
var n: usize = 0;
for (0..16) |i| if (c(self.b, @intCast(i)) == 0) {
a[n] = @intCast(i);
n += 1;
};
if (n == 0) {
self.h = -1;
return;
}
const i = a[@intCast(self.x() % n)];
self.b = w(self.b, i, if (self.x() % 10 == 0) 2 else 1);
self.h = @intCast(i);
}
fn mv(self: *G, d: D) bool {
const m: M = switch (d) {
.l => l(self.b),
.r => r(self.b),
.u => u(self.b),
.d => dwn(self.b),
};
if (m.b == self.b) return false;
self.p = self.b;
self.ps = self.s;
self.u = true;
self.b = m.b;
self.s +%= m.s;
if (self.s > self.m) self.m = self.s;
self.sp();
return true;
}
fn un(self: *G) void {
if (!self.u) return;
self.b = self.p;
self.s = self.ps;
self.h = -1;
self.u = false;
}
fn re(self: *G) void {
const m = self.m;
self.* = .new(self.x(), m);
}
};
pub fn main(i: std.process.Init) !void {
const rm = try raw();
defer std.posix.tcsetattr(std.posix.STDIN_FILENO, .FLUSH, rm) catch {};
try o(i.io, "\x1b[?1049h\x1b[?25l");
defer o(i.io, "\x1b[?25h\x1b[?1049l") catch {};
var g = G.new(sd(), 0);
while (true) {
try dr(i.io, &g, false);
switch (try k()) {
.q => break,
.r => g.re(),
.u => g.un(),
.n => {},
.m => |d| if (g.mv(d)) for (0..2) |j| {
try dr(i.io, &g, j & 1 == 0);
try std.Io.Clock.Duration.sleep(.{ .clock = .awake, .raw = .fromMilliseconds(18) }, i.io);
},
}
}
}
fn raw() !std.posix.termios {
const fd = std.posix.STDIN_FILENO;
const o0 = try std.posix.tcgetattr(fd);
var q = o0;
q.lflag.ECHO = false;
q.lflag.ICANON = false;
q.iflag.IXON = false;
q.iflag.ICRNL = false;
q.cc[@intFromEnum(std.posix.V.MIN)] = 1;
q.cc[@intFromEnum(std.posix.V.TIME)] = 0;
try std.posix.tcsetattr(fd, .FLUSH, q);
return o0;
}
fn sd() u64 {
var q: u64 = 0;
return @as(u64, @intFromPtr(&q)) ^ @as(u64, @intFromPtr(&sd)) ^ 0xd1b54a32d192ed03;
}
fn k() !K {
var b: [3]u8 = .{ 0, 0, 0 };
_ = try std.posix.read(std.posix.STDIN_FILENO, b[0..1]);
return switch (b[0]) {
'q', 'Q' => .q,
'r', 'R' => .r,
'u', 'U', 'z', 'Z' => .u,
'a', 'A', 'h', 'H' => .{ .m = .l },
'd', 'D', 'l', 'L' => .{ .m = .r },
'w', 'W', 'k', 'K' => .{ .m = .u },
's', 'S', 'j', 'J' => .{ .m = .d },
'\x1b' => blk: {
const n = std.posix.read(std.posix.STDIN_FILENO, b[1..3]) catch 0;
if (n >= 2 and b[1] == '[') break :blk switch (b[2]) {
'A' => .{ .m = .u },
'B' => .{ .m = .d },
'C' => .{ .m = .r },
'D' => .{ .m = .l },
else => .n,
};
break :blk .n;
},
else => .n,
};
}
fn dr(io: std.Io, g: *const G, z: bool) !void {
var b: [4096]u8 = undefined;
var s: []u8 = b[0..];
x(&s, "\x1b[H\x1b[2J\x1b[1;38;5;228m2048.zig\x1b[0m ");
f(&s, "s:{d} best:{d}\nwasd/hjkl/arrows move, u undo, r reset, q quit\n\n┌────────┬────────┬────────┬────────┐\n", .{ g.s, g.m });
for (0..4) |y| {
x(&s, "│");
inline for (0..4) |q| {
const i: u8 = @intCast(y * 4 + q);
x(&s, S[@intFromBool(z and g.h == @as(i8, @intCast(i)))][c(g.b, i)]);
x(&s, "│");
}
x(&s, if (y == 3) "\n└────────┴────────┴────────┴────────┘\n" else "\n├────────┼────────┼────────┼────────┤\n");
}
x(&s, if (wn(g.b)) "\n\x1b[1;38;5;220m2048 reached. lookup table has accepted your offering.\x1b[0m\n" else if (!cn(g.b)) "\n\x1b[1;38;5;203mNo moves left. r to reset, u to deny fate.\x1b[0m\n" else "\nbitboard + transpose + comptime row oracle\n");
try o(io, b[0 .. b.len - s.len]);
}
fn o(io: std.Io, s: []const u8) !void {
try std.Io.File.stdout().writeStreamingAll(io, s);
}
fn x(d: *[]u8, s: []const u8) void {
@memcpy(d.*[0..s.len], s);
d.* = d.*[s.len..];
}
fn f(d: *[]u8, comptime p: []const u8, a: anytype) void {
const s = std.fmt.bufPrint(d.*, p, a) catch unreachable;
d.* = d.*[s.len..];
}
fn c(b: B, i: u8) u4 {
return @truncate(b >> @as(u6, @intCast(i * 4)));
}
fn w(b: B, i: u8, y: u4) B {
const s: u6 = @intCast(i * 4);
return (b & ~(@as(u64, 0xF) << s)) | (@as(u64, y) << s);
}
fn v(y: u16) u16 {
return ((y & 0x000F) << 12) | ((y & 0x00F0) << 4) | ((y & 0x0F00) >> 4) | ((y & 0xF000) >> 12);
}
fn t(y: u64) u64 {
const a = (y & 0xF0F00F0FF0F00F0F) | ((y & 0x0000F0F00000F0F0) << 12) | ((y & 0x0F0F00000F0F0000) >> 12);
return (a & 0xFF00FF0000FF00FF) | ((a & 0x00FF00FF00000000) >> 24) | ((a & 0x00000000FF00FF00) << 24);
}
fn l(b: B) M {
var n: B = 0;
var s: u32 = 0;
for (0..4) |i| {
const h: u6 = @intCast(i * 16);
const r0 = T[@as(u16, @truncate(b >> h))];
n |= @as(B, r0.r) << h;
s +%= r0.s;
}
return .{ .b = n, .s = s };
}
fn r(b: B) M {
var n: B = 0;
var s: u32 = 0;
for (0..4) |i| {
const h: u6 = @intCast(i * 16);
const r0 = T[v(@as(u16, @truncate(b >> h)))];
n |= @as(B, v(r0.r)) << h;
s +%= r0.s;
}
return .{ .b = n, .s = s };
}
fn u(b: B) M {
const m = l(t(b));
return .{ .b = t(m.b), .s = m.s };
}
fn dwn(b: B) M {
const m = r(t(b));
return .{ .b = t(m.b), .s = m.s };
}
fn cn(b: B) bool {
return l(b).b != b or r(b).b != b or u(b).b != b or dwn(b).b != b;
}
fn wn(b: B) bool {
for (0..16) |i| if (c(b, @intCast(i)) >= 11) return true;
return false;
}
fn mk() [1 << 16]R {
@setEvalBranchQuota(1_000_000);
var t0: [1 << 16]R = undefined;
for (0..t0.len) |i| t0[i] = sl(@intCast(i));
return t0;
}
fn sl(y: u16) R {
const a = [4]u4{ @truncate(y), @truncate(y >> 4), @truncate(y >> 8), @truncate(y >> 12) };
var b = [4]u4{ 0, 0, 0, 0 };
var j: usize = 0;
var s: u32 = 0;
for (a) |n| if (n != 0) {
if (j > 0 and b[j - 1] == n) {
b[j - 1] +%= 1;
s +%= @as(u32, 1) << b[j - 1];
} else {
b[j] = n;
j += 1;
}
};
return .{ .r = @as(u16, b[0]) | (@as(u16, b[1]) << 4) | (@as(u16, b[2]) << 8) | (@as(u16, b[3]) << 12), .s = s };
}
fn sk() [2][16][]const u8 {
const a = [_]u8{ 236, 223, 222, 215, 209, 203, 197, 191, 185, 179, 141, 135, 129, 165, 201, 213 };
var zz: [2][16][]const u8 = undefined;
inline for (0..2) |z| inline for (0..16) |e| {
const n = comptime blk: {
var q = [8]u8{ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
if (e != 0) {
const s = std.fmt.comptimePrint("{d}", .{@as(u32, 1) << e});
for (s, 0..) |c0, i| q[(8 - s.len) / 2 + i] = c0;
}
break :blk q;
};
zz[z][e] = std.fmt.comptimePrint("\x1b[48;5;{d};38;5;16m{s}\x1b[0m", .{ a[e] + z * 6, n[0..] });
};
return zz;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment