// Path: src/main/java/pl/setblack/pongi/games/api/GameInfo.java
// @Immutable
// @JsonDeserialize
// public class GameInfo implements Serializable {
//     private static final long serialVersionUID = 1L;
//     public final String name;
// 
//     public final String uuid;
// 
//     public final List<String> players;
// 
//     @JsonCreator
//     public GameInfo(String name, String uuid, List<String> players) {
//         this.name = name;
//         this.uuid = uuid;
//         this.players = players;
//     }
// 
//     public GameInfo(String name, String uuid, String player1) {
//         this(name, uuid, List.of(player1));
//     }
// 
//     public Option<GameInfo> withPlayer(String userId) {
//         if (this.players.contains(userId)) {
//             return Option.some(this);
//         } else if (this.players.size() <= 1) {
//             return Option.some(new GameInfo(this.name, this.uuid, this.players.append(userId)));
//         } else {
//             return Option.none();
//         }
// 
//     }
// 
//     @Override
//     public String toString() {
//         return "GameInfo{" +
//                 "name='" + name + '\'' +
//                 ", uuid='" + uuid + '\'' +
//                 ", players=" + players +
//                 '}';
//     }
// }
// 
// Path: src/main/java/pl/setblack/pongi/games/api/GameState.java
// @Immutable
// @JsonDeserialize
// public class GameState implements Serializable {
//     private static final long serialVersionUID = 1L;
//     public final GamePhase phase;
//     public final Ball ball;
//     public final Players players;
//     public final long updateTime;
// 
// 
//     @JsonCreator
//     public GameState(
//             final Ball ball,
//             final Players players,
//             final long updateTime) {
//         this.ball = ball;
//         this.players = players;
//         this.updateTime = updateTime;
//         this.phase = players.phaseFromScore();
//     }
// 
// 
//     public static Option<GameState> startFrom(
//             GameInfo info,
//             long startTime,
//             final Random rnd) {
// 
//         if (info.players.size() == 2) {
//             final Ball ball = new Ball(0.5f, 0.5f);
//             final Player player1 = new Player(0, info.players.get(0), Paddle.createPaddleForPlayer(1));
//             final Player player2 = new Player(0, info.players.get(1), Paddle.createPaddleForPlayer(2));
//             return Option.some(new GameState(ball, Players.of(player1, player2), startTime).start(startTime, rnd));
//         } else {
//             return Option.none();
//         }
//     }
// 
// 
//     private GameState start(long startTime, final Random rnd) {
// 
//         return new GameState(
//                 Ball.randomDirection(rnd),
//                 this.players,
//                 startTime);
// 
//     }
// 
//     public GameState push(long newTime, final Random rnd) {
//         if (this.phase == GamePhase.STARTED) {
//             long diff = newTime - this.updateTime;
//             float scale = diff / GameParams.RELATIVE_SPEED;
//             final Tuple2<Ball, Players> newPositions = this.ball
//                     .move(scale)
//                     .bounce(this.players, rnd);
//             final Function<Player, Player> movePaddle = player -> player.movePaddle(diff);
//             final Players newPlayers = newPositions._2.map(movePaddle);
// 
//             return new GameState(newPositions._1, newPlayers, newTime);
//         } else {
//             return this;
//         }
// 
//     }
// 
// 
//     public GameState playerMovingTo(String userId, float targetY) {
//         final Function<Player, Player> movePaddle = player -> player.makeMoving(userId, targetY);
//         final Players newPlayers = this.players.map(movePaddle);
//         return new GameState(this.ball, newPlayers, this.updateTime);
//     }
// 
//     @Override
//     public String toString() {
//         return "GameState{" +
//                 "ball=" + ball +
//                 ", players=" + players +
//                 ", updateTime=" + updateTime +
//                 '}';
//     }
// }


import javaslang.collection.HashMap;
import javaslang.collection.Map;
import javaslang.collection.Seq;
import javaslang.control.Option;
import pl.setblack.pongi.games.api.GameInfo;
import pl.setblack.pongi.games.api.GameState;
import java.io.Serializable;
import java.time.Clock;
import java.util.Random;
    }

    @Override
    public Seq<GameInfo> listGames() {
        return allGamesInfo.values();
    }

    public Option<GameState> startNewGame(final GameInfo info) {
        final Option<GameState> currentState =
                this.allGamesState.get(info.uuid);
        return currentState.orElse(() -> {
            Option<GameState> newState = GameState.startFrom(info, this.clock.millis(), this.random);
            newState.forEach(s -> this.allGamesState = this.allGamesState.put(info.uuid, s));
            return newState;
        });

    }

    @Override
    public Option<GameState> joinGame(final String uuid, final String userId) {
        return this.allGamesInfo.get(uuid)
                .flatMap(g -> g.withPlayer(userId))
                .flatMap(g -> {
                    this.allGamesInfo = this.allGamesInfo.put(uuid, g);

                    return startNewGame(g);
                });
    }

    @Override
