Skip to content

Commit c564969

Browse files
committed
Implement server and starter-bot example
1 parent b436b9d commit c564969

File tree

22 files changed

+1305
-28
lines changed

22 files changed

+1305
-28
lines changed

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,4 +255,26 @@ TODO
255255

256256
### Формат лога матча
257257

258-
TODO
258+
```
259+
match
260+
mode {MATCH_MODE}
261+
num_rounds {NUM_ROUNDS}
262+
num_bots {NUM_BOTS}
263+
map_size {MAP_WIDTH} {MAP_HEIGHT}
264+
view_radius {VIEW_RADIUS}
265+
mining_radius {MINING_RADIUS}
266+
attack_radius {ATTACK_RADIUS}
267+
move_time_limit {MOVE_TIME_LIMIT}
268+
bot_name {BOT_ID} {BOT_NAME}
269+
bot {BOT_ID} {X} {Y}
270+
bot_coins {BOT_ID} {NUM_COINS}
271+
block {X} {Y}
272+
coin {X} {Y}
273+
round {ROUND}
274+
bot {BOT_ID} {X} {Y}
275+
bot_coins {BOT_ID} {X} {Y}
276+
attack {BOT_1_ID} {BOT_2_ID}
277+
coin_collected {X} {Y} {BOT_ID}
278+
coin {X} {Y}
279+
match_over {BOT_ID}
280+
```

