Skip to content

Commit

Permalink
swap to dict and add traversable test
Browse files Browse the repository at this point in the history
  • Loading branch information
Acepie committed Apr 20, 2024
1 parent e6a62ca commit 9f62c36
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 162 deletions.
1 change: 0 additions & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ target = "javascript"
[dependencies]
gleam_stdlib = ">= 0.34.0 and < 2.0.0"
p5js_gleam = ">= 1.0.1 and < 2.0.0"
glearray = ">= 0.2.1 and < 1.0.0"
prng = ">= 3.0.2 and < 4.0.0"

[dev-dependencies]
Expand Down
4 changes: 1 addition & 3 deletions manifest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ packages = [
{ name = "filepath", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "EFB6FF65C98B2A16378ABC3EE2B14124168C0CE5201553DE652E2644DCFDB594" },
{ name = "gleam_bitwise", version = "1.3.1", build_tools = ["gleam"], requirements = [], otp_app = "gleam_bitwise", source = "hex", outer_checksum = "B36E1D3188D7F594C7FD4F43D0D2CE17561DE896202017548578B16FE1FE9EFC" },
{ name = "gleam_stdlib", version = "0.37.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "5398BD6C2ABA17338F676F42F404B9B7BABE1C8DC7380031ACB05BBE1BCF3742" },
{ name = "glearray", version = "0.2.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "glearray", source = "hex", outer_checksum = "908154F695D330E06A37FAB2C04119E8F315D643206F8F32B6A6C14A8709FFF4" },
{ name = "gleeunit", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "72CDC3D3F719478F26C4E2C5FED3E657AC81EC14A47D2D2DEBB8693CA3220C3B" },
{ name = "p5js_gleam", version = "1.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "p5js_gleam", source = "hex", outer_checksum = "5DCFACE980EDE238BC81B8837C1A8DBE24B277B54B004B7BBCC6B8F473A9BDAD" },
{ name = "prng", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_bitwise", "gleam_stdlib"], otp_app = "prng", source = "hex", outer_checksum = "C61B103F9AF5031ADAA35187CCE7130845EF5088D88FD084E5995D4FBEC9D745" },
Expand All @@ -16,7 +15,6 @@ packages = [
[requirements]
esgleam = { version = ">= 0.6.0 and < 1.0.0" }
gleam_stdlib = { version = ">= 0.34.0 and < 2.0.0" }
glearray = { version = ">= 0.2.1 and < 1.0.0" }
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
p5js_gleam = { version = ">= 1.0.1 and < 2.0.0" }
prng = { version = ">= 3.0.2 and < 4.0.0"}
prng = { version = ">= 3.0.2 and < 4.0.0" }
244 changes: 90 additions & 154 deletions src/dungeon.gleam
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import gleam/bool
import gleam/dict.{type Dict}
import gleam/int
import gleam/iterator
import gleam/list
import gleam/option
import gleam/result
import glearray.{type Array}
import p5js_gleam.{type P5}
import prng/random
import room
Expand All @@ -28,7 +26,7 @@ pub fn total_size() -> Float {
}

type Rooms =
Array(Array(option.Option(room.Room)))
Dict(#(Int, Int), room.Room)

/// Represents the dungeon and all its hazards
pub type Dungeon {
Expand All @@ -39,10 +37,7 @@ pub type Dungeon {
pub fn generate_dungeon() -> Dungeon {
let center = center()
let rooms =
glearray.from_list(list.repeat(
glearray.from_list(list.repeat(option.None, dungeon_size)),
dungeon_size,
))
dict.new()
|> generate_rooms(center, center, 0, room.Left)
|> break_walls
|> compute_corner_walls
Expand All @@ -66,7 +61,7 @@ fn generate_rooms(
0 -> r
_ -> room.set_navigable(r, previous_direction, True)
}
let assert Ok(rooms) = set_room(rooms, option.Some(r), column, row)
let rooms = dict.insert(rooms, #(column, row), r)

// Exit if we've recursed too far
use <- bool.guard(recursion_depth > max_depth, rooms)
Expand All @@ -77,35 +72,31 @@ fn generate_rooms(
direction == previous_direction && recursion_depth != 0,
rooms,
)

let #(next_column, next_row) = next_room_indices(column, row, direction)
// Do not try to go out of bounds
use <- bool.guard(out_of_bounds(next_column, next_row), rooms)
// Do not go some place that already has a room
let assert Ok(col) = glearray.get(rooms, next_column)
let assert Ok(next_room) = glearray.get(col, next_row)
use <- bool.guard(option.is_some(next_room), rooms)
use <- bool.guard(dict.has_key(rooms, #(next_column, next_row)), rooms)
// Decide if we should advance
let room_roll: Float =
random.float(0.0, 1.0)
|> random.random_sample
case room_roll <. room_rate {
True -> {
// Update current room to remove wall to next room
let assert Ok(col) = glearray.get(rooms, column)
let assert Ok(option.Some(room)) = glearray.get(col, row)
let room = room.set_navigable(room, direction, True)
let assert Ok(rooms) = set_room(rooms, option.Some(room), column, row)
// Advance to next room and recur
generate_rooms(
rooms,
next_column,
next_row,
recursion_depth + 1,
room.inverse_direction(direction),
)
}
False -> rooms
}

use <- bool.guard(room_roll >. room_rate, rooms)

// Update current room to remove wall to next room
let assert Ok(r) = dict.get(rooms, #(column, row))
let r = room.set_navigable(r, direction, True)
let rooms = dict.insert(rooms, #(column, row), r)
// Advance to next room and recur
generate_rooms(
rooms,
next_column,
next_row,
recursion_depth + 1,
room.inverse_direction(direction),
)
}

rooms
Expand All @@ -115,8 +106,8 @@ fn generate_rooms(
|> advance_direction(room.Bottom)
}

// Gets the indices of the room that would be in the given direction from the given indices
fn next_room_indices(
/// Gets the indices of the room that would be in the given direction from the given indices
pub fn next_room_indices(
column: Int,
row: Int,
direction: room.Direction,
Expand All @@ -140,144 +131,89 @@ fn out_of_bounds(column: Int, row: Int) -> Bool {

// Randomly break walls to make dungeon feel less caverny
fn break_walls(rooms: Rooms) -> Rooms {
use rooms, r, column, row <- fold_rooms(rooms, rooms)

// Ignore missing rooms
let new_rooms = {
use r <- option.then(r)

let break_in_direction = fn(rooms: Rooms, dir: room.Direction) {
// Check if there is a room to the right
let #(next_column, next_row) = next_room_indices(column, row, dir)
use <- bool.guard(
out_of_bounds(next_column, next_row),
option.Some(rooms),
)
let assert Ok(col) = glearray.get(rooms, next_column)
let assert Ok(next_room) = glearray.get(col, next_row)
use next_room <- option.map(next_room)

// Decide if we should break wall
let break_roll: Float =
random.float(0.0, 1.0)
|> random.random_sample
case break_roll <. break_rate {
True -> {
// Update current room to remove wall to next room
let assert Ok(col) = glearray.get(rooms, column)
let assert Ok(option.Some(r)) = glearray.get(col, row)
let room = room.set_navigable(r, dir, True)
let assert Ok(rooms) = set_room(rooms, option.Some(room), column, row)
// Update next room to remove wall to current room
let next_room =
room.set_navigable(next_room, room.inverse_direction(dir), True)
let assert Ok(rooms) =
set_room(rooms, option.Some(next_room), next_column, next_row)
rooms
}
False -> rooms
use rooms, #(column, row), _ <- dict.fold(rooms, rooms)

let break_in_direction = fn(rooms: Rooms, dir: room.Direction) -> Rooms {
// Check if there is a room to the right
let #(next_column, next_row) = next_room_indices(column, row, dir)
use <- bool.guard(out_of_bounds(next_column, next_row), rooms)

let next_room = dict.get(rooms, #(next_column, next_row))
case next_room {
Error(_) -> rooms
Ok(next_room) -> {
// Decide if we should break wall
let break_roll: Float =
random.float(0.0, 1.0)
|> random.random_sample
use <- bool.guard(break_roll >. break_rate, rooms)

// Update current room to remove wall to next room
let assert Ok(room) = dict.get(rooms, #(column, row))
let room = room.set_navigable(room, dir, True)
let rooms = dict.insert(rooms, #(column, row), room)

// Update next room to remove wall to current room
let next_room =
room.set_navigable(next_room, room.inverse_direction(dir), True)
dict.insert(rooms, #(next_column, next_row), next_room)
}
}

rooms
|> break_in_direction(room.Right)
|> option.then(break_in_direction(_, room.Bottom))
}

option.unwrap(new_rooms, rooms)
rooms
|> break_in_direction(room.Right)
|> break_in_direction(room.Bottom)
}

// Update corners based on cardinal directions
fn compute_corner_walls(rooms: Rooms) -> Rooms {
use rooms, r, column, row <- fold_rooms(rooms, rooms)

// Ignore missing rooms
let new_rooms = {
use r <- option.map(r)

let open_corner = fn(
room: room.Room,
dir1: room.Direction,
dir2: room.Direction,
out_dir: room.Direction,
) -> room.Room {
let can_go_in_dir =
room.is_navigable(room, dir1) && room.is_navigable(room, dir2)
use <- bool.guard(!can_go_in_dir, room)

// These asserts are ok because if we can naviage then the room must exist already
let #(next_column, next_row) = next_room_indices(column, row, out_dir)
use <- bool.guard(out_of_bounds(next_column, next_row), room)
let assert Ok(col) = glearray.get(rooms, next_column)
let assert Ok(next_room) = glearray.get(col, next_row)
case next_room {
option.None -> room
option.Some(next_room) -> {
let can_go_in_dir =
room.is_navigable(next_room, room.inverse_direction(dir1))
&& room.is_navigable(next_room, room.inverse_direction(dir2))
case can_go_in_dir {
True -> room.set_navigable(room, out_dir, True)
_ -> room
}
use rooms, #(column, row), r <- dict.fold(rooms, rooms)

let open_corner = fn(
room: room.Room,
dir1: room.Direction,
dir2: room.Direction,
out_dir: room.Direction,
) -> room.Room {
let can_go_in_dir =
room.is_navigable(room, dir1) && room.is_navigable(room, dir2)
use <- bool.guard(!can_go_in_dir, room)

// These asserts are ok because if we can naviage then the room must exist already
let #(next_column, next_row) = next_room_indices(column, row, out_dir)
use <- bool.guard(out_of_bounds(next_column, next_row), room)
let next_room = dict.get(rooms, #(next_column, next_row))
case next_room {
Error(_) -> room
Ok(next_room) -> {
let can_go_in_dir =
room.is_navigable(next_room, room.inverse_direction(dir1))
&& room.is_navigable(next_room, room.inverse_direction(dir2))
case can_go_in_dir {
True -> room.set_navigable(room, out_dir, True)
_ -> room
}
}
}

let r =
r
|> open_corner(room.Right, room.Bottom, room.BottomRight)
|> open_corner(room.Left, room.Bottom, room.BottomLeft)
|> open_corner(room.Right, room.Top, room.TopRight)
|> open_corner(room.Left, room.Top, room.TopLeft)

let assert Ok(rooms) = set_room(rooms, option.Some(r), column, row)
rooms
}

option.unwrap(new_rooms, rooms)
let r =
r
|> open_corner(room.Right, room.Bottom, room.BottomRight)
|> open_corner(room.Left, room.Bottom, room.BottomLeft)
|> open_corner(room.Right, room.Top, room.TopRight)
|> open_corner(room.Left, room.Top, room.TopLeft)

dict.insert(rooms, #(column, row), r)
}

/// Renders the dungeon to the screen.
pub fn draw_dungeon(p: P5, dungeon: Dungeon) {
use _, r, col, row <- fold_rooms(dungeon.rooms, p)
case r {
option.Some(r) -> room.draw_room(p, r, col, row, room_size)
option.None -> p
}
}
// rendering with shadows depends on order so we are using ranges
use col <- iterator.each(iterator.range(0, dungeon_size))
use row <- iterator.each(iterator.range(0, dungeon_size))
use r <- result.map(dict.get(dungeon.rooms, #(col, row)))

// creates an iterator to traverse the dungeon rooms
fn fold_rooms(
rooms: Rooms,
initial: b,
f: fn(b, option.Option(room.Room), Int, Int) -> b,
) -> b {
let col_iter =
rooms
|> glearray.iterate
|> iterator.index
use col_acc, #(column, column_number) <- iterator.fold(col_iter, initial)

let row_iter =
column
|> glearray.iterate
|> iterator.index

use row_acc, #(room, row_number) <- iterator.fold(row_iter, col_acc)
f(row_acc, room, column_number, row_number)
}

// updates the room at the given indices
fn set_room(
rooms: Rooms,
room: option.Option(room.Room),
column: Int,
row: Int,
) -> Result(Rooms, Nil) {
let col = glearray.get(rooms, column)
use col <- result.try(col)
let new_col = glearray.copy_set(col, row, room)
use new_col <- result.try(new_col)
glearray.copy_set(rooms, column, new_col)
room.draw_room(p, r, col, row, room_size)
}
Loading

0 comments on commit 9f62c36

Please sign in to comment.