Skip to content

Commit

Permalink
feat: Major syntax change
Browse files Browse the repository at this point in the history
- Type variable -> var variable: Type
- Comments are now `//` and docblocks `///`
- Xor operator is now `|`
- Namespaces are now any number of `\` separated identifiers
- Function type use `fun` keyword
- Function type error list must be in parenthesis when more than one error type

closes #310
  • Loading branch information
giann committed Sep 18, 2024
1 parent 4884fdb commit 072c202
Show file tree
Hide file tree
Showing 156 changed files with 2,817 additions and 2,646 deletions.
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Unreleased

## Major syntax changes
- Types are now specified *after* the identifier + `:`. This includes:
- Variable declarations
- Function argument definitions
- Catch clauses
- `if (...: type as ...)`
- Arrow function use `=>` instead of `->`
- Comments and docblocks must now be prefixed by `//` and `///` instead of `|` and `||`
- Bitwise or operator is `|` instead of `\`
- Function type now use `fun` keyword instead of `Function`
- If a function type has multiple error types, they must be put them in parenthesis
- Namespace can now be multiple `\` separated identifiers
- Qualified name now use `\` separator instead of `.`

## Added
- Object can have `const` properties (https://github.com/buzz-language/buzz/issues/13). A object with only `const` properties is considered itself `const`. Although we don't do anything yet with that concept. https://github.com/buzz-language/buzz/issues/114 is the objective but it requires being able to build objects and instances at compile time which is not yet possible.
- `rg.subsetOf`, `rg.intersect`, `rg.union`
Expand All @@ -22,7 +36,6 @@ list[?10] == null;

## Modified
- Enum can now have `rg`, `ud`, `void`, `pat` has value type
- `var` can be used for key and/or value type in `for` and `foreach` loop
- Anonymous object can also omit property name when initial value is a named variable

## Fixed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ A small/lightweight statically typed scripting language written in Zig

## How to build and install

_Latest zig version supported: 0.14.0-dev.121+ab4c461b7_
_Latest zig version supported: 0.14.0-dev.121+ab4c461b7 (https://github.com/ziglang/zig/issues/20847 prevents us from upgrading further)_

### Requirements
- Since this is built with Zig, you should be able to build buzz on a wide variety of architectures even though this has only been tested on x86/M1.
Expand Down
Binary file modified example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
187 changes: 93 additions & 94 deletions examples/2048.buzz
Original file line number Diff line number Diff line change
@@ -1,63 +1,62 @@
| The rules are that on each turn the player must choose a direction (up, down,
| left or right) and all tiles move as far as possible in that direction, some
| more than others. Two adjacent tiles (in that direction only) with matching
| numbers combine into one bearing the sum of those numbers. A move is valid
| when at least one tile can be moved, if only by combination. A new tile with
| the value of 2 is spawned at the end of each turn at a randomly chosen empty
| square, if there is one. To win the player must create a tile with the number
| 2048. The player loses if no valid moves are possible.
|
| The name comes from the popular open-src implementation of this game
| mechanic: https://gabrielecirulli.github.io/2048/.
|
| Requirements:
| - "Non-greedy" movement. The tiles that were created by combining other tiles
| should not be combined again during the same turn (move). That is to say that
| moving the tile row of [2][2][2][2] to the right should result in
| ......[4][4] and not .........[8]
| - "Move direction priority". If more than one variant of combining is
| possible, move direction shows one that will take effect. For example, moving
| the tile row of ...[2][2][2] to the right should result in ......[2][4] and
| not ......[4][2]
| - Adding a new tile on a blank space. Most of the time new "2" is to be added
| and occasionally (10% of the time) - "4"
| - Check for valid moves. The player shouldn't be able to skip their turn by
| trying a move that doesn't change the board.
| - Win condition.
| - Lose condition.

// The rules are that on each turn the player must choose a direction (up, down,
// left or right) and all tiles move as far as possible in that direction, some
// more than others. Two adjacent tiles (in that direction only) with matching
// numbers combine into one bearing the sum of those numbers. A move is valid
// when at least one tile can be moved, if only by combination. A new tile with
// the value of 2 is spawned at the end of each turn at a randomly chosen empty
// square, if there is one. To win the player must create a tile with the number
// 2048. The player loses if no valid moves are possible.
//
// The name comes from the popular open-src implementation of this game
// mechanic: https://gabrielecirulli.github.io/2048/.
//
// Requirements:
// - "Non-greedy" movement. The tiles that were created by combining other tiles
// should not be combined again during the same turn (move). That is to say that
// moving the tile row of [2][2][2][2] to the right should result in
// ......[4][4] and not .........[8]
// - "Move direction priority". If more than one variant of combining is
// possible, move direction shows one that will take effect. For example, moving
// the tile row of ...[2][2][2] to the right should result in ......[2][4] and
// not ......[4][2]
// - Adding a new tile on a blank space. Most of the time new "2" is to be added
// and occasionally (10% of the time) - "4"
// - Check for valid moves. The player shouldn't be able to skip their turn by
// trying a move that doesn't change the board.
// - Win condition.
// - Lose condition.
import "std";
import "io";
import "errors";
import "os";

object Pair {
int srcX,
int srcY,
int srcValue,
srcX: int,
srcY: int,
srcValue: int,

int destX,
int destY,
int destValue,
destX: int,
destY: int,
destValue: int,
}

object Game {
[[int]] board = [<[int]>],
int size = 4,
int goal = 2048,
int score = 0,
bool invalidMove = false,
board: [[int]] = [<[int]>],
size: int = 4,
goal: int = 2048,
score: int = 0,
invalidMove: bool = false,

static fun init(int size = 4, int goal = 2048) > Game {
static fun init(size: int = 4, goal: int = 2048) > Game {
const game = Game{
size,
goal
};

foreach (int x in 0..game.size) {
foreach (x in 0..game.size) {
game.board.append([<int>]);

foreach (int _ in 0..game.size) {
foreach (_ in 0..game.size) {
game.board[x].append(0);
}
}
Expand All @@ -68,34 +67,34 @@ object Game {
fun reset() > void {
this.score = 0;

foreach (int x in 0..this.size) {
foreach (int y in 0..this.size) {
foreach (x in 0..this.size) {
foreach (y in 0..this.size) {
this.board[x][y] = 0;
}
}
}

| Add new tile at a random free sp
// Add new tile at a random free sp
fun addNewTile() > void {
const free = this.freeSpots();

if (free.len() == 0) {
return;
}

const spot = free[std.random(min: 0, max: free.len() - 1)];
this.board[spot.x][spot.y] = if (std.random(min: 0, max: 9) == 0)
const spot = free[std\random(min: 0, max: free.len() - 1)];
this.board[spot.x][spot.y] = if (std\random(min: 0, max: 9) == 0)
4
else
2;
}

| Return list of free spots
fun freeSpots() > [obj{ int x, int y }] {
[obj{ int x, int y}] spots = [];
// Return list of free spots
fun freeSpots() > [obj{ x: int, y: int }] {
var spots = [<obj{ x: int, y: int }>];

foreach (int x, [int] row in this.board) {
foreach (int y, int value in row) {
foreach (x, row in this.board) {
foreach (y, value in row) {
if (value == 0) {
spots.append(.{ x = x, y = y });
}
Expand All @@ -105,60 +104,60 @@ object Game {
return spots;
}

| Render board
// Render board
fun render() > void {
try {
io.stdout.write("\27\91H\27\91J╭{"─".repeat(this.size * 7)}╮\n");
io\stdout.write("\27\91H\27\91J╭{"─".repeat(this.size * 7)}╮\n");

foreach (int x, [int] row in this.board) {
io.stdout.write("│ ");
foreach (x, row in this.board) {
io\stdout.write("│ ");

foreach (int y, int value in row) {
foreach (y, value in row) {
if (value != 0) {
| TODO: we need formatting to be available in buzz
// TODO: we need formatting to be available in buzz
if (value < 10) {
io.stdout.write(" {value} ");
io\stdout.write(" {value} ");
} else if (value < 100) {
io.stdout.write(" {value} ");
io\stdout.write(" {value} ");
} else if (value < 1000) {
io.stdout.write(" {value} ");
io\stdout.write(" {value} ");
} else {
io.stdout.write(" {value} ");
io\stdout.write(" {value} ");
}
} else {
io.stdout.write(" ");
io\stdout.write(" ");
}

if (y < this.size - 1) {
io.stdout.write("│");
io\stdout.write("│");
}
}

io.stdout.write("│\n");
io\stdout.write("│\n");

if (x < this.size - 1) {
io.stdout.write("├{"─".repeat(this.size * 7)}┤\n");
io\stdout.write("├{"─".repeat(this.size * 7)}┤\n");
}
}

io.stdout.write("╰{"─".repeat(this.size * 7)}╯\n");
io\stdout.write("╰{"─".repeat(this.size * 7)}╯\n");

io.stdout.write("Score: {this.score} / {this.goal}\n");
io\stdout.write("Score: {this.score} / {this.goal}\n");

if (this.invalidMove) {
this.invalidMove = false;
io.stdout.write("Invalid move, try again\n");
io\stdout.write("Invalid move, try again\n");
}
} catch {
io.stderr.write("Error while rendering board\n") catch void;
io\stderr.write("Error while rendering board\n") catch void;
}
}

fun iterate(bool axis, int direction) > [Pair] {
[Pair] result = [];
fun iterate(axis: bool, direction: int) > [Pair] {
var result = [<Pair>];

foreach (int x in 0..this.size) {
foreach (int y in 0..this.size) {
foreach (x in 0..this.size) {
foreach (y in 0..this.size) {
const pair = Pair{
srcX = x,
srcY = y,
Expand Down Expand Up @@ -186,13 +185,13 @@ object Game {
return result;
}

fun shift(bool axis, int direction) > int {
fun shift(axis: bool, direction: int) > int {
var totalDone = 0;

foreach (int _ in 0..this.size) {
foreach (_ in 0..this.size) {
var done = 0;

foreach (Pair pair in this.iterate(axis, direction)) {
foreach (pair in this.iterate(axis, direction)) {
if (pair.destValue == 0 and pair.srcValue != 0) {
this.board[pair.destX][pair.destY] = pair.srcValue;
this.board[pair.srcX][pair.srcY] = 0;
Expand All @@ -210,10 +209,10 @@ object Game {
return totalDone;
}

fun merge(bool axis, int direction) > int {
fun merge(axis: bool, direction: int) > int {
var done = 0;

foreach (Pair pair in this.iterate(axis, direction)) {
foreach (pair in this.iterate(axis, direction)) {
if (pair.destValue != 0 and pair.destValue == pair.srcValue) {
this.board[pair.srcX][pair.srcY] = this.board[pair.srcX][pair.srcY] + pair.destValue;
this.board[pair.destX][pair.destY] = 0;
Expand All @@ -227,11 +226,11 @@ object Game {
}

fun move() > bool {
io.stdout.write("(wasd to move, q to quit, r to restart)\n> ") catch void;
const command = io.stdin.read(1) catch void;
io\stdout.write("(wasd to move, q to quit, r to restart)\n> ") catch void;
const command = io\stdin.read(1) catch void;

if (command == "q") {
os.exit(0);
os\exit(0);
}

if (command == "r") {
Expand All @@ -254,16 +253,16 @@ object Game {
direction = 1;
axis = true;
} else {
io.stderr.write("Invalid command\n") catch void;
io\stderr.write("Invalid command\n") catch void;
return false;
}

var done = 0;
| Shift in that direction
// Shift in that direction
done = done + this.shift(axis, direction);
| Merge equal numbers in opposite direction
// Merge equal numbers in opposite direction
done = done + this.merge(axis, direction: direction * -1);
| Shift merged numebrs in that direction
// Shift merged numebrs in that direction
done = done + this.shift(axis, direction);

return done > 0;
Expand All @@ -276,25 +275,25 @@ object Game {
this.addNewTile();
} else {
if (this.freeSpots().len() == 0) {
io.stderr.write("No move left, you lost.\n") catch void;
os.exit(0);
io\stderr.write("No move left, you lost.\n") catch void;
os\exit(0);
} else {
this.invalidMove = true;
}
}

| Did we win?
foreach (int x in 0..this.size) {
foreach (int y in 0..this.size) {
// Did we win?
foreach (x in 0..this.size) {
foreach (y in 0..this.size) {
if (this.board[x][y] == this.goal) {
io.stdout.write("YOU WON!\n") catch void;
io\stdout.write("YOU WON!\n") catch void;
}
}
}
}
}

fun main([str] _) > void {
fun main(_: [str]) > void {
const game = Game.init();

game.addNewTile();
Expand All @@ -303,4 +302,4 @@ fun main([str] _) > void {
while (true) {
game.step();
}
}
}
Loading

0 comments on commit 072c202

Please sign in to comment.