Aframe and Streamlit Test for Self Modify and App as Multiplayer Content

#2
by awacke1 - opened

Create a python streamlit app that maintains an html5 app inside that shows a aframe scene. As the user uses it maintains gameplay and clicks save, save the current state of the html5 aframe code. Use the streamlit app to make changes to the app as content with the html5 and then use streamlit sessions to keep variables that are changing in one place. Save the new copy of the file for everyone and provide refresh timer at 5 seconds that reloads the file to pick up changes from any client. When the player joins create a random name for them and add them to the scene, which is a 3d house with about 20 rooms. Make this random at first but add each player. Use the program below as a starter app:

Chofko's Diverse Ecosystem Simulator surrealist sky
  <a-sky src="#sky"></a-sky>

  <a-plane position="0 0 -4" rotation="-90 0 0" width="100" height="100" color="#7BC8A4"></a-plane>

  <a-entity id="entities"></a-entity>

  <a-entity id="camera" camera look-controls position="0 40 0" rotation="-90 0 0"></a-entity>
</a-scene>

<div id="score">Score: 0</div>

<div class="controls">
  <button onmousedown="startMove('left')" onmouseup="stopMove('left')" ontouchstart="startMove('left')" ontouchend="stopMove('left')">Left</button>
  <button onmousedown="startMove('right')" onmouseup="stopMove('right')" ontouchstart="startMove('right')" ontouchend="stopMove('right')">Right</button>
  <button onmousedown="startMove('up')" onmouseup="stopMove('up')" ontouchstart="startMove('up')" ontouchend="stopMove('up')">Up</button>
  <button onmousedown="startMove('down')" onmouseup="stopMove('down')" ontouchstart="startMove('down')" ontouchend="stopMove('down')">Down</button>
  <button onclick="toggleSpeed()">Toggle Speed</button>
</div>

