WIP deck methods, added flake, and setup build deps
This commit is contained in:
commit
777e07035b
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.direnv/**
|
||||||
|
.zig-cache/**
|
||||||
|
zig-out/**
|
73
build.zig
Normal file
73
build.zig
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void {
|
||||||
|
const target = b.standardTargetOptions(.{
|
||||||
|
.whitelist = &[_]std.Target.Query{
|
||||||
|
std.Target.Query{ .cpu_arch = .aarch64, .os_tag = .macos },
|
||||||
|
std.Target.Query{ .cpu_arch = .x86_64, .os_tag = .linux },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Prefer small size binaries for optimization
|
||||||
|
const optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .Debug });
|
||||||
|
|
||||||
|
const exe = b.addExecutable(.{
|
||||||
|
.name = "genius_deck",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
// Add zap dependency
|
||||||
|
const zap = b.dependency("zap", .{
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
.openssl = false,
|
||||||
|
});
|
||||||
|
exe.root_module.addImport("zap", zap.module("zap"));
|
||||||
|
b.installArtifact(exe);
|
||||||
|
|
||||||
|
// Create Check step for zls
|
||||||
|
const exe_check = b.addExecutable(.{
|
||||||
|
.name = "zerver",
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
exe_check.root_module.addImport("zap", zap.module("zap"));
|
||||||
|
const check = b.step("check", "Check if project compiles, used by Zig Language Server");
|
||||||
|
check.dependOn(&exe_check.step);
|
||||||
|
|
||||||
|
// Create Run Step
|
||||||
|
const run_cmd = b.addRunArtifact(exe);
|
||||||
|
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
// Create Test Step
|
||||||
|
const lib_unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = b.path("src/root.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
|
||||||
|
|
||||||
|
const exe_unit_tests = b.addTest(.{
|
||||||
|
.root_source_file = b.path("src/main.zig"),
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
|
||||||
|
const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&run_lib_unit_tests.step);
|
||||||
|
test_step.dependOn(&run_exe_unit_tests.step);
|
||||||
|
}
|
17
build.zig.zon
Normal file
17
build.zig.zon
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
.{
|
||||||
|
.name = "genius_deck",
|
||||||
|
.version = "1.0.0",
|
||||||
|
.minimum_zig_version = "0.13.0",
|
||||||
|
.dependencies = .{
|
||||||
|
.zap = .{
|
||||||
|
.url = "https://github.com/zigzap/zap/archive/refs/tags/v0.8.0.tar.gz",
|
||||||
|
.hash = "12209936c3333b53b53edcf453b1670babb9ae8c2197b1ca627c01e72670e20c1a21",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.paths = .{
|
||||||
|
"build.zig",
|
||||||
|
"build.zig.zon",
|
||||||
|
"src",
|
||||||
|
"README.md",
|
||||||
|
},
|
||||||
|
}
|
92
flake.lock
generated
Normal file
92
flake.lock
generated
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1710146030,
|
||||||
|
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1719690277,
|
||||||
|
"narHash": "sha256-0xSej1g7eP2kaUF+JQp8jdyNmpmCJKRpO12mKl/36Kc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "2741b4b489b55df32afac57bc4bfd220e8bf617e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1719942202,
|
||||||
|
"narHash": "sha256-7sP4PzxRsUfRwN8rmFtRvU/nYqTI5YYeIG9P3KJV41g=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4b015946c99a5bbe9c7a685e66f165aa44644b7d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"zlsPkg": "zlsPkg"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"zlsPkg": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1,
|
||||||
|
"narHash": "sha256-5LNEr6ek90U0PwmUjyZta4lCB93maBUr86ikbiwSsoU=",
|
||||||
|
"path": "./nix/zls",
|
||||||
|
"type": "path"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"path": "./nix/zls",
|
||||||
|
"type": "path"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
27
flake.nix
Normal file
27
flake.nix
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
description = "An example project using flutter";
|
||||||
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
inputs.zlsPkg.url = "path:./nix/zls";
|
||||||
|
outputs = {
|
||||||
|
flake-utils,
|
||||||
|
nixpkgs,
|
||||||
|
zlsPkg,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (system: let
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
};
|
||||||
|
zls = zlsPkg.defaultPackage.${system};
|
||||||
|
in {
|
||||||
|
devShell = pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
lldb
|
||||||
|
sqlite
|
||||||
|
zig
|
||||||
|
zls
|
||||||
|
];
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
26
nix/zls/flake.lock
generated
Normal file
26
nix/zls/flake.lock
generated
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1719942202,
|
||||||
|
"narHash": "sha256-7sP4PzxRsUfRwN8rmFtRvU/nYqTI5YYeIG9P3KJV41g=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "4b015946c99a5bbe9c7a685e66f165aa44644b7d",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
34
nix/zls/flake.nix
Normal file
34
nix/zls/flake.nix
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
description = "Zig language server";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = {self, nixpkgs}: {
|
||||||
|
defaultPackage.x86_64-linux =
|
||||||
|
with import nixpkgs { system = "x86_64-linux"; };
|
||||||
|
|
||||||
|
stdenv.mkDerivation rec {
|
||||||
|
name = "zls-${version}";
|
||||||
|
version = "0.13.0";
|
||||||
|
src = pkgs.fetchurl {
|
||||||
|
url = "https://github.com/zigtools/zls/releases/download/${version}/zls-x86_64-linux.tar.xz";
|
||||||
|
sha256 = "sha256-7EwbRcr4jivLnrsWxnBgPMWW5PYhuWGE376DeznNhBA=";
|
||||||
|
};
|
||||||
|
|
||||||
|
sourceRoot = ".";
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
install -m755 -D zls $out/bin/zls
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
homepage = "https://github.com/zigtools/zls/releases";
|
||||||
|
description = "Zig language server";
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
# I still lack stuff here!
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
437
src/deck.zig
Normal file
437
src/deck.zig
Normal file
@ -0,0 +1,437 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const assert = std.debug.assert;
|
||||||
|
|
||||||
|
pub const DeckError = error{
|
||||||
|
DuplicateDiscard,
|
||||||
|
Overflow,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Card = struct { suite: Suit, faceValue: Face };
|
||||||
|
|
||||||
|
pub const Suit = enum(u4) { Diamonds = 0, Clubs = 1, Hearts = 2, Spades = 3 };
|
||||||
|
|
||||||
|
pub const Face = enum(u4) {
|
||||||
|
Two = 2,
|
||||||
|
Three = 3,
|
||||||
|
Four = 4,
|
||||||
|
Five = 5,
|
||||||
|
Six = 6,
|
||||||
|
Seven = 7,
|
||||||
|
Eight = 8,
|
||||||
|
Nine = 9,
|
||||||
|
Ten = 10,
|
||||||
|
Jack = 11,
|
||||||
|
Queen = 12,
|
||||||
|
King = 13,
|
||||||
|
Ace = 14,
|
||||||
|
};
|
||||||
|
|
||||||
|
comptime {
|
||||||
|
if (@typeInfo(Suit).Enum.fields.len != 4) {
|
||||||
|
@compileError("Only four suites allowed");
|
||||||
|
}
|
||||||
|
if (@typeInfo(Face).Enum.fields.len != 13) {
|
||||||
|
@compileError("Only 13 face cards permitted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUM_SUITES = @typeInfo(Suit).Enum.fields.len;
|
||||||
|
const NUM_CARDS_IN_SUITE = @typeInfo(Face).Enum.fields.len;
|
||||||
|
const MAX_CARDS = NUM_SUITES * NUM_CARDS_IN_SUITE;
|
||||||
|
const CARD_STRUCT_SIZE = @sizeOf(Card) / 2;
|
||||||
|
|
||||||
|
/// A Bounded Array with a fixed size to fit 52 `Card` structs inside it.
|
||||||
|
/// Requires no allocations because of the fixed max size known at compile time.
|
||||||
|
|
||||||
|
// pub fn printCards(cards: CardSlice, options: bufPrintCardOptions) void {
|
||||||
|
// std.debug.print("Deck with {d} cards\tBuf len: {d}\n", .{ cards.len, cards.buffer.len });
|
||||||
|
// std.debug.print("--- Bottom of Deck ---\n", .{});
|
||||||
|
// var buf: [18]u8 = undefined;
|
||||||
|
// for (0..cards.len) |cardIdx| {
|
||||||
|
// const card = cards.get(cardIdx);
|
||||||
|
// std.debug.print(" - {d}: {s}\n", .{ cardIdx, bufPrintCard(card, &buf, options) });
|
||||||
|
// }
|
||||||
|
// std.debug.print("--- Top of Deck ---\n", .{});
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn printDeck(deck: Deck, options: bufPrintCardOptions) void {
|
||||||
|
std.debug.print("Deck with {d} cards\n", .{deck.num_cards});
|
||||||
|
std.debug.print("--- Bottom of Deck ---\n", .{});
|
||||||
|
var buf: [18]u8 = undefined;
|
||||||
|
for (0..deck.num_cards) |cardIdx| {
|
||||||
|
std.debug.print(" - {d}: {s}\n", .{ cardIdx, bufPrintCard(deck.cards[cardIdx], &buf, options) });
|
||||||
|
}
|
||||||
|
std.debug.print("--- Top of Deck ---\n", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const bufPrintCardOptions = struct {
|
||||||
|
use_icon: bool = true,
|
||||||
|
};
|
||||||
|
/// Returns a string representation of the card in the `buffer`
|
||||||
|
pub fn bufPrintCard(card: Card, buffer: []u8, options: bufPrintCardOptions) []const u8 {
|
||||||
|
// std.debug.print("\n\t{any}", .{card});
|
||||||
|
const fv = @tagName(card.faceValue);
|
||||||
|
const icon = if (options.use_icon) suiteToIcon(card.suite) else @tagName(card.suite);
|
||||||
|
return std.fmt.bufPrint(buffer, "{s} of {s}", .{ fv, icon }) catch return "*err printing card";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suiteToIcon(s: Suit) []const u8 {
|
||||||
|
return switch (s) {
|
||||||
|
.Spades => "",
|
||||||
|
.Hearts => "",
|
||||||
|
.Clubs => "",
|
||||||
|
.Diamonds => "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const Deck = struct {
|
||||||
|
num_cards: u8 = 0,
|
||||||
|
num_discards: u8 = 0,
|
||||||
|
cb1: [MAX_CARDS]Card = [_]Card{Card{ .suite = .Diamonds, .faceValue = .Two }} ** MAX_CARDS,
|
||||||
|
cb2: [MAX_CARDS]Card = [_]Card{Card{ .suite = .Diamonds, .faceValue = .Two }} ** MAX_CARDS,
|
||||||
|
cards: []Card = undefined,
|
||||||
|
discard_pile: []Card = undefined,
|
||||||
|
|
||||||
|
pub fn init(self: *Deck) DeckError!void {
|
||||||
|
// var cardBuf = [_]Card{Card{ .suite = .Diamonds, .faceValue = .Two }} ** MAX_CARDS;
|
||||||
|
self.cards = &self.cb1;
|
||||||
|
// var discardBuf = [_]Card{Card{ .suite = .Diamonds, .faceValue = .Two }} ** MAX_CARDS;
|
||||||
|
self.discard_pile = &self.cb2;
|
||||||
|
// Construct the deck
|
||||||
|
for (0..NUM_SUITES) |suiteIdx| {
|
||||||
|
const suite: Suit = @enumFromInt(suiteIdx);
|
||||||
|
for (0..NUM_CARDS_IN_SUITE) |f| {
|
||||||
|
const faceIdx = NUM_CARDS_IN_SUITE - f + 1;
|
||||||
|
const face: Face = @enumFromInt(faceIdx);
|
||||||
|
self.cards[self.num_cards] = Card{ .suite = suite, .faceValue = face };
|
||||||
|
self.num_cards += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(self.num_cards == MAX_CARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deal(self: *Deck) ?Card {
|
||||||
|
if (self.num_cards == 0) return null;
|
||||||
|
self.num_cards -= 1;
|
||||||
|
return self.cards[self.num_cards];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn peak(self: *Deck) ?Card {
|
||||||
|
if (self.num_cards == 0) return null;
|
||||||
|
return self.cards[self.num_cards - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn discard(self: *Deck, card: Card) DeckError!void {
|
||||||
|
// Check a duplicate isnt introduced
|
||||||
|
for (0..self.num_discards) |discardIdx| {
|
||||||
|
if (std.meta.eql(self.discard_pile[discardIdx], card)) {
|
||||||
|
return DeckError.DuplicateDiscard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.discard_pile[self.num_discards] = card;
|
||||||
|
self.num_discards += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rebuild(self: *Deck) DeckError!void {
|
||||||
|
const newTotal = self.num_cards + self.num_discards;
|
||||||
|
assert(newTotal <= MAX_CARDS);
|
||||||
|
|
||||||
|
for (0..self.num_discards) |disIdx| {
|
||||||
|
self.cards[self.num_cards + disIdx];
|
||||||
|
self.num_cards += 1;
|
||||||
|
}
|
||||||
|
self.num_discards = 0;
|
||||||
|
assert(self.num_cards == newTotal);
|
||||||
|
|
||||||
|
try sort(self.cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn order_deck(self: *Deck) DeckError!void {
|
||||||
|
try sort(self.cards);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sort(cards: []Card, numCards: u8) DeckError!void {
|
||||||
|
if (cards.len <= 1) return;
|
||||||
|
var cb = [MAX_CARDS]?Card{null} ** MAX_CARDS;
|
||||||
|
var sortedGapsDeck: []?Card = cb[0..MAX_CARDS];
|
||||||
|
|
||||||
|
for (cards) |card| {
|
||||||
|
const cardIdx = faceValueIdx(card.faceValue) + suiteValueIdx(card.suite);
|
||||||
|
sortedGapsDeck[cardIdx] = card;
|
||||||
|
}
|
||||||
|
var cb2: [MAX_CARDS]Card = undefined;
|
||||||
|
var sortedGaplessDeck = cb2[0..numCards];
|
||||||
|
var glIdx: u8 = 0;
|
||||||
|
for (0..sortedGapsDeck.len) |gIdx| {
|
||||||
|
if (sortedGapsDeck[gIdx] != null) {
|
||||||
|
sortedGaplessDeck[glIdx] = sortedGapsDeck[gIdx];
|
||||||
|
glIdx += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var newCards = []Card.init(0) catch return DeckError.Overflow;
|
||||||
|
for (sortedGaplessDeck) |card| {
|
||||||
|
newCards.append(card) catch return DeckError.Overflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cloneDeck(deck: *[]Card) DeckError![]Card {
|
||||||
|
var newDeck = []Card.init(0) catch return DeckError.Overflow;
|
||||||
|
|
||||||
|
for (deck.buffer) |card| {
|
||||||
|
newDeck.append(card) catch return DeckError.Overflow;
|
||||||
|
}
|
||||||
|
return newDeck;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.cloneDeck" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
const clonedCards = try Deck.cloneDeck(&deck.cards);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(deck.cards.len, clonedCards.len);
|
||||||
|
for (deck.cards, 0..) |card, i| {
|
||||||
|
try std.testing.expectEqual(card, clonedCards[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn faceValueIdx(f: Face) u8 {
|
||||||
|
const negIdx: i16 = @intCast(@intFromEnum(f));
|
||||||
|
return @intCast(@abs(negIdx - 14));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.faceValueIdx" {
|
||||||
|
try std.testing.expectEqual(12, faceValueIdx(Face.Two));
|
||||||
|
try std.testing.expectEqual(1, faceValueIdx(Face.King));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suiteValueIdx(s: Suit) u8 {
|
||||||
|
const cards: u8 = @intCast(NUM_CARDS_IN_SUITE);
|
||||||
|
return @intFromEnum(s) * cards;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn mergeSplit(a: *BoundedArrayOfCards, b: *BoundedArrayOfCards, iBegin: u8, iEnd: u8) void {
|
||||||
|
// if (iEnd - iBegin <= 1) return;
|
||||||
|
// const iMiddle: u8 = (iBegin + iEnd) / 2;
|
||||||
|
// mergeSplit(a, b, iBegin, iMiddle);
|
||||||
|
// mergeSplit(a, b, iMiddle, iEnd);
|
||||||
|
// merge(b, a, iBegin, iMiddle, iEnd);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /// Merges two arrays of cards, deck `a` is merged into deck `b`
|
||||||
|
// fn merge(a: *BoundedArrayOfCards, b: *BoundedArrayOfCards, iBegin: u8, iMiddle: u8, iEnd: u8) void {
|
||||||
|
// var i = iBegin;
|
||||||
|
// var j = iMiddle;
|
||||||
|
// for (iBegin..iEnd) |k| {
|
||||||
|
// if (i < iMiddle and (j >= iEnd or gt(a.get(j), a.get(i)))) {
|
||||||
|
// b.set(k, a.get(i));
|
||||||
|
// i = i + 1;
|
||||||
|
// } else {
|
||||||
|
// b.set(k, a.get(j));
|
||||||
|
// j = j + 1;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// Greater than comparison function for two cards
|
||||||
|
/// A card is considered greater, if it is found closer
|
||||||
|
/// to the top of the deck when set to the default sort order
|
||||||
|
///
|
||||||
|
/// Default Order from Top to Bottom is:
|
||||||
|
/// Suites: Spades > Hearts > Clubs > Diamonds
|
||||||
|
/// FaceValue: 2 > 10 > Jack > Queen > King > Ace
|
||||||
|
/// 2 of Spades is on top, Ace of Diamonds is the bottom card
|
||||||
|
fn gt(l: Card, r: Card) bool {
|
||||||
|
const lSuite: u4 = @intFromEnum(l.suite);
|
||||||
|
const lFace: u4 = @intFromEnum(l.faceValue);
|
||||||
|
const rSuite: u4 = @intFromEnum(r.suite);
|
||||||
|
const rFace: u4 = @intFromEnum(r.faceValue);
|
||||||
|
|
||||||
|
if (lSuite != rSuite) {
|
||||||
|
return lSuite > rSuite;
|
||||||
|
}
|
||||||
|
// reverse comparison for face value, because 3 (3) is > ace (14)
|
||||||
|
return lFace < rFace;
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.gt" {
|
||||||
|
const twoHearts = Card{ .suite = .Hearts, .faceValue = .Two };
|
||||||
|
const kingSpades = Card{ .suite = .Spades, .faceValue = .King };
|
||||||
|
const aceSpades = Card{ .suite = .Spades, .faceValue = .Ace };
|
||||||
|
|
||||||
|
try std.testing.expect(Deck.gt(kingSpades, twoHearts));
|
||||||
|
try std.testing.expect(Deck.gt(kingSpades, aceSpades));
|
||||||
|
try std.testing.expect(!Deck.gt(aceSpades, aceSpades));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "Deck.init" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(MAX_CARDS, deck.num_cards);
|
||||||
|
try std.testing.expectEqual(Card{ .suite = .Diamonds, .faceValue = .Ace }, deck.cards[0]);
|
||||||
|
try std.testing.expectEqual(Card{ .suite = .Spades, .faceValue = .Two }, deck.cards[51]);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.deal" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(deck.deal(), Card{ .suite = .Spades, .faceValue = .Two });
|
||||||
|
try std.testing.expectEqual(51, deck.num_cards);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(deck.deal(), Card{ .suite = .Spades, .faceValue = .Three });
|
||||||
|
try std.testing.expectEqual(50, deck.num_cards);
|
||||||
|
|
||||||
|
for (0..50) |_| {
|
||||||
|
_ = deck.deal();
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.testing.expectEqual(0, deck.num_cards);
|
||||||
|
try std.testing.expectEqual(null, deck.deal());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.peak" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(Card{ .suite = .Spades, .faceValue = .Two }, deck.peak());
|
||||||
|
try std.testing.expectEqual(MAX_CARDS, deck.num_cards);
|
||||||
|
|
||||||
|
// Deal out 20 cards
|
||||||
|
for (0..20) |_| {
|
||||||
|
_ = deck.deal();
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.testing.expectEqual(deck.peak(), Card{ .suite = .Hearts, .faceValue = .Nine });
|
||||||
|
try std.testing.expectEqual(deck.num_cards, 32);
|
||||||
|
|
||||||
|
// Try to peak empty deck
|
||||||
|
for (0..32) |_| {
|
||||||
|
_ = deck.deal();
|
||||||
|
}
|
||||||
|
try std.testing.expectEqual(null, deck.peak());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.discard" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
|
||||||
|
const twoSpadesCard = deck.deal();
|
||||||
|
try std.testing.expect(twoSpadesCard != null);
|
||||||
|
try std.testing.expect(twoSpadesCard.?.suite == .Spades);
|
||||||
|
try std.testing.expect(twoSpadesCard.?.faceValue == .Two);
|
||||||
|
|
||||||
|
// Deal out 13 cards
|
||||||
|
for (0..12) |_| {
|
||||||
|
_ = deck.deal();
|
||||||
|
}
|
||||||
|
const twoHeartsCard = deck.deal();
|
||||||
|
try std.testing.expect(twoHeartsCard != null);
|
||||||
|
try std.testing.expect(twoHeartsCard.?.suite == .Hearts);
|
||||||
|
try std.testing.expect(twoHeartsCard.?.faceValue == .Two);
|
||||||
|
|
||||||
|
// Discard two cards
|
||||||
|
try deck.discard(twoSpadesCard.?);
|
||||||
|
try deck.discard(twoHeartsCard.?);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(2, deck.num_discards);
|
||||||
|
try std.testing.expectEqual(twoSpadesCard.?, deck.discard_pile[0]);
|
||||||
|
|
||||||
|
// Fails to add duplicate card
|
||||||
|
try std.testing.expectError(DeckError.DuplicateDiscard, deck.discard(twoSpadesCard.?));
|
||||||
|
}
|
||||||
|
|
||||||
|
test "Deck.rebuild" {
|
||||||
|
var deck = Deck{};
|
||||||
|
try deck.init();
|
||||||
|
var buf1 = [_]?Card{null} ** 5;
|
||||||
|
var buf2 = [_]?Card{null} ** 5;
|
||||||
|
var buf3 = [_]?Card{null} ** 5;
|
||||||
|
var playerOneHand: []?Card = &(buf1);
|
||||||
|
var playerTwoHand: []?Card = &(buf2);
|
||||||
|
var playerThreeHand: []?Card = &(buf3);
|
||||||
|
|
||||||
|
// Alternate dealing player one and two hands
|
||||||
|
for (0..10) |i| {
|
||||||
|
if (i % 2 == 0) try playerOneHand.append(deck.deal().?) else try playerTwoHand.append(deck.deal().?);
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.testing.expectEqual(5, playerOneHand.len);
|
||||||
|
try std.testing.expectEqual(5, playerTwoHand.len);
|
||||||
|
|
||||||
|
// Discard top 10 cards from deck
|
||||||
|
for (0..10) |_| {
|
||||||
|
try deck.discard(deck.deal().?);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0..5) |i| {
|
||||||
|
playerThreeHand[i] = deck.deal().?;
|
||||||
|
}
|
||||||
|
|
||||||
|
try std.testing.expectEqual(5, playerThreeHand.len);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(10, deck.discard_pile.len);
|
||||||
|
try std.testing.expectEqual(27, deck.cards.len);
|
||||||
|
|
||||||
|
for (0..15) |i| {
|
||||||
|
switch (i % 3) {
|
||||||
|
0 => try deck.discard(playerThreeHand.pop()),
|
||||||
|
1 => try deck.discard(playerOneHand.pop()),
|
||||||
|
2 => try deck.discard(playerTwoHand.pop()),
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try std.testing.expectEqual(25, deck.discard_pile.len);
|
||||||
|
|
||||||
|
try deck.rebuild();
|
||||||
|
|
||||||
|
try std.testing.expectEqual(0, deck.discard_pile.len);
|
||||||
|
try std.testing.expectEqual(52, deck.cards.len);
|
||||||
|
|
||||||
|
try std.testing.expectEqual(Card{ .suite = .Diamonds, .faceValue = .Ace }, deck.cards[0]);
|
||||||
|
try std.testing.expectEqual(Card{ .suite = .Spades, .faceValue = .Two }, deck.cards[51]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test "Deck.rebuild with missing cards" {
|
||||||
|
// var deck = Deck{};
|
||||||
|
// try deck.init();
|
||||||
|
// // var playerOneHand = []Card.init(0) catch unreachable;
|
||||||
|
// // var playerTwoHand = []Card.init(0) catch unreachable;
|
||||||
|
|
||||||
|
// // Alternate dealing player one discarding cards
|
||||||
|
// for (0..52) |i| {
|
||||||
|
// switch (i % 4) {
|
||||||
|
// 0 => try playerTwoHand.append(deck.deal().?),
|
||||||
|
// 1 => try deck.discard(deck.deal().?),
|
||||||
|
// 2 => try playerOneHand.append(deck.deal().?),
|
||||||
|
// 3 => {
|
||||||
|
// _ = deck.deal();
|
||||||
|
// },
|
||||||
|
// else => unreachable,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// try std.testing.expectEqual(13, playerOneHand.len);
|
||||||
|
// try std.testing.expectEqual(13, playerTwoHand.len);
|
||||||
|
// try std.testing.expectEqual(13, deck.discard_pile.len);
|
||||||
|
// try std.testing.expectEqual(0, deck.cards.len);
|
||||||
|
|
||||||
|
// for (0..26) |i| {
|
||||||
|
// switch (i % 2) {
|
||||||
|
// 0 => try deck.discard(playerOneHand.pop()),
|
||||||
|
// 1 => try deck.discard(playerTwoHand.pop()),
|
||||||
|
// else => unreachable,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// try std.testing.expectEqual(39, deck.discard_pile.len);
|
||||||
|
|
||||||
|
// try deck.rebuild();
|
||||||
|
|
||||||
|
// try std.testing.expectEqual(0, deck.discard_pile.len);
|
||||||
|
// try std.testing.expectEqual(39, deck.cards.len);
|
||||||
|
|
||||||
|
// try std.testing.expectEqual(Card{ .suite = .Diamonds, .faceValue = .Ace }, deck.cards[0]);
|
||||||
|
// try std.testing.expectEqual(Card{ .suite = .Spades, .faceValue = .Two }, deck.cards[38]);
|
||||||
|
// }
|
6
src/main.zig
Normal file
6
src/main.zig
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const deck = @import("deck.zig");
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
std.debug.print("It works!\n", .{});
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user