Skip to content

Commit

Permalink
2.0.0 savedata overhaul
Browse files Browse the repository at this point in the history
  • Loading branch information
bboonstra committed Oct 4, 2024
1 parent ea7362b commit 4d74074
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 28 deletions.
8 changes: 5 additions & 3 deletions idlegame/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,12 @@ class AutosavedPlayer():
'tree_health': 100,
'gold': 0,
'last_claim_timestamp': None,
'last_trivia_timestamp': None,
'last_trivia_bonus_timestamp': None,
'settings': {},
'aliases': {},
'nano_cores': {'normal': 0, 'miner': 0, 'fighter': 0, 'super': 0, 'warper': 0},
'nanos': [],
'nanobots': [],
'system_complexity': 0.0,
'warps': 0,
'packages': [],
Expand Down Expand Up @@ -84,12 +86,12 @@ def automigrate(self) -> None:
for attr, default_value in self.DEFAULT_ATTRIBUTES.copy().items():
if attr not in self._data:
self._data[attr] = default_value
for bot in self.nanos:
for bot in self.nanobots:
bot.update_complexity()
self.update_complexity()

def update_complexity(self) -> None:
self.system_complexity = sum(bot.complexity for bot in self.nanos) + (len(self.aliases) / 10)
self.system_complexity = sum(bot.complexity for bot in self.nanobots) + (len(self.aliases) / 10)

def handle_login() -> AutosavedPlayer:
"""Automatically login as the current system user."""
Expand Down
24 changes: 23 additions & 1 deletion idlegame/idle.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def handle_claim(player: AutosavedPlayer, *args, **kwargs) -> None:
# Collect all defenders for this chunk
defending_bots = []

for bot in player.nanos:
for bot in player.nanobots:
# Bot does nothing if broken
if not bot.functional:
continue
Expand Down Expand Up @@ -96,3 +96,25 @@ def handle_claim(player: AutosavedPlayer, *args, **kwargs) -> None:
print(f"{nanobots_broken} of your bots was broken during the invasions.")

install_package(player, 'apt')

def handle_crontab(player, *args, **kwargs):
"""See what commands are ready to be used.
Usage:
crontab
"""
cooldown_period = timedelta(minutes=10)
now = datetime.now(timezone.utc)

if player.last_trivia_timestamp is not None:
last_attempt_time = player.last_trivia_timestamp
if now < last_attempt_time + cooldown_period:
remaining_time = (last_attempt_time + cooldown_period) - now
print(f"Your next trivia will be available in {remaining_time.seconds // 60} minutes and {remaining_time.seconds % 60} seconds!")
return
else:
print("TRIVIA is ready!")

time_offline = now - player.last_claim_timestamp
total_seconds_offline = int(time_offline.total_seconds())
print(f"Uptime: {total_seconds_offline.seconds // 60}min {total_seconds_offline.seconds % 60}sec")
5 changes: 3 additions & 2 deletions idlegame/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import cmd
import shlex
from idlegame.data import handle_login, handle_reboot
from idlegame.idle import handle_claim
from idlegame.idle import handle_claim, handle_crontab
from idlegame.profile import handle_profile
from idlegame.nanobots import handle_nano, handle_list, handle_remove, handle_fsck, handle_echo, handle_truncate
from idlegame.nanobots import handle_cat, handle_head, handle_tail
Expand All @@ -23,6 +23,7 @@ def __init__(self, player):
"idlegame": self.handle_info,
"alias": self.handle_alias,
"whoami": handle_profile,
"crontab": handle_crontab,
"nano": handle_nano,
"rm": handle_remove,
"ls": handle_list,
Expand Down Expand Up @@ -60,7 +61,7 @@ def handle_top(self, player, *args, **kwargs):
# Print complexity summary
print(f"System complexity: {player.system_complexity}")
print("Nanobot contributions to complexity:")
for bot in player.nanos:
for bot in player.nanobots:
print(f" - {bot.name}: {bot.complexity} complexity")
print(f"Aliases contribution: {len(player.aliases) / 10:.1f} complexity")

Expand Down
26 changes: 13 additions & 13 deletions idlegame/nanobots.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def handle_nano(player: AutosavedPlayer, *args, **kwargs) -> None:
if not bot_name:
bot_name = input("Enter a name for your nanobot: ")

if len(bot_name) > 15 or any(bot.name == bot_name for bot in player.nanos):
if len(bot_name) > 15 or any(bot.name == bot_name for bot in player.nanobots):
print("Invalid name. Names must be unique and less than 16 characters long.")
return

Expand All @@ -120,9 +120,9 @@ def handle_nano(player: AutosavedPlayer, *args, **kwargs) -> None:
# Join the lines into a single string
nano_logic = '\n'.join(nano_logic_lines)