<script>
  const entitiesEl = document.querySelector('#entities');
  const entities = [];
  const initialEntityCount = 100;
  let score = 0;
  const maxEntities = 300;
  let isSlowMode = false;

  const primitiveTypes = ['a-sphere', 'a-box', 'a-cone', 'a-cylinder', 'a-tetrahedron', 'a-octahedron'];
  const typeColors = {
    'a-sphere': ['#FF69B4', '#FF1493'],
    'a-box': ['#4169E1', '#1E90FF'],
    'a-cone': ['#32CD32', '#00FF00'],
    'a-cylinder': ['#FFD700', '#FFA500'],
    'a-tetrahedron': ['#8A2BE2', '#9400D3'],
    'a-octahedron': ['#FF4500', '#FF6347']
  };

  class Entity {
    constructor(x, z, isPlayer = false, type = null, gender = null, radius = null) {
      this.isPlayer = isPlayer;
      this.type = type || (isPlayer ? 'a-sphere' : primitiveTypes[Math.floor(Math.random() * primitiveTypes.length)]);
      this.gender = gender || (Math.random() < 0.5 ? 'male' : 'female');
      this.el = document.createElement(this.type);
      this.radius = radius || (isPlayer ? 1.5 : 0.5);
      this.color = isPlayer ? '#FF00FF' : typeColors[this.type][this.gender === 'male' ? 0 : 1];
      
      if (this.type === 'a-sphere' || this.type === 'a-cylinder') {
        this.el.setAttribute('radius', this.radius);
      } else {
        this.el.setAttribute('width', this.radius * 2);
        this.el.setAttribute('height', this.radius * 2);
        this.el.setAttribute('depth', this.radius * 2);
      }

      this.el.setAttribute('color', this.color);
      this.el.setAttribute('position', {x: x, y: this.radius, z: z});
      entitiesEl.appendChild(this.el);
      
      this.velocity = {
        x: isPlayer ? 0 : (Math.random() - 0.5) * 0.2,
        z: isPlayer ? 0 : (Math.random() - 0.5) * 0.2
      };
      
      this.acceleration = { x: 0, z: 0 };
      
      if (!isPlayer) {
        entities.push(this);
      }

      if (isPlayer) {
        // Add a text label above the player
        this.label = document.createElement('a-text');
        this.label.setAttribute('value', 'PLAYER');
        this.label.setAttribute('align', 'center');
        this.label.setAttribute('position', {x: 0, y: this.radius * 2, z: 0});
        this.label.setAttribute('scale', {x: 5, y: 5, z: 5});
        this.label.setAttribute('color', '#FFFFFF');
        this.el.appendChild(this.label);
      }
    }

    update(deltaTime) {
      this.velocity.x += this.acceleration.x * deltaTime;
      this.velocity.z += this.acceleration.z * deltaTime;

      this.velocity.x *= 0.99;
      this.velocity.z *= 0.99;

      const position = this.el.getAttribute('position');
      position.x += this.velocity.x * deltaTime;
      position.z += this.velocity.z * deltaTime;

      if (Math.abs(position.x) > 50) {
        this.velocity.x *= -1;
        position.x = Math.sign(position.x) * 50;
      }
      if (Math.abs(position.z) > 50) {
        this.velocity.z *= -1;
        position.z = Math.sign(position.z) * 50;
      }

      this.el.setAttribute('position', position);

      this.acceleration = { x: 0, z: 0 };
    }

    applyForce(force) {
      this.acceleration.x += force.x / this.radius;
      this.acceleration.z += force.z / this.radius;
    }

    grow(amount) {
      this.radius += amount;
      if (this.type === 'a-sphere' || this.type === 'a-cylinder') {
        this.el.setAttribute('radius', this.radius);
      } else {
        this.el.setAttribute('width', this.radius * 2);
        this.el.setAttribute('height', this.radius * 2);
        this.el.setAttribute('depth', this.radius * 2);
      }
      const position = this.el.getAttribute('position');
      position.y = this.radius;
      this.el.setAttribute('position', position);

      if (this.isPlayer && this.label) {
        this.label.setAttribute('position', {x: 0, y: this.radius * 2, z: 0});
      }
    }

    remove() {
      entitiesEl.removeChild(this.el);
      const index = entities.indexOf(this);
      if (index > -1) {
        entities.splice(index, 1);
      }
    }
  }

  const player = new Entity(0, 0, true);
  let moveDirection = { x: 0, z: 0 };

  function createEntities() {
    for (let i = 0; i < initialEntityCount; i++) {
      new Entity(
        (Math.random() - 0.5) * 100,
        (Math.random() - 0.5) * 100
      );
    }
  }

  function spawnNewEntity() {
    if (entities.length < maxEntities && Math.random() < 0.02) {
      new Entity(
        (Math.random() - 0.5) * 100,
        (Math.random() - 0.5) * 100
      );
    }
  }

  function startMove(direction) {
    switch(direction) {
      case 'left': moveDirection.x = -1; break;
      case 'right': moveDirection.x = 1; break;
      case 'up': moveDirection.z = -1; break;
      case 'down': moveDirection.z = 1; break;
    }
  }

  function stopMove(direction) {
    switch(direction) {
      case 'left': case 'right': moveDirection.x = 0; break;
      case 'up': case 'down': moveDirection.z = 0; break;
    }
  }

  function toggleSpeed() {
    isSlowMode = !isSlowMode;
  }

  function canEat(entity1, entity2) {
    const typeIndex = primitiveTypes.indexOf(entity1.type);
    const otherTypeIndex = primitiveTypes.indexOf(entity2.type);
    return (typeIndex + 1) % primitiveTypes.length === otherTypeIndex;
  }

  function tryMating(entity1, entity2) {
    if (entity1.type === entity2.type && entity1.gender !== entity2.gender && Math.random() < 0.1) {
      const childRadius = (entity1.radius + entity2.radius) / 2 * 0.8;
      new Entity(
        (entity1.el.getAttribute('position').x + entity2.el.getAttribute('position').x) / 2,
        (entity1.el.getAttribute('position').z + entity2.el.getAttribute('position').z) / 2,
        false,
        entity1.type,
        Math.random() < 0.5 ? 'male' : 'female',
        childRadius
      );
    }
  }

  function checkCollisions() {
    const playerPosition = player.el.getAttribute('position');
    for (let i = 0; i < entities.length; i++) {
      const entity = entities[i];
      const entityPosition = entity.el.getAttribute('position');
      const dx = playerPosition.x - entityPosition.x;
      const dz = playerPosition.z - entityPosition.z;
      const distance = Math.sqrt(dx * dx + dz * dz);
      
      if (distance < player.radius + entity.radius) {
        if (player.radius > entity.radius * 1.5 && canEat(player, entity)) {
          player.grow(entity.radius * 0.02);  // Further reduced growth rate
          entity.remove();
          score += 1;
          updateScore();
          i--;
        } else {
          const angle = Math.atan2(dz, dx);
          const force = 0.01 / distance;
          player.applyForce({ x: -Math.cos(angle) * force, z: -Math.sin(angle) * force });
          entity.applyForce({ x: Math.cos(angle) * force, z: Math.sin(angle) * force });
        }
      }

      for (let j = i + 1; j < entities.length; j++) {
        const otherEntity = entities[j];
        const otherPosition = otherEntity.el.getAttribute('position');
        const entityDx = entityPosition.x - otherPosition.x;
        const entityDz = entityPosition.z - otherPosition.z;
        const entityDistance = Math.sqrt(entityDx * entityDx + entityDz * entityDz);

        if (entityDistance < entity.radius + otherEntity.radius) {
          if (entity.radius > otherEntity.radius * 1.2 && canEat(entity, otherEntity)) {
            entity.grow(otherEntity.radius * 0.1);
            otherEntity.remove();
            j--;
          } else if (otherEntity.radius > entity.radius * 1.2 && canEat(otherEntity, entity)) {
            otherEntity.grow(entity.radius * 0.1);
            entity.remove();
            i--;
            break;
          } else {
            tryMating(entity, otherEntity);
            const angle = Math.atan2(entityDz, entityDx);
            const force = 0.005 / entityDistance;
            entity.applyForce({ x: -Math.cos(angle) * force, z: -Math.sin(angle) * force });
            otherEntity.applyForce({ x: Math.cos(angle) * force, z: Math.sin(angle) * force });
          }
        }
      }
    }
  }

  function updateScore() {
    document.getElementById('score').textContent = `Score: ${score}`;
  }

  let lastTime = 0;
  function gameLoop(timestamp) {
    const deltaTime = timestamp - lastTime;
    lastTime = timestamp;

    const speedFactor = isSlowMode ? 0.5 : 1;
    player.applyForce({ 
      x: moveDirection.x * 0.0005 * speedFactor, 
      z: moveDirection.z * 0.0005 * speedFactor 
    });
    
    entities.forEach(e => e.update(deltaTime * speedFactor));
    player.update(deltaTime * speedFactor);
    checkCollisions();
    spawnNewEntity();
    requestAnimationFrame(gameLoop);
  }

  document.querySelector('a-scene').addEventListener('loaded', function () {
    createEntities();
    requestAnimationFrame(gameLoop);
  });

  document.addEventListener('keydown', (event) => {
    switch(event.key) {
      case 'ArrowLeft': startMove('left'); break;
      case 'ArrowRight': startMove('right'); break;
      case 'ArrowUp': startMove('up'); break;
      case 'ArrowDown': startMove('down'); break;
      case 'Shift': toggleSpeed(); break;
    }
  });

  document.addEventListener('keyup', (event) => {
    switch(event.key) {
      case 'ArrowLeft': stopMove('left'); break;
      case 'ArrowRight': stopMove('right'); break;
      case 'ArrowUp': stopMove('up'); break;
      case 'ArrowDown': stopMove('down'); break;
    }
  });
