To illustrate what SpiderPig was getting at (and at the fear of not making it any more readable), you can use a stream of integers:
return IntStream.rangeClosed(1, rounds).boxed()
.reduce(CompletableFuture.completedFuture(null), (previous, t) -> previous.thenCompose(x -> playRound(t)), (a, b) -> a.thenCombine(b, (x, y) -> y));
The Java Streams API wasn't designed for this sort of use case though (which is why reduce is the only option you get). If for whatever reason your stream is run in parallel, it won't work (that's when the combine method is called).
You can use a different library like vavr to do a fold instead:
return List.rangeClosed(1, rounds)
.foldLeft(CompletableFuture.completedFuture(null), (previous, t) -> previous.thenCompose(x -> playRound(t)));
And if you already have vavr and are able to use a different type of return value, just use their Future
:
return List.rangeClosed(1, rounds)
.foldLeft(Future.successful(null), (previous, t) -> previous.flatMap(x -> playRound(t)));
See here for the differences between reduce and foldLeft.