Trouble with comptime expressions

I am making a board game and am implementing a card system that mutates how board pieces are scored. My confusion comes from my understanding that comptime errors should only be happening if a length is unknown at compile time. In this line p.points[i] = pattern_points[i]; both p.points and pattern_points length should be known at compile time. However, I’m obviously missing something, so I’m hoping someone can point out my error. I’ve been stuck on this one for a while now.

const game = @import("./game.zig");

// Material Pattern Map
const Mp_map = struct {
    points: [10]?PatternPoint,

    pub fn new(comptime pattern_points: []PatternPoint) Mp_map {
        var p: Mp_map = undefined;
        p.points = @splat(null);
        for (0..pattern_points.len) |i| {
            p.points[i] = pattern_points[i];
        }

        return p;
    }
};
pub const BASE_CARDS = [_]Card{CottageCard};

const PatternPoint = struct {
    x: u8,
    y: u8,
    material: game.MaterialType,
    pub fn new(x: u8, y: u8, m: game.MaterialType) PatternPoint {
        return PatternPoint{ .x = x, .y = y, .material = m };
    }
};
pub const Card = struct {
    name: []const u8,
    desc: []const u8,
    kind: game.BuildingType,
    pattern: Mp_map,
    effect: fn (p: *game.Player, b: game.BoardPiece, *game.Game) void,
};

//5 default cards
const pp = PatternPoint;
var cc_pattern = [_]PatternPoint{
    pp.new(0, 1, game.MaterialType.Brick),
    pp.new(1, 1, game.MaterialType.Glass),
    pp.new(1, 0, game.MaterialType.Wheat),
};
const CottageCard: Card = Card{ .name = "cottage", .desc = "a simple residence", .kind = game.BuildingType.Cottage, .pattern = Mp_map.new(&cc_pattern), .effect = cottage_card_effect };
//Cottage Card Effect
fn cottage_card_effect(p: *game.Player, b: game.BoardPiece, _: *game.Game) void {
    if (b.fed) {
        p.points += 3;
    }
}

error:

src/card.zig:11:41: error: unable to evaluate comptime expression
            p.points[i] = pattern_points[i];
                          ~~~~~~~~~~~~~~^~~
src/card.zig:42:138: note: called at comptime from here
const CottageCard: Card = Card{ .name = "cottage", .desc = "a simple residence", .kind = game.BuildingType.Cottage, .pattern = Mp_map.new(&cc_pattern), .effect = cottage_card_effect };
                                                                                                                               ~~~~~~~~~~^~~~~~~~~~~~~
src/card.zig:42:31: note: initializer of container-level variable must be comptime-known
const CottageCard: Card = Card{ .name = "cottage", .desc = "a simple residence", .kind = game.BuildingType.Cottage, .pattern = Mp_map.new(&cc_pattern), .effect = cottage_card_effect };
                          ~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You’ve not posted the error you’re getting, but you may simply need to inline that for loop

added error for context

You should specify

comptime const CottageCard: Card = ...

I also think your for should be an inline for in Mp_map.new.

I don’t think I cant set CottageCard to comptime, It should already be due to being in global scope.

Inline for didnt change the error unfortunately.

Try this:

pub fn new(comptime pattern_points: []const PatternPoint) Mp_map {
   const p = comptime blk: {
        var p: Mp_map = undefined;
        p.points = @splat(null);
        for (0..pattern_points.len) |i| {
            p.points[i] = pattern_points[i];
        }
    };
    return p;
}

Common misconception in Zig is that you need to sprinkle comptime and inline in a bunch of places where it’s actually not needed at all. You don’t need a single comptime keyword anywhere in your example, including in your new function.

Problem is spelled out by the error message:

src/card.zig:11:41: error: unable to evaluate comptime expression
            p.points[i] = pattern_points[i];
                          ~~~~~~~~~~~~~~^~~

pattern_points is pointing to

var cc_pattern = [_]PatternPoint{

which is a global variable, meaning it can be mutated at runtime. You’re trying to read data that can change at runtime, at compile time.


By the way you don’t need this alias:

const pp = PatternPoint;

Take advantage of decl literals instead:

[_]PatternPoint{
    .new(0, 1, .Brick),
    .new(1, 1, .Glass),
    .new(1, 0, .Wheat),
}
7 Likes

I must have changed that to a var during troubleshooting another issue. This is the solution. Also thank you for showing me this use of decl literals. This will make the Card declarations more concise!

2 Likes