Skip to content

Commit

Permalink
Matchday and now also Round interfaces, no inheritance just delegation
Browse files Browse the repository at this point in the history
  • Loading branch information
pfichtner committed Nov 10, 2023
1 parent 4975dc5 commit 54699bc
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 100 deletions.
86 changes: 86 additions & 0 deletions core/src/main/java/org/ase/fourwins/season/DefaultRound.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package org.ase.fourwins.season;

import static java.lang.Math.max;
import static java.util.stream.Stream.iterate;

import java.util.Collection;
import java.util.List;
import java.util.stream.Stream;

import org.ase.fourwins.util.ListDelegate;

import lombok.Value;

@Value
public class DefaultRound<T> implements Round<T> {

/**
* When accessing this list at a certain index the result is not the element at
* that index but the element next to it (better said n-elements, see
* {@link #shiftBy}. This does <b>not</b> apply for the element at index
* <code>0</code>.
* <p>
* So all elements but the first elements are rotated by {@link #shiftBy}.
* <p>
* Example: When accessing the underlying list with elements A,B,C,D via
* {@link #get(int)} you will get the results as follow:
*
* <pre>
* given {@link #shiftBy} is <code>1</code>
* index 0 -> index 0 (A) (element at index <code>0</code> is always fix)
* index 1 -> index 2 (C)
* index 2 -> index 3 (D)
* index 3 -> index 1 (B)
*
* given {@link #shiftBy} is <code>2</code>
* index 0 -> index 0 (A) (element at index <code>0</code> is always fix)
* index 1 -> index 3 (D)
* index 2 -> index 1 (B)
* index 3 -> index 2 (C)
* </pre>
*
*/
private static final class RotatedLeagueList<T> extends ListDelegate<T> {

/** how far should the shifting be done */
private final int shiftBy;

private RotatedLeagueList(List<T> delegate) {
this(delegate, 1);
}

private RotatedLeagueList(List<T> delegate, int shiftBy) {
// constructor is optimized by doing decomposition, this is pure performance
// optimization only, could be removed without changing behavior. Just done for
// fun (and no profit) :D
super(delegate instanceof RotatedLeagueList ? ((RotatedLeagueList<T>) delegate).getDelegate() : delegate);
this.shiftBy = delegate instanceof RotatedLeagueList ? ((RotatedLeagueList<T>) delegate).shiftBy + shiftBy
: shiftBy;
}

@Override
public T get(int index) {
return super.get(index == 0 ? 0 : shiftedIndex(index));
}

private int shiftedIndex(int index) {
return (shiftBy + index - 1) % (size() - 1) + 1;
}

}

List<T> teams;

public DefaultRound(List<T> teams) {
this.teams = List.copyOf(teams);
}

public Stream<Matchday<T>> getMatchdays() {
return iterate(teams, RotatedLeagueList<T>::new).limit(matchdays(teams)).map(DefaultMatchday::new);
}

private static int matchdays(Collection<?> teams) {
return max(0, teams.size() - 1);
}

}
86 changes: 2 additions & 84 deletions core/src/main/java/org/ase/fourwins/season/Round.java
Original file line number Diff line number Diff line change
@@ -1,89 +1,7 @@
package org.ase.fourwins.season;

import static java.lang.Math.max;
import static java.util.stream.Stream.iterate;

import java.util.Collection;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import org.ase.fourwins.util.ListDelegate;

import lombok.Value;

