Created
April 16, 2026 09:44
-
-
Save xqm32/87591aed0884cf3709f7ebbc20ab3f82 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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