feat: allow user to undo card movements
parent
9de25c4129
commit
35067f824e
115
src/main.zig
115
src/main.zig
|
@ -12,6 +12,8 @@ var drawn_cards: std.ArrayListUnmanaged(Card) = .{};
|
||||||
var stacks: [7]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 7;
|
var stacks: [7]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 7;
|
||||||
var foundations: [4]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 4;
|
var foundations: [4]std.ArrayListUnmanaged(Card) = [_]std.ArrayListUnmanaged(Card){.{}} ** 4;
|
||||||
|
|
||||||
|
var history: std.ArrayListUnmanaged(Snapshot) = .{};
|
||||||
|
|
||||||
var last_hovered_stack: usize = 0;
|
var last_hovered_stack: usize = 0;
|
||||||
var last_hovered_foundation: usize = 0;
|
var last_hovered_foundation: usize = 0;
|
||||||
var hovered_deck: ?*std.ArrayListUnmanaged(Card) = null;
|
var hovered_deck: ?*std.ArrayListUnmanaged(Card) = null;
|
||||||
|
@ -85,6 +87,11 @@ pub fn init(context: *seizer.Context) !void {
|
||||||
.on_event = toggleMenu,
|
.on_event = toggleMenu,
|
||||||
.default_bindings = &.{.start},
|
.default_bindings = &.{.start},
|
||||||
});
|
});
|
||||||
|
try context.addButtonInput(.{
|
||||||
|
.title = "undo",
|
||||||
|
.on_event = undo,
|
||||||
|
.default_bindings = &.{.x},
|
||||||
|
});
|
||||||
|
|
||||||
const app_data_dir_path = try std.fs.getAppDataDir(gpa, "seizer-solitaire");
|
const app_data_dir_path = try std.fs.getAppDataDir(gpa, "seizer-solitaire");
|
||||||
defer gpa.free(app_data_dir_path);
|
defer gpa.free(app_data_dir_path);
|
||||||
|
@ -135,6 +142,13 @@ fn resetGame() !void {
|
||||||
win_triggered = false;
|
win_triggered = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resetHistory() void {
|
||||||
|
for (history.items) |*snapshot| {
|
||||||
|
snapshot.deinit();
|
||||||
|
}
|
||||||
|
history.shrinkRetainingCapacity(0);
|
||||||
|
}
|
||||||
|
|
||||||
fn destroy(window: *seizer.Window) void {
|
fn destroy(window: *seizer.Window) void {
|
||||||
_ = window;
|
_ = window;
|
||||||
draw_pile.deinit(gpa);
|
draw_pile.deinit(gpa);
|
||||||
|
@ -161,6 +175,7 @@ fn render(window: *seizer.Window) !void {
|
||||||
std.mem.writeInt(u32, &win_count_buffer, win_count, .little);
|
std.mem.writeInt(u32, &win_count_buffer, win_count, .little);
|
||||||
try win_count_file.pwriteAll(&win_count_buffer, 0);
|
try win_count_file.pwriteAll(&win_count_buffer, 0);
|
||||||
|
|
||||||
|
resetHistory();
|
||||||
win_triggered = true;
|
win_triggered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +425,7 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
}
|
}
|
||||||
if (selected_deck == null) {
|
if (selected_deck == null) {
|
||||||
if (hovered_deck == &draw_pile and !draw_pile_exhausted) {
|
if (hovered_deck == &draw_pile and !draw_pile_exhausted) {
|
||||||
|
resetHistory();
|
||||||
drawn_cards.ensureUnusedCapacity(gpa, 3) catch return;
|
drawn_cards.ensureUnusedCapacity(gpa, 3) catch return;
|
||||||
for (0..3) |_| {
|
for (0..3) |_| {
|
||||||
if (draw_pile.popOrNull()) |card| drawn_cards.appendAssumeCapacity(card);
|
if (draw_pile.popOrNull()) |card| drawn_cards.appendAssumeCapacity(card);
|
||||||
|
@ -432,9 +448,11 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (hovered_deck == selected_deck) {
|
} else if (hovered_deck == selected_deck) {
|
||||||
|
const snapshot = try Snapshot.initFromCurrentGlobalState();
|
||||||
const selected_substack = selected_deck.?.items[selected_card..];
|
const selected_substack = selected_deck.?.items[selected_card..];
|
||||||
|
|
||||||
// try to move all selected cards into the foundations
|
// try to move all selected cards into the foundations
|
||||||
|
var cards_moved = false;
|
||||||
move_cards_to_foundations: for (0..selected_substack.len) |_| {
|
move_cards_to_foundations: for (0..selected_substack.len) |_| {
|
||||||
for (foundations[0..]) |*foundation| {
|
for (foundations[0..]) |*foundation| {
|
||||||
if (hovered_deck == foundation) break :move_cards_to_foundations;
|
if (hovered_deck == foundation) break :move_cards_to_foundations;
|
||||||
|
@ -449,14 +467,18 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
foundation.ensureUnusedCapacity(gpa, 1) catch continue;
|
foundation.ensureUnusedCapacity(gpa, 1) catch continue;
|
||||||
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
||||||
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
||||||
|
cards_moved = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
break :move_cards_to_foundations;
|
break :move_cards_to_foundations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try history.append(gpa, snapshot);
|
||||||
selected_deck = null;
|
selected_deck = null;
|
||||||
} else {
|
} else {
|
||||||
|
const snapshot = try Snapshot.initFromCurrentGlobalState();
|
||||||
if (selected_deck) |selected| move_from_selected_to_hovered: {
|
if (selected_deck) |selected| move_from_selected_to_hovered: {
|
||||||
const selected_substack = selected.items[selected_card..];
|
const selected_substack = selected.items[selected_card..];
|
||||||
if (selected_substack.len == 0) break :move_from_selected_to_hovered;
|
if (selected_substack.len == 0) break :move_from_selected_to_hovered;
|
||||||
|
@ -470,6 +492,7 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
hovered.appendSliceAssumeCapacity(selected_substack);
|
hovered.appendSliceAssumeCapacity(selected_substack);
|
||||||
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
||||||
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
||||||
|
try history.append(gpa, snapshot);
|
||||||
break :move_from_selected_to_hovered;
|
break :move_from_selected_to_hovered;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -478,6 +501,7 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
hovered.ensureUnusedCapacity(gpa, 1) catch break :move_from_selected_to_hovered;
|
hovered.ensureUnusedCapacity(gpa, 1) catch break :move_from_selected_to_hovered;
|
||||||
hovered.appendAssumeCapacity(selected_deck.?.pop());
|
hovered.appendAssumeCapacity(selected_deck.?.pop());
|
||||||
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
||||||
|
try history.append(gpa, snapshot);
|
||||||
break :move_from_selected_to_hovered;
|
break :move_from_selected_to_hovered;
|
||||||
} else for (foundations[0..]) |*foundation| {
|
} else for (foundations[0..]) |*foundation| {
|
||||||
if (foundation != selected) continue;
|
if (foundation != selected) continue;
|
||||||
|
@ -489,6 +513,7 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
foundation.ensureUnusedCapacity(gpa, 1) catch continue;
|
foundation.ensureUnusedCapacity(gpa, 1) catch continue;
|
||||||
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
foundation.appendAssumeCapacity(selected_deck.?.pop());
|
||||||
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
||||||
|
try history.append(gpa, snapshot);
|
||||||
break :move_from_selected_to_hovered;
|
break :move_from_selected_to_hovered;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -506,6 +531,7 @@ pub fn doSelectOrPlace(pressed: bool) !void {
|
||||||
hovered.appendSliceAssumeCapacity(selected_substack);
|
hovered.appendSliceAssumeCapacity(selected_substack);
|
||||||
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
selected.shrinkRetainingCapacity(selected.items.len - selected_substack.len);
|
||||||
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
hovered_card = indexOfTopOfStack(hovered_deck.?.items);
|
||||||
|
try history.append(gpa, snapshot);
|
||||||
}
|
}
|
||||||
selected_deck = null;
|
selected_deck = null;
|
||||||
}
|
}
|
||||||
|
@ -666,6 +692,15 @@ pub fn moveDown(pressed: bool) !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn undo(pressed: bool) !void {
|
||||||
|
if (!pressed) return;
|
||||||
|
if (history.popOrNull()) |snapshot_const| {
|
||||||
|
var snapshot = snapshot_const;
|
||||||
|
defer snapshot.deinit();
|
||||||
|
try snapshot.restore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggleMenu(pressed: bool) !void {
|
pub fn toggleMenu(pressed: bool) !void {
|
||||||
if (!pressed) return;
|
if (!pressed) return;
|
||||||
show_menu = !show_menu;
|
show_menu = !show_menu;
|
||||||
|
@ -684,6 +719,86 @@ pub fn indexOfTopOfStack(cards: []Card) usize {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Snapshot = struct {
|
||||||
|
draw_pile: []Card,
|
||||||
|
draw_pile_exhausted: bool,
|
||||||
|
drawn_cards: []Card,
|
||||||
|
stacks: [7][]Card,
|
||||||
|
foundations: [4][]Card,
|
||||||
|
selected_deck: *std.ArrayListUnmanaged(Card),
|
||||||
|
selected_card: usize,
|
||||||
|
|
||||||
|
pub fn initFromCurrentGlobalState() !Snapshot {
|
||||||
|
const snapshot_selected_deck = selected_deck orelse return error.InvalidSelectionForSnapshot;
|
||||||
|
|
||||||
|
const snapshot_draw_pile = try gpa.dupe(Card, draw_pile.items);
|
||||||
|
errdefer gpa.free(snapshot_draw_pile);
|
||||||
|
const snapshot_drawn_cards = try gpa.dupe(Card, drawn_cards.items);
|
||||||
|
errdefer gpa.free(snapshot_drawn_cards);
|
||||||
|
|
||||||
|
var snapshot_stacks: [7][]Card = undefined;
|
||||||
|
for (&snapshot_stacks, &stacks) |*snapshot_stack, stack| {
|
||||||
|
snapshot_stack.* = try gpa.dupe(Card, stack.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
var snapshot_foundations: [4][]Card = undefined;
|
||||||
|
for (&snapshot_foundations, &foundations) |*snapshot_foundation, foundation| {
|
||||||
|
snapshot_foundation.* = try gpa.dupe(Card, foundation.items);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Snapshot{
|
||||||
|
.draw_pile = snapshot_draw_pile,
|
||||||
|
.draw_pile_exhausted = draw_pile_exhausted,
|
||||||
|
.drawn_cards = snapshot_drawn_cards,
|
||||||
|
.stacks = snapshot_stacks,
|
||||||
|
.foundations = snapshot_foundations,
|
||||||
|
.selected_deck = snapshot_selected_deck,
|
||||||
|
.selected_card = selected_card,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(snapshot: *@This()) void {
|
||||||
|
gpa.free(snapshot.draw_pile);
|
||||||
|
gpa.free(snapshot.drawn_cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn restore(snapshot: @This()) !void {
|
||||||
|
// ensure there is enough space for all the cards
|
||||||
|
try draw_pile.ensureTotalCapacity(gpa, snapshot.draw_pile.len);
|
||||||
|
try drawn_cards.ensureTotalCapacity(gpa, snapshot.drawn_cards.len);
|
||||||
|
for (&snapshot.stacks, &stacks) |snapshot_stack, *stack| {
|
||||||
|
try stack.ensureTotalCapacity(gpa, snapshot_stack.len);
|
||||||
|
}
|
||||||
|
for (&snapshot.foundations, &foundations) |snapshot_foundation, *foundation| {
|
||||||
|
try foundation.ensureTotalCapacity(gpa, snapshot_foundation.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the state
|
||||||
|
draw_pile.shrinkRetainingCapacity(0);
|
||||||
|
draw_pile.appendSliceAssumeCapacity(snapshot.draw_pile);
|
||||||
|
|
||||||
|
draw_pile_exhausted = snapshot.draw_pile_exhausted;
|
||||||
|
|
||||||
|
drawn_cards.shrinkRetainingCapacity(0);
|
||||||
|
drawn_cards.appendSliceAssumeCapacity(snapshot.drawn_cards);
|
||||||
|
|
||||||
|
for (&snapshot.stacks, &stacks) |snapshot_stack, *stack| {
|
||||||
|
stack.shrinkRetainingCapacity(0);
|
||||||
|
stack.appendSliceAssumeCapacity(snapshot_stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (&snapshot.foundations, &foundations) |snapshot_foundation, *foundation| {
|
||||||
|
foundation.shrinkRetainingCapacity(0);
|
||||||
|
foundation.appendSliceAssumeCapacity(snapshot_foundation);
|
||||||
|
}
|
||||||
|
|
||||||
|
selected_deck = null;
|
||||||
|
selected_card = 0;
|
||||||
|
hovered_deck = snapshot.selected_deck;
|
||||||
|
hovered_card = snapshot.selected_card;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const DeckSprites = assets.DeckSprites;
|
const DeckSprites = assets.DeckSprites;
|
||||||
const Card = assets.Card;
|
const Card = assets.Card;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue