Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -675,15 +675,15 @@
"name": "Queen Attack",
"uuid": "6eb542c3-c14f-430a-81ce-ba9f2c0551c2",
"practices": [
"asserting",
"control-flow",
"error-sets",
"importing",
"methods",
"structs"
],
"prerequisites": [
"asserting",
"control-flow",
"error-sets",
"importing",
"methods",
"structs"
Expand Down
36 changes: 14 additions & 22 deletions exercises/practice/queen-attack/.meta/example.zig
Original file line number Diff line number Diff line change
@@ -1,30 +1,22 @@
const std = @import("std");

pub const QueenError = error{
InitializationFailure,
InvalidAttack,
};

pub const Queen = struct {
row: i8,
col: i8,
row: u3,
col: u3,

pub fn init(row: i8, col: i8) QueenError!Queen {
if (row < 0 or row > 7 or col < 0 or col > 7) {
return QueenError.InitializationFailure;
}
return .{
.row = row,
.col = col,
};
pub fn init(row: u3, col: u3) Queen {
return .{ .row = row, .col = col };
}

pub fn canAttack(self: Queen, other: Queen) QueenError!bool {
if (self.row == other.row and self.col == other.col) {
return QueenError.InvalidAttack;
}
const row_diff = self.row - other.row;
const col_diff = self.col - other.col;
return (row_diff == 0) or (col_diff == 0) or (row_diff == col_diff) or (row_diff == -col_diff);
/// Asserts that `a` and `b` are on different squares.
pub fn canAttack(a: Queen, b: Queen) bool {
std.debug.assert(a.row != b.row or a.col != b.col);
return a.row == b.row or a.col == b.col or
absDiff(u3, a.row, b.row) == absDiff(u3, a.col, b.col);
}
};

// Returns the absolute difference of `a` and `b`.
fn absDiff(comptime T: type, a: T, b: T) T {
return if (a > b) a - b else b - a;
}
4 changes: 4 additions & 0 deletions exercises/practice/queen-attack/.meta/tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,19 @@ description = "Test creation of Queens with valid and invalid positions -> queen

[4e812d5d-b974-4e38-9a6b-8e0492bfa7be]
description = "Test creation of Queens with valid and invalid positions -> queen must have positive row"
include = false

[f07b7536-b66b-4f08-beb9-4d70d891d5c8]
description = "Test creation of Queens with valid and invalid positions -> queen must have row on board"
include = false

[15a10794-36d9-4907-ae6b-e5a0d4c54ebe]
description = "Test creation of Queens with valid and invalid positions -> queen must have positive column"
include = false

[6907762d-0e8a-4c38-87fb-12f2f65f0ce4]
description = "Test creation of Queens with valid and invalid positions -> queen must have column on board"
include = false

[33ae4113-d237-42ee-bac1-e1e699c0c007]
description = "Test the ability of one queen to attack another -> cannot attack"
Expand Down
11 changes: 5 additions & 6 deletions exercises/practice/queen-attack/queen_attack.zig
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
pub const QueenError = error{
InitializationFailure,
};

pub const Queen = struct {
pub fn init(row: i8, col: i8) QueenError!Queen {
// Please implement the fields of this struct.

pub fn init(row: u3, col: u3) Queen {
_ = row;
_ = col;
@compileError("please implement the init method");
}

pub fn canAttack(self: Queen, other: Queen) QueenError!bool {
/// Asserts that `self` and `other` are on different squares.
pub fn canAttack(self: Queen, other: Queen) bool {
_ = self;
_ = other;
@compileError("please implement the canAttack method");
Expand Down
82 changes: 32 additions & 50 deletions exercises/practice/queen-attack/test_queen_attack.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,87 +2,69 @@ const std = @import("std");
const testing = std.testing;

const queen_attack = @import("queen_attack.zig");
const QueenError = queen_attack.QueenError;

test "queen has exactly two fields" {
try testing.expectEqual(2, @typeInfo(queen_attack.Queen).@"struct".field_names.len);
}

test "queen with a valid position" {
const queen = try queen_attack.Queen.init(2, 2);
// Allow the fields to have any name.
fn testCreate(row: u3, col: u3) !void {
const queen = queen_attack.Queen.init(row, col);
// Allow the fields to have any name, in any order.
const field_names = @typeInfo(@TypeOf(queen)).@"struct".field_names;
inline for (field_names) |name| {
const actual = @field(queen, name);
const expected = @as(@TypeOf(actual), 2);
try testing.expectEqual(expected, actual);
try testing.expect(actual == row or actual == col);
}
}

test "queen must have positive row" {
const queen = queen_attack.Queen.init(-2, 2);
try testing.expectError(QueenError.InitializationFailure, queen);
}

test "queen must have row on board" {
const queen = queen_attack.Queen.init(8, 4);
try testing.expectError(QueenError.InitializationFailure, queen);
}

test "queen must have positive column" {
const queen = queen_attack.Queen.init(2, -2);
try testing.expectError(QueenError.InitializationFailure, queen);
test "queen has exactly two fields" {
try testing.expectEqual(2, @typeInfo(queen_attack.Queen).@"struct".field_names.len);
}

test "queen must have column on board" {
const queen = queen_attack.Queen.init(4, 8);
try testing.expectError(QueenError.InitializationFailure, queen);
test "queen with a valid position" {
try testCreate(2, 2);
}

test "cannot attack" {
const white = try queen_attack.Queen.init(2, 4);
const black = try queen_attack.Queen.init(6, 6);
try testing.expect(!try white.canAttack(black));
const white = queen_attack.Queen.init(2, 4);
const black = queen_attack.Queen.init(6, 6);
try testing.expect(!white.canAttack(black));
}

test "can attack on same row" {
const white = try queen_attack.Queen.init(2, 4);
const black = try queen_attack.Queen.init(2, 6);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(2, 4);
const black = queen_attack.Queen.init(2, 6);
try testing.expect(white.canAttack(black));
}

test "can attack on same column" {
const white = try queen_attack.Queen.init(4, 5);
const black = try queen_attack.Queen.init(2, 5);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(4, 5);
const black = queen_attack.Queen.init(2, 5);
try testing.expect(white.canAttack(black));
}

test "can attack on first diagonal" {
const white = try queen_attack.Queen.init(2, 2);
const black = try queen_attack.Queen.init(0, 4);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(2, 2);
const black = queen_attack.Queen.init(0, 4);
try testing.expect(white.canAttack(black));
}

test "can attack on second diagonal" {
const white = try queen_attack.Queen.init(2, 2);
const black = try queen_attack.Queen.init(3, 1);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(2, 2);
const black = queen_attack.Queen.init(3, 1);
try testing.expect(white.canAttack(black));
}

test "can attack on third diagonal" {
const white = try queen_attack.Queen.init(2, 2);
const black = try queen_attack.Queen.init(1, 1);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(2, 2);
const black = queen_attack.Queen.init(1, 1);
try testing.expect(white.canAttack(black));
}

test "can attack on fourth diagonal" {
const white = try queen_attack.Queen.init(1, 7);
const black = try queen_attack.Queen.init(0, 6);
try testing.expect(try white.canAttack(black));
const white = queen_attack.Queen.init(1, 7);
const black = queen_attack.Queen.init(0, 6);
try testing.expect(white.canAttack(black));
}

test "cannot attack if falling diagonals are only the same when reflected across the longest falling diagonal" {
const white = try queen_attack.Queen.init(4, 1);
const black = try queen_attack.Queen.init(2, 5);
try testing.expect(!try white.canAttack(black));
const white = queen_attack.Queen.init(4, 1);
const black = queen_attack.Queen.init(2, 5);
try testing.expect(!white.canAttack(black));
}
41 changes: 16 additions & 25 deletions generators/exercises/queen_attack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from lib import is_error

HEADER = "const QueenError = queen_attack.QueenError;"
HEADER = """
fn testCreate(row: u3, col: u3) !void {
const queen = queen_attack.Queen.init(row, col);
// Allow the fields to have any name, in any order.
const field_names = @typeInfo(@TypeOf(queen)).@"struct".field_names;
inline for (field_names) |name| {
const actual = @field(queen, name);
try testing.expect(actual == row or actual == col);
}
}
"""


def describe(case, parent):
Expand All @@ -22,30 +30,13 @@ def gen_case(case):
return f" try testing.expectEqual({n}, @typeInfo(queen_attack.Queen).@\"struct\".field_names.len);\n"
if prop == "create":
pos = inp["queen"]["position"]
row, col = pos["row"], pos["column"]
if is_error(case["expected"]):
return (
f" const queen = queen_attack.Queen.init({row}, {col});\n"
f" try testing.expectError(QueenError.InitializationFailure, queen);\n"
)
# Valid position: allow any field names, all equal to the input value.
return (
f" const queen = try queen_attack.Queen.init({row}, {col});\n"
f" // Allow the fields to have any name.\n"
f" const field_names = @typeInfo(@TypeOf(queen)).@\"struct\".field_names;\n"
f" inline for (field_names) |name| {{\n"
f" const actual = @field(queen, name);\n"
f" const expected = @as(@TypeOf(actual), {row});\n"
f" try testing.expectEqual(expected, actual);\n"
f" }}\n"
)
return f" try testCreate({pos['row']}, {pos['column']});\n"
# canAttack
w = inp["white_queen"]["position"]
b = inp["black_queen"]["position"]
expected = case["expected"]
bang = "" if expected else "!"
bang = "" if case["expected"] else "!"
return (
f" const white = try queen_attack.Queen.init({w['row']}, {w['column']});\n"
f" const black = try queen_attack.Queen.init({b['row']}, {b['column']});\n"
f" try testing.expect({bang}try white.canAttack(black));\n"
f" const white = queen_attack.Queen.init({w['row']}, {w['column']});\n"
f" const black = queen_attack.Queen.init({b['row']}, {b['column']});\n"
f" try testing.expect({bang}white.canAttack(black));\n"
)
Loading