Cursorを使用してSwiftでCLI RPGを作ってみる(作らせてみる)

「SwiftでCLIでRPGを作ってください」から始まり、
少しずつ改良を命令していった、現状態。

ゲームの説明(当然Cursorにさせる)

ゲームの説明をしてください

はい、このRPGゲームの説明をさせていただきます:
ゲームの基本システム:
プレイヤーは勇者として、15種類のモンスターと戦闘を行います
モンスターを倒すと経験値(EXP)とゴールドを獲得できます
レベルアップすると、HP、攻撃力、防御力が上昇します
勇者の特徴:
名前と日付に基づいて初期ステータスが決定されます
基本値(HP: 100、攻撃力: 20、防御力: 10)を基準に、0%から50%の範囲でランダムな増加値が付加されます
レベルアップ時の成長:
HP: +20
攻撃力: +5
防御力: +3
モンスターの特徴:
15種類のモンスターが存在し、それぞれ異なるステータスを持っています
ゲーム開始時に、各モンスターの基本ステータスに±50%のランダムな変動が加えられます
モンスターは総合力(HP + 攻撃力 + 防御力)の順に弱い順から強い順にソートされます
ゲームの進行:
メインメニュー:
戦闘:モンスターと戦闘を行います
休憩:100Gを支払ってHPを全回復できます
終了:ゲームを終了します
戦闘システム:
ランダムなモンスターが出現します
各ターンで以下の選択が可能:
攻撃:モンスターにダメージを与えます
逃げる:戦闘から撤退します
ダメージ計算:攻撃力 – 防御力(最小1ダメージ)
報酬システム:
モンスターを倒すとEXPとゴールドを獲得
獲得EXPはモンスターの基本EXP値
獲得ゴールドはモンスターのEXP値の2倍
ゲームの目的:
全てのモンスター(15種類)を倒すこと
モンスターを倒すと記録され、進行状況が表示されます
全てのモンスターを倒すとゲームクリア
HPが0になるとゲームオーバー
表示される情報:
勇者の現在のステータス(HP、攻撃力、防御力、レベル、EXP、所持金)
モンスター一覧(総合力順)
倒したモンスターの記録(マークで表示)
このゲームは、戦略性と運の要素を組み合わせたシンプルなRPGとなっています。

Program

import Foundation

// キャラクターの基本クラス
class Character {
    var name: String
    var level: Int
    var hp: Int
    var maxHp: Int
    var attack: Int
    var defense: Int
    var exp: Int
    var expToNextLevel: Int
    var gold: Int  // 所持金を追加
    
    init(name: String) {
        self.name = name
        self.level = 1
        
        // 名前と日付からステータスを計算
        let calendar = Calendar.current
        let today = Date()
        let components = calendar.dateComponents([.year, .month, .day], from: today)
        
        // 名前の文字列を数値に変換
        let nameValue = name.unicodeScalars.reduce(0) { $0 + Int($1.value) }
        
        // 日付と名前から乱数のシードを生成
        let seed = (components.year ?? 0) * 10000 + (components.month ?? 0) * 100 + (components.day ?? 0) + nameValue
        
        // シードを使用して乱数を生成
        var random = seed
        func nextRandom() -> Double {
            random = (random * 1103515245 + 12345) & 0x7fffffff
            return Double(random) / Double(0x7fffffff)
        }
        
        // 基本値からランダムな増加値を計算
        let baseHp = 100
        let baseAttack = 20
        let baseDefense = 10
        
        // 0%から50%の範囲でランダムな増加
        let hpBonus = Int(Double(baseHp) * nextRandom() * 0.5)
        let attackBonus = Int(Double(baseAttack) * nextRandom() * 0.5)
        let defenseBonus = Int(Double(baseDefense) * nextRandom() * 0.5)
        
        self.maxHp = baseHp + hpBonus
        self.hp = maxHp
        self.attack = baseAttack + attackBonus
        self.defense = baseDefense + defenseBonus
        self.exp = 0
        self.expToNextLevel = 100
        self.gold = 1000
        
        // 初期ステータスを表示
        print("\n\(name)の初期ステータス:")
        print("HP: \(maxHp) (基本値: \(baseHp) + \(hpBonus))")
        print("攻撃力: \(attack) (基本値: \(baseAttack) + \(attackBonus))")
        print("防御力: \(defense) (基本値: \(baseDefense) + \(defenseBonus))")
    }
    
    func levelUp() {
        level += 1
        maxHp += 20
        hp = maxHp
        attack += 5
        defense += 3
        expToNextLevel *= 2
        print("\(name)はレベル\(level)になりました!")
        print("HP: \(maxHp) (+20)")
        print("攻撃力: \(attack) (+5)")
        print("防御力: \(defense) (+3)")
    }
    
    func gainExp(_ amount: Int) {
        exp += amount
        while exp >= expToNextLevel {
            exp -= expToNextLevel
            levelUp()
        }
    }
}

// モンスタークラス
class Monster {
    var name: String
    var hp: Int
    var attack: Int
    var defense: Int
    var exp: Int
    
    init(name: String, hp: Int, attack: Int, defense: Int, exp: Int) {
        self.name = name
        self.hp = hp
        self.attack = attack
        self.defense = defense
        self.exp = exp
    }
}

// ゲームクラス
class Game {
    private var player: Character
    private var defeatedMonsters: Set<String> = []  // 倒したモンスターの名前を記録
    private let monsterNames = [
        "スライム", "ゴブリン", "オーク", "ドラゴン", "キメラ",
        "ミノタウロス", "グリフォン", "ケルベロス", "フェニックス",
        "ハイドラ", "バジリスク", "サイクロプス", "メデューサ",
        "ケンタウロス", "ミイラ"
    ]
    
    private let baseMonsterStats: [(hp: Int, attack: Int, defense: Int, exp: Int)] = [
        (50, 15, 5, 30),   // 弱い
        (80, 25, 10, 50),
        (120, 35, 15, 80),
        (150, 40, 18, 120),
        (160, 38, 15, 100),
        (170, 40, 18, 130),
        (180, 42, 18, 150),
        (190, 45, 20, 160),
        (200, 45, 20, 150),
        (220, 48, 25, 180),
        (230, 52, 30, 190),
        (250, 50, 28, 200),
        (180, 42, 22, 140),
        (160, 38, 15, 120),
        (140, 35, 12, 100)  // 強い
    ]
    
    private var monsterStats: [(hp: Int, attack: Int, defense: Int, exp: Int)] = []
    
    init() {
        print("勇者の名前を入力してください: ", terminator: "")
        let name = readLine() ?? "勇者"
        player = Character(name: name)
        
        // モンスターの初期ステータスを設定
        monsterStats = baseMonsterStats.map { stats in
            let hpVariation = Int(Double(stats.hp) * 0.5)
            let attackVariation = Int(Double(stats.attack) * 0.5)
            let defenseVariation = Int(Double(stats.defense) * 0.5)
            let expVariation = Int(Double(stats.exp) * 0.5)
            
            let hp = max(1, stats.hp + Int.random(in: -hpVariation...hpVariation))
            let attack = max(1, stats.attack + Int.random(in: -attackVariation...attackVariation))
            let defense = max(1, stats.defense + Int.random(in: -defenseVariation...defenseVariation))
            let exp = max(1, stats.exp + Int.random(in: -expVariation...expVariation))
            
            return (hp: hp, attack: attack, defense: defense, exp: exp)
        }
        
        // 初期モンスターステータスを表示
        print("\nモンスターの初期ステータス:")
        for (index, stats) in monsterStats.enumerated() {
            print("\(monsterNames[index]): HP\(stats.hp) 攻撃力\(stats.attack) 防御力\(stats.defense) EXP\(stats.exp)")
        }
    }
    
    private func showProgress() {
        // モンスターの総合力を計算してソート
        let sortedMonsters = monsterNames.enumerated().map { (index, name) -> (name: String, power: Int) in
            let stats = monsterStats[index]
            let power = stats.hp + stats.attack + stats.defense
            return (name: name, power: power)
        }.sorted { $0.power < $1.power }
        
        print("\n進行状況: \(defeatedMonsters.count)/\(monsterNames.count)")
        print("\nモンスター一覧(総合力順):")
        for monster in sortedMonsters {
            let stats = monsterStats[monsterNames.firstIndex(of: monster.name)!]
            print("\(monster.name): HP\(stats.hp) + 攻撃力\(stats.attack) + 防御力\(stats.defense) = 総合力\(monster.power)")
        }
        print("\n倒したモンスター: ", terminator: "")
        for monster in sortedMonsters {
            print(defeatedMonsters.contains(monster.name) ? "*" : "-", terminator: "")
        }
        print("\n*: 倒したモンスター")
    }
    
    private func showEnding() {
        print("\n🎉 おめでとうございます! 🎉")
        print("\(player.name)は全てのモンスターを倒しました!")
        print("\n最終ステータス:")
        print("レベル: \(player.level)")
        print("HP: \(player.maxHp)")
        print("攻撃力: \(player.attack)")
        print("防御力: \(player.defense)")
        print("所持金: \(player.gold)G")
        print("\nゲームクリア!")
    }
    
    private func showBadEnding() {
        print("\n💀 ゲームオーバー 💀")
        print("\(player.name)は倒れてしまいました...")
        print("\n最終ステータス:")
        print("レベル: \(player.level)")
        print("HP: \(player.maxHp)")
        print("攻撃力: \(player.attack)")
        print("防御力: \(player.defense)")
        print("所持金: \(player.gold)G")
        print("倒したモンスター: \(defeatedMonsters.count)/\(monsterNames.count)")
        print("\nまた挑戦してください!")
    }
    
    private func createMonster() -> Monster {
        // ランダムな名前とステータスを選択
        let index = Int.random(in: 0..<monsterNames.count)
        let name = monsterNames[index]
        let stats = monsterStats[index]
        
        return Monster(name: name, hp: stats.hp, attack: stats.attack, defense: stats.defense, exp: stats.exp)
    }
    
    func start() {
        while true {
            print("\n=== \(player.name)のステータス ===")
            print("レベル: \(player.level)")
            print("HP: \(player.hp)/\(player.maxHp)")
            print("攻撃力: \(player.attack)")
            print("防御力: \(player.defense)")
            print("EXP: \(player.exp)/\(player.expToNextLevel)")
            print("所持金: \(player.gold)G")
            
            showProgress()
            
            print("\n1: 戦闘")
            print("2: 休憩(100G)")
            print("3: 終了")
            print("選択してください (1-3): ", terminator: "")
            
            if let choice = readLine(), let choiceInt = Int(choice) {
                switch choiceInt {
                case 1:
                    battle()
                    if defeatedMonsters.count == monsterNames.count {
                        showEnding()
                        return
                    }
                case 2:
                    if player.gold >= 100 {
                        player.hp = player.maxHp
                        player.gold -= 100
                        print("HPが全回復しました!(100G支払いました)")
                    } else {
                        print("所持金が足りません!(100G必要です)")
                    }
                case 3:
                    print("ゲームを終了します。")
                    return
                default:
                    print("無効な選択です。")
                }
            }
        }
    }
    
    private func battle() {
        let monster = createMonster()
        print("\n\(monster.name)が現れた!")
        print("HP: \(monster.hp)")
        print("攻撃力: \(monster.attack)")
        print("防御力: \(monster.defense)")
        
        while monster.hp > 0 && player.hp > 0 {
            print("\n\(player.name)のHP: \(player.hp)")
            print("\(monster.name)のHP: \(monster.hp)")
            print("\n1: 攻撃")
            print("2: 逃げる")
            print("選択してください (1-2): ", terminator: "")
            
            if let choice = readLine(), let choiceInt = Int(choice) {
                switch choiceInt {
                case 1:
                    // プレイヤーの攻撃
                    let damage = max(0, player.attack - monster.defense)
                    monster.hp -= damage
                    print("\(player.name)の攻撃!\(monster.name)に\(damage)のダメージ!")
                    
                    // モンスターの攻撃
                    if monster.hp > 0 {
                        let monsterDamage = max(0, monster.attack - player.defense)
                        player.hp -= monsterDamage
                        print("\(monster.name)の攻撃!\(player.name)に\(monsterDamage)のダメージ!")
                    }
                case 2:
                    print("逃げ出した!")
                    return
                default:
                    print("無効な選択です。")
                }
            }
        }
        
        if monster.hp <= 0 {
            print("\n\(monster.name)を倒した!")
            player.gainExp(monster.exp)
            // モンスターの強さに応じて獲得金を設定
            let goldReward = monster.exp * 2
            player.gold += goldReward
            print("\(goldReward)Gを獲得しました!")
            
            // 倒したモンスターを記録
            defeatedMonsters.insert(monster.name)
            
            // 初めて倒したモンスターの場合、特別なメッセージを表示
            if defeatedMonsters.count == 1 {
                print("\n初めてのモンスターを倒しました!")
            } else if defeatedMonsters.count == monsterNames.count {
                print("\n全てのモンスターを倒しました!")
            }
        } else if player.hp <= 0 {
            print("\n\(player.name)は倒れてしまった...")
            showBadEnding()
            exit(0)
        }
    }
}

// シード付き乱数生成器
struct SeededRandomNumberGenerator: RandomNumberGenerator {
    private var seed: UInt64
    
    init(seed: UInt64) {
        self.seed = seed
    }
    
    mutating func next() -> UInt64 {
        seed = seed &* 6364136223846793005 &+ 1
        return seed
    }
}

// ゲームを開始
let game = Game()
game.start() 

SwiftCursor,Swift

Posted by shi-n