# Create a new Nanobot instance and add it to the player's nanos list
# Create a new Nanobot instance and add it to the player's nanobots list
new_nanobot = Nanobot(name=bot_name, logic=nano_logic.strip(), type=nanobot_type)
player.nanos.append(new_nanobot)
player.nanobots.append(new_nanobot)
player.nano_cores['normal'] -= 1 # Deduct a nano core for creating a new nanobot
if bot_type:
player.nano_cores[bot_type.lower()] -= 1 # Deduct a specialized core if applicable
Expand All @@ -145,7 +145,7 @@ def handle_remove(player: AutosavedPlayer, *args, **kwargs) -> None:
bot_name = args[0] # Get the bot name from the arguments

# Find the nanobot by name
nanobot_to_destroy = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot_to_destroy = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot_to_destroy is None:
print(f"No nanobot found with the name '{bot_name}'. Find its name with `ls`!")
Expand All @@ -164,7 +164,7 @@ def handle_remove(player: AutosavedPlayer, *args, **kwargs) -> None:
player.nano_cores[core_type] += 1 # Reclaiming the typed core if applicable

# Remove the nanobot from the list
player.nanos.remove(nanobot_to_destroy)
player.nanobots.remove(nanobot_to_destroy)
player.save() # Save changes to the player's data

