Aframe and Streamlit Test for Self Modify and App as Multiplayer Content
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:
<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 = """
<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()