I hope this message finds you well. I am seeking your advice and help regarding a Swift project for university, specifically a roguelike game that operates with terminal-based commands and outputs.
This is my first time programming in Swift (probably the code contains some logical errors), as I usually work with Java or Python. Currently, I am responsible for implementing the map creation. Although I can print a map in the terminal, the output does not exactly match my expectations. Attached are two images: the first shows the current output, and the second illustrates my preferred outcome. I aim for each connected stage to display a path, indicating possible routes.
The stages are stored in a list, each linked to an adjacency list of connected stages. My initial strategy involves generating all stages (typically connected sequentially from 0 to 1 to 2 to 3, etc.), placing them in the grid, and then creating logical yet random paths between stages, ideally without crossing existing paths.
Any guidance or suggestions on how to achieve this would be greatly appreciated.
Thank you in advance for your help.
P.S.: I haven’t posted for a while, so if you need any more information or have questions, feel free to ask. I apologize if I did something wrong initially.
Code from my Realm part (which prints the stages and makes a list for it) :
class Realm {
var id:Int?
var stages: [Stage] = []
var adjacencyList: [Stage: [Stage]] = [:]
init(_ id:Int, _ enemies: [Characters] ) {
generateStages(enemies)
self.id = id
}
func generateStages(_ enemies:[Characters]) {
let numberOfStages = Int.random(in: 7...11)
var previousStage: Stage? = nil
for i in 0..<numberOfStages {
let stage = Stage()
// Link the previous stage to the current one if it exists
if let prev: Stage = previousStage {
adjacencyList[prev, default: []].append(stage)
}
// 15% chance that you need a key to enter the stage
if randomPercentage() == 1 && stage.stageNumber > 2{
stage.requiresKey = true
}
// 15% chance that you need a specific level to enter the stage
if randomPercentage() == -1 && stage.stageNumber > 2{
let maxLevelRequired: Int = stage.player!.exp + 2
stage.requiredLevel = Int.random(in: 0...maxLevelRequired)
}
stage.stageNumber = i
stages.append(stage)
previousStage = stage
}
// Set the entry and exit
stages[0].isEntry = true
stages[stages.count-1].isExit = true
stages[0].isPlayerPosition = true
//stages.last?.boss = Boss(name: "Realm Boss", health: 100) // Assign the realm boss
// Add Shop after stage 3 and before exit stage
let shop = stages[Int.random(in: 3..<stages.count-1)]
shop.isShop = true
// Add criteria that you need a key to enter shop and don't need a specific level
shop.requiresKey = true
shop.requiredLevel = 0
}
// Return random percentage (if 70% or 15%)
private func randomPercentage() -> Int {
let randNumber = Int.random(in: 1..<101)
// 70% chance
if (1...70).contains(randNumber) {
return 0
}
// 15% chance
if (71...85).contains(randNumber) {
return 1
}
// 15% chance
else {
return -1
}
}
func displayMap() {
print("Displaying Map")
// Display Map with Grid Representation
let rows: Int = 4
let columns: Int = 4
let stageWidth: Int = 8
let stageHeight: Int = 3
var grid: [[String]] = Array(repeating: Array(repeating: " ", count: columns * stageWidth), count: rows * stageHeight)
let randomStartRow = Int.random(in: 0..<rows)
let randomEndRow = Int.random(in: 0..<rows)
// Place the start and exit boxes
placeStage(in: &grid, stage: stages[0], row: randomStartRow, column: 0 ,stageWidth: stageWidth, stageHeight: stageHeight)
placeStage(in: &grid, stage: stages[stages.count-1], row: randomEndRow, column: columns-1, stageWidth: stageWidth, stageHeight: stageHeight)
for stage in stages.filter({ $0.isEntry == false && $0.isExit == false }) {
var placed = false
while !placed {
let row = Int.random(in: 0..<rows)
let column = Int.random(in: 1..<columns - 1) // Avoid the first and last columns
if grid[row * stageHeight][column * stageWidth] == " " { // Check if the position is empty
placeStage(in: &grid, stage: stage, row: row, column: column, stageWidth: stageWidth, stageHeight: stageHeight)
placed = true
}
}
}
for (fromStage, connectedStages) in adjacencyList {
for toStage in connectedStages {
drawPaths(in: &grid, from: fromStage, to: toStage, stageWidth: stageWidth, stageHeight: stageHeight)
}
}
for row in grid {
print(row.joined(separator: ""))
}
}
private func drawPaths(in grid: inout [[String]], from: Stage, to: Stage, stageWidth: Int, stageHeight: Int) {
}
private func placeStage(in grid: inout [[String]], stage: Stage, row: Int, column: Int, stageWidth: Int, stageHeight: Int) {
stage.row = row
stage.column = column
let stageRepresentation = stage.displayStage()
let xOffset = column * stageWidth
let yOffset = row * stageHeight
// Place each line of the stage representation in the grid
for (lineIndex, line) in stageRepresentation.enumerated() {
let chars = Array(line)
for (charIndex, char) in chars.enumerated() {
grid[yOffset + lineIndex][xOffset + charIndex] = String(char)
}
}
}
}
Code part in Stage.class which shows how to display each stage
func displayStage() -> [String] {
if isEntry {
return [
"+------+",
"| ENTR |",
"+------+"
]
} else if isExit {
return [
"+------+",
"| BOSS |",
"+------+"
]
} else if isShop {
return [
"+------+",
"| SHOP |",
"+------+"
]
} else if isPlayerPosition && (!isEntry || !isExit) {
return [
"+------+",
"| OO |",
"+------+"
]
} else {
return [
"+------+",
"| |",
"+------+"
]
}
}