Created
March 5, 2019 21:54
-
-
Save donarb/1502a12cb16fa74f4fd2e295867da253 to your computer and use it in GitHub Desktop.
Swift playground implementation of random dungeon generator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ported from https://gist.github.com/munificent/b1bcd969063da3e6c298be070a22b604 | |
// | |
// can be run in a Swift playground | |
import Cocoa | |
let HEIGHT = 40 | |
let WIDTH = 80 | |
var FIELD: [[Character]] = Array(repeating: Array(repeating: " ", count: WIDTH), count: HEIGHT) | |
let tileVoid: Character = " " | |
let tileFloor: Character = "." | |
let tileWall: Character = "#" | |
let tileCorner: Character = "!" | |
let tileOpenDoor: Character = "\\" | |
let tileClosedDoor: Character = "+" | |
let tilePlayer: Character = "@" | |
// cave() | |
func cave(withPlayer: Bool) { | |
// width, height, left and top are all inner | |
// dimensions/coordinates (w/o walls) | |
let width = Int.random(in: 5..<15) | |
let height = Int.random(in: 3..<9) | |
let left = Int.random(in: 0..<WIDTH-width-2) + 1 | |
let top = Int.random(in: 0..<HEIGHT-height-2) + 1 | |
// Check if the new cave (with walls) intersects with the interior of | |
// any already existing cave. Touching walls/corners are okay | |
for y in top-1..<top+height+2 { | |
for x in left-1..<left+width+2 { | |
guard FIELD[y][x] != tileFloor else { return } | |
} | |
} | |
// Find a suitable place for a door | |
var doorCounter = 0 | |
var doorX, doorY: Int? | |
if !withPlayer { | |
for y in top-1..<top+height+2 { | |
for x in left-1..<left+width+2 { | |
let atVerticalWall = x < left || x > left + width | |
let atHorizontalWall = y < top || y > top + height | |
let atWallButNotAtCorner = atVerticalWall != atHorizontalWall | |
// The door should not be created in the cave's corner or over | |
// another door, or in another cave's corner. It's impossible | |
// to make a cave without a door, because randInt(1) always | |
// returns 0. | |
if atWallButNotAtCorner && FIELD[y][x] == tileWall { | |
doorCounter += 1 | |
if Int.random(in: 0..<doorCounter) == 0 { | |
doorX = x | |
doorY = y | |
} | |
} | |
} | |
} | |
// If the cave's walls were made completely out of corners | |
// and doors, don't make such a cave | |
guard doorCounter > 0 else { return } | |
} | |
// The cave looks ok, let's draw it | |
for y in top-1..<top+height+2 { | |
for x in left-1..<left+width+2 { | |
let atVerticalWall = x < left || x > left + width | |
let atHorizontalWall = y < top || y > top + height | |
let atCorner = atVerticalWall && atHorizontalWall | |
let atWallButNotAtCorner = atVerticalWall != atHorizontalWall | |
// We need to somehow record corners of all caves to check | |
// for intersections later, so we use a special tile for it | |
FIELD[y][x] = atCorner ? tileCorner : (atWallButNotAtCorner ? tileWall : tileFloor) | |
} | |
// Now draw the door. The test is redundant, btw, because | |
// of "if (doorCounter == 0) { return; } earlier | |
if doorCounter > 0 { | |
if let dy = doorY, let dx = doorX { | |
FIELD[dy][dx] = Bool.random() ? tileOpenDoor : tileClosedDoor | |
} | |
} | |
if withPlayer { | |
// A cave with a player has only the player inside it | |
FIELD[Int.random(in: 0..<height) + top][Int.random(in: 0..<width) + left] = tilePlayer | |
} else { | |
// A cave without a player | |
let mob = "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz~" | |
for _ in 1..<Int.random(in: 1..<7) { | |
let idx = Int.random(in: 0..<mob.count) | |
let range = mob.index(mob.startIndex, offsetBy: idx)..<mob.index(mob.startIndex, offsetBy: idx+1) | |
FIELD[Int.random(in: 0..<height)+top][Int.random(in: 0..<width)+left] = | |
Int.random(in: 0..<4) == 0 ? "$" : Character(String(mob[range])) | |
} | |
} | |
} | |
} | |
// main() | |
// Fill in the field with the void | |
for y in 0..<HEIGHT { | |
for x in 0..<WIDTH { | |
FIELD[y][x] = tileVoid | |
} | |
} | |
// A call to cave() is not guranteed to actually make a new cave, | |
// so call it many times | |
for j in 0..<1_000 { | |
cave(withPlayer: j == 0) | |
} | |
// Print the generated field | |
for y in 0..<HEIGHT { | |
for x in 0..<WIDTH { | |
let c = FIELD[y][x] | |
// The cave corners should be drawn as walls | |
print(c == tileCorner ? tileWall : c, separator: "", terminator: "") | |
if x == WIDTH - 1 { | |
print("") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment