use package core.intrinsics.onyx
Pos :: struct { x, y: i32; }
+#match hash.to_u32 (a: Pos) => 13 * hash.to_u32(a.x) + 17 * hash.to_u32(a.y);
+#operator == (a, b: Pos) => a.x == b.x && a.y == b.y;
Board :: struct {
Cell :: enum #flags {
width, height : i32;
visible_cells : [] Cell;
true_cells : [] Cell;
+
+ mine_locations : [] Pos;
}
init_board :: (use this: ^Board, w, h: i32) {
for ^cell: visible_cells do *cell = .Covered;
for ^cell: true_cells do *cell = .Empty;
- mine_positions := array.make(Pos);
+ memory.alloc_slice(^mine_locations, mine_count);
for i: mine_count {
while true {
mx, my := random.between(0, width - 1), random.between(0, height - 1);
if get_true_cell(this, mx, my) != .Mine {
set_true_cell(this, mx, my, .Mine);
- mine_positions << .{mx, my};
+ mine_locations[i] = .{mx, my};
break;
}
}
}
- for ^mine_pos: mine_positions {
+ for ^mine_pos: mine_locations {
for neighbor: neighbors(this, mine_pos.x, mine_pos.y) {
cell := get_true_cell(this, neighbor.x, neighbor.y);
if cell == .Empty do cell = .Number1;
}
context := new(Context);
- *context = .{ board, x, y, -1, -1, include_corners };
+ *context = .{ board, x, y, -1 if include_corners else 0, -1, include_corners };
next :: (use _: ^Context) -> (Pos, bool) {
step :: macro () {
@Gross @Cleanup
dx += 1;
if dx > 1 { dx = -1; dy += 1; }
- if math.abs(dx) + math.abs(dy) == 2 && !include_corners{
+ if math.abs(dx) + math.abs(dy) == 2 && !include_corners {
dx += 1;
if dx > 1 { dx = -1; dy += 1; }
}
}
flood_reveal :: (use this: ^Board, x, y: i32) => {
- positions := array.make(Pos);
+
+ positions := set.make(Pos);
positions << .{x, y};
- while positions.count != 0 {
- p := positions[0];
- array.delete(^positions, 0);
+ while !positions->empty() {
+ p := positions.entries[0].value;
+ positions->remove(p);
reveal_cell(this, p.x, p.y);
if get_true_cell(this, p.x, p.y) & .Is_Number do continue;
- for n: neighbors(this, p.x, p.y, include_corners=false) {
+ for n: neighbors(this, p.x, p.y, include_corners=true) {
true_cell := get_true_cell(this, n.x, n.y);
visible_cell := get_visible_cell(this, n.x, n.y);
if true_cell != .Mine && visible_cell == .Covered {
}
}
-cell_size :: 32.0f
-border :: 2.0f
+reveal_mines :: (use this: ^Board) {
+ for ^mine: mine_locations {
+ reveal_cell(this, mine.x, mine.y);
+ }
+}
+
+toggle_flag :: (use this: ^Board, x, y: i32) {
+ cell := get_visible_cell(this, x, y);
+ switch cell {
+ case .Flag do set_visible_cell(this, x, y, .Covered);
+ case .Covered do set_visible_cell(this, x, y, .Flag);
+ }
+}
+
+cell_size := 32.0f
+border := 2.0f
board: Board;
load :: () {
random.set_seed(clock.time());
- init_board(^board, 20, 20);
- generate_board(^board, 5);
+
+ hb.system.config(.Wait_For_Events, true);
+
+ init_board(^board, 30, 20);
+ generate_board(^board, 80);
}
update :: (dt: f32) {
hb.window.setShouldClose(true);
}
- #persist last_mouse_down := false;
- mouse_down := hb.input.mouseIsDown(.Left);
- if last_mouse_down && !mouse_down {
- mx, my := hb.input.mouseGetPos();
- cx, cy := cast(i32) math.floor(mx / cell_size), cast(i32) math.floor(my / cell_size);
- if cx < board.width && cy < board.height {
- flood_reveal(^board, cx, cy);
+ mx, my := hb.input.mouseGetPos();
+ cx, cy := cast(i32) math.floor(mx / cell_size), cast(i32) math.floor(my / cell_size);
+
+ { // Left mouse clicking
+ #persist last_mouse_down := false;
+ mouse_down := hb.input.mouseIsDown(.Left);
+ defer last_mouse_down = mouse_down;
+
+ if !last_mouse_down || mouse_down do break;
+ if cx >= board.width || cy >= board.height do break;
+
+ if get_visible_cell(^board, cx, cy) == .Covered {
+ if array.contains(board.mine_locations, .{ cx, cy }) {
+ reveal_mines(^board);
+ } else {
+ flood_reveal(^board, cx, cy);
+ }
}
}
- last_mouse_down = mouse_down;
+
+ { // Right mouse clicking
+ #persist last_mouse_down := false;
+ mouse_down := hb.input.mouseIsDown(.Right);
+ defer last_mouse_down = mouse_down;
+
+ if !last_mouse_down || mouse_down do break;
+ if cx >= board.width || cy >= board.height do break;
+
+ toggle_flag(^board, cx, cy);
+ }
}
draw :: () {
- size :: cell_size
+ window_width, window_height := hb.window.getDimensions();
+ cell_size = ~~math.min(window_width / board.width, window_height / board.height);
hb.graphics.setClearColor(0.05, 0.05, 0.05, 1);
hb.graphics.clear();
cell := get_visible_cell(^board, x, y);
if cell == .Empty {
hb.graphics.setColor(0.1, 0.1, 0.1);
- hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size, ~~y * cell_size, cell_size - border, cell_size - border);
}
if cell == .Covered {
hb.graphics.setColor(0.6, 0.6, 0.6);
- hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size, ~~y * cell_size, cell_size - border, cell_size - border);
}
if cell == .Mine {
hb.graphics.setColor(0.1, 0.1, 0.1);
- hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size, ~~y * cell_size, cell_size - border, cell_size - border);
hb.graphics.setColor(1, 0, 0);
- hb.graphics.circle(.Fill, ~~x * size + 0.5 * size, ~~y * size + 0.5 * size, (size - 2 * border) / 2);
+ hb.graphics.circle(.Fill, ~~x * cell_size + 0.5 * cell_size, ~~y * cell_size + 0.5 * cell_size, (cell_size - 2 * border) / 2);
+ }
+
+ if cell == .Flag {
+ hb.graphics.setColor(0.6, 0.6, 0.6);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size, ~~y * cell_size, cell_size - border, cell_size - border);
+
+ hb.graphics.setColor(0.3, 0.2, 0.1);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size + 0.2 * cell_size, ~~y * cell_size + 0.2 * cell_size, 0.1 * cell_size, 0.6 * cell_size);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size + 0.2 * cell_size, ~~y * cell_size + 0.2 * cell_size, 0.6 * cell_size, 0.35 * cell_size);
}
if cell & .Is_Number {
buf: [2] u8;
- @Bug // Unary bitwise-not does not work on enum values
s := conv.str_format(buf, "{}", cast(i32) (cell - .Is_Number));
w := hb.graphics.getTextWidth(s);
+ h := hb.graphics.getTextHeight(s, cell_size);
+
+ #persist number_colors := ([3] f32).[
+ f32.[ 0, 0, 1 ],
+ f32.[ 0, 1, 0 ],
+ f32.[ 0, 0.5, 0.5 ],
+ f32.[ 0.5, 0, 0 ],
+ f32.[ 0.7, 0, 0 ],
+ f32.[ 1.0, 0, 0 ],
+ f32.[ 1.0, 0, 1.0 ],
+ f32.[ 0, 0, 0 ],
+ ];
+
+ hb.graphics.setColor(0.1, 0.1, 0.1);
+ hb.graphics.rectangle(.Fill, ~~x * cell_size, ~~y * cell_size, cell_size - border, cell_size - border);
- hb.graphics.setColor(0.3, 0.3, 0.3);
- hb.graphics.rectangle(.Fill, ~~x * size, ~~y * size, size - border, size - border);
- hb.graphics.setColor(0.7, 0.5, 0.5);
- hb.graphics.print(s, ~~x * size + (size - w) / 2, ~~(y + 1) * size - 3 * border);
+ col := number_colors[cast(i32) (cell - .Is_Number) - 1];
+ hb.graphics.setColor(col[0], col[1], col[2]);
+ hb.graphics.print(s, ~~x * cell_size + (cell_size - w) / 2, ~~y * cell_size + (cell_size - h) / 2 + h);
}
}
}
mx, my := hb.input.mouseGetPos();
- mx, my = size * math.floor(mx / size), size * math.floor(my / size);
- if mx < ~~board.width * size && my < ~~board.height * size {
+ mx, my = cell_size * math.floor(mx / cell_size), cell_size * math.floor(my / cell_size);
+ if mx < ~~board.width * cell_size && my < ~~board.height * cell_size {
hb.graphics.setColor(1, 1, 1, 0.3);
- hb.graphics.rectangle(.Fill, ~~mx, ~~my, size - border, size - border);
+ hb.graphics.rectangle(.Fill, ~~mx, ~~my, cell_size - border, cell_size - border);
}
}