Skip to content

Commit 1a5391e

Browse files
committed
Implement matchlog player
1 parent 481b396 commit 1a5391e

File tree

14 files changed

+1196
-0
lines changed

14 files changed

+1196
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ target/
33
out/
44
.idea/
55
*.iml
6+
matchlogs/

player/pom.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>ru.croccode.hypernull</groupId>
8+
<artifactId>hypernull</artifactId>
9+
<version>1.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>player</artifactId>
12+
<version>1.0-SNAPSHOT</version>
13+
<dependencies>
14+
<dependency>
15+
<groupId>ru.croccode.hypernull</groupId>
16+
<artifactId>common</artifactId>
17+
<version>1.0-SNAPSHOT</version>
18+
</dependency>
19+
</dependencies>
20+
<build>
21+
<plugins>
22+
<plugin>
23+
<groupId>org.apache.maven.plugins</groupId>
24+
<artifactId>maven-assembly-plugin</artifactId>
25+
<version>3.3.0</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.player.App</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>
50+
</project>
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package ru.croccode.hypernull.player;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.net.URL;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.concurrent.Executors;
11+
import java.util.concurrent.ScheduledExecutorService;
12+
import java.util.function.Predicate;
13+
14+
import javafx.application.Application;
15+
import javafx.geometry.Pos;
16+
import javafx.scene.Scene;
17+
import javafx.scene.input.Dragboard;
18+
import javafx.scene.input.TransferMode;
19+
import javafx.scene.layout.Priority;
20+
import javafx.scene.layout.StackPane;
21+
import javafx.scene.layout.VBox;
22+
import javafx.stage.Stage;
23+
import ru.croccode.hypernull.player.controller.PlaybackController;
24+
import ru.croccode.hypernull.player.model.MatchModel;
25+
import ru.croccode.hypernull.player.model.PlaybackModel;
26+
import ru.croccode.hypernull.player.view.InfoView;
27+
import ru.croccode.hypernull.player.view.MatchView;
28+
import ru.croccode.hypernull.player.view.PlaybackView;
29+
30+
public class App extends Application {
31+
32+
public static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);
33+
34+
private final MatchModel matchModel;
35+
private final PlaybackModel playbackModel;
36+
private final PlaybackController playbackController;
37+
38+
public static void main(String[] args) {
39+
launch(args);
40+
}
41+
42+
public App() {
43+
matchModel = new MatchModel();
44+
playbackModel = new PlaybackModel();
45+
playbackController = new PlaybackController(matchModel, playbackModel);
46+
}
47+
48+
@Override
49+
public void start(Stage primaryStage) {
50+
51+
// info pane
52+
InfoView infoView = new InfoView(matchModel, playbackModel);
53+
StackPane infoPane = new StackPane(infoView);
54+
infoPane.setMinHeight(60);
55+
infoPane.setAlignment(Pos.CENTER_LEFT);
56+
infoPane.setPadding(Styles.defaultPadding());
57+
58+
// canvas pane
59+
MatchView matchView = new MatchView(matchModel, playbackModel);
60+
StackPane canvasPane = matchView;
61+
canvasPane.setMinSize(200, 200);
62+
63+
// playback pane
64+
PlaybackView playbackView = new PlaybackView(matchModel, playbackModel);
65+
StackPane playbackPane = new StackPane(playbackView);
66+
playbackPane.setMinHeight(50);
67+
playbackPane.setBackground(Styles.solidBackground(Styles.CLEAR_COLOR.brighter()));
68+
playbackPane.setPadding(Styles.defaultPadding());
69+
StackPane.setAlignment(playbackView, Pos.CENTER);
70+
71+
// layout
72+
VBox vbox = new VBox(infoPane, canvasPane, playbackPane);
73+
vbox.setFillWidth(true);
74+
VBox.setVgrow(canvasPane, Priority.ALWAYS);
75+
vbox.setBackground(Styles.solidBackground(Styles.CLEAR_COLOR));
76+
vbox.setOnDragOver(e -> {
77+
if (e.getGestureSource() != vbox && e.getDragboard().hasFiles()) {
78+
e.acceptTransferModes(TransferMode.COPY);
79+
}
80+
e.consume();
81+
});
82+
vbox.setOnDragDropped(e -> {
83+
Dragboard board = e.getDragboard();
84+
boolean done = false;
85+
if (board.hasFiles()) {
86+
try {
87+
queueReplays(board.getFiles());
88+
} catch (IOException ex) {
89+
ex.printStackTrace();
90+
}
91+
done = true;
92+
}
93+
e.setDropCompleted(done);
94+
e.consume();
95+
});
96+
97+
Scene scene = new Scene(vbox);
98+
URL style = getClass().getResource("/style.css");
99+
if (style != null) {
100+
scene.getStylesheets().add(style.toExternalForm());
101+
}
102+
primaryStage.setTitle("¤ HyperNull Player ¤");
103+
primaryStage.setScene(scene);
104+
primaryStage.setMinWidth(800);
105+
primaryStage.setMinHeight(600);
106+
primaryStage.show();
107+
}
108+
109+
@Override
110+
public void stop() {
111+
SCHEDULER.shutdownNow();
112+
}
113+
114+
private void queueReplays(List<File> files) throws IOException {
115+
Predicate<Path> isReplay = f -> Files.isRegularFile(f)
116+
&& f.getFileName().toString().toLowerCase().endsWith(".log");
117+
List<Path> paths = new ArrayList<>();
118+
if (files != null) {
119+
for (File file : files) {
120+
Path path = file.toPath();
121+
if (!Files.exists(path))
122+
continue;
123+
if (Files.isDirectory(path)) {
124+
Files.walk(path)
125+
.filter(isReplay)
126+
.forEach(paths::add);
127+
} else {
128+
if (isReplay.test(path))
129+
paths.add(path);
130+
}
131+
}
132+
}
133+
matchModel.queueReplays(paths);
134+
}
135+
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package ru.croccode.hypernull.player;
2+
3+
import java.io.InputStream;
4+
import java.util.Scanner;
5+
6+
import ru.croccode.hypernull.domain.MatchMode;
7+
import ru.croccode.hypernull.geometry.Point;
8+
import ru.croccode.hypernull.geometry.Size;
9+
import ru.croccode.hypernull.player.model.MatchLog;
10+
import ru.croccode.hypernull.util.Check;
11+
import ru.croccode.hypernull.util.Strings;
12+
13+
public final class MatchLogParser {
14+
15+
private MatchLogParser() {
16+
}
17+
18+
public static MatchLog parse(InputStream in) {
19+
Check.notNull(in);
20+
21+
MatchLog match = new MatchLog();
22+
MatchLog.Round round = new MatchLog.Round(); // round zero
23+
round.setRound(0);
24+
match.getRounds().put(0, round);
25+
Scanner s = new Scanner(in);
26+
while (s.hasNext()) {
27+
String line = s.nextLine();
28+
if (line != null)
29+
line = line.trim();
30+
if (Strings.isNullOrEmpty(line) || line.startsWith("#"))
31+
continue;
32+
String[] tokens = line.split(" ");
33+
String key = tokens[0];
34+
35+
switch (key) {
36+
case "match":
37+
// do nothing
38+
break;
39+
40+
case "match_id":
41+
match.setMatchId(tokens[1]);
42+
break;
43+
44+
case "num_bots":
45+
match.setNumBots(Integer.parseInt(tokens[1]));
46+
break;
47+
48+
case "mode":
49+
match.setMode(MatchMode.valueOf(tokens[1]));
50+
break;
51+
52+
case "num_rounds":
53+
match.setNumRounds(Integer.parseInt(tokens[1]));
54+
break;
55+
56+
case "random_seed":
57+
case "move_time_limit":
58+
case "coin_spawn_period":
59+
case "coin_spawn_volume":
60+
// ignore
61+
break;
62+
63+
case "map_size":
64+
match.setMapSize(new Size(
65+
Integer.parseInt(tokens[1]),
66+
Integer.parseInt(tokens[2])));
67+
break;
68+
69+
case "view_radius":
70+
match.setViewRadius(Integer.parseInt(tokens[1]));
71+
break;
72+
73+
case "mining_radius":
74+
match.setMiningRadius(Integer.parseInt(tokens[1]));
75+
break;
76+
77+
case "attack_radius":
78+
match.setAttackRadius(Integer.parseInt(tokens[1]));
79+
break;
80+
81+
case "block":
82+
match.getBlocks().add(new Point(
83+
Integer.parseInt(tokens[1]),
84+
Integer.parseInt(tokens[2])));
85+
break;
86+
87+
case "bot_name":
88+
match.getBotNames().put(
89+
Integer.parseInt(tokens[1]),
90+
tokens[2]);
91+
break;
92+
93+
case "bot":
94+
Point bot = new Point(
95+
Integer.parseInt(tokens[2]),
96+
Integer.parseInt(tokens[3]));
97+
round.getBots().put(Integer.parseInt(tokens[1]), bot);
98+
break;
99+
100+
case "bot_coins":
101+
round.getBotCoins().put(
102+
Integer.parseInt(tokens[1]),
103+
Integer.parseInt(tokens[2]));
104+
break;
105+
106+
case "coin":
107+
Point coin = new Point(
108+
Integer.parseInt(tokens[1]),
109+
Integer.parseInt(tokens[2]));
110+
round.getCoins().add(coin);
111+
break;
112+
113+
case "coin_collected":
114+
Point collected = new Point(
115+
Integer.parseInt(tokens[1]),
116+
Integer.parseInt(tokens[2]));
117+
round.getCoins().remove(collected);
118+
round.getCollectedCoins().add(collected);
119+
break;
120+
121+
case "round":
122+
MatchLog.Round nextRound = new MatchLog.Round();
123+
nextRound.getBots().putAll(round.getBots());
124+
nextRound.getBotCoins().putAll(round.getBotCoins());
125+
nextRound.getDeadBots().addAll(round.getDeadBots());
126+
nextRound.getCoins().addAll(round.getCoins());
127+
round = nextRound;
128+
int num = Integer.parseInt(tokens[1]);
129+
round.setRound(num);
130+
match.getRounds().put(num, round);
131+
break;
132+
133+
case "attack":
134+
round.getDeadBots().add(Integer.parseInt(tokens[2]));
135+
break;
136+
137+
case "match_over":
138+
// do nothing
139+
break;
140+
}
141+
}
142+
143+
return match;
144+
}
145+
}

0 commit comments

Comments
 (0)