</script>
use the program here as a template for the streamlit app part: import streamlit as st import anthropic import os import base64 import glob import json import pytz from datetime import datetime from streamlit.components.v1 import html from PIL import Image import re from urllib.parse import quote

Set up the Anthropic client

client = anthropic.Anthropic(api_key=os.environ.get("ANTHROPIC_API_KEY"))

Initialize session state

if "chat_history" not in st.session_state:
st.session_state.chat_history = []

Function to get file download link

def get_download_link(file_path):
with open(file_path, "rb") as file:
contents = file.read()
b64 = base64.b64encode(contents).decode()
file_name = os.path.basename(file_path)
return f'Download {file_name}'

Function to generate filename

def generate_filename(prompt, file_type):
central = pytz.timezone('US/Central')
safe_date_time = datetime.now(central).strftime("%m%d_%H%M")
replaced_prompt = prompt.replace(" ", "").replace("\n", "")
safe_prompt = "".join(x for x in replaced_prompt if x.isalnum() or x == "")[:90]
return f"{safe_date_time}
{safe_prompt}.{file_type}"

Function to create file

def create_file(filename, prompt, response, should_save=True):
if not should_save:
return
with open(filename, 'w', encoding='utf-8') as file:
file.write(prompt + "\n\n" + response)

Function to load file content

def load_file(file_name):
with open(file_name, "r", encoding='utf-8') as file:
content = file.read()
return content

Function to display glossary entity

def display_glossary_entity(k):
search_urls = {
"πŸš€πŸŒŒArXiv": lambda k: f"/?q={quote(k)}",
"πŸ“–": lambda k: f"https://en.wikipedia.org/wiki/{quote(k)}",
"πŸ”": lambda k: f"https://www.google.com/search?q={quote(k)}",
"πŸŽ₯": lambda k: f"https://www.youtube.com/results?search_query={quote(k)}",
}
links_md = ' '.join([f"{emoji}" for emoji, url in search_urls.items()])
st.markdown(f"{k} {links_md}", unsafe_allow_html=True)

Function to create zip of files

def create_zip_of_files(files):
import zipfile
zip_name = "all_files.zip"
with zipfile.ZipFile(zip_name, 'w') as zipf:
for file in files:
zipf.write(file)
return zip_name

Function to get zip download link

def get_zip_download_link(zip_file):
with open(zip_file, 'rb') as f:
data = f.read()
b64 = base64.b64encode(data).decode()
href = f'Download All'
return href

Streamlit app

def main():
st.title("Claude 3.5 Sonnet API Demo")

# Sidebar
st.sidebar.title("File Operations")

# File management
all_files = glob.glob("*.md")
all_files = [file for file in all_files if len(os.path.splitext(file)[0]) >= 10]
all_files.sort(key=lambda x: (os.path.splitext(x)[1], x), reverse=True)

if st.sidebar.button("πŸ—‘ Delete All"):
    for file in all_files:
        os.remove(file)
    st.rerun()

if st.sidebar.button("⬇️ Download All"):
    zip_file = create_zip_of_files(all_files)
    st.sidebar.markdown(get_zip_download_link(zip_file), unsafe_allow_html=True)

for file in all_files:
    col1, col2, col3, col4 = st.sidebar.columns([1,3,1,1])
    with col1:
        if st.button("🌐", key="view_"+file):
            st.session_state.current_file = file
            st.session_state.file_content = load_file(file)
    with col2:
        st.markdown(get_download_link(file), unsafe_allow_html=True)
    with col3:
        if st.button("πŸ“‚", key="edit_"+file):
            st.session_state.current_file = file
            st.session_state.file_content = load_file(file)
    with col4:
        if st.button("πŸ—‘", key="delete_"+file):
            os.remove(file)
            st.rerun()

# Main content area
user_input = st.text_area("Enter your message:", height=100)

if st.button("Send"):
    if user_input:
        response = client.messages.create(
            model="claude-3-sonnet-20240229",
            max_tokens=1000,
            messages=[
                {"role": "user", "content": user_input}
            ]
        )
        
        st.write("Claude's response:")
        st.write(response.content[0].text)

        # Save response to file
        filename = generate_filename(user_input, "md")
        create_file(filename, user_input, response.content[0].text)

        # Add to chat history
        st.session_state.chat_history.append({"user": user_input, "claude": response.content[0].text})

# Display chat history
st.subheader("Chat History")
for chat in st.session_state.chat_history:
    st.text_area("You:", chat["user"], height=100, disabled=True)
    st.text_area("Claude:", chat["claude"], height=200, disabled=True)
    st.markdown("---")

# File content viewer/editor
if hasattr(st.session_state, 'current_file'):
    st.subheader(f"Editing: {st.session_state.current_file}")
    new_content = st.text_area("File Content:", st.session_state.file_content, height=300)
    if st.button("Save Changes"):
        with open(st.session_state.current_file, 'w', encoding='utf-8') as file:
            file.write(new_content)
        st.success("File updated successfully!")

# Image Gallery
st.subheader("Image Gallery")
image_files = glob.glob("*.png") + glob.glob("*.jpg") + glob.glob("*.jpeg")
cols = st.columns(3)
for idx, image_file in enumerate(image_files):
    with cols[idx % 3]:
        img = Image.open(image_file)
        st.image(img, caption=image_file, use_column_width=True)
        display_glossary_entity(os.path.splitext(image_file)[0])

# Video Gallery
st.subheader("Video Gallery")
video_files = glob.glob("*.mp4")
for video_file in video_files:
    st.video(video_file)
    display_glossary_entity(os.path.splitext(video_file)[0])

if name == "main":
main()

Claude's response:

