-
Notifications
You must be signed in to change notification settings - Fork 2
/
day_6.pde
209 lines (161 loc) · 4.7 KB
/
day_6.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
import java.util.List;
// The radius of the circles
int minRadius = 50;
int maxRadius = 190;
// The circles are represented as a 2D array wrapped onto itself
// When the snake go over cols in x direction, it loops to 0
int cols = 100;
int rows = 10;
// The initial sizes of the snakes
int minSnakeSize = 10;
int maxSnakeSize = cols / 2;
float turnPercent = 0.03;
List<Snake> snakes;
/*
* A simple wrapper of a point
* I made this instead of PVector because of float approximation
*/
class Location {
int x, y;
Location(int x, int y) {
this.x = x;
this.y = y;
}
Location copy() {
return new Location(x, y);
}
void add(int xOffset, int yOffset) {
x += xOffset;
y += yOffset;
}
}
/*
* The Snake class is inspired from the game snake
* https://en.wikipedia.org/wiki/Snake_(video_game_genre)
*/
class Snake {
// Stores a list of positions
List<Location> positions;
// value to prevent the snake to turn twice
boolean justTurned = false;
Snake(int x, int y, int initialSize) {
positions = new ArrayList<Location>();
positions.add(new Location(x, y));
// Add the pieces
for (int i = 1; i < initialSize; i++) {
addPiece();
}
}
void display() {
noFill();
stroke(0);
strokeWeight(8);
beginShape();
for (int i = 0; i < positions.size(); i++) {
Location pos = positions.get(i);
// The x location is on the circle
float angle = pos.x * (TWO_PI / cols);
// The y location is on the rings
float radius = pos.y * ((maxRadius - minRadius) / rows) + minRadius;
// Vary the stroke with the length
stroke(0, map(i, 0, positions.size() - 1, 255, 0));
vertex(cos(angle) * radius, sin(angle) * radius);
}
endShape();
}
// Shift all the pieces of the snake
void update() {
for (int i = positions.size() - 1; i >= 1; i--) {
positions.set(i, positions.get(i - 1).copy());
}
}
void moveForward() {
Location head = getHead();
head.add(1, 0);
if (head.x >= cols) head.x = 0;
justTurned = false;
}
void moveInside() {
positions.get(0).add(0, -1);
justTurned = true;
}
void moveOutside() {
positions.get(0).add(0, 1);
justTurned = true;
}
Location getHead() {
return positions.get(0);
}
Location getTail() {
return positions.get(positions.size() - 1);
}
// Add a piece at the end of the snake
void addPiece() {
Location tail = getTail();
int newX = tail.x - 1;
if (newX < 0) newX = cols - 1;
positions.add(new Location(newX, tail.y));
}
}
void setup() {
size(500, 500, P2D);
snakes = new ArrayList<Snake>();
for (int i = 0; i < rows; i++) {
int randomPosition = int(random(maxSnakeSize, cols));
int randomSize = int(random(minSnakeSize, maxSnakeSize));
snakes.add(new Snake(randomPosition, i, randomSize));
}
}
// Display the tracks
void displayCircles() {
noFill();
stroke(180, 100);
strokeWeight(2);
for (float radius = minRadius; radius < maxRadius; radius += (maxRadius - minRadius) / rows) {
circle(width / 2, height / 2, radius * 2);
}
}
void draw() {
background(255);
// In order to optimize the colision detection
// Create a 2D boolean map of the current configuration
// true when there's a snake on a cell, false otherwise
boolean[][] map = new boolean[cols][rows];
for (Snake snake : snakes) {
for (Location loc : snake.positions) {
map[loc.x][loc.y] = true;
}
}
translate(width / 2, height / 2);
// Go through every snake
for (int i = 0; i < snakes.size(); i++) {
Snake snake = snakes.get(i);
// Display and update the position of the snake
snake.display();
snake.update();
Location head = snake.getHead();
boolean canTurnInside, canTurnOutside;
int insideY = head.y - 1;
if (insideY < 0) insideY = rows - 1;
// Test if the snake can turn inside
if (head.y == 0) canTurnInside = false;
else canTurnInside = !map[head.x][insideY];
// Test if snake can turn outside
if (head.y == rows - 1) canTurnOutside = false;
else canTurnOutside = !map[head.x][(head.y + 1) % rows];
boolean obstacleInFront = map[(head.x + 1) % cols][head.y];
// Turn if there's an obstacle or randomly (if the snake didn't just turned before)
if ((random(1) > (1 - turnPercent) && !snake.justTurned) || obstacleInFront) {
if (canTurnInside && canTurnOutside) {
if (random(1) < 0.5) snake.moveInside();
else snake.moveOutside();
} else if (canTurnInside) {
snake.moveInside();
} else if (canTurnOutside) {
snake.moveOutside();
}
} else { // Otherwise move forward
snake.moveForward();
}
}
}