diff options
author | Ratakor <ratakor@disroot.org> | 2023-08-25 14:58:50 -0400 |
---|---|---|
committer | Ratakor <ratakor@disroot.org> | 2023-08-25 14:58:50 -0400 |
commit | f644e380f0519c1421afdd658204b7d30d9be010 (patch) | |
tree | b008cc07dad5f4bf5e0a986209cabea624960e4d | |
parent | b38fe4f4ed6b830dd17721270924a536c99f7e81 (diff) |
Add sighandler, add highscores, add turn count
Also added fmt and clean build steps, save only on quit or
SIGHUP/TERM/INT instead of everytime a move succeeded and changed score
to u64 instead of u32.
-rw-r--r-- | build.zig | 11 | ||||
-rw-r--r-- | src/Board.zig | 116 | ||||
-rw-r--r-- | src/args.zig | 16 | ||||
-rw-r--r-- | src/main.zig | 25 | ||||
-rw-r--r-- | src/term.zig | 2 |
5 files changed, 118 insertions, 52 deletions
@@ -10,7 +10,7 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); - exe.strip = b.option(bool, "strip", "strip the binary") orelse switch (optimize) { + exe.strip = b.option(bool, "strip", "Strip the binary") orelse switch (optimize) { .Debug, .ReleaseSafe => false, .ReleaseFast, .ReleaseSmall => true, }; @@ -33,7 +33,7 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Run unit tests"); test_step.dependOn(&run_tests.step); - const release = b.step("release", "make an upstream binary release"); + const release = b.step("release", "Make an upstream binary release"); const release_targets = &[_][]const u8{ "aarch64-linux", "x86_64-linux", "x86-linux", "riscv64-linux", }; @@ -54,4 +54,11 @@ pub fn build(b: *std.Build) void { release.dependOn(&install.step); } + + const fmt_step = b.step("fmt", "Format all source files"); + fmt_step.dependOn(&b.addFmt(.{ .paths = &.{ "build.zig", "src" } }).step); + + const clean_step = b.step("clean", "Delete all artifacts created by zig build"); + clean_step.dependOn(&b.addRemoveDirTree("zig-cache").step); + clean_step.dependOn(&b.addRemoveDirTree("zig-out").step); } diff --git a/src/Board.zig b/src/Board.zig index 04005e9..fa0bd18 100644 --- a/src/Board.zig +++ b/src/Board.zig @@ -11,16 +11,18 @@ const writer = main.writer; const CELL_SIZE = 7; const cwd = std.fs.cwd(); var rnd: DefaultPrng = undefined; -var savefile: []u8 = undefined; pub const Board = @This(); cells: [][]u8, size: usize, -score: u32, -win: bool, +highscore: u64, +score: u64, +turns: usize, prev: *Board, tmp: *Board, +savefile: []u8, +win: bool, pub fn addRandom(self: *Board) !void { var len: usize = 0; @@ -58,23 +60,24 @@ fn destroyBoard(board: [][]u8) void { allocator.free(board); } -pub fn init(size: usize) !Board { +pub fn init(size: usize) !*Board { rnd = DefaultPrng.init(@intCast(std.time.microTimestamp())); - var board = Board{ - .cells = try createBoard(size), - .size = size, - .score = 0, - .win = false, - .prev = try allocator.create(Board), - .tmp = try allocator.create(Board), - }; + var board = try allocator.create(Board); + board.cells = try createBoard(size); + board.size = size; + board.highscore = 0; + board.score = 0; + board.turns = 0; + board.win = false; + board.prev = try allocator.create(Board); + board.tmp = try allocator.create(Board); board.prev.cells = try createBoard(size); board.tmp.cells = try createBoard(size); if (!try board.load()) { try board.addRandom(); try board.addRandom(); } - boardCopy(board.prev, &board); + boardCopy(board.prev, board); return board; } @@ -84,7 +87,7 @@ pub fn deinit(self: *Board) void { destroyBoard(self.tmp.cells); allocator.destroy(self.prev); allocator.destroy(self.tmp); - allocator.free(savefile); + allocator.free(self.savefile); } pub fn reset(self: *Board) void { @@ -92,34 +95,40 @@ pub fn reset(self: *Board) void { @memset(row, 0); } self.score = 0; + self.turns = 0; + self.win = false; } fn load(self: *Board) !bool { var buf: [4096]u8 = undefined; var path: []u8 = undefined; - if (os.getenv("XDG_CACHE_HOME")) |xdg_cache| { - path = try fmt.bufPrint(buf[0..], "{s}/2048/", .{xdg_cache}); + if (os.getenv("XDG_DATA_HOME")) |xdg_data| { + path = try fmt.bufPrint(buf[0..], "{s}/2048/", .{xdg_data}); } else if (os.getenv("HOME")) |home| { - path = try fmt.bufPrint(buf[0..], "{s}/.cache/2048/", .{home}); + path = try fmt.bufPrint(buf[0..], "{s}/.local/share/2048/", .{home}); } else { unreachable; // TODO: windows } try cwd.makePath(path); const len = path.len + (try fmt.bufPrint(buf[path.len..], "{d}", .{self.size})).len; - savefile = try allocator.dupe(u8, buf[0..len]); + self.savefile = try allocator.dupe(u8, buf[0..len]); - const f = cwd.openFile(savefile, .{ .mode = .read_only }) catch |err| switch (err) { + const f = cwd.openFile(self.savefile, .{ .mode = .read_only }) catch |err| switch (err) { error.FileNotFound => return false, else => |e| return e, }; defer f.close(); - errdefer cwd.deleteFile(savefile) catch {}; + errdefer cwd.deleteFile(self.savefile) catch {}; var buf_reader = io.bufferedReader(f.reader()); const reader = buf_reader.reader(); - var score_buf: [32]u8 = undefined; - const score = try reader.readUntilDelimiter(score_buf[0..], '\n'); - self.score = try fmt.parseInt(u32, score, 10); + var nbuf: [32]u8 = undefined; + const highscore = try reader.readUntilDelimiter(nbuf[0..], '\n'); + self.highscore = try fmt.parseInt(u64, highscore, 10); + const score = try reader.readUntilDelimiter(nbuf[0..], '\n'); + self.score = try fmt.parseInt(u64, score, 10); + const turns = try reader.readUntilDelimiter(nbuf[0..], '\n'); + self.turns = try fmt.parseInt(usize, turns, 10); var i: usize = 0; while (i < self.size) : (i += 1) { var j: usize = 0; @@ -128,8 +137,7 @@ fn load(self: *Board) !bool { } } - if (gameOver(self)) { - try cwd.deleteFile(savefile); + if (self.turns == 0 or gameOver(self)) { self.reset(); return false; } @@ -138,13 +146,17 @@ fn load(self: *Board) !bool { } pub fn save(self: *Board) !void { - const f = try cwd.createFile(savefile, .{}); + const f = try cwd.createFile(self.savefile, .{}); defer f.close(); - errdefer cwd.deleteFile(savefile) catch {}; + errdefer cwd.deleteFile(self.savefile) catch {}; var buf_writer = io.bufferedWriter(f.writer()); const fwriter = buf_writer.writer(); - try fwriter.print("{d}\n", .{self.score}); + try fwriter.print("{d}\n{d}\n{d}\n", .{ + self.highscore, + self.score, + self.turns, + }); for (self.cells) |row| { for (row) |cell| { try fwriter.writeByte(cell); @@ -226,8 +238,12 @@ fn slide(self: *Board) bool { } } + if (self.score > self.highscore) { + self.highscore = self.score; + } if (success) { boardCopy(self.prev, self.tmp); + self.turns += 1; } return success; @@ -237,7 +253,9 @@ fn boardCopy(dst: *Board, src: *Board) void { for (dst.cells, src.cells) |dst_row, src_row| { @memcpy(dst_row, src_row); } + dst.highscore = src.highscore; dst.score = src.score; + dst.turns = src.turns; } pub fn moveLeft(self: *Board) bool { @@ -333,7 +351,7 @@ fn setColors(cell: u8) !void { } } -fn countDigits(number: u32) u8 { +fn countDigits(number: u64) u8 { var count: u8 = 1; var n = number / 10; while (n != 0) { @@ -345,10 +363,25 @@ fn countDigits(number: u32) u8 { fn printHeader(self: *Board) !void { const board_size = CELL_SIZE * self.size; - const n = board_size -| ("2048.zig ".len + countDigits(self.score) + " pts".len); - try writer.writeAll("2048.zig "); - try writer.writeByteNTimes(' ', n); - try writer.print("{d} pts\n\n", .{self.score}); + const title = "2048.zig" ++ " "; + const score_digits = countDigits(self.score); + const turns_digits = countDigits(self.turns); + const highscore_digits = countDigits(self.highscore); + + const stext1 = title.len + highscore_digits + "Highscore: ".len; + const n1 = board_size -| stext1; + try writer.writeAll(title); + try writer.writeByteNTimes(' ', n1); + try writer.print("Highscore: {d}\n", .{self.highscore}); + + const padding = highscore_digits - score_digits; + const stext2 = "Turns: ".len + turns_digits + "Score: ".len + padding + score_digits; + const n2 = board_size -| stext2; + try writer.print("Turns: {d} ", .{self.turns}); + try writer.writeByteNTimes(' ', n2); + try writer.writeAll("Score: "); + try writer.writeByteNTimes(' ', padding); + try writer.print("{d}\n", .{self.score}); } pub fn print(self: *Board, str: []const u8) !void { @@ -376,14 +409,15 @@ pub fn draw(self: *Board) !void { const real = @as(u32, 1) << @intCast(cell); const digits = countDigits(real); // TODO kinda unsafe negative - const t = CELL_SIZE - digits; + var n = CELL_SIZE - digits; + n = n - n / 2; if (digits % 2 == 0) { - try writer.writeByteNTimes(' ', t - t / 2 - 1); + try writer.writeByteNTimes(' ', n - 1); } else { - try writer.writeByteNTimes(' ', t - t / 2); + try writer.writeByteNTimes(' ', n); } try writer.print("{d}", .{real}); - try writer.writeByteNTimes(' ', t - t / 2); + try writer.writeByteNTimes(' ', n); } } try writer.writeAll("\n"); @@ -441,5 +475,11 @@ test "gameOver" { board.cells[0][1] = 3; board.cells[1][0] = 3; board.cells[1][1] = 2; - try std.testing.expect(board.gameOver()); + + const expect = std.testing.expect; + try expect(board.gameOver()); + board.cells[1][0] = 0; + try expect(!board.gameOver()); + board.cells[1][0] = 2; + try expect(!board.gameOver()); } diff --git a/src/args.zig b/src/args.zig index a662c2e..3290cec 100644 --- a/src/args.zig +++ b/src/args.zig @@ -7,16 +7,16 @@ const usage = \\Usage: {s} [options] \\ \\Options: - \\-s|--size [n] | Set the board size to n - \\-h|--help │ Print this help message - \\-v|--version | Print version information + \\-s, --size [n] | Set the board size to n + \\-h, --help │ Print this help message + \\-v, --version | Print version information \\ \\Commands: - \\ ↑ w k | Classic movements - \\ ←↓→ asd hjl | - \\ q | Quit the game - \\ r | Restart the game - \\ u | Undo one action + \\ ↑ w k | Classic movements + \\ ←↓→ asd hjl | + \\ q | Quit the game + \\ r | Restart the game + \\ u | Undo one action \\ ; diff --git a/src/main.zig b/src/main.zig index 041935c..942f73d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const os = std.os; const args = @import("args.zig"); const Board = @import("Board.zig"); const term = @import("term.zig"); @@ -8,14 +9,32 @@ pub const writer = buf_writer.writer(); var buffer: [std.mem.page_size]u8 = undefined; var fba = std.heap.FixedBufferAllocator.init(&buffer); pub const allocator = fba.allocator(); +var board: *Board = undefined; + +fn sigHandler(sig: c_int) callconv(.C) void { + _ = sig; + board.save() catch {}; + board.deinit(); + term.deinit() catch {}; + os.exit(0); +} pub fn main() !void { const reader = std.io.getStdIn().reader(); - var board = try Board.init(try args.parse()); + board = try Board.init(try args.parse()); defer board.deinit(); try term.init(); defer term.deinit() catch {}; + const sa = os.Sigaction{ + .handler = .{ .handler = sigHandler }, + .mask = os.empty_sigset, + .flags = os.SA.RESTART, + }; + try os.sigaction(os.SIG.HUP, &sa, null); + try os.sigaction(os.SIG.INT, &sa, null); + try os.sigaction(os.SIG.TERM, &sa, null); + try board.draw(); while (true) { var success = false; @@ -24,7 +43,7 @@ pub fn main() !void { 'a', 'h', 68 => success = board.moveLeft(), 's', 'j', 66 => success = board.moveDown(), 'd', 'l', 67 => success = board.moveRight(), - 'q', '' => { + 'q' => { try board.print("QUIT? (y/n)"); try buf_writer.flush(); if (try reader.readByte() == 'y') { @@ -51,7 +70,7 @@ pub fn main() !void { if (success) { try board.addRandom(); try board.draw(); - try board.save(); } } + try board.save(); } diff --git a/src/term.zig b/src/term.zig index d07f437..6d42b1b 100644 --- a/src/term.zig +++ b/src/term.zig @@ -44,7 +44,7 @@ pub fn init() !void { var raw = orig; raw.lflag &= ~@as( os.system.tcflag_t, - os.system.ECHO | os.system.ICANON | os.system.ISIG + os.system.ECHO | os.system.ICANON, ); try os.tcsetattr(handle, .FLUSH, raw); |