Sure, I can help you with that! Here's a Python Streamlit app that maintains an HTML5 app inside it, showing an A-Frame scene. The app saves the current state of the HTML5 A-Frame code when the user clicks "Save." It also allows you to make changes to the app's content using the Streamlit interface and keeps track of the changing variables using Streamlit sessions. Additionally, it saves a new copy of the file for everyone and provides a refresh timer that reloads the file every 5 seconds to pick up changes from any client. When a player joins, it creates a random name for them and adds them to the scene, a 3D house with about 20 rooms.

import streamlit as st
import streamlit.components.v1 as components
import random
import string
from datetime import datetime, timedelta

Initial HTML5 A-Frame code

initial_html_code = """

Chofko's Diverse Ecosystem Simulator surrealist sky
  <a-sky src="#sky"></a-sky>

  <a-plane position="0 0 -4" rotation="-90 0 0" width="100" height="100" color="#7BC8A4"></a-plane>

  <a-entity id="entities"></a-entity>

  <a-entity id="camera" camera look-controls position="0 40 0" rotation="-90 0 0"></a-entity>
</a-scene>

<div id="score">Score: 0</div>

<div class="controls">
  <button onmousedown="startMove('left')" onmouseup="stopMove('left')" ontouchstart="startMove('left')" ontouchend="stopMove('left')">Left</button>
  <button onmousedown="startMove('right')" onmouseup="stopMove('right')" ontouchstart="startMove('right')" ontouchend="stopMove('right')">Right</button>
  <button onmousedown="startMove('up')" onmouseup="stopMove('up')" ontouchstart="startMove('up')" ontouchend="stopMove('up')">Up</button>
  <button onmousedown="startMove('down')" onmouseup="stopMove('down')" ontouchstart="startMove('down')" ontouchend="stopMove('down')">Down</button>
  <button onclick="toggleSpeed()">Toggle Speed</button>
</div>

<script>
  // ... (the rest of the JavaScript code from the provided program)
</script>
"""

Helper function to generate random names

def generate_random_name():
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for _ in range(10))

Initialize session state

if "html_code" not in st.session_state:
st.session_state.html_code = initial

Show full code listing. Create a python streamlit app that maintains an html5 app inside that shows a aframe scene. As the user uses it maintains gameplay and clicks save, save the current state of the html5 aframe code. Use the streamlit app to make changes to the app as content with the html5 and then use streamlit sessions to keep variables that are changing in one place. Save the new copy of the file for everyone and provide refresh timer at 5 seconds that reloads the file to pick up changes from any client. When the player joins create a random name for them and add them to the scene, which is a 3d house with about 20 rooms. Make this random at first but add each player. Use the program below as a starter app:

...

import streamlit as st
import streamlit.components.v1 as components
import random
import string
import time

Load the initial HTML5 app

with open("aframe_app.html", "r") as f:
initial_html = f.read()

Initialize session state

if "html_content" not in st.session_state:
st.session_state.html_content = initial_html
if "player_names" not in st.session_state:
st.session_state.player_names = []

Function to generate a random name for a new player

def generate_player_name():
adjectives = ["Adventurous", "Brave", "Curious", "Daring", "Fearless", "Intrepid", "Valiant"]
nouns = ["Explorer", "Wanderer", "Pathfinder", "Trailblazer", "Voyager", "Wayfarer", "Traveler"]
return random.choice(adjectives) + " " + random.choice(nouns)

Function to add a new player to the scene

def add_player():
player_name = generate_player_name()
while player_name in st.session_state.player_names:
player_name = generate_player_name()
st.session_state.player_names.append(player_name)
player_html = f''
st.session_state.html_content = st.session_state.html_content.replace("", player_html + "\n")

Streamlit app

def main():
st.set_page_config(page_title="3D House Exploration", layout="wide")
st.title("3D House Exploration")

# Add a new player button
if st.button("Join Game"):
    add_player()

# Display the HTML5 app
components.html(st.session_state.html_content, height=800)

# Editor for HTML5 content
st.subheader("Edit Content")
new_html = st.text_area("HTML5 Content", st.session_state.html_content, height=300)

# Save button
if st.button("Save"):
    st.session_state.html_content = new_html

# Refresh timer
last_refresh = st.button("Refresh")
if last_refresh:
    st.experimental_rerun()

if not st.button("Refresh", key="refresh_button"):
    refresh_timer = st.empty()
    while True:
        time.sleep(5)
        refresh_timer.text(f"Refreshing in {5 - int(time.time()) % 5} seconds...")
        if st.button("Refresh", key="refresh_button"):
            st.experimental_rerun()
            break

if name == "main":
main()

Sign up or log in to comment