@Value
public class Round<T> {

/**
* When accessing this list at a certain index the result is not the element at
* that index but the element next to it (better said n-elements, see
* {@link #shiftBy}. This does <b>not</b> apply for the element at index
* <code>0</code>.
* <p>
* So all elements but the first elements are rotated by {@link #shiftBy}.
* <p>
* Example: When accessing the underlying list with elements A,B,C,D via
* {@link #get(int)} you will get the results as follow:
*
* <pre>
* given {@link #shiftBy} is <code>1</code>
* index 0 -> index 0 (A) (element at index <code>0</code> is always fix)
* index 1 -> index 2 (C)
* index 2 -> index 3 (D)
* index 3 -> index 1 (B)
*
* given {@link #shiftBy} is <code>2</code>
* index 0 -> index 0 (A) (element at index <code>0</code> is always fix)
* index 1 -> index 3 (D)
* index 2 -> index 1 (B)
* index 3 -> index 2 (C)
* </pre>
*
*/
private static final class RotatedLeagueList<T> extends ListDelegate<T> {

/** how far should the shifting be done */
private final int shiftBy;

private RotatedLeagueList(List<T> delegate) {
this(delegate, 1);
}

private RotatedLeagueList(List<T> delegate, int shiftBy) {
// constructor is optimized by doing decomposition, this is pure performance
// optimization only, could be removed without changing behavior. Just done for
// fun (and no profit) :D
super(delegate instanceof RotatedLeagueList ? ((RotatedLeagueList<T>) delegate).getDelegate() : delegate);
this.shiftBy = delegate instanceof RotatedLeagueList ? ((RotatedLeagueList<T>) delegate).shiftBy + shiftBy
: shiftBy;
}

@Override
public T get(int index) {
return super.get(index == 0 ? 0 : shiftedIndex(index));
}

private int shiftedIndex(int index) {
return (shiftBy + index - 1) % (size() - 1) + 1;
}

}

List<T> teams;
Function<List<T>, Matchday<T>> matchdayMaker;

public Round(List<T> teams, Function<List<T>, Matchday<T>> matchdayMaker) {
this.matchdayMaker = matchdayMaker;
this.teams = List.copyOf(teams);
}

public Stream<Matchday<T>> getMatchdays() {
return iterate(teams, RotatedLeagueList<T>::new).limit(matchdays(teams)).map(matchdayMaker);
}

private static int matchdays(Collection<?> teams) {
return max(0, teams.size() - 1);
}

public interface Round<T> {
Stream<Matchday<T>> getMatchdays();
}
38 changes: 22 additions & 16 deletions core/src/main/java/org/ase/fourwins/season/Season.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,31 @@
import java.util.stream.Stream;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Value;

public class Season<T> {

@RequiredArgsConstructor
private static final class ReversedMatchday<U> implements Matchday<U> {
private final Matchday<U> delegate;
@Value
private static final class ReversedRound<U> implements Round<U> {

public Stream<Match<U>> getMatches() {
return delegate.getMatches().map(Match::reverse);
@Value
private static final class ReversedMatchday<V> implements Matchday<V> {

Matchday<V> delegate;

public Stream<Match<V>> getMatches() {
return delegate.getMatches().map(Match::reverse);
}

}

Round<U> delegate;

@Override
public Stream<Matchday<U>> getMatchdays() {
return delegate.getMatchdays().map(ReversedMatchday<U>::new);
}

}

@Getter
Expand All @@ -26,16 +40,8 @@ public Stream<Match<U>> getMatches() {
public Season(List<T> teams) {
List<T> unmodifableTeams = List.copyOf(teams);
verifySizeIsEven(unmodifableTeams);
this.firstRound = new Round<T>(unmodifableTeams, Season::matchday);
this.secondRound = new Round<T>(unmodifableTeams, Season::reversedMatchday);
}

private static <U> Matchday<U> matchday(List<U> teams) {
return new DefaultMatchday<U>(teams);
}

private static <U> Matchday<U> reversedMatchday(List<U> teams) {
return new ReversedMatchday<U>(matchday(teams));
this.firstRound = new DefaultRound<T>(unmodifableTeams);
this.secondRound = new ReversedRound<T>(this.firstRound);
}

private void verifySizeIsEven(Collection<T> teams) {
Expand Down

0 comments on commit 54699bc

Please sign in to comment.