fucked UI
This commit is contained in:
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Environment-dependent path to Maven home directory
|
||||
/mavenHomeManager.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
12
.idea/material_theme_project_new.xml
generated
Normal file
12
.idea/material_theme_project_new.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MaterialThemeProjectNewConfig">
|
||||
<option name="metadata">
|
||||
<MTProjectMetadataState>
|
||||
<option name="migrated" value="true" />
|
||||
<option name="pristineConfig" value="false" />
|
||||
<option name="userId" value="3730c89c:19672acdc5f:-7ff9" />
|
||||
</MTProjectMetadataState>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
14
.idea/misc.xml
generated
Normal file
14
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="openjdk-24" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -10,7 +10,7 @@ import java.util.List;
|
||||
public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
private javax.swing.Timer timer;
|
||||
private int gridSize = 20;
|
||||
private int cellSize = 20;
|
||||
private int cellSize = 25;
|
||||
private int delay = 100;
|
||||
|
||||
private Snake snake;
|
||||
@@ -27,6 +27,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
private JCheckBox showPathBox;
|
||||
private JLabel scoreLabel;
|
||||
private JLabel statusLabel;
|
||||
private JPanel gamePanel;
|
||||
|
||||
public HamiltonianSnakeAI() {
|
||||
initGame();
|
||||
@@ -42,87 +43,152 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
|
||||
private void setupUI() {
|
||||
setLayout(new BorderLayout());
|
||||
setBackground(Color.BLACK);
|
||||
setBackground(new Color(30, 30, 30));
|
||||
|
||||
// Game panel
|
||||
JPanel gamePanel = new JPanel() {
|
||||
// Game panel with better styling
|
||||
gamePanel = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
draw(g);
|
||||
}
|
||||
};
|
||||
gamePanel.setBackground(Color.BLACK);
|
||||
gamePanel.setBackground(new Color(20, 20, 20));
|
||||
gamePanel.setBorder(BorderFactory.createLineBorder(new Color(60, 60, 60), 2));
|
||||
gamePanel.setPreferredSize(new Dimension(gridSize * cellSize, gridSize * cellSize));
|
||||
|
||||
// Control panel
|
||||
JPanel controlPanel = new JPanel(new GridLayout(6, 2, 5, 5));
|
||||
controlPanel.setBackground(Color.DARK_GRAY);
|
||||
controlPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
// Control panel with modern look
|
||||
JPanel controlPanel = new JPanel();
|
||||
controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.Y_AXIS));
|
||||
controlPanel.setBackground(new Color(40, 40, 40));
|
||||
controlPanel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
|
||||
|
||||
// Title label
|
||||
JLabel titleLabel = new JLabel("Hamiltonian Snake AI");
|
||||
titleLabel.setFont(new Font("Arial", Font.BOLD, 18));
|
||||
titleLabel.setForeground(new Color(220, 220, 220));
|
||||
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
|
||||
controlPanel.add(titleLabel);
|
||||
controlPanel.add(Box.createRigidArea(new Dimension(0, 15)));
|
||||
|
||||
// Speed control
|
||||
controlPanel.add(new JLabel("Speed:", SwingConstants.CENTER));
|
||||
speedSlider = new JSlider(1, 20, 10);
|
||||
JPanel speedPanel = createControlPanel("Speed:", speedSlider = new JSlider(1, 20, 10));
|
||||
speedSlider.addChangeListener(e -> {
|
||||
delay = 210 - speedSlider.getValue() * 10;
|
||||
if (timer != null) timer.setDelay(delay);
|
||||
});
|
||||
controlPanel.add(speedSlider);
|
||||
controlPanel.add(speedPanel);
|
||||
|
||||
// Grid size control
|
||||
controlPanel.add(new JLabel("Grid Size:", SwingConstants.CENTER));
|
||||
gridSlider = new JSlider(10, 50, 20); // Reduced max grid size for better performance
|
||||
JPanel gridPanel = createControlPanel("Grid Size:", gridSlider = new JSlider(10, 30, 20));
|
||||
gridSlider.addChangeListener(e -> {
|
||||
if (!gameRunning) {
|
||||
gridSize = gridSlider.getValue();
|
||||
cellSize = Math.max(5, Math.min(20, 800 / gridSize)); // Minimum cell size of 5
|
||||
cellSize = Math.max(15, Math.min(30, 600 / gridSize)); // Better cell size range
|
||||
gamePanel.setPreferredSize(new Dimension(gridSize * cellSize, gridSize * cellSize));
|
||||
resetGame();
|
||||
revalidate();
|
||||
repaint();
|
||||
}
|
||||
});
|
||||
controlPanel.add(gridSlider);
|
||||
controlPanel.add(gridPanel);
|
||||
|
||||
// Control buttons
|
||||
startButton = new JButton("Start");
|
||||
// Button panel
|
||||
JPanel buttonPanel = new JPanel(new GridLayout(1, 3, 10, 0));
|
||||
buttonPanel.setBackground(new Color(40, 40, 40));
|
||||
buttonPanel.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
|
||||
|
||||
startButton = createStyledButton("Start");
|
||||
startButton.addActionListener(e -> startGame());
|
||||
controlPanel.add(startButton);
|
||||
buttonPanel.add(startButton);
|
||||
|
||||
pauseButton = new JButton("Pause");
|
||||
pauseButton = createStyledButton("Pause");
|
||||
pauseButton.addActionListener(e -> pauseGame());
|
||||
pauseButton.setEnabled(false);
|
||||
controlPanel.add(pauseButton);
|
||||
buttonPanel.add(pauseButton);
|
||||
|
||||
// Show path checkbox
|
||||
showPathBox = new JCheckBox("Show Path");
|
||||
JButton resetButton = createStyledButton("Reset");
|
||||
resetButton.addActionListener(e -> resetGame());
|
||||
buttonPanel.add(resetButton);
|
||||
|
||||
controlPanel.add(buttonPanel);
|
||||
|
||||
// Checkbox panel
|
||||
JPanel checkBoxPanel = new JPanel();
|
||||
checkBoxPanel.setBackground(new Color(40, 40, 40));
|
||||
showPathBox = new JCheckBox("Show Hamiltonian Path");
|
||||
showPathBox.setForeground(Color.WHITE);
|
||||
showPathBox.setBackground(new Color(40, 40, 40));
|
||||
showPathBox.setFocusPainted(false);
|
||||
showPathBox.addActionListener(e -> {
|
||||
showPath = showPathBox.isSelected();
|
||||
repaint();
|
||||
});
|
||||
controlPanel.add(showPathBox);
|
||||
checkBoxPanel.add(showPathBox);
|
||||
controlPanel.add(checkBoxPanel);
|
||||
|
||||
JButton resetButton = new JButton("Reset");
|
||||
resetButton.addActionListener(e -> resetGame());
|
||||
controlPanel.add(resetButton);
|
||||
// Status panel
|
||||
JPanel statusPanel = new JPanel(new GridLayout(2, 1, 5, 5));
|
||||
statusPanel.setBackground(new Color(40, 40, 40));
|
||||
|
||||
// Status labels
|
||||
scoreLabel = new JLabel("Score: 0", SwingConstants.CENTER);
|
||||
scoreLabel.setForeground(Color.WHITE);
|
||||
controlPanel.add(scoreLabel);
|
||||
scoreLabel = createStatusLabel("Score: 0");
|
||||
statusPanel.add(scoreLabel);
|
||||
|
||||
statusLabel = new JLabel("Ready", SwingConstants.CENTER);
|
||||
statusLabel.setForeground(Color.GREEN);
|
||||
controlPanel.add(statusLabel);
|
||||
statusLabel = createStatusLabel("Ready");
|
||||
statusLabel.setForeground(new Color(100, 255, 100));
|
||||
statusPanel.add(statusLabel);
|
||||
|
||||
controlPanel.add(statusPanel);
|
||||
|
||||
add(gamePanel, BorderLayout.CENTER);
|
||||
add(controlPanel, BorderLayout.EAST);
|
||||
}
|
||||
|
||||
private JPanel createControlPanel(String labelText, JSlider slider) {
|
||||
JPanel panel = new JPanel(new BorderLayout(10, 0));
|
||||
panel.setBackground(new Color(40, 40, 40));
|
||||
|
||||
JLabel label = new JLabel(labelText);
|
||||
label.setForeground(Color.WHITE);
|
||||
panel.add(label, BorderLayout.WEST);
|
||||
|
||||
slider.setBackground(new Color(40, 40, 40));
|
||||
slider.setForeground(Color.WHITE);
|
||||
slider.setPaintTicks(true);
|
||||
slider.setPaintLabels(true);
|
||||
slider.setMajorTickSpacing(slider.getMaximum() / 4);
|
||||
panel.add(slider, BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JButton createStyledButton(String text) {
|
||||
JButton button = new JButton(text);
|
||||
button.setBackground(new Color(70, 70, 70));
|
||||
button.setForeground(Color.WHITE);
|
||||
button.setFocusPainted(false);
|
||||
button.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createLineBorder(new Color(100, 100, 100), 1),
|
||||
BorderFactory.createEmptyBorder(5, 15, 5, 15)
|
||||
));
|
||||
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
return button;
|
||||
}
|
||||
|
||||
private JLabel createStatusLabel(String text) {
|
||||
JLabel label = new JLabel(text, SwingConstants.CENTER);
|
||||
label.setFont(new Font("Arial", Font.BOLD, 14));
|
||||
label.setForeground(Color.WHITE);
|
||||
label.setOpaque(true);
|
||||
label.setBackground(new Color(50, 50, 50));
|
||||
label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
return label;
|
||||
}
|
||||
|
||||
private void generateHamiltonianCycle() {
|
||||
hamiltonianPath = new int[gridSize][gridSize];
|
||||
|
||||
// Generate a more efficient Hamiltonian cycle
|
||||
// Generate a more efficient Hamiltonian cycle that's better for longer snakes
|
||||
if (gridSize % 2 == 0) {
|
||||
generateEvenGridCycle();
|
||||
} else {
|
||||
@@ -148,7 +214,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
}
|
||||
|
||||
private void generateOddGridCycle() {
|
||||
// More complex cycle for odd-sized grids
|
||||
// More complex cycle for odd-sized grids that reduces tail collisions
|
||||
int pathIndex = 0;
|
||||
int row = 0, col = 0;
|
||||
boolean movingRight = true;
|
||||
@@ -160,28 +226,70 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
if (col < gridSize - 1) {
|
||||
col++;
|
||||
} else {
|
||||
// At right edge, move down and reverse direction
|
||||
row++;
|
||||
if (row < gridSize) {
|
||||
hamiltonianPath[row][col] = pathIndex++;
|
||||
}
|
||||
movingRight = false;
|
||||
}
|
||||
} else {
|
||||
if (col > 0) {
|
||||
col--;
|
||||
} else {
|
||||
// At left edge, move down and reverse direction
|
||||
row++;
|
||||
if (row < gridSize) {
|
||||
hamiltonianPath[row][col] = pathIndex++;
|
||||
}
|
||||
movingRight = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect the last cell back to the first
|
||||
hamiltonianPath[gridSize-1][0] = pathIndex;
|
||||
}
|
||||
|
||||
private void spawnFood() {
|
||||
Random rand = new Random();
|
||||
int attempts = 0;
|
||||
int maxAttempts = 100;
|
||||
|
||||
do {
|
||||
food = new Point(rand.nextInt(gridSize), rand.nextInt(gridSize));
|
||||
} while (snake.contains(food));
|
||||
attempts++;
|
||||
// Ensure food isn't placed where it would be impossible to reach
|
||||
if (attempts >= maxAttempts || !snake.contains(food)) {
|
||||
break;
|
||||
}
|
||||
} while (snake.contains(food) || isFoodInDeadZone(food));
|
||||
}
|
||||
|
||||
private boolean isFoodInDeadZone(Point foodPos) {
|
||||
// Check if food is placed in a position that would make it unreachable
|
||||
// due to the snake's body blocking all paths
|
||||
Set<Point> obstacles = new HashSet<>(snake.getBody());
|
||||
obstacles.remove(snake.getTail());
|
||||
|
||||
// Simple flood fill to check reachability from snake head
|
||||
Set<Point> visited = new HashSet<>();
|
||||
Queue<Point> queue = new LinkedList<>();
|
||||
queue.add(snake.getHead());
|
||||
visited.add(snake.getHead());
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
Point current = queue.poll();
|
||||
if (current.equals(foodPos)) {
|
||||
return false; // Food is reachable
|
||||
}
|
||||
|
||||
for (Point neighbor : getNeighbors(current)) {
|
||||
if (!visited.contains(neighbor) && !obstacles.contains(neighbor)) {
|
||||
visited.add(neighbor);
|
||||
queue.add(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Food is in a dead zone
|
||||
}
|
||||
|
||||
private void startGame() {
|
||||
@@ -191,7 +299,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
pauseButton.setEnabled(true);
|
||||
gridSlider.setEnabled(false);
|
||||
statusLabel.setText("Running");
|
||||
statusLabel.setForeground(Color.GREEN);
|
||||
statusLabel.setForeground(new Color(100, 255, 100));
|
||||
}
|
||||
|
||||
private void pauseGame() {
|
||||
@@ -214,7 +322,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
gridSlider.setEnabled(true);
|
||||
scoreLabel.setText("Score: 0");
|
||||
statusLabel.setText("Ready");
|
||||
statusLabel.setForeground(Color.GREEN);
|
||||
statusLabel.setForeground(new Color(100, 255, 100));
|
||||
repaint();
|
||||
}
|
||||
|
||||
@@ -228,7 +336,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
gameRunning = false;
|
||||
timer.stop();
|
||||
statusLabel.setText("YOU WIN!");
|
||||
statusLabel.setForeground(Color.yellow);
|
||||
statusLabel.setForeground(Color.YELLOW);
|
||||
startButton.setEnabled(true);
|
||||
pauseButton.setEnabled(false);
|
||||
}
|
||||
@@ -268,38 +376,53 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
return head; // Stay in place (will trigger collision detection)
|
||||
}
|
||||
|
||||
// If we can reach food safely with a shortcut, take it
|
||||
Point bestMove = findBestShortcut(head, possibleMoves);
|
||||
if (bestMove != null) {
|
||||
return bestMove;
|
||||
// For longer snakes, be more cautious about shortcuts
|
||||
if (snake.getLength() < gridSize * gridSize * 0.6) {
|
||||
// Try to find a safe shortcut to food
|
||||
Point bestMove = findBestShortcut(head, possibleMoves);
|
||||
if (bestMove != null) {
|
||||
return bestMove;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise follow the Hamiltonian cycle
|
||||
// Follow the Hamiltonian cycle with tail collision awareness
|
||||
return followHamiltonianCycle(head, possibleMoves);
|
||||
}
|
||||
|
||||
private Point findBestShortcut(Point head, List<Point> possibleMoves) {
|
||||
// Only consider shortcuts when snake is not too long
|
||||
if (snake.getLength() > gridSize * gridSize * 0.75) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Point bestMove = null;
|
||||
int minDistance = Integer.MAX_VALUE;
|
||||
|
||||
for (Point move : possibleMoves) {
|
||||
int distance = estimateDistance(move, food);
|
||||
if (distance < minDistance && isPathSafe(move, food)) {
|
||||
minDistance = distance;
|
||||
bestMove = move;
|
||||
// Additional check for tail collision risk
|
||||
if (!willCollideWithTail(move)) {
|
||||
minDistance = distance;
|
||||
bestMove = move;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMove;
|
||||
}
|
||||
|
||||
private boolean willCollideWithTail(Point move) {
|
||||
// Predict if this move might lead to tail collision in the near future
|
||||
if (snake.getLength() < gridSize * 0.75) {
|
||||
return false; // Not long enough to worry about tail collisions
|
||||
}
|
||||
|
||||
// Check if this move brings us closer to the tail's future position
|
||||
Point tail = snake.getTail();
|
||||
int currentDistToTail = estimateDistance(snake.getHead(), tail);
|
||||
int newDistToTail = estimateDistance(move, tail);
|
||||
|
||||
return newDistToTail < currentDistToTail - 2; // Approaching tail too quickly
|
||||
}
|
||||
|
||||
private boolean isPathSafe(Point from, Point to) {
|
||||
// Simple flood fill to check if path exists
|
||||
// More efficient path safety check with early termination
|
||||
Set<Point> visited = new HashSet<>();
|
||||
Queue<Point> queue = new LinkedList<>();
|
||||
Set<Point> obstacles = new HashSet<>(snake.getBody());
|
||||
@@ -336,7 +459,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
}
|
||||
}
|
||||
|
||||
// If exact next in cycle isn't possible, find the closest
|
||||
// If exact next in cycle isn't possible, find the closest safe move
|
||||
return possibleMoves.stream()
|
||||
.min(Comparator.comparingInt(p -> {
|
||||
int diff = hamiltonianPath[p.y][p.x] - currentIndex;
|
||||
@@ -362,7 +485,7 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
if (newX >= 0 && newX < gridSize && newY >= 0 && newY < gridSize) {
|
||||
Point newPos = new Point(newX, newY);
|
||||
|
||||
// Check if this position is safe
|
||||
// Check if this position is safe (not occupied by snake body, except tail which will move)
|
||||
if (!snake.contains(newPos) || newPos.equals(snake.getTail())) {
|
||||
safeMoves.add(newPos);
|
||||
}
|
||||
@@ -389,11 +512,11 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
|
||||
private void draw(Graphics g) {
|
||||
// Clear background
|
||||
g.setColor(Color.BLACK);
|
||||
g.setColor(new Color(20, 20, 20));
|
||||
g.fillRect(0, 0, getWidth(), getHeight());
|
||||
|
||||
// Draw grid
|
||||
g.setColor(Color.DARK_GRAY);
|
||||
// Draw grid with better visibility
|
||||
g.setColor(new Color(50, 50, 50));
|
||||
for (int i = 0; i <= gridSize; i++) {
|
||||
g.drawLine(i * cellSize, 0, i * cellSize, gridSize * cellSize);
|
||||
g.drawLine(0, i * cellSize, gridSize * cellSize, i * cellSize);
|
||||
@@ -401,40 +524,74 @@ public class HamiltonianSnakeAI extends JPanel implements ActionListener {
|
||||
|
||||
// Draw Hamiltonian path if enabled
|
||||
if (showPath) {
|
||||
g.setColor(new Color(50, 50, 100, 100));
|
||||
g.setColor(new Color(30, 30, 70, 150));
|
||||
for (int y = 0; y < gridSize; y++) {
|
||||
for (int x = 0; x < gridSize; x++) {
|
||||
int pathNum = hamiltonianPath[y][x];
|
||||
g.fillRect(x * cellSize + 1, y * cellSize + 1, cellSize - 2, cellSize - 2);
|
||||
|
||||
if (cellSize > 10) {
|
||||
g.setColor(Color.CYAN);
|
||||
if (cellSize > 15) {
|
||||
g.setColor(new Color(100, 200, 255));
|
||||
g.setFont(new Font("Arial", Font.PLAIN, Math.max(8, cellSize / 3)));
|
||||
String text = String.valueOf(pathNum);
|
||||
String text = String.valueOf(hamiltonianPath[y][x]);
|
||||
FontMetrics fm = g.getFontMetrics();
|
||||
int textX = x * cellSize + (cellSize - fm.stringWidth(text)) / 2;
|
||||
int textY = y * cellSize + (cellSize + fm.getAscent()) / 2;
|
||||
g.drawString(text, textX, textY);
|
||||
g.setColor(new Color(50, 50, 100, 100));
|
||||
g.setColor(new Color(30, 30, 70, 150));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw food
|
||||
g.setColor(Color.RED);
|
||||
// Draw food with better visual
|
||||
GradientPaint foodGradient = new GradientPaint(
|
||||
food.x * cellSize, food.y * cellSize, new Color(255, 50, 50),
|
||||
food.x * cellSize + cellSize, food.y * cellSize + cellSize, new Color(200, 0, 0)
|
||||
);
|
||||
((Graphics2D)g).setPaint(foodGradient);
|
||||
g.fillOval(food.x * cellSize + 2, food.y * cellSize + 2, cellSize - 4, cellSize - 4);
|
||||
g.setColor(new Color(150, 0, 0));
|
||||
g.drawOval(food.x * cellSize + 2, food.y * cellSize + 2, cellSize - 4, cellSize - 4);
|
||||
|
||||
// Draw snake
|
||||
// Draw snake with better visual
|
||||
List<Point> body = snake.getBody();
|
||||
for (int i = 0; i < body.size(); i++) {
|
||||
Point segment = body.get(i);
|
||||
|
||||
// Create gradient for snake segments
|
||||
Color startColor, endColor;
|
||||
if (i == 0) {
|
||||
g.setColor(Color.GREEN); // Head
|
||||
// Head
|
||||
startColor = new Color(100, 255, 100);
|
||||
endColor = new Color(50, 200, 50);
|
||||
} else {
|
||||
g.setColor(new Color(0, 255 - i * 2, 0)); // Body gradient
|
||||
// Body - gradient from head to tail
|
||||
float ratio = (float)i / body.size();
|
||||
startColor = new Color(
|
||||
(int)(100 + 155 * (1 - ratio)),
|
||||
(int)(255 - 155 * ratio),
|
||||
(int)(100 + 155 * (1 - ratio))
|
||||
);
|
||||
endColor = new Color(
|
||||
(int)(50 + 50 * (1 - ratio)),
|
||||
(int)(200 - 150 * ratio),
|
||||
(int)(50 + 50 * (1 - ratio))
|
||||
);
|
||||
}
|
||||
g.fillRect(segment.x * cellSize + 1, segment.y * cellSize + 1, cellSize - 2, cellSize - 2);
|
||||
|
||||
GradientPaint segmentGradient = new GradientPaint(
|
||||
segment.x * cellSize, segment.y * cellSize, startColor,
|
||||
segment.x * cellSize + cellSize, segment.y * cellSize + cellSize, endColor
|
||||
);
|
||||
|
||||
((Graphics2D)g).setPaint(segmentGradient);
|
||||
g.fillRoundRect(segment.x * cellSize + 1, segment.y * cellSize + 1,
|
||||
cellSize - 2, cellSize - 2, 5, 5);
|
||||
|
||||
// Add border to segments
|
||||
g.setColor(new Color(0, 80, 0));
|
||||
g.drawRoundRect(segment.x * cellSize + 1, segment.y * cellSize + 1,
|
||||
cellSize - 2, cellSize - 2, 5, 5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user