// Path: src/main/java/pl/setblack/pongi/games/api/GameInfo.java
// @Immutable
// @JsonDeserialize
// public class GameInfo implements Serializable {
//     private static final long serialVersionUID = 1L;
//     public final String name;
// 
//     public final String uuid;
// 
//     public final List<String> players;
// 
//     @JsonCreator
//     public GameInfo(String name, String uuid, List<String> players) {
//         this.name = name;
//         this.uuid = uuid;
//         this.players = players;
//     }
// 
//     public GameInfo(String name, String uuid, String player1) {
//         this(name, uuid, List.of(player1));
//     }
// 
//     public Option<GameInfo> withPlayer(String userId) {
//         if (this.players.contains(userId)) {
//             return Option.some(this);
//         } else if (this.players.size() <= 1) {
//             return Option.some(new GameInfo(this.name, this.uuid, this.players.append(userId)));
//         } else {
//             return Option.none();
//         }
// 
//     }
// 
//     @Override
//     public String toString() {
//         return "GameInfo{" +
//                 "name='" + name + '\'' +
//                 ", uuid='" + uuid + '\'' +
//                 ", players=" + players +
//                 '}';
//     }
// }
// 
// Path: src/main/java/pl/setblack/pongi/games/api/GameState.java
// @Immutable
// @JsonDeserialize
// public class GameState implements Serializable {
//     private static final long serialVersionUID = 1L;
//     public final GamePhase phase;
//     public final Ball ball;
//     public final Players players;
//     public final long updateTime;
// 
// 
//     @JsonCreator
//     public GameState(
//             final Ball ball,
//             final Players players,
//             final long updateTime) {
//         this.ball = ball;
//         this.players = players;
//         this.updateTime = updateTime;
//         this.phase = players.phaseFromScore();
//     }
// 
// 
//     public static Option<GameState> startFrom(
//             GameInfo info,
//             long startTime,
//             final Random rnd) {
// 
//         if (info.players.size() == 2) {
//             final Ball ball = new Ball(0.5f, 0.5f);
//             final Player player1 = new Player(0, info.players.get(0), Paddle.createPaddleForPlayer(1));
//             final Player player2 = new Player(0, info.players.get(1), Paddle.createPaddleForPlayer(2));
//             return Option.some(new GameState(ball, Players.of(player1, player2), startTime).start(startTime, rnd));
//         } else {
//             return Option.none();
//         }
//     }
// 
// 
//     private GameState start(long startTime, final Random rnd) {
// 
//         return new GameState(
//                 Ball.randomDirection(rnd),
//                 this.players,
//                 startTime);
// 
//     }
// 
//     public GameState push(long newTime, final Random rnd) {
//         if (this.phase == GamePhase.STARTED) {
//             long diff = newTime - this.updateTime;
//             float scale = diff / GameParams.RELATIVE_SPEED;
//             final Tuple2<Ball, Players> newPositions = this.ball
//                     .move(scale)
//                     .bounce(this.players, rnd);
//             final Function<Player, Player> movePaddle = player -> player.movePaddle(diff);
//             final Players newPlayers = newPositions._2.map(movePaddle);
// 
//             return new GameState(newPositions._1, newPlayers, newTime);
//         } else {
//             return this;
//         }
// 
//     }
// 
// 
//     public GameState playerMovingTo(String userId, float targetY) {
//         final Function<Player, Player> movePaddle = player -> player.makeMoving(userId, targetY);
//         final Players newPlayers = this.players.map(movePaddle);
//         return new GameState(this.ball, newPlayers, this.updateTime);
//     }
// 
//     @Override
//     public String toString() {
//         return "GameState{" +
//                 "ball=" + ball +
//                 ", players=" + players +
//                 ", updateTime=" + updateTime +
//                 '}';
//     }
// }


// Path: src/main/java/pl/setblack/pongi/games/repo/GamesRepositoryInMemory.java
import javaslang.collection.HashMap;
import javaslang.collection.Map;
import javaslang.collection.Seq;
import javaslang.control.Option;
import pl.setblack.pongi.games.api.GameInfo;
import pl.setblack.pongi.games.api.GameState;
import java.io.Serializable;
import java.time.Clock;
import java.util.Random;

    }

    @Override
    public Seq<GameInfo> listGames() {
        return allGamesInfo.values();
    }

    public Option<GameState> startNewGame(final GameInfo info) {
        final Option<GameState> currentState =
                this.allGamesState.get(info.uuid);
        return currentState.orElse(() -> {
            Option<GameState> newState = GameState.startFrom(info, this.clock.millis(), this.random);
            newState.forEach(s -> this.allGamesState = this.allGamesState.put(info.uuid, s));
            return newState;
        });

    }

    @Override
    public Option<GameState> joinGame(final String uuid, final String userId) {
        return this.allGamesInfo.get(uuid)
                .flatMap(g -> g.withPlayer(userId))
                .flatMap(g -> {
                    this.allGamesInfo = this.allGamesInfo.put(uuid, g);

                    return startNewGame(g);
                });
    }

    @Override
    public Option<GameState> getGame(final String uuid) {