# Print the appropriate message based on nanobot type
Expand All @@ -180,7 +180,7 @@ def handle_list(player: AutosavedPlayer, *args, **kwargs) -> None:
ls
"""

if not player.nanos:
if not player.nanobots:
print("You have no nanobots.")
return

Expand All @@ -189,7 +189,7 @@ def handle_list(player: AutosavedPlayer, *args, **kwargs) -> None:
print("-" * 100)

# Loop through each nanobot and display relevant details
for bot in player.nanos:
for bot in player.nanobots:
idle_action = bot.idle_action or "None"
current_action = bot.get_current_action() or "None"
event_actions = list(bot.event_actions.items())
Expand Down Expand Up @@ -237,7 +237,7 @@ def handle_fsck(player: AutosavedPlayer, *args, **kwargs) -> None:
bot_name = args[0] # Get the bot name from the arguments

# Find the nanobot by name
nanobot_to_fsck = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot_to_fsck = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot_to_fsck is None:
print(f"No nanobot found with the name '{bot_name}'. Find its name with `ls`!")
Expand Down Expand Up @@ -294,7 +294,7 @@ def handle_truncate(player: AutosavedPlayer, *args, **kwargs) -> None:
bot_name = args[0] # Get the bot name from the arguments

# Find the nanobot by name
nanobot_to_truncate = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot_to_truncate = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot_to_truncate is None:
print(f"No nanobot found with the name '{bot_name}'. Find its name with `ls`!")
Expand Down Expand Up @@ -326,7 +326,7 @@ def handle_echo(player: AutosavedPlayer, *args, **kwargs) -> None:
bot_name = args[2] # The bot name

# Find the nanobot by name
nanobot_to_echo = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot_to_echo = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot_to_echo is None:
print(f"No nanobot found with the name '{bot_name}'. Find its name with `ls`!")
Expand Down Expand Up @@ -357,7 +357,7 @@ def handle_cat(player: AutosavedPlayer, *args, **kwargs) -> None:
return

bot_name = args[0]
nanobot = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot is None:
print(f"No nanobot found with the name '{bot_name}'.")
Expand All @@ -376,7 +376,7 @@ def handle_head(player: AutosavedPlayer, *args, **kwargs) -> None:
return

bot_name = args[0]
nanobot = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot is None:
print(f"No nanobot found with the name '{bot_name}'.")
Expand All @@ -398,7 +398,7 @@ def handle_tail(player: AutosavedPlayer, *args, **kwargs) -> None:
return

bot_name = args[0]
nanobot = next((bot for bot in player.nanos if bot.name == bot_name), None)
nanobot = next((bot for bot in player.nanobots if bot.name == bot_name), None)

if nanobot is None:
print(f"No nanobot found with the name '{bot_name}'.")
Expand Down
119 changes: 114 additions & 5 deletions idlegame/packages.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import random
import subprocess
from datetime import datetime, timezone, timedelta
from colorama import Fore, Style, init
import re
init()

# Define package requirements
package_requirements = {
'apt': {},
Expand Down Expand Up @@ -40,7 +47,7 @@ def install_package(player, package_name):
return

for required_bot in requirements.get('required_bots', []):
if not any(bot.name == required_bot for bot in player.nanos):
if not any(bot.name == required_bot for bot in player.nanobots):
print(f"You need a nanobot named '{required_bot}' to install {package_name}.")
return

Expand Down Expand Up @@ -95,13 +102,115 @@ def handle_apt(player, *args, **kwargs):
elif selection:
print(f"{selection} is not a valid package.")


def get_random_zsh_command():
"""Pull a random Zsh command from the system."""
try:
# Get a list of available commands using compgen in Zsh
result = subprocess.run(["zsh", "-ic", "compgen -c"], capture_output=True, text=True)
commands = result.stdout.splitlines()
if commands:
# Randomly select a command from the list
return random.choice(list(filter(lambda cmd: not cmd.startswith('_'), commands)))
else:
print("No commands found.")
return None
except subprocess.CalledProcessError:
print("Error fetching Zsh commands.")
return None

def get_command_description(command):
"""Get a description of a command using 'whatis' or 'man'."""
try:
# Use 'whatis' to get a brief description of the command
result = subprocess.run(["whatis", command], capture_output=True, text=True)
description = result.stdout.strip()

if description:
description = description.splitlines()[0].split('-')[1].strip()
# Replace the command name with question marks equal to the command's length
question_marks = "[???]"
# Replace variations of the command name (add more variations as needed)
variations = [command, command.lower(), command.upper()]
for variation in variations:
# \b denotes a word boundary, ensuring we match whole words only
description = re.sub(r'\b' + re.escape(variation) + r'\b', question_marks, description)

if len(description) > 50:
return description[:50] + "..."
return description
else:
return f"No description available for {command}."
except subprocess.CalledProcessError:
return f"Error fetching description for {command}."


def handle_trivia(player, *args, **kwargs):
"""Lean about zsh, bash, and sh. Get a bonus once per day!"""
if not is_package_installed(player, 'trivia'):
print("trivia has not been installed!")
"""Learn about zsh, bash, and sh. Get a bonus once per day!"""

# Cooldown settings
cooldown_period = timedelta(minutes=10)
current_time = datetime.now(timezone.utc)

# Check if the cooldown has passed
if player.last_trivia_timestamp is not None:
last_attempt_time = player.last_trivia_timestamp
if current_time < last_attempt_time + cooldown_period:
remaining_time = (last_attempt_time + cooldown_period) - current_time
print(f"Your next trivia will be available in {remaining_time.seconds // 60} minutes and {remaining_time.seconds % 60} seconds!")
return

attempts = 5 # Limit the number of rerolls to prevent an infinite loop
command = None
description = None

print("Loading trivia from your system...")
# Reroll the command if the description says "No description available"
for _ in range(attempts):
# Get a random Zsh command
command = get_random_zsh_command()
if not command:
print("Could not fetch a random command.")
return

# Get the description for the random command
description = get_command_description(command)

# Check if description is valid
if "No description available" not in description:
break
else:
print(f"Rerolling... No description available for {command}.")
else:
print("Failed to find a valid command in your ZSH system with a description.")
return

print("Coming soon...")
print(f"What is the name of this command?\n{Fore.GREEN}>> {description} <<{Style.RESET_ALL}")

# Ask the player for the command
user_input = input("% ").strip()

# Check if the user's answer is correct
if user_input == command:
print("That's correct!")
gold_award = max(round(player.gold * (random.random() / 10)), 100)
player.gold += gold_award
print(f"You have been awarded {gold_award} gold!")

# Check for daily bonus
if player.last_trivia_bonus_timestamp is None or current_time >= player.last_trivia_bonus_timestamp + timedelta(days=1):
bonus_gold = gold_award * 10 # 10x bonus
player.gold += bonus_gold
player.last_trivia_bonus_timestamp = current_time
print(f"Daily bonus awarded! You receive an additional {bonus_gold} gold!")
else:
print("You have already claimed your daily bonus.")
else:
print(f"Nope! The correct answer was: {command}")

player.last_trivia_timestamp = current_time



def handle_yum(player, *args, **kwargs):
"""Buy things from the shop."""
Expand Down
4 changes: 2 additions & 2 deletions idlegame/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ def handle_profile(player: AutosavedPlayer, *args, **kwargs) -> None:

if short:
print(f"System Complexity: {player.system_complexity} | Tree Health: {player.tree_health}\n"
f"Gold: {player.gold} | Nanobots: {len(player.nanos)}")
f"Gold: {player.gold} | Nanobots: {len(player.nanobots)}")
else:
print(f"--- {player.username} ---\n"
f"- System Complexity: {player.system_complexity} (see top)\n"
f"- Tree Health: {player.tree_health}\n"
f"- Gold: {player.gold}\n"
f"- Nanobots: {len(player.nanos)} (see ls)")
f"- Nanobots: {len(player.nanobots)} (see ls)")
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='idlegame',
version='1.6.2.1',
version='2.0.0',
packages=find_packages(),
install_requires=[
'colorama',
Expand Down
2 changes: 1 addition & 1 deletion tests/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def test_complexity_updates(self):
"""Test that the complexity of the system updates when a bot is added."""
self.assertEqual(self.player.system_complexity, 0.0, "System complexity should be 0 by default.")

self.player.nanos.append(Nanobot("testbot", "1234567890", Nanotype.NORMAL))
self.player.nanobots.append(Nanobot("testbot", "1234567890", Nanotype.NORMAL))
self.player.update_complexity() # Call a method to update complexity after adding a bot

self.assertEqual(self.player.system_complexity, 1.0, "System complexity should be 1 with a bot with len(10) of code.")
Expand Down

0 comments on commit 4d74074

Please sign in to comment.