Dungeon Crawler - text based game

Yes, we are learning Python now! Python is cool.

"""
Dungeon Crawler Adventure
A text-based dungeon exploration game with multiple rooms, items, and challenges.

Covers: Variables, functions, dictionaries, lists, loops, comparisons, error handling
"""

import random
import time

# Game data structures
dungeon_map = {
    "entrance": {
        "description": "You stand at the dungeon entrance. Torches flicker on damp stone walls.",
        "exits": {"north": "hallway", "east": "armory"},
        "items": ["torch"],
        "visited": False
    },
    "hallway": {
        "description": "A long dark hallway stretches before you. You hear dripping water.",
        "exits": {"south": "entrance", "north": "throne_room", "west": "treasury"},
        "items": ["rusty_key"],
        "visited": False
    },
    "armory": {
        "description": "Weapons line the walls, most broken or rusted beyond use.",
        "exits": {"west": "entrance"},
        "items": ["sword", "shield"],
        "visited": False
    },
    "treasury": {
        "description": "Gold coins scatter the floor! But something guards them...",
        "exits": {"east": "hallway"},
        "items": ["gold_coins"],
        "monster": "goblin",
        "visited": False
    },
    "throne_room": {
        "description": "An ancient throne sits empty. The exit portal shimmers behind it!",
        "exits": {"south": "hallway"},
        "items": ["crown"],
        "requires": "rusty_key",
        "visited": False
    }
}

# Player state
player = {
    "location": "entrance",
    "inventory": [],
    "health": 100,
    "score": 0
}


def print_slow(text, delay=0.03):
    """Print text with a typewriter effect for atmosphere."""
    for char in text:
        print(char, end='', flush=True)
        time.sleep(delay)
    print()  # New line at the end


def display_room(room_name):
    """Display current room information and mark it as visited."""
    room = dungeon_map[room_name]
    
    print("\n" + "=" * 60)
    print_slow(room["description"])
    
    # Show items in the room
    if room["items"]:
        print(f"\nYou see: {', '.join(room['items'])}")
    
    # Show available exits
    exits = list(room["exits"].keys())
    print(f"Exits: {', '.join(exits)}")
    
    # Mark room as visited and award points for exploration
    if not room["visited"]:
        room["visited"] = True
        player["score"] += 10
        print("(+10 points for exploring a new room!)")
    
    print("=" * 60)


def show_inventory():
    """Display player's current inventory."""
    if player["inventory"]:
        print(f"\nInventory: {', '.join(player['inventory'])}")
    else:
        print("\nInventory: (empty)")


def show_stats():
    """Display player's health and score."""
    print(f"\n❤️  Health: {player['health']} | ⭐ Score: {player['score']}")


def take_item(item_name):
    """Pick up an item from the current room."""
    current_room = dungeon_map[player["location"]]
    
    if item_name in current_room["items"]:
        current_room["items"].remove(item_name)
        player["inventory"].append(item_name)
        player["score"] += 5
        print(f"\n✓ You took the {item_name}. (+5 points)")
    else:
        print(f"\n✗ There is no {item_name} here.")


def move(direction):
    """Move player to a new room if the exit exists."""
    current_room = dungeon_map[player["location"]]
    
    # Check if direction is valid
    if direction not in current_room["exits"]:
        print("\n✗ You can't go that way!")
        return
    
    next_room_name = current_room["exits"][direction]
    next_room = dungeon_map[next_room_name]
    
    # Check if room requires a key
    if "requires" in next_room and next_room["requires"] not in player["inventory"]:
        print(f"\n✗ The door is locked! You need a {next_room['requires']}.")
        return
    
    # Check for monsters
    if "monster" in next_room and next_room["monster"]:
        if not fight_monster(next_room["monster"]):
            return  # Combat failed, don't move
        else:
            next_room["monster"] = None  # Monster defeated
    
    # Move to new room
    player["location"] = next_room_name
    display_room(next_room_name)


def fight_monster(monster_name):
    """Simple combat encounter."""
    print(f"\n⚔️  A wild {monster_name} appears!")
    
    # Check if player has a weapon
    weapons = ["sword", "torch"]
    has_weapon = any(weapon in player["inventory"] for weapon in weapons)
    
    if has_weapon:
        print("You fight bravely with your weapon!")
        damage = random.randint(10, 30)
        player["health"] -= damage
        player["score"] += 25
        print(f"You defeated the {monster_name}! (-{damage} health, +25 points)")
        
        if player["health"] <= 0:
            return False
        return True
    else:
        print("You have no weapon! You flee in terror, losing health.")
        player["health"] -= 40
        print(f"(-40 health)")
        
        if player["health"] <= 0:
            return False
        return False  # Couldn't enter the room


def check_win_condition():
    """Check if player has won the game."""
    if player["location"] == "throne_room" and "crown" in player["inventory"]:
        return True
    return False


def show_help():
    """Display available commands."""
    print("\n" + "=" * 60)
    print("COMMANDS:")
    print("  go <direction>   - Move (e.g., 'go north')")
    print("  take <item>      - Pick up an item")
    print("  inventory        - View your items")
    print("  stats            - View health and score")
    print("  help             - Show this help")
    print("  quit             - Exit game")
    print("=" * 60)


def game_over(won=False):
    """Handle game ending."""
    print("\n" + "=" * 60)
    if won:
        print_slow("🎉 VICTORY! You found the crown and escaped the dungeon!")
        print(f"\nFinal Score: {player['score']}")
        print(f"Health Remaining: {player['health']}")
    else:
        print_slow("💀 GAME OVER - You have perished in the dungeon...")
        print(f"\nFinal Score: {player['score']}")
    print("=" * 60)


def main():
    """Main game loop."""
    print("\n" + "=" * 60)
    print_slow("🏰  DUNGEON CRAWLER ADVENTURE  🏰", delay=0.05)
    print("=" * 60)
    print_slow("\nYour quest: Find the ancient crown in the throne room and escape!")
    print_slow("Explore carefully, collect items, and survive encounters...\n")
    
    show_help()
    display_room(player["location"])
    
    # Main game loop
    while True:
        show_stats()
        
        # Check win condition
        if check_win_condition():
            game_over(won=True)
            break
        
        # Check death
        if player["health"] <= 0:
            game_over(won=False)
            break
        
        # Get player input
        try:
            command = input("\n> ").lower().strip()
            
            if not command:
                continue
            
            # Parse command
            parts = command.split()
            action = parts[0]
            
            if action == "quit":
                print("\nThanks for playing!")
                break
            
            elif action == "help":
                show_help()
            
            elif action == "inventory":
                show_inventory()
            
            elif action == "stats":
                show_stats()
            
            elif action == "go" or action == "move":
                if len(parts) < 2:
                    print("Go where? (e.g., 'go north')")
                else:
                    move(parts[1])
            
            elif action == "take" or action == "get" or action == "grab":
                if len(parts) < 2:
                    print("Take what? (e.g., 'take sword')")
                else:
                    take_item(parts[1])
            
            else:
                print("Unknown command. Type 'help' for available commands.")
        
        except Exception as e:
            print(f"Error: {e}. Please try again.")


# Run the game
if __name__ == "__main__":
    main()