Add player names to snake display
Show human-readable player names instead of player IDs in both desktop and web clients. Player names are now stored in the Snake model and synchronized across all clients. Changes: - Added player_name field to Snake model - Updated create_snake() to accept player_name parameter - Desktop client shows "YOU:" or "PlayerName:" - Web client shows "You (Name)" or "Name" 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,10 @@
|
||||
"Bash(python:*)",
|
||||
"Bash(pip install:*)",
|
||||
"Bash(git log:*)",
|
||||
"Bash(git add:*)"
|
||||
"Bash(git add:*)",
|
||||
"Bash(git commit -m \"$(cat <<''EOF''\nImplement stuck snake mechanics, persistent colors, and length display\n\nMajor gameplay changes:\n- Snakes no longer die from collisions\n- When blocked, snakes get \"stuck\" - head stays in place, tail shrinks by 1 per tick\n- Snakes auto-unstick when obstacle clears (other snakes move/shrink away)\n- Minimum snake length is 1 (head-only)\n- Game runs continuously without rounds or game-over state\n\nColor system:\n- Each player gets a persistent color for their entire connection\n- Colors assigned on join, rotate through available colors\n- Color follows player even after disconnect/reconnect\n- Works for both desktop and web clients\n\nDisplay improvements:\n- Show snake length instead of score\n- Length accurately reflects current snake size\n- Updates in real-time as snakes grow/shrink\n\nServer fixes:\n- Fixed HTTP server initialization issues\n- Changed default host to 0.0.0.0 for network multiplayer\n- Improved file serving with proper routing\n\nTesting:\n- Updated all collision tests for stuck mechanics\n- Added tests for stuck/unstick behavior\n- Added tests for color persistence\n- All 12 tests passing\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
|
||||
"Bash(git push:*)",
|
||||
"Bash(git commit:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -109,7 +109,7 @@ class Renderer:
|
||||
color = COLOR_SNAKES[snake.color_index % len(COLOR_SNAKES)]
|
||||
|
||||
# Prepare length text
|
||||
prefix = "YOU: " if snake.player_id == player_id else f"Player: "
|
||||
prefix = "YOU: " if snake.player_id == player_id else f"{snake.player_name}: "
|
||||
status = "" if snake.alive else " (DEAD)"
|
||||
text = f"{prefix}Length {len(snake.body)}{status}"
|
||||
|
||||
|
||||
@@ -21,12 +21,13 @@ class GameLogic:
|
||||
"""Initialize game logic."""
|
||||
self.state = GameState()
|
||||
|
||||
def create_snake(self, player_id: str, color_index: int = 0) -> Snake:
|
||||
def create_snake(self, player_id: str, color_index: int = 0, player_name: str = "") -> Snake:
|
||||
"""Create a new snake for a player.
|
||||
|
||||
Args:
|
||||
player_id: Unique identifier for the player
|
||||
color_index: Index in COLOR_SNAKES array for this snake's color
|
||||
player_name: Human-readable name for the player
|
||||
|
||||
Returns:
|
||||
New Snake instance
|
||||
@@ -41,7 +42,7 @@ class GameLogic:
|
||||
for i in range(INITIAL_SNAKE_LENGTH)
|
||||
]
|
||||
|
||||
snake = Snake(player_id=player_id, body=body, direction=RIGHT, color_index=color_index)
|
||||
snake = Snake(player_id=player_id, body=body, direction=RIGHT, color_index=color_index, player_name=player_name)
|
||||
return snake
|
||||
|
||||
def spawn_food(self) -> Food:
|
||||
|
||||
@@ -169,7 +169,7 @@ class GameServer:
|
||||
# Add snake to game if game is running
|
||||
if self.game_logic.state.game_running:
|
||||
color_index = self.player_colors[player_id]
|
||||
snake = self.game_logic.create_snake(player_id, color_index)
|
||||
snake = self.game_logic.create_snake(player_id, color_index, player_name)
|
||||
self.game_logic.state.snakes.append(snake)
|
||||
|
||||
print(f"Player {player_name} ({player_id}) joined via {client_type.value}. Total players: {len(self.clients)}")
|
||||
@@ -193,7 +193,8 @@ class GameServer:
|
||||
self.game_logic.state.snakes = []
|
||||
for player_id in self.clients.keys():
|
||||
color_index = self.player_colors.get(player_id, 0)
|
||||
snake = self.game_logic.create_snake(player_id, color_index)
|
||||
player_name = self.player_names.get(player_id, f"Player_{player_id[:8]}")
|
||||
snake = self.game_logic.create_snake(player_id, color_index, player_name)
|
||||
self.game_logic.state.snakes.append(snake)
|
||||
|
||||
# Spawn initial food
|
||||
|
||||
@@ -34,6 +34,7 @@ class Snake:
|
||||
score: int = 0
|
||||
stuck: bool = False # True when snake is blocked and shrinking
|
||||
color_index: int = 0 # Index in COLOR_SNAKES array for persistent color
|
||||
player_name: str = "" # Human-readable player name
|
||||
|
||||
def get_head(self) -> Position:
|
||||
"""Get the head position of the snake."""
|
||||
@@ -49,6 +50,7 @@ class Snake:
|
||||
"score": self.score,
|
||||
"stuck": self.stuck,
|
||||
"color_index": self.color_index,
|
||||
"player_name": self.player_name,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@@ -61,6 +63,7 @@ class Snake:
|
||||
snake.score = data["score"]
|
||||
snake.stuck = data.get("stuck", False) # Default to False for backward compatibility
|
||||
snake.color_index = data.get("color_index", 0) # Default to 0 for backward compatibility
|
||||
snake.player_name = data.get("player_name", "") # Default to empty string for backward compatibility
|
||||
return snake
|
||||
|
||||
|
||||
|
||||
@@ -322,8 +322,8 @@ class GameClient {
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'player-name';
|
||||
nameSpan.textContent = snake.player_id === this.playerId ?
|
||||
`You (${snake.player_id.substring(0, 8)})` :
|
||||
snake.player_id.substring(0, 8);
|
||||
`You (${snake.player_name})` :
|
||||
snake.player_name;
|
||||
|
||||
const scoreSpan = document.createElement('span');
|
||||
scoreSpan.className = 'player-score';
|
||||
|
||||
Reference in New Issue
Block a user