commit 5555af34cdee3e1d9e1e506718099c5e5ac0f84a
parent f6425cc5d112cbbd4231ad6baa4461a4fda1de0f
Author: A Koens <[email protected]>
Date: Sat, 22 Oct 2022 22:22:30 +0200
Merge branch 'develop' of https://github.com/Akoens/ISYGameClient into develop
Diffstat:
11 files changed, 212 insertions(+), 172 deletions(-)
diff --git a/src/main/java/nl/isygameclient/network/Event.java b/src/main/java/nl/isygameclient/network/Event.java
@@ -0,0 +1,15 @@
+package nl.isygameclient.network;
+
+public class Event {
+ public final EventType type;
+ public final Object data;
+
+ public Event(EventType type, Object data) {
+ this.type = type;
+ this.data = data;
+ }
+
+ public String toString() {
+ return String.format("Event.%s(%s)", type, data);
+ }
+}
+\ No newline at end of file
diff --git a/src/main/java/nl/isygameclient/network/EventParser.java b/src/main/java/nl/isygameclient/network/EventParser.java
@@ -2,7 +2,6 @@ package nl.isygameclient.network;
import java.util.HashMap;
import java.util.LinkedList;
-import nl.isygameclient.network.GameClientBase.ClientException;
public class EventParser {
private final String str;
@@ -18,7 +17,7 @@ public class EventParser {
this.str = str;
}
- public Object parseData() throws ClientException {
+ public Object parseData() throws GameClientException {
try {
stripLeft();
@@ -43,7 +42,7 @@ public class EventParser {
} else if (str.charAt(index) == ',') {
index++;
} else {
- throw new ClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
+ throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
}
}
case '{':
@@ -62,17 +61,17 @@ public class EventParser {
} else if (str.charAt(index) == ',') {
index++;
} else {
- throw new ClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
+ throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
}
}
default:
if (index != 0)
- throw new ClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
+ throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
index = str.length();
return str;
}
} catch (Exception ext) {
- throw new ClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
+ throw new GameClientException(String.format("invalid server response: unexpected '%c' at %d in '%s'", str.charAt(index), index, str));
}
}
diff --git a/src/main/java/nl/isygameclient/network/EventType.java b/src/main/java/nl/isygameclient/network/EventType.java
@@ -0,0 +1,20 @@
+package nl.isygameclient.network;
+
+public enum EventType {
+ PLAYERLIST("PLAYERLIST"),
+ GAMELIST("GAMELIST"),
+ MATCH("GAME MATCH"),
+ YOURTURN("GAME YOURTURN"),
+ MOVE("GAME MOVE"),
+ CHALLENGE_ACCEPTED("GAME CALLENGE ACCEPT"),
+ CHALLENGE("GAME CHALLENGE"),
+ WIN("GAME WIN"),
+ LOSS("GAME LOSS"),
+ DRAW("GAME DRAW");
+
+ public final String identifier;
+
+ EventType(String identifier) {
+ this.identifier = identifier;
+ }
+}
+\ No newline at end of file
diff --git a/src/main/java/nl/isygameclient/network/GameClient.java b/src/main/java/nl/isygameclient/network/GameClient.java
@@ -9,106 +9,6 @@ import java.util.Random;
public class GameClient extends GameClientBase {
private final String name;
- public static class Match {
- private final GameClient client;
- private final String game;
- private final String opponent;
-
- private Event outcome = null;
- private final int[] moves;
- private int pointsSelf = 0;
- private int pointsOther = 0;
-
- private Match(GameClient client, int maxMoves) throws ClientException, IOException, InterruptedException {
- this.client = client;
- this.moves = new int[maxMoves];
-
- @SuppressWarnings("unchecked")
- var data = (Map<String, String>) client.event(-1, EventType.MATCH).data;
-
- this.game = data.get("gametype").toLowerCase();
- this.opponent = data.get("opponent");
- }
-
- public boolean update() throws ClientException, IOException, InterruptedException {
- if (outcome != null)
- return false;
-
- Event event;
- for (;;) {
- event = client.event(-1, EventType.MOVE, EventType.YOURTURN, EventType.WIN, EventType.DRAW, EventType.LOSS);
-
- switch (event.type) {
- case YOURTURN:
- return true;
- case MOVE:
- @SuppressWarnings("unchecked")
- var moveMap = (Map<String, String>) event.data;
-
- moves[Integer.parseInt(moveMap.get("move"))] = moveMap.get("player").equals(client.getName()) ? 1 : -1;
- break;
- case WIN:
- pointsSelf++;
- outcome = event;
- return false;
- case LOSS:
- pointsOther++;
- outcome = event;
- return false;
- default: // loss
- outcome = event;
- return false;
- }
- }
- }
-
- public Integer getMove(int move) {
- return moves[move];
- }
-
- public void abort() throws ClientException, IOException {
- if (outcome == null)
- client.send("forfeit");
- }
-
- public void rematch() throws IOException, ClientException, InterruptedException {
- if (outcome == null)
- return;
-
- Event evt;
- if ((evt = client.event(0.5, EventType.CHALLENGE)) != null) {
- @SuppressWarnings("unchecked")
- var id = ((Map<String, String>) evt.data).get("challengenumber");
- client.send("challenge", "accept", id);
- } else {
- client.send("challenge", opponent, game);
- client.event(-1, EventType.MATCH);
- }
-
- for (int i = 0; i < moves.length; i++)
- moves[i] = 0;
- outcome = null;
- }
-
- public Event getOutcome() {
- return outcome;
- }
-
- public int getPointsSelf() {
- return pointsSelf;
- }
-
- public int getPointsOther() {
- return pointsOther;
- }
-
- public void move(int move) throws IOException, ClientException {
- if (outcome != null)
- return;
- client.send("move " + move);
- }
- }
-
public GameClient(String host, int port, String name) throws UnknownHostException, IOException, InterruptedException {
super(host, port);
@@ -121,42 +21,42 @@ public class GameClient extends GameClientBase {
}
@SuppressWarnings("unchecked")
- public List<String> games() throws IOException, ClientException, InterruptedException {
+ public List<String> games() throws IOException, GameClientException, InterruptedException {
send("get gamelist");
return (List<String>) event(EventType.GAMELIST).data;
}
@SuppressWarnings("unchecked")
- public List<String> players() throws IOException, ClientException, InterruptedException {
+ public List<String> players() throws IOException, GameClientException, InterruptedException {
send("get playerlist");
return (List<String>) event(EventType.GAMELIST).data;
}
- public void login() throws ClientException, IOException {
+ public void login() throws GameClientException, IOException {
send("login", name);
}
- public Match match(int maxMoves, String game, String other) throws IOException, ClientException, InterruptedException {
- send("challenge", other, game);
+ public Match match(GameType game, String other) throws IOException, GameClientException, InterruptedException {
+ send("challenge", other, game.name);
- return new Match(this, maxMoves);
+ return new Match(this);
}
- public Match match(int maxMoves, String game) throws IOException, ClientException, InterruptedException {
- send("subscribe", game);
+ public Match match(GameType game) throws IOException, GameClientException, InterruptedException {
+ send("subscribe", game.name);
- return new Match(this, maxMoves);
+ return new Match(this);
}
- public Match match(int maxMoves) throws IOException, ClientException, InterruptedException {
+ public Match match() throws IOException, GameClientException, InterruptedException {
@SuppressWarnings("unchecked")
var challengeEvent = (Map<String, String>) event(-1, EventType.CHALLENGE).data;
var challengeID = challengeEvent.get("challengenumber");
send("challenge", "accept", challengeID);
- return new Match(this, maxMoves);
+ return new Match(this);
}
public static void main(String[] args) throws Exception {
@@ -166,17 +66,17 @@ public class GameClient extends GameClientBase {
client.login();
System.out.println("connected as " + name);
- var current = client.match(9, "tic-tac-toe");
+ var current = client.match(GameType.TICTACTOE4);
for (;;) {
while (current.update()) {
int move;
do {
- move = new Random().nextInt(9);
+ move = new Random().nextInt(current.getGame().maxMoves);
} while (current.getMove(move) != 0);
current.move(move);
}
- System.out.printf("[%s] %2d:%2d\n", current.getOutcome().type, current.getPointsSelf(), current.getPointsOther());
- if (current.getPointsSelf() >= 25 || current.getPointsOther() >= 25)
+ System.out.printf("[%4s] %02d:%02d\n", current.getOutcome().type, current.getPointsSelf(), current.getPointsOther());
+ if (current.getPointsSelf() >= 10 || current.getPointsOther() >= 10)
break;
current.rematch();
diff --git a/src/main/java/nl/isygameclient/network/GameClientBase.java b/src/main/java/nl/isygameclient/network/GameClientBase.java
@@ -9,47 +9,7 @@ import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.LinkedList;
-public class GameClientBase {
- public static enum EventType {
- PLAYERLIST("PLAYERLIST"),
- GAMELIST("GAMELIST"),
- MATCH("GAME MATCH"),
- YOURTURN("GAME YOURTURN"),
- MOVE("GAME MOVE"),
- ACCEPTED("GAME CALLENGE ACCEPT"),
- CHALLENGE("GAME CHALLENGE"),
- WIN("GAME WIN"),
- LOSS("GAME LOSS"),
- DRAW("GAME DRAW");
-
- public final String identifier;
-
- EventType(String identifier) {
- this.identifier = identifier;
- }
- }
-
- public static class Event {
- public final EventType type;
- public final Object data;
-
- public Event(EventType type, Object data) {
- this.type = type;
- this.data = data;
- }
-
- public String toString() {
- return String.format("Event.%s(%s)", type, data);
- }
- }
-
- public static class ClientException extends Exception {
- public ClientException(String reason) {
- super(reason);
- }
- }
-
-
+public abstract class GameClientBase {
private final Socket socket;
private final OutputStream outputStream;
private final BufferedReader inputBuffer;
@@ -69,7 +29,7 @@ public class GameClientBase {
}
}
- protected void sendQuoted(String command, String... arguments) throws IOException, ClientException {
+ protected void sendQuoted(String command, String... arguments) throws IOException, GameClientException {
for (int i = 0; i < arguments.length; i++)
arguments[i] = '"' + arguments[i] + '"';
@@ -77,7 +37,7 @@ public class GameClientBase {
}
- protected void send(String command, String... arguments) throws IOException, ClientException {
+ protected void send(String command, String... arguments) throws IOException, GameClientException {
var requestBuilder = new StringBuilder(command);
for (var argument : arguments) {
requestBuilder.append(' ');
@@ -91,25 +51,25 @@ public class GameClientBase {
for (;;) {
response = inputBuffer.readLine();
if (response.startsWith("ERR "))
- throw new ClientException(response.substring(4));
+ throw new GameClientException(response.substring(4));
else if (response.startsWith("SVR ")) {
eventLineQueue.add(response);
continue;
} else if (response.equals("OK"))
break;
- throw new ClientException("invalid server response: '" + response + "'");
+ throw new GameClientException("invalid server response: '" + response + "'");
}
}
- public Event event(EventType... target) throws ClientException, IOException, InterruptedException {
+ protected Event event(EventType... target) throws GameClientException, IOException, InterruptedException {
return event(1, true, target);
}
- public Event event(double timeout, EventType... target) throws ClientException, IOException, InterruptedException {
+ protected Event event(double timeout, EventType... target) throws GameClientException, IOException, InterruptedException {
return event(timeout, true, target);
}
- public Event event(double timeout, boolean consume, EventType... target) throws ClientException, IOException, InterruptedException {
+ protected Event event(double timeout, boolean consume, EventType... target) throws GameClientException, IOException, InterruptedException {
var targetList = Arrays.asList(target);
if (!eventQueue.isEmpty()) {
if (target == null)
@@ -136,7 +96,7 @@ public class GameClientBase {
}
if (!line.startsWith("SVR "))
- throw new ClientException("invalid server response: '" + line + "'");
+ throw new GameClientException("invalid server response: '" + line + "'");
result = null;
@@ -148,7 +108,7 @@ public class GameClientBase {
}
if (result == null)
- throw new ClientException("invalid server response: unknown event '" + line + "'");
+ throw new GameClientException("invalid server response: unknown event '" + line + "'");
if (target.length == 0 || target.length > 0 && targetList.contains(result.type)) {
@@ -163,7 +123,7 @@ public class GameClientBase {
return null;
}
- public void close() throws IOException {
+ protected void close() throws IOException {
outputStream.write("logout\n".getBytes());
socket.close();
}
diff --git a/src/main/java/nl/isygameclient/network/GameClientException.java b/src/main/java/nl/isygameclient/network/GameClientException.java
@@ -0,0 +1,7 @@
+package nl.isygameclient.network;
+
+public class GameClientException extends Exception {
+ public GameClientException(String reason) {
+ super(reason);
+ }
+}
+\ No newline at end of file
diff --git a/src/main/java/nl/isygameclient/network/GameType.java b/src/main/java/nl/isygameclient/network/GameType.java
@@ -0,0 +1,24 @@
+package nl.isygameclient.network;
+
+public enum GameType {
+ TICTACTOE("tic-tac-toe", 9),
+ TICTACTOE4("tic-tac-toe-4", 16),
+ REVERSI("reversi", 64, 27, 28, 35, 36);
+
+ public final String name;
+ public final int maxMoves;
+ public final int[] reserved;
+
+ private GameType(String name, int maxMoves, int... reserved) {
+ this.name = name;
+ this.maxMoves = maxMoves;
+ this.reserved = reserved;
+ }
+
+ public static GameType byName(String name) {
+ for (GameType game : GameType.values())
+ if (game.name.equals(name))
+ return game;
+ return null;
+ }
+}
+\ No newline at end of file
diff --git a/src/main/java/nl/isygameclient/network/Match.java b/src/main/java/nl/isygameclient/network/Match.java
@@ -0,0 +1,110 @@
+package nl.isygameclient.network;
+
+import java.io.IOException;
+import java.util.Map;
+
+public class Match {
+ private final GameClient client;
+ private final GameType game;
+ private final String opponent;
+
+ private Event outcome = null;
+ private final int[] moves;
+ private int pointsSelf = 0;
+ private int pointsOther = 0;
+
+ public Match(GameClient client) throws GameClientException, IOException, InterruptedException {
+ this.client = client;
+
+ @SuppressWarnings("unchecked")
+ var data = (Map<String, String>) client.event(-1, EventType.MATCH).data;
+
+ game = GameType.byName(data.get("gametype").toLowerCase());
+ opponent = data.get("opponent");
+ moves = new int[game.maxMoves];
+ for (int reserved : game.reserved)
+ moves[reserved] = 2;
+ }
+
+ public GameType getGame() {
+ return game;
+ }
+
+ public boolean update() throws GameClientException, IOException, InterruptedException {
+ if (outcome != null)
+ return false;
+
+ Event event;
+ for (;;) {
+ event = client.event(-1, EventType.MOVE, EventType.YOURTURN, EventType.WIN, EventType.DRAW, EventType.LOSS);
+
+ switch (event.type) {
+ case YOURTURN:
+ return true;
+ case MOVE:
+ @SuppressWarnings("unchecked")
+ var moveMap = (Map<String, String>) event.data;
+
+ moves[Integer.parseInt(moveMap.get("move"))] = moveMap.get("player").equals(client.getName()) ? 1 : -1;
+ break;
+ case WIN:
+ pointsSelf++;
+ outcome = event;
+ return false;
+ case LOSS:
+ pointsOther++;
+ outcome = event;
+ return false;
+ default: // loss
+ outcome = event;
+ return false;
+ }
+ }
+ }
+
+ public int getMove(int move) {
+ return moves[move];
+ }
+
+ public void abort() throws GameClientException, IOException {
+ if (outcome == null)
+ client.send("forfeit");
+ }
+
+ public void rematch() throws IOException, GameClientException, InterruptedException {
+ if (outcome == null)
+ return;
+
+ Event evt;
+ if ((evt = client.event(0.5, EventType.CHALLENGE)) != null) {
+ @SuppressWarnings("unchecked")
+ var id = ((Map<String, String>) evt.data).get("challengenumber");
+ client.send("challenge", "accept", id);
+ } else {
+ client.send("challenge", opponent, game.name);
+ client.event(-1, EventType.MATCH);
+ }
+
+ for (int i = 0; i < moves.length; i++)
+ moves[i] = 0;
+ outcome = null;
+ }
+
+ public Event getOutcome() {
+ return outcome;
+ }
+
+ public int getPointsSelf() {
+ return pointsSelf;
+ }
+
+ public int getPointsOther() {
+ return pointsOther;
+ }
+
+ public void move(int move) throws IOException, GameClientException {
+ if (outcome != null)
+ return;
+ client.send("move " + move);
+ }
+}
+\ No newline at end of file
diff --git a/target/classes/nl/isygameclient/network/EventParser.class b/target/classes/nl/isygameclient/network/EventParser.class
Binary files differ.
diff --git a/target/classes/nl/isygameclient/network/GameClient.class b/target/classes/nl/isygameclient/network/GameClient.class
Binary files differ.
diff --git a/target/classes/nl/isygameclient/network/GameClientBase.class b/target/classes/nl/isygameclient/network/GameClientBase.class
Binary files differ.