├── .gitignore ├── readme.md ├── requirements.txt ├── snake.py └── templates └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .env 3 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 🐍 Claude 3.5 Sssonnet: AI-Powered Snake Game 🧠 2 | 3 | Welcome to the most mind-bending snake game you've ever encountered! 🎮 This isn't your average serpentine adventure – it's a window into the fascinating world of AI decision-making. 4 | 5 | ## 🚀 What's This All About? 6 | 7 | Ever wondered how an AI would play Snake? Well, wonder no more! We've enlisted the help of Claude, to take on the classic game of Snake. This project is a fusion of nostalgia and cutting-edge technology, designed to showcase Claude's reasoning capabilities in real-time. 8 | 9 | ### 🧠 Features That'll Blow Your Mind 10 | 11 | - **AI-Powered Gameplay**: Watch as Claude makes split-second decisions to navigate the snake through the digital wilderness. 12 | - **Real-Time Thought Process**: Peek into Claude's "mind" as it explains its moves. It's like having an AI sportscaster for a snake game! 13 | - **Sleek Dark Mode Interface**: Easy on the eyes, heavy on the style. Perfect for those late-night AI observation sessions. 14 | - **Responsive Design**: From desktop to mobile, Claude slithers smoothly across all devices. 15 | 16 | ## 🛠️ Tech Stack 17 | 18 | - **Backend**: Python with FastAPI 19 | - **Frontend**: HTML5, JavaScript, and the magic of Tailwind CSS 20 | - **AI Brain**: Powered by Anthropic's Claude API – The secret sauce of our intelligent snake 21 | 22 | ## 🚦 Getting Started 23 | 24 | 1. Clone this repository (because who wouldn't want this on their machine?) 25 | 2. Install the requirements: `pip install -r requirements.txt` 26 | 3. Set up your Anthropic API key. 27 | 4. Run the server: `python snake.py` 28 | 5. Open your browser and navigate to `http://localhost:8000` 29 | 30 | ## 🔬 For the Curious Minds 31 | 32 | This project isn't just fun and games (okay, it is, but it's more than that too!). It's a fascinating look into: 33 | 34 | - How AI models like Claude make decisions in real-time environments 35 | - The balance between exploration and exploitation in AI decision-making 36 | - The potential of AI in gaming and beyond 37 | 38 | ## 🤝 Contribute 39 | 40 | Got ideas to make Claude an even better snake charmer? We're all ears! Fork this repo, slither in some improvements, and send us a pull request. 41 | 42 | ## 📜 License 43 | 44 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. 45 | 46 | Remember, in this game, the snake isn't just eating pixels – it's consuming knowledge! 🍎🧠 47 | 48 | Happy coding, and may your snake always find its food! 🐍✨ 49 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | anthropic 4 | websockets 5 | python-dotenv 6 | -------------------------------------------------------------------------------- /snake.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI, WebSocket, WebSocketDisconnect 2 | from fastapi.staticfiles import StaticFiles 3 | from fastapi.responses import HTMLResponse 4 | from anthropic import Anthropic 5 | from dotenv import load_dotenv 6 | import random 7 | import os 8 | import asyncio 9 | import json 10 | import logging 11 | from collections import deque 12 | 13 | load_dotenv() 14 | 15 | logging.basicConfig(level=logging.INFO) 16 | logger = logging.getLogger(__name__) 17 | 18 | # Create static and templates directories if they don't exist 19 | os.makedirs("static", exist_ok=True) 20 | os.makedirs("templates", exist_ok=True) 21 | 22 | # Available Claude models: 23 | # Claude 3 Opus claude-3-opus-20240229 24 | # Claude 3 Sonnet claude-3-sonnet-20240229 25 | # Claude 3 Haiku claude-3-haiku-20240307 26 | # Claude 3.5 Sonnet claude-3-5-sonnet-20240620 27 | # Claude 3.5 Haiku claude-3-5-haiku-20241022 28 | 29 | SNAKE_MODEL = "claude-3-5-haiku-20241022" 30 | 31 | app = FastAPI() 32 | app.mount("/static", StaticFiles(directory="static"), name="static") 33 | 34 | client = Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY")) 35 | 36 | MAX_HISTORY = 60 # Maximum number of previous states to remember 37 | 38 | class SnakeGame: 39 | def __init__(self, width, height): 40 | self.width = width 41 | self.height = height 42 | self.snake = [(width // 2, height // 2)] 43 | self.direction = (1, 0) 44 | self.food = self.generate_food() 45 | self.message_history = deque(maxlen=MAX_HISTORY) 46 | 47 | def generate_food(self): 48 | while True: 49 | food = (random.randint(0, self.width - 1), random.randint(0, self.height - 1)) 50 | if food not in self.snake: 51 | return food 52 | 53 | def move(self): 54 | head = self.snake[0] 55 | new_head = ((head[0] + self.direction[0]) % self.width, (head[1] + self.direction[1]) % self.height) 56 | 57 | if new_head in self.snake[1:]: 58 | return False # Game over 59 | 60 | self.snake.insert(0, new_head) 61 | 62 | if new_head == self.food: 63 | self.food = self.generate_food() 64 | else: 65 | self.snake.pop() 66 | 67 | return True 68 | 69 | def change_direction(self, new_direction): 70 | if (new_direction[0] * -1, new_direction[1] * -1) != self.direction: 71 | self.direction = new_direction 72 | 73 | def get_state(self): 74 | return { 75 | "snake": self.snake, 76 | "food": self.food, 77 | "width": self.width, 78 | "height": self.height 79 | } 80 | 81 | def add_to_history(self, message): 82 | self.message_history.append(message) 83 | 84 | game = SnakeGame(20, 20) 85 | 86 | @app.get("/") 87 | async def get(): 88 | with open('templates/index.html', 'r') as f: 89 | html_content = f.read() 90 | return HTMLResponse(content=html_content) 91 | 92 | @app.websocket("/ws") 93 | async def websocket_endpoint(websocket: WebSocket): 94 | await websocket.accept() 95 | logger.info("WebSocket connection accepted") 96 | try: 97 | while True: 98 | await handle_next_move(websocket) 99 | except WebSocketDisconnect: 100 | logger.info("Client disconnected") 101 | 102 | async def handle_next_move(websocket: WebSocket): 103 | try: 104 | logger.info("Asking Claude for next move...") 105 | 106 | # Prepare the context with game state and message history 107 | context = f"You are a sassy, witty snake in a 20x20 Snake game. You're living your best serpentine life! The current game state is: {game.get_state()}. " 108 | context += "You can wrap around the board" 109 | context += "That food is calling your name - better get there before it goes bad! No pressure, just your entire existence at stake. 🙄" 110 | context += "Use the move_snake tool to work that body! Up, down, left, right - the dance floor is yours!" 111 | context += "Give us your sassy thought process behind your next move (max 5 words).\n\n" 112 | context += "Drop a spicy one-liner about your fabulous life as a snake (5 words max)." 113 | 114 | if game.message_history: 115 | context += "Previous moves:\n" 116 | for msg in game.message_history: 117 | context += f"- {msg}\n" 118 | 119 | response = client.messages.create( 120 | model=SNAKE_MODEL, 121 | max_tokens=1024, 122 | temperature=0.5, 123 | tools=[{ 124 | "name": "move_snake", 125 | "description": "This tool moves the snake in the Snake game in the specified direction. Use this tool when deciding the next move for the snake to avoid obstacles and eat the food. The direction parameter specifies which way the snake should move and can be 'up', 'down', 'left', or 'right'. Ensure that the snake does not move in the opposite direction of its current movement to avoid an immediate collision.", 126 | "input_schema": { 127 | "type": "object", 128 | "properties": { 129 | "direction": { 130 | "type": "string", 131 | "enum": ["up", "down", "left", "right"], 132 | "description": "The direction to move the snake" 133 | } 134 | }, 135 | "required": ["direction"] 136 | } 137 | }], 138 | tool_choice={"type": "auto"}, 139 | # tool_choice={"type": "any"}, 140 | # tool_choice={"type": "tool", "name": "move_snake"}, 141 | messages=[{ 142 | "role": "user", 143 | "content": context 144 | }] 145 | ) 146 | 147 | logger.info(f"Claude's response: {response.content}") 148 | 149 | # Extract thinking and tool use blocks 150 | thinking_block = next((block for block in response.content if block.type == "text"), None) 151 | tool_use_block = next((block for block in response.content if block.type == "tool_use"), None) 152 | 153 | if thinking_block: 154 | logger.info(f"Claude's thought process: {thinking_block.text}") 155 | await websocket.send_json({"type": "claude_thinking", "thought": thinking_block.text}) 156 | 157 | if tool_use_block: 158 | direction = tool_use_block.input["direction"] 159 | logger.info(f"Claude decided to move: {direction}") 160 | 161 | # Send tool usage message 162 | await websocket.send_json({"type": "tool_usage", "direction": direction}) 163 | 164 | # Add the move to the game's message history 165 | game.add_to_history(f"Moved {direction}") 166 | 167 | if direction == "up": 168 | game.change_direction((0, -1)) 169 | elif direction == "down": 170 | game.change_direction((0, 1)) 171 | elif direction == "left": 172 | game.change_direction((-1, 0)) 173 | elif direction == "right": 174 | game.change_direction((1, 0)) 175 | 176 | if not game.move(): 177 | logger.info("Game over") 178 | await websocket.send_json({"type": "game_over"}) 179 | else: 180 | game_state = game.get_state() 181 | logger.info(f"Sending game state: {game_state}") 182 | await websocket.send_json({"type": "game_state", "state": game_state}) 183 | 184 | # Add a delay to slow down the game 185 | await asyncio.sleep(1) 186 | 187 | except Exception as e: 188 | logger.error(f"An error occurred: {e}") 189 | await websocket.send_json({"type": "error", "message": str(e)}) 190 | 191 | if __name__ == "__main__": 192 | import uvicorn 193 | uvicorn.run(app, host="0.0.0.0", port=8000) -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Snake Game with Claude AI 7 | 8 | 19 | 20 | 21 |
22 |
23 |
24 |

Claude 3.5 Sssonnet 🐍

25 |
26 | 27 | 30 |
31 |
32 | 38 |
39 |
40 | 41 | 152 | 153 | 154 | --------------------------------------------------------------------------------