common/src/main/java/ru/croccode/hypernull/domain/MatchMap.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
public interface MatchMap {
99

10-
int DEFAULT_VIEW_RADIUS = 16;
11-
int DEFAULT_MINING_RADIUS = 3;
12-
int DEFAULT_ATTACK_RADIUS = 5;
10+
int DEFAULT_VIEW_RADIUS = 6;
11+
int DEFAULT_MINING_RADIUS = 2;
12+
int DEFAULT_ATTACK_RADIUS = 4;
1313

1414
Size getSize();
1515

@@ -39,5 +39,5 @@ default int getAttackRadius() {
3939
return DEFAULT_ATTACK_RADIUS;
4040
}
4141

42-
List<Point> getSpawnPoints();
42+
List<Point> getSpawnPositions();
4343
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package ru.croccode.hypernull.io;
2+
3+
import java.io.BufferedReader;
4+
import java.io.BufferedWriter;
5+
import java.io.Closeable;
6+
import java.io.IOException;
7+
import java.io.Reader;
8+
import java.io.Writer;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
import java.util.Objects;
12+
13+
import ru.croccode.hypernull.message.Message;
14+
import ru.croccode.hypernull.message.Messages;
15+
import ru.croccode.hypernull.util.Check;
16+
17+
public class Session implements Closeable {
18+
19+
private final BufferedReader reader;
20+
21+
private final BufferedWriter writer;
22+
23+
private volatile boolean closed;
24+
25+
public Session(Reader reader, Writer writer) {
26+
Check.notNull(reader);
27+
Check.notNull(writer);
28+
29+
this.reader = reader instanceof BufferedReader
30+
? (BufferedReader)reader
31+
: new BufferedReader(reader);
32+
this.writer = writer instanceof BufferedWriter
33+
? (BufferedWriter)writer
34+
: new BufferedWriter(writer);
35+
}
36+
37+
public void write(Message message) throws IOException {
38+
if (message == null)
39+
return;
40+
List<String> lines = Messages.format(message);
41+
for (String line : lines) {
42+
writer.write(line);
43+
writer.write("\n");
44+
}
45+
writer.flush();
46+
}
47+
48+
public <T extends Message> T read(Class<T> type) throws IOException {
49+
Check.notNull(type);
50+
51+
while (true) {
52+
Message message = read();
53+
if (message == null)
54+
return null; // end of stream
55+
if (message.getClass() == type)
56+
return (T)message;
57+
}
58+
}
59+
60+
public Message read() throws IOException {
61+
List<String> lines = new ArrayList<>();
62+
while (true) {
63+
String line = reader.readLine();
64+
if (line == null)
65+
return null;
66+
if (line.isEmpty())
67+
continue;
68+
lines.add(line);
69+
if (Objects.equals(line.trim(), "end")) {
70+
return Messages.parse(lines);
71+
}
72+
}
73+
}
74+
75+
@Override
76+
public void close() throws IOException {
77+
writer.close();
78+
reader.close();
79+
closed = true;
80+
}
81+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package ru.croccode.hypernull.io;
2+
3+
import java.io.IOException;
4+
import java.io.InputStreamReader;
5+
import java.io.OutputStreamWriter;
6+
import java.io.Reader;
7+
import java.io.Writer;
8+
import java.net.Socket;
9+
import java.net.SocketException;
10+
11+
import ru.croccode.hypernull.message.Message;
12+
import ru.croccode.hypernull.util.Check;
13+
14+
public class SocketSession extends Session {
15+
16+
private final Socket socket;
17+
18+
public SocketSession(Socket socket) throws IOException {
19+
super(socketReader(socket), socketWriter(socket));
20+
this.socket = socket;
21+
}
22+
23+
private static Reader socketReader(Socket socket) throws IOException {
24+
Check.notNull(socket);
25+
return new InputStreamReader(socket.getInputStream());
26+
}
27+
28+
private static Writer socketWriter(Socket socket) throws IOException {
29+
Check.notNull(socket);
30+
return new OutputStreamWriter(socket.getOutputStream());
31+
}
32+
33+
public boolean isOpen() {
34+
return socket.isConnected() && !socket.isClosed();
35+
}
36+
37+
@Override
38+
public void close() throws IOException {
39+
if (!socket.isClosed())
40+
socket.close();
41+
}
42+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package ru.croccode.hypernull.util;
2+
3+
import java.util.function.Supplier;
4+
5+
public final class Silent {
6+
7+
private Silent() {
8+
}
9+
10+
public static Runnable runnableOf(Block block) {
11+
return () -> {
12+
try {
13+
block.invoke();
14+
} catch (Exception e) {
15+
e.printStackTrace();
16+
}
17+
};
18+
}
19+
20+
public static <T> Supplier<T> supplierOf(BlockWithResult<T> block) {
21+
return () -> {
22+
try {
23+
return block.invoke();
24+
} catch (Exception e) {
25+
e.printStackTrace();
26+
}
27+
return null;
28+
};
29+
}
30+
31+
@FunctionalInterface
32+
public interface Block {
33+
34+
void invoke() throws Exception;
35+
}
36+
37+
@FunctionalInterface
38+
public interface BlockWithResult<T> {
39+
40+
T invoke() throws Exception;
41+
}
42+
}

server/hypernull.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
server.port=2021

server/pom.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,34 @@
1717
<version>1.0-SNAPSHOT</version>
1818
</dependency>
1919
</dependencies>
20+
<build>
21+
<plugins>
22+
<plugin>
23+
<groupId>org.apache.maven.plugins</groupId>
24+
<artifactId>maven-assembly-plugin</artifactId>
25+
<version>3.1.1</version>
26+
<executions>
27+
<execution>
28+
<id>assembly</id>
29+
<phase>prepare-package</phase>
30+
<goals>
31+
<goal>single</goal>
32+
</goals>
33+
</execution>
34+
</executions>
35+
<configuration>
36+
<archive>
37+
<manifest>
38+
<mainClass>ru.croccode.hypernull.server.HyperNull</mainClass>
39+
</manifest>
40+
</archive>
41+
<descriptorRefs>
42+
<descriptorRef>jar-with-dependencies</descriptorRef>
43+
</descriptorRefs>
44+
<finalName>${project.artifactId}</finalName>
45+
<appendAssemblyId>false</appendAssemblyId>
46+
</configuration>
47+
</plugin>
48+
</plugins>
49+
</build>
2050
</project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package ru.croccode.hypernull.map;
2+
3+
import ru.croccode.hypernull.domain.MatchMap;
4+
5+
public interface MapRegistry {
6+
7+
MatchMap randomMap(int numBots);
8+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package ru.croccode.hypernull.map;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashSet;
5+
import java.util.List;
6+
import java.util.Random;
7+
import java.util.Set;
8+
9+
import ru.croccode.hypernull.domain.MatchMap;
10+
import ru.croccode.hypernull.geometry.Offset;
11+
import ru.croccode.hypernull.geometry.Point;
12+
import ru.croccode.hypernull.geometry.Size;
13+
14+
public class RandomMap implements MatchMap {
15+
16+
private static final int MIN_WIDTH = 48;
17+
private static final int MAX_WIDTH = 80;
18+
private static final int MIN_HEIGHT = 20;
19+
private static final int MAX_HEIGHT = 40;
20+
21+
private static final int MIN_BLOCKS = 6;
22+
private static final int MAX_BLOCKS = 30;
23+
private static final int MIN_BLOCK_WIDTH = 2;
24+
private static final int MAX_BLOCK_WIDTH = 8;
25+
private static final int MIN_BLOCK_HEIGHT = 2;
26+
private static final int MAX_BLOCK_HEIGHT = 8;
27+
28+
private static final int MIN_VIEW_RADIUS = 5;
29+
private static final int MAX_VIEW_RADIUS = 7;
30+
private static final int MIN_ATTACK_RADIUS = 3;
31+
private static final int MAX_ATTACK_RADIUS = 5;
32+
private static final int MIN_MINING_RADIUS = 1;
33+
private static final int MAX_MINING_RADIUS = 3;
34+
35+
private static final Random rnd = new Random(System.currentTimeMillis());
36+
37+
private final Set<Point> blocked = new HashSet<>();
38+
39+
private final Size size;
40+
41+
private final int viewRadius;
42+
43+
private final int miningRadius;
44+
45+
private final int attackRadius;
46+
47+
private final List<Point> spawnPositions;
48+
49+
public RandomMap(int numSpawnPositions) {
50+
size = new Size(
51+
MIN_WIDTH + rnd.nextInt(MAX_WIDTH - MIN_WIDTH),
52+
MIN_HEIGHT + rnd.nextInt(MAX_HEIGHT - MIN_HEIGHT)
53+
);
54+
viewRadius = MIN_VIEW_RADIUS + rnd.nextInt(MAX_VIEW_RADIUS - MIN_VIEW_RADIUS);
55+
attackRadius = Math.min(viewRadius - 1,
56+
MIN_ATTACK_RADIUS + rnd.nextInt(MAX_ATTACK_RADIUS - MIN_ATTACK_RADIUS));
57+
miningRadius = Math.min(attackRadius - 1,
58+
MIN_MINING_RADIUS + rnd.nextInt(MAX_MINING_RADIUS - MIN_MINING_RADIUS));
59+
int n = MIN_BLOCKS + rnd.nextInt(MAX_BLOCKS - MIN_BLOCKS);
60+
for (int i = 0; i < n; i++) {
61+
Point rectPoint = new Point(
62+
rnd.nextInt(size.width()),
63+
rnd.nextInt(size.height())
64+
);
65+
Size rectSize = new Size(
66+
MIN_BLOCK_WIDTH + rnd.nextInt(MAX_BLOCK_WIDTH - MIN_BLOCK_WIDTH),
67+
MIN_BLOCK_HEIGHT + rnd.nextInt(MAX_BLOCK_HEIGHT - MIN_BLOCK_HEIGHT)
68+
);
69+
blockRect(rectPoint, rectSize);
70+
}
71+
spawnPositions = new ArrayList<>();
72+
generate:
73+
while (spawnPositions.size() < numSpawnPositions) {
74+
Point point = new Point(
75+
rnd.nextInt(size.width()),
76+
rnd.nextInt(size.height())
77+
);
78+
if (!blocked.contains(point)) {
79+
int r2 = attackRadius * attackRadius;
80+
for (Point generated : spawnPositions) {
81+
int d2 = point.offsetTo(generated, size).length2();
82+
if (d2 <= r2)
83+
continue generate;
84+
}
85+
spawnPositions.add(point);
86+
}
87+
}
88+
}
89+
90+
private void blockRect(Point point, Size size) {
91+
for (int dx = 0; dx < size.width(); dx++) {
92+
for (int dy = 0; dy < size.height(); dy++) {
93+
blocked.add(point.apply(new Offset(dx, dy), this.size));
94+
}
95+
}
96+
}
97+
98+
@Override
99+
public Size getSize() {
100+
return size;
101+
}
102+
103+
@Override
104+
public boolean isBlocked(Point point) {
105+
return blocked.contains(point);
106+
}
107+
108+
@Override
109+
public int getViewRadius() {
110+
return viewRadius;
111+
}
112+
113+
@Override
114+
public int getMiningRadius() {
115+
return miningRadius;
116+
}
117+
118+
@Override
119+
public int getAttackRadius() {
120+
return attackRadius;
121+
}
122+
123+
@Override
124+
public List<Point> getSpawnPositions() {
125+
return spawnPositions;
126+
}
127+
}

0 commit comments

Comments
 (0)