Typed u8 square for scrabble game

For my scrabble game I am contemplating an enum instead of a raw u8 value for my board squares. It will become better readable in many cases and the enum can have functions, which is convenient.
Would this down here be the way to go? Are there other or better ways to do it?
Should I inline things for speed or does the compiler crack that for me?
Would there be any runtime performance cost using this instead of a raw u8?

pub const Square = enum(u8)
{
    a1, b1, c1, d1, e1, f1, g1, h1, i1, j1, k1, l1, m1, n1, o1,
    a2, b2, c2, d2, e2, f2, g2, h2, i2, j2, k2, l2, m2, n2, o2,
    a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3,
    a4, b4, c4, d4, e4, f4, g4, h4, i4, j4, k4, l4, m4, n4, o4,
    a5, b5, c5, d5, e5, f5, g5, h5, i5, j5, k5, l5, m5, n5, o5,
    a6, b6, c6, d6, e6, f6, g6, h6, i6, j6, k6, l6, m6, n6, o6,
    a7, b7, c7, d7, e7, f7, g7, h7, i7, j7, k7, l7, m7, n7, o7,
    a8, b8, c8, d8, e8, f8, g8, h8, i8, j8, k8, l8, m8, n8, o8,
    a9, b9, c9, d9, e9, f9, g9, h9, i9, j9, k9, l9, m9, n9, o9,
    a10, b10, c10, d10, e10, f10, g10, h10, i10, j10, k10, l10, m10, n10, o10,
    a11, b11, c11, d11, e11, f11, g11, h11, i11, j11, k11, l11, m11, n11, o11,
    a12, b12, c12, d12, e12, f12, g12, h12, i12, j12, k12, l12, m12, n12, o12,
    a13, b13, c13, d13, e13, f13, g13, h13, i13, j13, k13, l13, m13, n13, o13,
    a14, b14, c14, d14, e14, f14, g14, h14, i14, j14, k14, l14, m14, n14, o14,
    a15, b15, c15, d15, e15, f15, g15, h15, i15, j15, k15, l15, m15, n15, o15,

    pub fn init(val: u8) Square {
        return @enumFromInt(val);
    }

    pub fn value(self: Square) u8 {
        return @intFromEnum(self);
    }

    pub fn x(self: Square) u4 {
        return @truncate(self.value() % 15);
    }

    pub fn y(self: Square) u4 {
        return @truncate(self.value() / 15);
    }

    pub fn name(self: Square) []const u8 {
        return @tagName(self);
    }
};

Are these field coords like in chess? Why not go with regular integer x/y coordinates and only use the a3 (etc…) notation for the UI?

e.g. pub const Square = struct { x: u4, y: u4 };

That way it’s trivial to look at the neighbouring coordinates (just add/subtract from x/y values).

1 Like

Yes these are coordinates like in chess.
I work with raw indices (like - good - chessprograms do as well).
The coordinates I monstly need for output and the names are a conventient way to get the name

it could also be

const Square = enum(u8)
{
    _, // no names
}

I think it’s a good idea. It shouldn’t have performance penalty. It might result in a performance boost, because you’re telling the compiler that values above 225 are invalid.
It’s usually better not to inline anything, unless you have observed that compiler actually making a bad decision.

Ok thank you. I will go for it.
Yes I use only inline if comptimes involved and the compiler yells about it during inline loops (I believe). I think the compiler is reasonably smart about it.

Hm… too much shit because of indexing, incrementing, calculations, casts.
If the language ever will support easier access I will re-contemplate.

Ok still learning Zig… I was thinking about a tagged union to avoid the hassle of above mentioned stuff.

pub const Tag = enum { square, byte };
pub const Tagged = union(Tag)
{
square: Square2,
byte: u8,
};
But the sizeof Tagged is 2. That should definitely be one in my case.
Is there some other trick?
I would like to avoid writing all kinds of functions for Square2 (my test name).
Or is it completely and utterly unavoidable?

var q: Square = .h8;
q += 1; // would be so nice :)
    pub fn inc(self: *Square2) void {
        self.* = .init(self.val() + 1);
    }

    pub fn dec(self: *Square2) void {
        self.* = .init(self.val() + 1);
    }

    pub fn plus(self: Square2, comptime delta: u8) Square2  {
        return .init(self.val() + delta);
    }

    pub fn minus(self: Square2, comptime delta: u8) Square2  {
        return .init(self.val() - delta);
    }

The tag of Tagged will take up an extra byte. I think you could use an extern union (or packed union?) here which supports type punning and doesn’t store a tag. Then you could do:

var q: Tagged = .{ .square = .h8 };
q.byte += 1;

But you would have to be very careful to never accidentaly produce a value that isn’t in your Square enum I think that would result in UB or at least panic as soon as you try to access .square.

Or just use raw u8 ints internally and only use the Square enum for initialization with @intFromEnum() / UI with @enumFromInt() or even std.enums.fromInt() to catch calculation errors as flooh has already kind of suggested then you could use all the math operators you want without any workarounds.

Thanks. I did not think about that one.
The extern union seems nice and maybe give both of best worlds.
I’ll check if it works nicely…